diff options
author | lookshe <github@lookshe.org> | 2015-08-11 17:35:53 +0200 |
---|---|---|
committer | lookshe <github@lookshe.org> | 2015-08-11 17:35:53 +0200 |
commit | 8fd688ca96005152be754eeba1be72c7c0aab9ad (patch) | |
tree | c4d2e3dfe7250a6794aaa3fa9816ad3da70a813e /src/main | |
parent | 553f41a2dbc068a8a43f613e088713cd3ec7680e (diff) |
Merge tag '1.5.2' into trz/rebasetrz/1.5.2.1
Diffstat (limited to '')
413 files changed, 7126 insertions, 2701 deletions
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index e4e91d80..9fe37017 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -122,6 +122,13 @@ <data android:mimeType="*/*" /> </intent-filter> + <intent-filter> + <action android:name="android.intent.action.SEND_MULTIPLE" /> + + <category android:name="android.intent.category.DEFAULT" /> + + <data android:mimeType="image/*" /> + </intent-filter> </activity> <activity android:name="de.duenndns.ssl.MemorizingActivity" diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index fa9345a2..00225f7b 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -11,6 +11,7 @@ public final class Config { public static final int PING_MAX_INTERVAL = 300; public static final int PING_MIN_INTERVAL = 30; public static final int PING_TIMEOUT = 10; + public static final int SOCKET_TIMEOUT = 15; public static final int CONNECT_TIMEOUT = 90; public static final int CARBON_GRACE_PERIOD = 60; public static final int MINI_GRACE_PERIOD = 750; @@ -30,6 +31,10 @@ public final class Config { public static final boolean NO_PROXY_LOOKUP = false; //useful to debug ibb public static final boolean DISABLE_STRING_PREP = false; // setting to true might increase startup performance + public static final boolean EXTENDED_SM_LOGGING = true; // log stanza counts + public static final boolean RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE = true; //setting to true might increase power consumption + + public static final boolean ENCRYPT_ON_HTTP_UPLOADED = false; public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000; public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2; diff --git a/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java b/src/main/java/eu/siacs/conversations/crypto/OtrService.java index 20427d7b..c23a7fc8 100644 --- a/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java +++ b/src/main/java/eu/siacs/conversations/crypto/OtrService.java @@ -36,14 +36,14 @@ import net.java.otr4j.session.InstanceTag; import net.java.otr4j.session.SessionID; import net.java.otr4j.session.FragmenterInstructions; -public class OtrEngine extends OtrCryptoEngineImpl implements OtrEngineHost { +public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost { private Account account; private OtrPolicy otrPolicy; private KeyPair keyPair; private XmppConnectionService mXmppConnectionService; - public OtrEngine(XmppConnectionService service, Account account) { + public OtrService(XmppConnectionService service, Account account) { this.account = account; this.otrPolicy = new OtrPolicyImpl(); this.otrPolicy.setAllowV1(false); @@ -182,8 +182,8 @@ public class OtrEngine extends OtrCryptoEngineImpl implements OtrEngineHost { packet.setBody(body); packet.addChild("private", "urn:xmpp:carbons:2"); packet.addChild("no-copy", "urn:xmpp:hints"); - packet.addChild("no-store", "urn:xmpp:hints"); - + packet.addChild("no-permanent-store", "urn:xmpp:hints"); + packet.addChild("no-permanent-storage", "urn:xmpp:hints"); try { Jid jid = Jid.fromSessionID(session); Conversation conversation = mXmppConnectionService.find(account,jid); @@ -202,20 +202,7 @@ public class OtrEngine extends OtrCryptoEngineImpl implements OtrEngineHost { @Override public void messageFromAnotherInstanceReceived(SessionID session) { - try { - Jid jid = Jid.fromSessionID(session); - Conversation conversation = mXmppConnectionService.find(account, jid); - String id = conversation == null ? null : conversation.getLastReceivedOtrMessageId(); - if (id != null) { - MessagePacket packet = mXmppConnectionService.getMessageGenerator().generateOtrError(jid,id); - packet.setFrom(account.getJid()); - mXmppConnectionService.sendMessagePacket(account,packet); - Log.d(Config.LOGTAG,packet.toString()); - Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": unreadable OTR message in "+conversation.getName()); - } - } catch (InvalidJidException e) { - return; - } + sendOtrErrorMessage(session, "Message from another OTR-instance received"); } @Override @@ -267,9 +254,28 @@ public class OtrEngine extends OtrCryptoEngineImpl implements OtrEngineHost { } @Override - public void unreadableMessageReceived(SessionID arg0) throws OtrException { + public void unreadableMessageReceived(SessionID session) throws OtrException { Log.d(Config.LOGTAG,"unreadable message received"); - throw new OtrException(new Exception("unreadable message received")); + sendOtrErrorMessage(session, "You sent me an unreadable OTR-encrypted message"); + } + + public void sendOtrErrorMessage(SessionID session, String errorText) { + try { + Jid jid = Jid.fromSessionID(session); + Conversation conversation = mXmppConnectionService.find(account, jid); + String id = conversation == null ? null : conversation.getLastReceivedOtrMessageId(); + if (id != null) { + MessagePacket packet = mXmppConnectionService.getMessageGenerator() + .generateOtrError(jid, id, errorText); + packet.setFrom(account.getJid()); + mXmppConnectionService.sendMessagePacket(account,packet); + Log.d(Config.LOGTAG,packet.toString()); + Log.d(Config.LOGTAG,account.getJid().toBareJid().toString() + +": unreadable OTR message in "+conversation.getName()); + } + } catch (InvalidJidException e) { + return; + } } @Override @@ -279,7 +285,7 @@ public class OtrEngine extends OtrCryptoEngineImpl implements OtrEngineHost { @Override public void verify(SessionID id, String fingerprint, boolean approved) { - Log.d(Config.LOGTAG,"OtrEngine.verify("+id.toString()+","+fingerprint+","+String.valueOf(approved)+")"); + Log.d(Config.LOGTAG,"OtrService.verify("+id.toString()+","+fingerprint+","+String.valueOf(approved)+")"); try { final Jid jid = Jid.fromSessionID(id); Conversation conversation = this.mXmppConnectionService.find(this.account,jid); diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java index 3dc3fd34..4a4d3f03 100644 --- a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java +++ b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java @@ -60,11 +60,11 @@ public class PgpEngine { message.setEncryption(Message.ENCRYPTION_DECRYPTED); final HttpConnectionManager manager = mXmppConnectionService.getHttpConnectionManager(); if (message.trusted() + && message.treatAsDownloadable() != Message.Decision.NEVER && Settings.DOWNLOAD_IMAGE_LINKS && mXmppConnectionService.isDownloadAllowedInConnection() - && message.bodyContainsDownloadable() && manager.getAutoAcceptFileSize() > 0) { - manager.createNewConnection(message); + manager.createNewDownloadConnection(message); } callback.success(message); } @@ -101,7 +101,7 @@ public class PgpEngine { switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) { case OpenPgpApi.RESULT_CODE_SUCCESS: - URL url = message.getImageParams().url; + URL url = message.getFileParams().url; mXmppConnectionService.getFileBackend().updateFileParams(message,url); message.setEncryption(Message.ENCRYPTION_DECRYPTED); PgpEngine.this.mXmppConnectionService @@ -146,11 +146,15 @@ public class PgpEngine { params.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, message .getConversation().getAccount().getJid().toBareJid().toString()); - if (message.getType() == Message.TYPE_TEXT) { + if (!message.needsUploading()) { params.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); - - InputStream is = new ByteArrayInputStream(message.getBody() - .getBytes()); + String body; + if (message.hasFileOnRemoteHost()) { + body = message.getFileParams().url.toString(); + } else { + body = message.getBody(); + } + InputStream is = new ByteArrayInputStream(body.getBytes()); final OutputStream os = new ByteArrayOutputStream(); api.executeApiAsync(params, is, os, new IOpenPgpCallback() { @@ -187,7 +191,7 @@ public class PgpEngine { } } }); - } else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) { + } else { try { DownloadableFile inputFile = this.mXmppConnectionService .getFileBackend().getFile(message, true); diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java b/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java index 10cd3167..c95a62df 100644 --- a/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java +++ b/src/main/java/eu/siacs/conversations/crypto/sasl/ScramSha1.java @@ -185,7 +185,7 @@ public class ScramSha1 extends SaslMechanism { case RESPONSE_SENT: final String clientCalculatedServerFinalMessage = "v=" + Base64.encodeToString(serverSignature, Base64.NO_WRAP); - if (!clientCalculatedServerFinalMessage.equals(new String(Base64.decode(challenge, Base64.DEFAULT)))) { + if (challenge == null || !clientCalculatedServerFinalMessage.equals(new String(Base64.decode(challenge, Base64.DEFAULT)))) { throw new AuthenticationException("Server final message does not match calculated final message"); } state = State.VALID_SERVER_RESPONSE; diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java index 2bc2c954..f472361f 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -19,7 +19,7 @@ import java.util.concurrent.CopyOnWriteArraySet; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; -import eu.siacs.conversations.crypto.OtrEngine; +import eu.siacs.conversations.crypto.OtrService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.jid.InvalidJidException; @@ -44,6 +44,10 @@ public class Account extends AbstractEntity { public static final int OPTION_REGISTER = 2; public static final int OPTION_USECOMPRESSION = 3; + public boolean httpUploadAvailable() { + return xmppConnection != null && xmppConnection.getFeatures().httpUpload(); + } + public static enum State { DISABLED, OFFLINE, @@ -117,7 +121,7 @@ public class Account extends AbstractEntity { protected JSONObject keys = new JSONObject(); protected String avatar; protected boolean online = false; - private OtrEngine otrEngine = null; + private OtrService mOtrService = null; private XmppConnection xmppConnection = null; private long mEndGracePeriod = 0L; private String otrFingerprint; @@ -229,11 +233,17 @@ public class Account extends AbstractEntity { return jid.getResourcepart(); } - public void setResource(final String resource) { - try { - jid = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), resource); - } catch (final InvalidJidException ignored) { + public boolean setResource(final String resource) { + final String oldResource = jid.getResourcepart(); + if (oldResource == null || !oldResource.equals(resource)) { + try { + jid = Jid.fromParts(jid.getLocalpart(), jid.getDomainpart(), resource); + return true; + } catch (final InvalidJidException ignored) { + return true; + } } + return false; } public Jid getJid() { @@ -267,12 +277,12 @@ public class Account extends AbstractEntity { return values; } - public void initOtrEngine(final XmppConnectionService context) { - this.otrEngine = new OtrEngine(context, this); + public void initAccountServices(final XmppConnectionService context) { + this.mOtrService = new OtrService(context, this); } - public OtrEngine getOtrEngine() { - return this.otrEngine; + public OtrService getOtrService() { + return this.mOtrService; } public XmppConnection getXmppConnection() { @@ -286,10 +296,10 @@ public class Account extends AbstractEntity { public String getOtrFingerprint() { if (this.otrFingerprint == null) { try { - if (this.otrEngine == null) { + if (this.mOtrService == null) { return null; } - final PublicKey publicKey = this.otrEngine.getPublicKey(); + final PublicKey publicKey = this.mOtrService.getPublicKey(); if (publicKey == null || !(publicKey instanceof DSAPublicKey)) { return null; } diff --git a/src/main/java/eu/siacs/conversations/entities/Bookmark.java b/src/main/java/eu/siacs/conversations/entities/Bookmark.java index cc3d3424..06c46c1c 100644 --- a/src/main/java/eu/siacs/conversations/entities/Bookmark.java +++ b/src/main/java/eu/siacs/conversations/entities/Bookmark.java @@ -82,12 +82,7 @@ public class Bookmark extends Element implements ListItem { } public String getNick() { - Element nick = this.findChild("nick"); - if (nick != null) { - return nick.getContent(); - } else { - return null; - } + return this.findChildContent("nick"); } public void setNick(String nick) { @@ -103,12 +98,7 @@ public class Bookmark extends Element implements ListItem { } public String getPassword() { - Element password = this.findChild("password"); - if (password != null) { - return password.getContent(); - } else { - return null; - } + return this.findChildContent("password"); } public void setPassword(String password) { diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index 26145d82..a215874f 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -16,6 +16,7 @@ import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; +import eu.siacs.conversations.xmpp.pep.Avatar; public class Contact implements ListItem, Blockable { public static final String TABLENAME = "contacts"; @@ -41,11 +42,11 @@ public class Contact implements ListItem, Blockable { protected int subscription = 0; protected String systemAccount; protected String photoUri; - protected String avatar; protected JSONObject keys = new JSONObject(); protected JSONArray groups = new JSONArray(); protected Presences presences = new Presences(); protected Account account; + protected Avatar avatar; public Contact(final String account, final String systemName, final String serverName, final Jid jid, final int subscription, final String photoUri, @@ -62,7 +63,11 @@ public class Contact implements ListItem, Blockable { } catch (JSONException e) { this.keys = new JSONObject(); } - this.avatar = avatar; + if (avatar != null) { + this.avatar = new Avatar(); + this.avatar.sha1sum = avatar; + this.avatar.origin = Avatar.Origin.VCARD; //always assume worst + } try { this.groups = (groups == null ? new JSONArray() : new JSONArray(groups)); } catch (JSONException e) { @@ -136,10 +141,10 @@ public class Contact implements ListItem, Blockable { tags.add(new Tag("away", 0xffff9800)); break; case Presences.XA: - tags.add(new Tag("not available", 0xffe51c23)); + tags.add(new Tag("not available", 0xfff44336)); break; case Presences.DND: - tags.add(new Tag("dnd", 0xffe51c23)); + tags.add(new Tag("dnd", 0xfff44336)); break; } if (isBlocked()) { @@ -193,7 +198,7 @@ public class Contact implements ListItem, Blockable { values.put(SYSTEMACCOUNT, systemAccount); values.put(PHOTOURI, photoUri); values.put(KEYS, keys.toString()); - values.put(AVATAR, avatar); + values.put(AVATAR, avatar == null ? null : avatar.getFilename()); values.put(LAST_PRESENCE, lastseen.presence); values.put(LAST_TIME, lastseen.time); values.put(GROUPS, groups.toString()); @@ -238,8 +243,16 @@ public class Contact implements ListItem, Blockable { return this.presences.getMostAvailableStatus(); } - public void setPhotoUri(String uri) { - this.photoUri = uri; + public boolean setPhotoUri(String uri) { + if (uri != null && !uri.equals(this.photoUri)) { + this.photoUri = uri; + return true; + } else if (this.photoUri != null && uri == null) { + this.photoUri = null; + return true; + } else { + return false; + } } public void setServerName(String serverName) { @@ -417,17 +430,20 @@ public class Contact implements ListItem, Blockable { return getJid().toDomainJid(); } - public boolean setAvatar(String filename) { - if (this.avatar != null && this.avatar.equals(filename)) { + public boolean setAvatar(Avatar avatar) { + if (this.avatar != null && this.avatar.equals(avatar)) { return false; } else { - this.avatar = filename; + if (this.avatar != null && this.avatar.origin == Avatar.Origin.PEP && avatar.origin == Avatar.Origin.VCARD) { + return false; + } + this.avatar = avatar; return true; } } public String getAvatar() { - return this.avatar; + return avatar == null ? null : avatar.getFilename(); } public boolean deleteOtrFingerprint(String fingerprint) { @@ -484,6 +500,10 @@ public class Contact implements ListItem, Blockable { } } + public boolean isSelf() { + return account.getJid().toBareJid().equals(getJid().toBareJid()); + } + public static class Lastseen { public long time; public String presence; diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index bfee5007..289ed4ea 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -16,6 +16,7 @@ import java.security.interfaces.DSAPublicKey; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.Iterator; import java.util.List; import eu.siacs.conversations.Config; @@ -218,6 +219,11 @@ public class Conversation extends AbstractEntity implements Blockable { messages.clear(); messages.addAll(this.messages); } + for(Iterator<Message> iterator = messages.iterator(); iterator.hasNext();) { + if (iterator.next().wasMergedIntoPrevious()) { + iterator.remove(); + } + } } @Override @@ -243,6 +249,12 @@ public class Conversation extends AbstractEntity implements Blockable { this.mLastReceivedOtrMessageId = id; } + public int countMessages() { + synchronized (this.messages) { + return this.messages.size(); + } + } + public interface OnMessageFound { public void onMessageFound(final Message message); @@ -413,7 +425,7 @@ public class Conversation extends AbstractEntity implements Blockable { final SessionID sessionId = new SessionID(this.getJid().toBareJid().toString(), presence, "xmpp"); - this.otrSession = new SessionImpl(sessionId, getAccount().getOtrEngine()); + this.otrSession = new SessionImpl(sessionId, getAccount().getOtrService()); try { if (sendStart) { this.otrSession.startSession(); @@ -485,7 +497,7 @@ public class Conversation extends AbstractEntity implements Blockable { return null; } DSAPublicKey remotePubKey = (DSAPublicKey) getOtrSession().getRemotePublicKey(); - this.otrFingerprint = getAccount().getOtrEngine().getFingerprint(remotePubKey); + this.otrFingerprint = getAccount().getOtrService().getFingerprint(remotePubKey); } catch (final OtrCryptoException | UnsupportedOperationException ignored) { return null; } diff --git a/src/main/java/eu/siacs/conversations/entities/Downloadable.java b/src/main/java/eu/siacs/conversations/entities/Downloadable.java deleted file mode 100644 index d25bf93a..00000000 --- a/src/main/java/eu/siacs/conversations/entities/Downloadable.java +++ /dev/null @@ -1,28 +0,0 @@ -package eu.siacs.conversations.entities; - -public interface Downloadable { - - public final String[] VALID_IMAGE_EXTENSIONS = {"webp", "jpeg", "jpg", "png", "jpe"}; - public final String[] VALID_CRYPTO_EXTENSIONS = {"pgp", "gpg", "otr"}; - - public static final int STATUS_UNKNOWN = 0x200; - public static final int STATUS_CHECKING = 0x201; - public static final int STATUS_FAILED = 0x202; - public static final int STATUS_OFFER = 0x203; - public static final int STATUS_DOWNLOADING = 0x204; - public static final int STATUS_DELETED = 0x205; - public static final int STATUS_OFFER_CHECK_FILESIZE = 0x206; - public static final int STATUS_UPLOADING = 0x207; - - public boolean start(); - - public int getStatus(); - - public long getFileSize(); - - public int getProgress(); - - public String getMimeType(); - - public void cancel(); -} diff --git a/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java b/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java index 7c8f95d1..ae9ba1f1 100644 --- a/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java +++ b/src/main/java/eu/siacs/conversations/entities/DownloadableFile.java @@ -20,6 +20,8 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import eu.siacs.conversations.Config; +import eu.siacs.conversations.utils.MimeUtils; + import android.util.Log; public class DownloadableFile extends File { @@ -56,16 +58,11 @@ public class DownloadableFile extends File { public String getMimeType() { String path = this.getAbsolutePath(); - try { - String mime = URLConnection.guessContentTypeFromName(path.replace("#","")); - if (mime != null) { - return mime; - } else if (mime == null && path.endsWith(".webp")) { - return "image/webp"; - } else { - return ""; - } - } catch (final StringIndexOutOfBoundsException e) { + int start = path.lastIndexOf('.') + 1; + if (start < path.length()) { + String mime = MimeUtils.guessMimeTypeFromExtension(path.substring(start)); + return mime == null ? "" : mime; + } else { return ""; } } diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index bcb1ca24..9a904cab 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -9,6 +9,8 @@ import java.util.Arrays; import eu.siacs.conversations.Config; import eu.siacs.conversations.utils.GeoHelper; +import eu.siacs.conversations.utils.MimeUtils; +import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; @@ -16,6 +18,8 @@ public class Message extends AbstractEntity { public static final String TABLENAME = "messages"; + public static final String MERGE_SEPARATOR = " \u200B\n\n"; + public static final int STATUS_RECEIVED = 0; public static final int STATUS_UNSEND = 1; public static final int STATUS_SEND = 2; @@ -66,7 +70,7 @@ public class Message extends AbstractEntity { protected String remoteMsgId = null; protected String serverMsgId = null; protected Conversation conversation = null; - protected Downloadable downloadable = null; + protected Transferable transferable = null; private Message mNextMessage = null; private Message mPreviousMessage = null; @@ -95,9 +99,9 @@ public class Message extends AbstractEntity { } private Message(final String uuid, final String conversationUUid, final Jid counterpart, - final Jid trueCounterpart, final String body, final long timeSent, - final int encryption, final int status, final int type, final String remoteMsgId, - final String relativeFilePath, final String serverMsgId) { + final Jid trueCounterpart, final String body, final long timeSent, + final int encryption, final int status, final int type, final String remoteMsgId, + final String relativeFilePath, final String serverMsgId) { this.uuid = uuid; this.conversationUuid = conversationUUid; this.counterpart = counterpart; @@ -179,7 +183,7 @@ public class Message extends AbstractEntity { values.put(TYPE, type); values.put(REMOTE_MSG_ID, remoteMsgId); values.put(RELATIVE_FILE_PATH, relativeFilePath); - values.put(SERVER_MSG_ID,serverMsgId); + values.put(SERVER_MSG_ID, serverMsgId); return values; } @@ -211,7 +215,7 @@ public class Message extends AbstractEntity { return null; } else { return this.conversation.getAccount().getRoster() - .getContactFromRoster(this.trueCounterpart); + .getContactFromRoster(this.trueCounterpart); } } } @@ -304,12 +308,12 @@ public class Message extends AbstractEntity { this.trueCounterpart = trueCounterpart; } - public Downloadable getDownloadable() { - return this.downloadable; + public Transferable getTransferable() { + return this.transferable; } - public void setDownloadable(Downloadable downloadable) { - this.downloadable = downloadable; + public void setTransferable(Transferable transferable) { + this.transferable = transferable; } public boolean equals(Message message) { @@ -317,15 +321,25 @@ public class Message extends AbstractEntity { return this.serverMsgId.equals(message.getServerMsgId()); } else if (this.body == null || this.counterpart == null) { return false; - } else if (message.getRemoteMsgId() != null) { - return (message.getRemoteMsgId().equals(this.remoteMsgId) || message.getRemoteMsgId().equals(this.uuid)) - && this.counterpart.equals(message.getCounterpart()) - && this.body.equals(message.getBody()); } else { - return this.remoteMsgId == null - && this.counterpart.equals(message.getCounterpart()) - && this.body.equals(message.getBody()) - && Math.abs(this.getTimeSent() - message.getTimeSent()) < Config.PING_TIMEOUT * 500; + String body, otherBody; + if (this.hasFileOnRemoteHost()) { + body = getFileParams().url.toString(); + otherBody = message.body == null ? null : message.body.trim(); + } else { + body = this.body; + otherBody = message.body; + } + if (message.getRemoteMsgId() != null) { + return (message.getRemoteMsgId().equals(this.remoteMsgId) || message.getRemoteMsgId().equals(this.uuid)) + && this.counterpart.equals(message.getCounterpart()) + && body.equals(otherBody); + } else { + return this.remoteMsgId == null + && this.counterpart.equals(message.getCounterpart()) + && body.equals(otherBody) + && Math.abs(this.getTimeSent() - message.getTimeSent()) < Config.MESSAGE_MERGE_WINDOW * 1000; + } } } @@ -359,41 +373,43 @@ public class Message extends AbstractEntity { public boolean mergeable(final Message message) { return message != null && - (message.getType() == Message.TYPE_TEXT && - this.getDownloadable() == null && - message.getDownloadable() == null && - message.getEncryption() != Message.ENCRYPTION_PGP && - this.getType() == message.getType() && - //this.getStatus() == message.getStatus() && - isStatusMergeable(this.getStatus(),message.getStatus()) && - this.getEncryption() == message.getEncryption() && - this.getCounterpart() != null && - this.getCounterpart().equals(message.getCounterpart()) && - (message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) && - !GeoHelper.isGeoUri(message.getBody()) && - !GeoHelper.isGeoUri(this.body) && - !message.bodyContainsDownloadable() && - !this.bodyContainsDownloadable() && - !message.getBody().startsWith(ME_COMMAND) && - !this.getBody().startsWith(ME_COMMAND) - ); + (message.getType() == Message.TYPE_TEXT && + this.getTransferable() == null && + message.getTransferable() == null && + message.getEncryption() != Message.ENCRYPTION_PGP && + this.getType() == message.getType() && + //this.getStatus() == message.getStatus() && + isStatusMergeable(this.getStatus(), message.getStatus()) && + this.getEncryption() == message.getEncryption() && + this.getCounterpart() != null && + this.getCounterpart().equals(message.getCounterpart()) && + (message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) && + !GeoHelper.isGeoUri(message.getBody()) && + !GeoHelper.isGeoUri(this.body) && + message.treatAsDownloadable() == Decision.NEVER && + this.treatAsDownloadable() == Decision.NEVER && + !message.getBody().startsWith(ME_COMMAND) && + !this.getBody().startsWith(ME_COMMAND) && + !this.bodyIsHeart() && + !message.bodyIsHeart() + ); } private static boolean isStatusMergeable(int a, int b) { return a == b || ( - ( a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_UNSEND) - || (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_SEND) - || (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND) - || (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND_RECEIVED) - || (a == Message.STATUS_SEND && b == Message.STATUS_UNSEND) - || (a == Message.STATUS_SEND && b == Message.STATUS_SEND_RECEIVED) + (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_UNSEND) + || (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_SEND) + || (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND) + || (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND_RECEIVED) + || (a == Message.STATUS_SEND && b == Message.STATUS_UNSEND) + || (a == Message.STATUS_SEND && b == Message.STATUS_SEND_RECEIVED) ); } public String getMergedBody() { final Message next = this.next(); if (this.mergeable(next)) { - return getBody() + '\n' + next.getMergedBody(); + return getBody() + MERGE_SEPARATOR + next.getMergedBody(); } return getBody(); } @@ -429,119 +445,175 @@ public class Message extends AbstractEntity { return (status > STATUS_RECEIVED || (contact != null && contact.trusted())); } - public boolean bodyContainsDownloadable() { - /** - * there are a few cases where spaces result in an unwanted behavior, e.g. - * "http://upload.mitsu-freunde-bw.de/uploads/2015/03/i43b4bpr8.png /abc.png" - * or more than one image link in one message. - */ - if (body.contains(" ")) { - return false; - } - try { - URL url = new URL(body); - if (!url.getProtocol().equalsIgnoreCase("http") - && !url.getProtocol().equalsIgnoreCase("https")) { - return false; - } - - String sUrlPath = url.getPath(); - if (sUrlPath == null || sUrlPath.isEmpty()) { + public boolean fixCounterpart() { + Presences presences = conversation.getContact().getPresences(); + if (counterpart != null && presences.has(counterpart.getResourcepart())) { + return true; + } else if (presences.size() >= 1) { + try { + counterpart = Jid.fromParts(conversation.getJid().getLocalpart(), + conversation.getJid().getDomainpart(), + presences.asStringArray()[0]); + return true; + } catch (InvalidJidException e) { + counterpart = null; return false; } + } else { + counterpart = null; + return false; + } + } - int iSlashIndex = sUrlPath.lastIndexOf('/') + 1; + public enum Decision { + MUST, + SHOULD, + NEVER, + } - String sLastUrlPath = sUrlPath.substring(iSlashIndex).toLowerCase(); + private static String extractRelevantExtension(URL url) { + String path = url.getPath(); + if (path == null || path.isEmpty()) { + return null; + } + String filename = path.substring(path.lastIndexOf('/') + 1).toLowerCase(); + String[] extensionParts = filename.split("\\."); + if (extensionParts.length == 2) { + return extensionParts[extensionParts.length - 1]; + } else if (extensionParts.length == 3 && Arrays + .asList(Transferable.VALID_CRYPTO_EXTENSIONS) + .contains(extensionParts[extensionParts.length - 1])) { + return extensionParts[extensionParts.length -2]; + } + return null; + } - String[] extensionParts = sLastUrlPath.split("\\."); - if (extensionParts.length == 2 - && Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains( - extensionParts[extensionParts.length - 1])) { - return true; - } else if (extensionParts.length == 3 - && Arrays - .asList(Downloadable.VALID_CRYPTO_EXTENSIONS) - .contains(extensionParts[extensionParts.length - 1]) - && Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains( - extensionParts[extensionParts.length - 2])) { - return true; + public String getMimeType() { + if (relativeFilePath != null) { + int start = relativeFilePath.lastIndexOf('.') + 1; + if (start < relativeFilePath.length()) { + return MimeUtils.guessMimeTypeFromExtension(relativeFilePath.substring(start)); } else { - return false; + return null; + } + } else { + try { + return MimeUtils.guessMimeTypeFromExtension(extractRelevantExtension(new URL(body.trim()))); + } catch (MalformedURLException e) { + return null; } + } + } + + public Decision treatAsDownloadable() { + if (body.trim().contains(" ")) { + return Decision.NEVER; + } + try { + URL url = new URL(body); + if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) { + return Decision.NEVER; + } + String extension = extractRelevantExtension(url); + if (extension == null) { + return Decision.NEVER; + } + String ref = url.getRef(); + boolean encrypted = ref != null && ref.matches("([A-Fa-f0-9]{2}){48}"); + + if (encrypted) { + if (MimeUtils.guessMimeTypeFromExtension(extension) != null) { + return Decision.MUST; + } else { + return Decision.NEVER; + } + } else if (Arrays.asList(Transferable.VALID_IMAGE_EXTENSIONS).contains(extension) + || Arrays.asList(Transferable.WELL_KNOWN_EXTENSIONS).contains(extension)) { + return Decision.SHOULD; + } else { + return Decision.NEVER; + } + } catch (MalformedURLException e) { - return false; + return Decision.NEVER; } } - public ImageParams getImageParams() { - ImageParams params = getLegacyImageParams(); + public boolean bodyIsHeart() { + return body != null && UIHelper.HEARTS.contains(body.trim()); + } + + public FileParams getFileParams() { + FileParams params = getLegacyFileParams(); if (params != null) { return params; } - params = new ImageParams(); - if (this.downloadable != null) { - params.size = this.downloadable.getFileSize(); + params = new FileParams(); + if (this.transferable != null) { + params.size = this.transferable.getFileSize(); } if (body == null) { return params; } String parts[] = body.split("\\|"); - if (parts.length == 1) { - try { - params.size = Long.parseLong(parts[0]); - } catch (NumberFormatException e) { - params.origin = parts[0]; + switch (parts.length) { + case 1: + try { + params.size = Long.parseLong(parts[0]); + } catch (NumberFormatException e) { + try { + params.url = new URL(parts[0]); + } catch (MalformedURLException e1) { + params.url = null; + } + } + break; + case 2: + case 4: try { params.url = new URL(parts[0]); } catch (MalformedURLException e1) { params.url = null; } - } - } else if (parts.length == 3) { - try { - params.size = Long.parseLong(parts[0]); - } catch (NumberFormatException e) { - params.size = 0; - } - try { - params.width = Integer.parseInt(parts[1]); - } catch (NumberFormatException e) { - params.width = 0; - } - try { - params.height = Integer.parseInt(parts[2]); - } catch (NumberFormatException e) { - params.height = 0; - } - } else if (parts.length == 4) { - params.origin = parts[0]; - try { - params.url = new URL(parts[0]); - } catch (MalformedURLException e1) { - params.url = null; - } - try { - params.size = Long.parseLong(parts[1]); - } catch (NumberFormatException e) { - params.size = 0; - } - try { - params.width = Integer.parseInt(parts[2]); - } catch (NumberFormatException e) { - params.width = 0; - } - try { - params.height = Integer.parseInt(parts[3]); - } catch (NumberFormatException e) { - params.height = 0; - } + try { + params.size = Long.parseLong(parts[1]); + } catch (NumberFormatException e) { + params.size = 0; + } + try { + params.width = Integer.parseInt(parts[2]); + } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { + params.width = 0; + } + try { + params.height = Integer.parseInt(parts[3]); + } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { + params.height = 0; + } + break; + case 3: + try { + params.size = Long.parseLong(parts[0]); + } catch (NumberFormatException e) { + params.size = 0; + } + try { + params.width = Integer.parseInt(parts[1]); + } catch (NumberFormatException e) { + params.width = 0; + } + try { + params.height = Integer.parseInt(parts[2]); + } catch (NumberFormatException e) { + params.height = 0; + } + break; } return params; } - public ImageParams getLegacyImageParams() { - ImageParams params = new ImageParams(); + public FileParams getLegacyFileParams() { + FileParams params = new FileParams(); if (body == null) { return params; } @@ -577,11 +649,18 @@ public class Message extends AbstractEntity { return type == TYPE_FILE || type == TYPE_IMAGE; } - public class ImageParams { + public boolean hasFileOnRemoteHost() { + return isFileOrImage() && getFileParams().url != null; + } + + public boolean needsUploading() { + return isFileOrImage() && getFileParams().url == null; + } + + public class FileParams { public URL url; public long size = 0; public int width = 0; public int height = 0; - public String origin; } } diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java index addee8db..d867a370 100644 --- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java +++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java @@ -343,8 +343,6 @@ public class MucOptions { setError(ERROR_BANNED); } else if (error != null && error.hasChild("registration-required")) { setError(ERROR_MEMBERS_ONLY); - } else { - setError(ERROR_UNKNOWN); } } } diff --git a/src/main/java/eu/siacs/conversations/entities/Roster.java b/src/main/java/eu/siacs/conversations/entities/Roster.java index ce058004..628a31d1 100644 --- a/src/main/java/eu/siacs/conversations/entities/Roster.java +++ b/src/main/java/eu/siacs/conversations/entities/Roster.java @@ -2,6 +2,7 @@ package eu.siacs.conversations.entities; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import eu.siacs.conversations.xmpp.jid.Jid; @@ -55,12 +56,15 @@ public class Roster { } } - public void clearSystemAccounts() { - for (Contact contact : getContacts()) { - contact.setPhotoUri(null); - contact.setSystemName(null); - contact.setSystemAccount(null); + public List<Contact> getWithSystemAccounts() { + List<Contact> with = getContacts(); + for(Iterator<Contact> iterator = with.iterator(); iterator.hasNext();) { + Contact contact = iterator.next(); + if (contact.getSystemAccount() == null) { + iterator.remove(); + } } + return with; } public List<Contact> getContacts() { @@ -70,6 +74,9 @@ public class Roster { } public void initContact(final Contact contact) { + if (contact == null) { + return; + } contact.setAccount(account); contact.setOption(Contact.Options.IN_ROSTER); synchronized (this.contacts) { diff --git a/src/main/java/eu/siacs/conversations/entities/Transferable.java b/src/main/java/eu/siacs/conversations/entities/Transferable.java new file mode 100644 index 00000000..2db6e3c9 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/entities/Transferable.java @@ -0,0 +1,28 @@ +package eu.siacs.conversations.entities; + +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"}; + + int STATUS_UNKNOWN = 0x200; + int STATUS_CHECKING = 0x201; + int STATUS_FAILED = 0x202; + int STATUS_OFFER = 0x203; + int STATUS_DOWNLOADING = 0x204; + int STATUS_DELETED = 0x205; + int STATUS_OFFER_CHECK_FILESIZE = 0x206; + int STATUS_UPLOADING = 0x207; + + + boolean start(); + + int getStatus(); + + long getFileSize(); + + int getProgress(); + + void cancel(); +} diff --git a/src/main/java/eu/siacs/conversations/entities/DownloadablePlaceholder.java b/src/main/java/eu/siacs/conversations/entities/TransferablePlaceholder.java index 03fceceb..e065953e 100644 --- a/src/main/java/eu/siacs/conversations/entities/DownloadablePlaceholder.java +++ b/src/main/java/eu/siacs/conversations/entities/TransferablePlaceholder.java @@ -1,10 +1,10 @@ package eu.siacs.conversations.entities; -public class DownloadablePlaceholder implements Downloadable { +public class TransferablePlaceholder implements Transferable { private int status; - public DownloadablePlaceholder(int status) { + public TransferablePlaceholder(int status) { this.status = status; } @Override @@ -28,11 +28,6 @@ public class DownloadablePlaceholder implements Downloadable { } @Override - public String getMimeType() { - return ""; - } - - @Override public void cancel() { } diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index 6bc629b5..47915e3f 100644 --- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java @@ -6,6 +6,7 @@ import java.util.List; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.services.MessageArchiveService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.PhoneHelper; @@ -91,7 +92,7 @@ public class IqGenerator extends AbstractGenerator { return publish("urn:xmpp:avatar:metadata", item); } - public IqPacket retrieveAvatar(final Avatar avatar) { + public IqPacket retrievePepAvatar(final Avatar avatar) { final Element item = new Element("item"); item.setAttribute("id", avatar.sha1sum); final IqPacket packet = retrieve("urn:xmpp:avatar:data", item); @@ -99,6 +100,13 @@ public class IqGenerator extends AbstractGenerator { return packet; } + public IqPacket retrieveVcardAvatar(final Avatar avatar) { + final IqPacket packet = new IqPacket(IqPacket.TYPE.GET); + packet.setTo(avatar.owner); + packet.addChild("vCard", "vcard-temp"); + return packet; + } + public IqPacket retrieveAvatarMetaData(final Jid to) { final IqPacket packet = retrieve("urn:xmpp:avatar:metadata", null); if (to != null) { @@ -187,4 +195,13 @@ public class IqGenerator extends AbstractGenerator { item.setAttribute("role", role); return packet; } + + public IqPacket requestHttpUploadSlot(Jid host, DownloadableFile file) { + IqPacket packet = new IqPacket(IqPacket.TYPE.GET); + packet.setTo(host); + Element request = packet.addChild("request",Xmlns.HTTP_UPLOAD); + request.addChild("filename").setContent(file.getName()); + request.addChild("size").setContent(String.valueOf(file.getExpectedSize())); + return packet; + } } diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java index 8f6a90b9..e698c151 100644 --- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java @@ -71,8 +71,16 @@ public class MessageGenerator extends AbstractGenerator { MessagePacket packet = preparePacket(message, addDelay); packet.addChild("private", "urn:xmpp:carbons:2"); packet.addChild("no-copy", "urn:xmpp:hints"); + packet.addChild("no-permanent-store", "urn:xmpp:hints"); + packet.addChild("no-permanent-storage", "urn:xmpp:hints"); try { - packet.setBody(otrSession.transformSending(message.getBody())[0]); + String content; + if (message.hasFileOnRemoteHost()) { + content = message.getFileParams().url.toString(); + } else { + content = message.getBody(); + } + packet.setBody(otrSession.transformSending(content)[0]); return packet; } catch (OtrException e) { return null; @@ -85,7 +93,11 @@ public class MessageGenerator extends AbstractGenerator { public MessagePacket generateChat(Message message, boolean addDelay) { MessagePacket packet = preparePacket(message, addDelay); - packet.setBody(message.getBody()); + if (message.hasFileOnRemoteHost()) { + packet.setBody(message.getFileParams().url.toString()); + } else { + packet.setBody(message.getBody()); + } return packet; } @@ -95,13 +107,11 @@ public class MessageGenerator extends AbstractGenerator { public MessagePacket generatePgpChat(Message message, boolean addDelay) { MessagePacket packet = preparePacket(message, addDelay); - packet.setBody("This is an XEP-0027 encryted message"); + packet.setBody("This is an XEP-0027 encrypted message"); if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { - packet.addChild("x", "jabber:x:encrypted").setContent( - message.getEncryptedBody()); + packet.addChild("x", "jabber:x:encrypted").setContent(message.getEncryptedBody()); } else if (message.getEncryption() == Message.ENCRYPTION_PGP) { - packet.addChild("x", "jabber:x:encrypted").setContent( - message.getBody()); + packet.addChild("x", "jabber:x:encrypted").setContent(message.getBody()); } return packet; } @@ -172,7 +182,7 @@ public class MessageGenerator extends AbstractGenerator { return receivedPacket; } - public MessagePacket generateOtrError(Jid to, String id) { + public MessagePacket generateOtrError(Jid to, String id, String errorText) { MessagePacket packet = new MessagePacket(); packet.setType(MessagePacket.TYPE_ERROR); packet.setAttribute("id",id); @@ -181,7 +191,7 @@ public class MessageGenerator extends AbstractGenerator { error.setAttribute("code","406"); error.setAttribute("type","modify"); error.addChild("not-acceptable","urn:ietf:params:xml:ns:xmpp-stanzas"); - error.addChild("text").setContent("unreadable OTR message received"); + error.addChild("text").setContent("?OTR Error:" + errorText); return packet; } } diff --git a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java index 1e896724..c40c6d05 100644 --- a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java @@ -49,9 +49,16 @@ public class PresenceGenerator extends AbstractGenerator { Element cap = packet.addChild("c", "http://jabber.org/protocol/caps"); cap.setAttribute("hash", "sha-1"); - cap.setAttribute("node", "http://conversions.im"); + cap.setAttribute("node", "http://conversations.im"); cap.setAttribute("ver", capHash); } return packet; } -}
\ No newline at end of file + + public PresencePacket sendOfflinePresence(Account account) { + PresencePacket packet = new PresencePacket(); + packet.setFrom(account.getJid()); + packet.setAttribute("type","unavailable"); + return packet; + } +} diff --git a/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java b/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java index 9a2a2405..58a6d1e3 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/http/HttpConnectionManager.java @@ -1,11 +1,22 @@ package eu.siacs.conversations.http; +import org.apache.http.conn.ssl.StrictHostnameVerifier; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; + import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.AbstractConnectionManager; import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.utils.CryptoHelper; public class HttpConnectionManager extends AbstractConnectionManager { @@ -13,16 +24,67 @@ public class HttpConnectionManager extends AbstractConnectionManager { super(service); } - private List<HttpConnection> connections = new CopyOnWriteArrayList<HttpConnection>(); + private List<HttpDownloadConnection> downloadConnections = new CopyOnWriteArrayList<>(); + private List<HttpUploadConnection> uploadConnections = new CopyOnWriteArrayList<>(); + + public HttpDownloadConnection createNewDownloadConnection(Message message) { + return this.createNewDownloadConnection(message, false); + } + + public HttpDownloadConnection createNewDownloadConnection(Message message, boolean interactive) { + HttpDownloadConnection connection = new HttpDownloadConnection(this); + connection.init(message,interactive); + this.downloadConnections.add(connection); + return connection; + } - public HttpConnection createNewConnection(Message message) { - HttpConnection connection = new HttpConnection(this); + public HttpUploadConnection createNewUploadConnection(Message message) { + HttpUploadConnection connection = new HttpUploadConnection(this); connection.init(message); - this.connections.add(connection); + this.uploadConnections.add(connection); return connection; } - public void finishConnection(HttpConnection connection) { - this.connections.remove(connection); + public void finishConnection(HttpDownloadConnection connection) { + this.downloadConnections.remove(connection); + } + + public void finishUploadConnection(HttpUploadConnection httpUploadConnection) { + this.uploadConnections.remove(httpUploadConnection); + } + + public void setupTrustManager(final HttpsURLConnection connection, final boolean interactive) { + final X509TrustManager trustManager; + final HostnameVerifier hostnameVerifier; + if (interactive) { + trustManager = mXmppConnectionService.getMemorizingTrustManager(); + hostnameVerifier = mXmppConnectionService + .getMemorizingTrustManager().wrapHostnameVerifier( + new StrictHostnameVerifier()); + } else { + trustManager = mXmppConnectionService.getMemorizingTrustManager() + .getNonInteractive(); + hostnameVerifier = mXmppConnectionService + .getMemorizingTrustManager() + .wrapHostnameVerifierNonInteractive( + new StrictHostnameVerifier()); + } + try { + final SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(null, new X509TrustManager[]{trustManager}, + mXmppConnectionService.getRNG()); + + final SSLSocketFactory sf = sc.getSocketFactory(); + final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites( + sf.getSupportedCipherSuites()); + if (cipherSuites.length > 0) { + sc.getDefaultSSLParameters().setCipherSuites(cipherSuites); + + } + + connection.setSSLSocketFactory(sf); + connection.setHostnameVerifier(hostnameVerifier); + } catch (final KeyManagementException | NoSuchAlgorithmException ignored) { + } } } diff --git a/src/main/java/eu/siacs/conversations/http/HttpConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java index e7d30919..62fe4191 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java @@ -3,8 +3,7 @@ package eu.siacs.conversations.http; import android.content.Intent; import android.net.Uri; import android.os.SystemClock; - -import org.apache.http.conn.ssl.StrictHostnameVerifier; +import android.util.Log; import java.io.BufferedInputStream; import java.io.IOException; @@ -12,25 +11,20 @@ import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; import java.util.Arrays; -import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.X509TrustManager; import eu.siacs.conversations.Config; -import eu.siacs.conversations.entities.Downloadable; +import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; -public class HttpConnection implements Downloadable { +public class HttpDownloadConnection implements Transferable { private HttpConnectionManager mHttpConnectionManager; private XmppConnectionService mXmppConnectionService; @@ -38,12 +32,12 @@ public class HttpConnection implements Downloadable { private URL mUrl; private Message message; private DownloadableFile file; - private int mStatus = Downloadable.STATUS_UNKNOWN; + private int mStatus = Transferable.STATUS_UNKNOWN; private boolean acceptedAutomatically = false; private int mProgress = 0; private long mLastGuiRefresh = 0; - public HttpConnection(HttpConnectionManager manager) { + public HttpDownloadConnection(HttpConnectionManager manager) { this.mHttpConnectionManager = manager; this.mXmppConnectionService = manager.getXmppConnectionService(); } @@ -63,8 +57,12 @@ public class HttpConnection implements Downloadable { } public void init(Message message) { + init(message, false); + } + + public void init(Message message, boolean interactive) { this.message = message; - this.message.setDownloadable(this); + this.message.setTransferable(this); try { mUrl = new URL(message.getBody()); String[] parts = mUrl.getPath().toLowerCase().split("\\."); @@ -92,7 +90,7 @@ public class HttpConnection implements Downloadable { && this.file.getKey() == null) { this.message.setEncryption(Message.ENCRYPTION_NONE); } - checkFileSize(false); + checkFileSize(interactive); } catch (MalformedURLException e) { this.cancel(); } @@ -104,7 +102,7 @@ public class HttpConnection implements Downloadable { public void cancel() { mHttpConnectionManager.finishConnection(this); - message.setDownloadable(null); + message.setTransferable(null); mXmppConnectionService.updateConversationUi(); } @@ -112,7 +110,7 @@ public class HttpConnection implements Downloadable { Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intent.setData(Uri.fromFile(file)); mXmppConnectionService.sendBroadcast(intent); - message.setDownloadable(null); + message.setTransferable(null); mHttpConnectionManager.finishConnection(this); mXmppConnectionService.updateConversationUi(); if (acceptedAutomatically) { @@ -125,42 +123,6 @@ public class HttpConnection implements Downloadable { mXmppConnectionService.updateConversationUi(); } - private void setupTrustManager(final HttpsURLConnection connection, - final boolean interactive) { - final X509TrustManager trustManager; - final HostnameVerifier hostnameVerifier; - if (interactive) { - trustManager = mXmppConnectionService.getMemorizingTrustManager(); - hostnameVerifier = mXmppConnectionService - .getMemorizingTrustManager().wrapHostnameVerifier( - new StrictHostnameVerifier()); - } else { - trustManager = mXmppConnectionService.getMemorizingTrustManager() - .getNonInteractive(); - hostnameVerifier = mXmppConnectionService - .getMemorizingTrustManager() - .wrapHostnameVerifierNonInteractive( - new StrictHostnameVerifier()); - } - try { - final SSLContext sc = SSLContext.getInstance("TLS"); - sc.init(null, new X509TrustManager[]{trustManager}, - mXmppConnectionService.getRNG()); - - final SSLSocketFactory sf = sc.getSocketFactory(); - final String[] cipherSuites = CryptoHelper.getOrderedCipherSuites( - sf.getSupportedCipherSuites()); - if (cipherSuites.length > 0) { - sc.getDefaultSSLParameters().setCipherSuites(cipherSuites); - - } - - connection.setSSLSocketFactory(sf); - connection.setHostnameVerifier(hostnameVerifier); - } catch (final KeyManagementException | NoSuchAlgorithmException ignored) { - } - } - private class FileSizeChecker implements Runnable { private boolean interactive = false; @@ -176,43 +138,46 @@ public class HttpConnection implements Downloadable { size = retrieveFileSize(); } catch (SSLHandshakeException e) { changeStatus(STATUS_OFFER_CHECK_FILESIZE); - HttpConnection.this.acceptedAutomatically = false; - HttpConnection.this.mXmppConnectionService.getNotificationService().push(message); + HttpDownloadConnection.this.acceptedAutomatically = false; + HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message); return; } catch (IOException e) { + Log.d(Config.LOGTAG, "io exception in http file size checker: " + e.getMessage()); + if (interactive) { + mXmppConnectionService.showErrorToastInUi(R.string.file_not_found_on_remote_host); + } cancel(); return; } file.setExpectedSize(size); if (size <= mHttpConnectionManager.getAutoAcceptFileSize()) { - HttpConnection.this.acceptedAutomatically = true; + HttpDownloadConnection.this.acceptedAutomatically = true; new Thread(new FileDownloader(interactive)).start(); } else { changeStatus(STATUS_OFFER); - HttpConnection.this.acceptedAutomatically = false; - HttpConnection.this.mXmppConnectionService.getNotificationService().push(message); + HttpDownloadConnection.this.acceptedAutomatically = false; + HttpDownloadConnection.this.mXmppConnectionService.getNotificationService().push(message); } } - private long retrieveFileSize() throws IOException, - SSLHandshakeException { - changeStatus(STATUS_CHECKING); - HttpURLConnection connection = (HttpURLConnection) mUrl - .openConnection(); - connection.setRequestMethod("HEAD"); - if (connection instanceof HttpsURLConnection) { - setupTrustManager((HttpsURLConnection) connection, interactive); - } - connection.connect(); - String contentLength = connection.getHeaderField("Content-Length"); - if (contentLength == null) { - throw new IOException(); - } - try { - return Long.parseLong(contentLength, 10); - } catch (NumberFormatException e) { - throw new IOException(); - } + private long retrieveFileSize() throws IOException { + Log.d(Config.LOGTAG,"retrieve file size. interactive:"+String.valueOf(interactive)); + changeStatus(STATUS_CHECKING); + HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection(); + connection.setRequestMethod("HEAD"); + if (connection instanceof HttpsURLConnection) { + mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); + } + connection.connect(); + String contentLength = connection.getHeaderField("Content-Length"); + if (contentLength == null) { + throw new IOException(); + } + try { + return Long.parseLong(contentLength, 10); + } catch (NumberFormatException e) { + throw new IOException(); + } } } @@ -235,19 +200,18 @@ public class HttpConnection implements Downloadable { } catch (SSLHandshakeException e) { changeStatus(STATUS_OFFER); } catch (IOException e) { + mXmppConnectionService.showErrorToastInUi(R.string.file_not_found_on_remote_host); cancel(); } } private void download() throws SSLHandshakeException, IOException { - HttpURLConnection connection = (HttpURLConnection) mUrl - .openConnection(); + HttpURLConnection connection = (HttpURLConnection) mUrl.openConnection(); if (connection instanceof HttpsURLConnection) { - setupTrustManager((HttpsURLConnection) connection, interactive); + mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, interactive); } connection.connect(); - BufferedInputStream is = new BufferedInputStream( - connection.getInputStream()); + BufferedInputStream is = new BufferedInputStream(connection.getInputStream()); file.getParentFile().mkdirs(); file.createNewFile(); OutputStream os = file.createOutputStream(); @@ -269,8 +233,8 @@ public class HttpConnection implements Downloadable { } private void updateImageBounds() { - message.setType(Message.TYPE_IMAGE); - mXmppConnectionService.getFileBackend().updateFileParams(message,mUrl); + message.setType(Message.TYPE_FILE); + mXmppConnectionService.getFileBackend().updateFileParams(message, mUrl); mXmppConnectionService.updateMessage(message); } @@ -302,9 +266,4 @@ public class HttpConnection implements Downloadable { public int getProgress() { return this.mProgress; } - - @Override - public String getMimeType() { - return ""; - } } diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java new file mode 100644 index 00000000..a3ab8dab --- /dev/null +++ b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java @@ -0,0 +1,204 @@ +package eu.siacs.conversations.http; + +import android.app.PendingIntent; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.net.ssl.HttpsURLConnection; + +import eu.siacs.conversations.Config; +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Transferable; +import eu.siacs.conversations.entities.DownloadableFile; +import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.ui.UiCallback; +import eu.siacs.conversations.utils.CryptoHelper; +import eu.siacs.conversations.utils.Xmlns; +import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.OnIqPacketReceived; +import eu.siacs.conversations.xmpp.jid.Jid; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; + +public class HttpUploadConnection implements Transferable { + + private HttpConnectionManager mHttpConnectionManager; + private XmppConnectionService mXmppConnectionService; + + private boolean canceled = false; + private Account account; + private DownloadableFile file; + private Message message; + private URL mGetUrl; + private URL mPutUrl; + + private byte[] key = null; + + private long transmitted = 0; + private long expected = 1; + + public HttpUploadConnection(HttpConnectionManager httpConnectionManager) { + this.mHttpConnectionManager = httpConnectionManager; + this.mXmppConnectionService = httpConnectionManager.getXmppConnectionService(); + } + + @Override + public boolean start() { + return false; + } + + @Override + public int getStatus() { + return STATUS_UPLOADING; + } + + @Override + public long getFileSize() { + return this.file.getExpectedSize(); + } + + @Override + public int getProgress() { + return (int) ((((double) transmitted) / expected) * 100); + } + + @Override + public void cancel() { + this.canceled = true; + } + + private void fail() { + mHttpConnectionManager.finishUploadConnection(this); + message.setTransferable(null); + mXmppConnectionService.markMessage(message,Message.STATUS_SEND_FAILED); + } + + public void init(Message message) { + this.message = message; + message.setTransferable(this); + mXmppConnectionService.markMessage(message,Message.STATUS_UNSEND); + this.account = message.getConversation().getAccount(); + this.file = mXmppConnectionService.getFileBackend().getFile(message, false); + this.file.setExpectedSize(this.file.getSize()); + + if (Config.ENCRYPT_ON_HTTP_UPLOADED) { + this.key = new byte[48]; + mXmppConnectionService.getRNG().nextBytes(this.key); + this.file.setKey(this.key); + } + + Jid host = account.getXmppConnection().findDiscoItemByFeature(Xmlns.HTTP_UPLOAD); + IqPacket request = mXmppConnectionService.getIqGenerator().requestHttpUploadSlot(host,file); + mXmppConnectionService.sendIqPacket(account, request, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.RESULT) { + Element slot = packet.findChild("slot",Xmlns.HTTP_UPLOAD); + if (slot != null) { + try { + mGetUrl = new URL(slot.findChildContent("get")); + mPutUrl = new URL(slot.findChildContent("put")); + if (!canceled) { + new Thread(new FileUploader()).start(); + } + } catch (MalformedURLException e) { + fail(); + } + } else { + fail(); + } + } else { + fail(); + } + } + }); + } + + private class FileUploader implements Runnable { + + @Override + public void run() { + this.upload(); + } + + private void upload() { + OutputStream os = null; + InputStream is = null; + HttpURLConnection connection = null; + try { + Log.d(Config.LOGTAG, "uploading to " + mPutUrl.toString()); + connection = (HttpURLConnection) mPutUrl.openConnection(); + if (connection instanceof HttpsURLConnection) { + mHttpConnectionManager.setupTrustManager((HttpsURLConnection) connection, true); + } + connection.setRequestMethod("PUT"); + connection.setFixedLengthStreamingMode((int) file.getExpectedSize()); + connection.setDoOutput(true); + connection.connect(); + os = connection.getOutputStream(); + is = file.createInputStream(); + transmitted = 0; + expected = file.getExpectedSize(); + int count = -1; + byte[] buffer = new byte[4096]; + while (((count = is.read(buffer)) != -1) && !canceled) { + transmitted += count; + os.write(buffer, 0, count); + mXmppConnectionService.updateConversationUi(); + } + os.flush(); + os.close(); + is.close(); + int code = connection.getResponseCode(); + if (code == 200 || code == 201) { + Log.d(Config.LOGTAG, "finished uploading file"); + Message.FileParams params = message.getFileParams(); + if (key != null) { + mGetUrl = new URL(mGetUrl.toString() + "#" + CryptoHelper.bytesToHex(key)); + } + mXmppConnectionService.getFileBackend().updateFileParams(message, mGetUrl); + message.setTransferable(null); + message.setCounterpart(message.getConversation().getJid().toBareJid()); + if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { + mXmppConnectionService.getPgpEngine().encrypt(message, new UiCallback<Message>() { + @Override + public void success(Message message) { + mXmppConnectionService.resendMessage(message); + } + + @Override + public void error(int errorCode, Message object) { + fail(); + } + + @Override + public void userInputRequried(PendingIntent pi, Message object) { + fail(); + } + }); + } else { + mXmppConnectionService.resendMessage(message); + } + } else { + fail(); + } + } catch (IOException e) { + Log.d(Config.LOGTAG, e.getMessage()); + fail(); + } finally { + FileBackend.close(is); + FileBackend.close(os); + if (connection != null) { + connection.disconnect(); + } + } + } + } +} diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java index 473195bd..11ddcc13 100644 --- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java +++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java @@ -15,6 +15,7 @@ import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.jid.Jid; +import eu.siacs.conversations.xmpp.stanzas.MessagePacket; public abstract class AbstractParser { @@ -33,24 +34,23 @@ public abstract class AbstractParser { * attribute or the current time is less than the value of the 'stamp' * attribute the current time is returned. */ - protected long getTimestamp(Element packet) { - long now = System.currentTimeMillis(); - Element delay = packet.findChild("delay"); - if (delay == null) { - return now; - } - String stamp = delay.getAttribute("stamp"); - if (stamp == null) { - return now; + public static Long getTimestamp(Element element, Long defaultValue) { + Element delay = element.findChild("delay","urn:xmpp:delay"); + if (delay != null) { + String stamp = delay.getAttribute("stamp"); + if (stamp != null) { + try { + return AbstractParser.parseTimestamp(delay.getAttribute("stamp")).getTime(); + } catch (ParseException e) { + return defaultValue; + } + } } - /*long time = parseTimestamp(stamp).getTime(); - return now < time ? now : time;*/ - try { - long time = parseTimestamp(stamp).getTime(); - return now < time ? now : time; - } catch (ParseException e) { - return now; - } + return defaultValue; + } + + protected long getTimestamp(Element packet) { + return getTimestamp(packet,System.currentTimeMillis()); } /** @@ -82,8 +82,7 @@ public abstract class AbstractParser { updateLastseen(packet, account, from, presenceOverwrite); } - protected void updateLastseen(final Element packet, final Account account, final Jid from, - final boolean presenceOverwrite) { + protected void updateLastseen(final Element packet, final Account account, final Jid from, final boolean presenceOverwrite) { final String presence = from == null || from.isBareJid() ? "" : from.getResourcepart(); final Contact contact = account.getRoster().getContact(from); final long timestamp = getTimestamp(packet); @@ -100,10 +99,6 @@ public abstract class AbstractParser { if (item == null) { return null; } - Element data = item.findChild("data", "urn:xmpp:avatar:data"); - if (data == null) { - return null; - } - return data.getContent(); + return item.findChildContent("data", "urn:xmpp:avatar:data"); } } diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 9f36f1e9..81fd89cd 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -1,9 +1,13 @@ package eu.siacs.conversations.parser; +import android.util.Log; +import android.util.Pair; + import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; import de.tzur.conversations.Settings; +import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; @@ -26,12 +30,12 @@ public class MessageParser extends AbstractParser implements super(service); } - private boolean extractChatState(Conversation conversation, final Element element) { - ChatState state = ChatState.parse(element); + private boolean extractChatState(Conversation conversation, final MessagePacket packet) { + ChatState state = ChatState.parse(packet); if (state != null && conversation != null) { final Account account = conversation.getAccount(); - Jid from = element.getAttributeAsJid("from"); - if (from != null && from.toBareJid().equals(account.getJid().toBareJid())) { + Jid from = packet.getFrom(); + if (from.toBareJid().equals(account.getJid().toBareJid())) { conversation.setOutgoingChatState(state); return false; } else { @@ -41,99 +45,40 @@ public class MessageParser extends AbstractParser implements return false; } - private Message parseChat(MessagePacket packet, Account account) { - final Jid jid = packet.getFrom(); - if (jid == null) { - return null; - } - Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid.toBareJid(), false); - String pgpBody = getPgpBody(packet); - Message finishedMessage; - if (pgpBody != null) { - finishedMessage = new Message(conversation, - pgpBody, Message.ENCRYPTION_PGP, Message.STATUS_RECEIVED); - } else { - finishedMessage = new Message(conversation, - packet.getBody(), Message.ENCRYPTION_NONE, - Message.STATUS_RECEIVED); - } - finishedMessage.setRemoteMsgId(packet.getId()); - finishedMessage.markable = isMarkable(packet); - if (conversation.getMode() == Conversation.MODE_MULTI - && !jid.isBareJid()) { - final Jid trueCounterpart = conversation.getMucOptions() - .getTrueCounterpart(jid.getResourcepart()); - if (trueCounterpart != null) { - updateLastseen(packet, account, trueCounterpart, false); - } - finishedMessage.setType(Message.TYPE_PRIVATE); - finishedMessage.setTrueCounterpart(trueCounterpart); - if (conversation.hasDuplicateMessage(finishedMessage)) { - return null; - } - } else { - updateLastseen(packet, account, true); - } - finishedMessage.setCounterpart(jid); - finishedMessage.setTime(getTimestamp(packet)); - extractChatState(conversation,packet); - return finishedMessage; - } - - private Message parseOtrChat(MessagePacket packet, Account account) { - final Jid to = packet.getTo(); - final Jid from = packet.getFrom(); - if (to == null || from == null) { - return null; - } - boolean properlyAddressed = !to.isBareJid() || account.countPresences() == 1; - Conversation conversation = mXmppConnectionService - .findOrCreateConversation(account, from.toBareJid(), false); + private Message parseOtrChat(String body, Jid from, String id, Conversation conversation) { String presence; if (from.isBareJid()) { presence = ""; } else { presence = from.getResourcepart(); } - extractChatState(conversation, packet); - updateLastseen(packet, account, true); - String body = packet.getBody(); if (body.matches("^\\?OTRv\\d{1,2}\\?.*")) { conversation.endOtrIfNeeded(); } if (!conversation.hasValidOtrSession()) { - if (properlyAddressed) { - conversation.startOtrSession(presence,false); - } else { - return null; - } + conversation.startOtrSession(presence,false); } else { - String foreignPresence = conversation.getOtrSession() - .getSessionID().getUserID(); + String foreignPresence = conversation.getOtrSession().getSessionID().getUserID(); if (!foreignPresence.equals(presence)) { conversation.endOtrIfNeeded(); - if (properlyAddressed) { - conversation.startOtrSession(presence, false); - } else { - return null; - } + conversation.startOtrSession(presence, false); } } try { - conversation.setLastReceivedOtrMessageId(packet.getId()); + conversation.setLastReceivedOtrMessageId(id); Session otrSession = conversation.getOtrSession(); - SessionStatus before = otrSession.getSessionStatus(); body = otrSession.transformReceiving(body); - SessionStatus after = otrSession.getSessionStatus(); - if ((before != after) && (after == SessionStatus.ENCRYPTED)) { + SessionStatus status = otrSession.getSessionStatus(); + if (body == null && status == SessionStatus.ENCRYPTED) { conversation.setNextEncryption(Message.ENCRYPTION_OTR); mXmppConnectionService.onOtrSessionEstablished(conversation); - } else if ((before != after) && (after == SessionStatus.FINISHED)) { + return null; + } else if (body == null && status == SessionStatus.FINISHED) { conversation.setNextEncryption(Message.ENCRYPTION_NONE); conversation.resetOtrSession(); mXmppConnectionService.updateConversationUi(); - } - if ((body == null) || (body.isEmpty())) { + return null; + } else if (body == null || (body.isEmpty())) { return null; } if (body.startsWith(CryptoHelper.FILETRANSFER)) { @@ -141,12 +86,7 @@ public class MessageParser extends AbstractParser implements conversation.setSymmetricKey(CryptoHelper.hexToBytes(key)); return null; } - Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR, - Message.STATUS_RECEIVED); - finishedMessage.setTime(getTimestamp(packet)); - finishedMessage.setRemoteMsgId(packet.getId()); - finishedMessage.markable = isMarkable(packet); - finishedMessage.setCounterpart(from); + Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR, Message.STATUS_RECEIVED); conversation.setLastReceivedOtrMessageId(null); return finishedMessage; } catch (Exception e) { @@ -155,499 +95,343 @@ public class MessageParser extends AbstractParser implements } } - private Message parseGroupchat(MessagePacket packet, Account account) { - int status; - final Jid from = packet.getFrom(); - if (from == null) { - return null; - } - if (mXmppConnectionService.find(account.pendingConferenceLeaves, - account, from.toBareJid()) != null) { - return null; - } - Conversation conversation = mXmppConnectionService - .findOrCreateConversation(account, from.toBareJid(), true); - final Jid trueCounterpart = conversation.getMucOptions().getTrueCounterpart(from.getResourcepart()); - if (trueCounterpart != null) { - updateLastseen(packet, account, trueCounterpart, false); - } - if (packet.hasChild("subject")) { - conversation.setHasMessagesLeftOnServer(true); - conversation.getMucOptions().setSubject(packet.findChild("subject").getContent()); - mXmppConnectionService.updateConversationUi(); - return null; + private class Invite { + Jid jid; + String password; + Invite(Jid jid, String password) { + this.jid = jid; + this.password = password; } - final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user"); - if (from.isBareJid() && (x == null || !x.hasChild("status"))) { - return null; - } else if (from.isBareJid() && x.hasChild("status")) { - for(Element child : x.getChildren()) { - if (child.getName().equals("status")) { - String code = child.getAttribute("code"); - if (code.contains(MucOptions.STATUS_CODE_ROOM_CONFIG_CHANGED)) { - mXmppConnectionService.fetchConferenceConfiguration(conversation); - } + public boolean execute(Account account) { + if (jid != null) { + Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid, true); + if (!conversation.getMucOptions().online()) { + conversation.getMucOptions().setPassword(password); + mXmppConnectionService.databaseBackend.updateConversation(conversation); + mXmppConnectionService.joinMuc(conversation); + mXmppConnectionService.updateConversationUi(); } + return true; } - return null; + return false; } + } - if (from.getResourcepart().equals(conversation.getMucOptions().getActualNick())) { - if (mXmppConnectionService.markMessage(conversation, - packet.getId(), Message.STATUS_SEND_RECEIVED)) { - return null; - } else if (packet.getId() == null) { - Message message = conversation.findSentMessageWithBody(packet.getBody()); - if (message != null) { - mXmppConnectionService.markMessage(message,Message.STATUS_SEND_RECEIVED); - return null; - } else { - status = Message.STATUS_SEND; - } - } else { - status = Message.STATUS_SEND; + private Invite extractInvite(Element message) { + Element x = message.findChild("x", "http://jabber.org/protocol/muc#user"); + if (x != null) { + Element invite = x.findChild("invite"); + if (invite != null) { + Element pw = x.findChild("password"); + return new Invite(message.getAttributeAsJid("from"), pw != null ? pw.getContent(): null); } } else { - status = Message.STATUS_RECEIVED; - } - String pgpBody = getPgpBody(packet); - Message finishedMessage; - if (pgpBody == null) { - finishedMessage = new Message(conversation, - packet.getBody(), Message.ENCRYPTION_NONE, status); - } else { - finishedMessage = new Message(conversation, pgpBody, - Message.ENCRYPTION_PGP, status); - } - finishedMessage.setRemoteMsgId(packet.getId()); - finishedMessage.markable = isMarkable(packet); - finishedMessage.setCounterpart(from); - if (status == Message.STATUS_RECEIVED) { - finishedMessage.setTrueCounterpart(conversation.getMucOptions() - .getTrueCounterpart(from.getResourcepart())); - } - if (packet.hasChild("delay") - && conversation.hasDuplicateMessage(finishedMessage)) { - return null; + x = message.findChild("x","jabber:x:conference"); + if (x != null) { + return new Invite(x.getAttributeAsJid("jid"),x.getAttribute("password")); + } } - finishedMessage.setTime(getTimestamp(packet)); - return finishedMessage; + return null; } - private Message parseCarbonMessage(final MessagePacket packet, final Account account) { - int status; - final Jid fullJid; - Element forwarded; - if (packet.hasChild("received", "urn:xmpp:carbons:2")) { - forwarded = packet.findChild("received", "urn:xmpp:carbons:2") - .findChild("forwarded", "urn:xmpp:forward:0"); - status = Message.STATUS_RECEIVED; - } else if (packet.hasChild("sent", "urn:xmpp:carbons:2")) { - forwarded = packet.findChild("sent", "urn:xmpp:carbons:2") - .findChild("forwarded", "urn:xmpp:forward:0"); - status = Message.STATUS_SEND; - } else { - return null; - } - if (forwarded == null) { - return null; - } - Element message = forwarded.findChild("message"); - if (message == null) { - return null; - } - if (!message.hasChild("body")) { - if (status == Message.STATUS_RECEIVED - && message.getAttribute("from") != null) { - parseNonMessage(message, account); - } else if (status == Message.STATUS_SEND - && message.hasChild("displayed", "urn:xmpp:chat-markers:0")) { - final Jid to = message.getAttributeAsJid("to"); - if (to != null) { - final Conversation conversation = mXmppConnectionService.find( - mXmppConnectionService.getConversations(), account, - to.toBareJid()); - if (conversation != null) { - mXmppConnectionService.markRead(conversation); + private void parseEvent(final Element event, final Jid from, final Account account) { + Element items = event.findChild("items"); + String node = items == null ? null : items.getAttribute("node"); + if ("urn:xmpp:avatar:metadata".equals(node)) { + Avatar avatar = Avatar.parseMetadata(items); + if (avatar != null) { + avatar.owner = from; + if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { + if (account.getJid().toBareJid().equals(from)) { + if (account.setAvatar(avatar.getFilename())) { + mXmppConnectionService.databaseBackend.updateAccount(account); + } + mXmppConnectionService.getAvatarService().clear(account); + mXmppConnectionService.updateConversationUi(); + mXmppConnectionService.updateAccountUi(); + } else { + Contact contact = account.getRoster().getContact(from); + contact.setAvatar(avatar); + mXmppConnectionService.getAvatarService().clear(contact); + mXmppConnectionService.updateConversationUi(); + mXmppConnectionService.updateRosterUi(); } + } else { + mXmppConnectionService.fetchAvatar(account, avatar); } } - return null; - } - if (status == Message.STATUS_RECEIVED) { - fullJid = message.getAttributeAsJid("from"); - if (fullJid == null) { - return null; - } else { - updateLastseen(message, account, true); - } - } else { - fullJid = message.getAttributeAsJid("to"); - if (fullJid == null) { - return null; + } else if ("http://jabber.org/protocol/nick".equals(node)) { + Element i = items.findChild("item"); + Element nick = i == null ? null : i.findChild("nick", "http://jabber.org/protocol/nick"); + if (nick != null) { + Contact contact = account.getRoster().getContact(from); + contact.setPresenceName(nick.getContent()); + mXmppConnectionService.getAvatarService().clear(account); + mXmppConnectionService.updateConversationUi(); + mXmppConnectionService.updateAccountUi(); } } - if (message.hasChild("x","http://jabber.org/protocol/muc#user") - && "chat".equals(message.getAttribute("type"))) { - return null; - } - Conversation conversation = mXmppConnectionService - .findOrCreateConversation(account, fullJid.toBareJid(), false); - String pgpBody = getPgpBody(message); - Message finishedMessage; - if (pgpBody != null) { - finishedMessage = new Message(conversation, pgpBody, - Message.ENCRYPTION_PGP, status); - } else { - String body = message.findChild("body").getContent(); - finishedMessage = new Message(conversation, body, - Message.ENCRYPTION_NONE, status); - } - extractChatState(conversation,message); - finishedMessage.setTime(getTimestamp(message)); - finishedMessage.setRemoteMsgId(message.getAttribute("id")); - finishedMessage.markable = isMarkable(message); - finishedMessage.setCounterpart(fullJid); - if (conversation.getMode() == Conversation.MODE_MULTI - && !fullJid.isBareJid()) { - finishedMessage.setType(Message.TYPE_PRIVATE); - finishedMessage.setTrueCounterpart(conversation.getMucOptions() - .getTrueCounterpart(fullJid.getResourcepart())); - if (conversation.hasDuplicateMessage(finishedMessage)) { - return null; + } + + private boolean handleErrorMessage(Account account, MessagePacket packet) { + if (packet.getType() == MessagePacket.TYPE_ERROR) { + Jid from = packet.getFrom(); + if (from != null) { + Message message = mXmppConnectionService.markMessage(account, + from.toBareJid(), + packet.getId(), + Message.STATUS_SEND_FAILED); + if (message != null && message.getEncryption() == Message.ENCRYPTION_OTR) { + message.getConversation().endOtrIfNeeded(); + } } + return true; } - return finishedMessage; + return false; } - private Message parseMamMessage(MessagePacket packet, final Account account) { - final Element result = packet.findChild("result","urn:xmpp:mam:0"); - if (result == null ) { - return null; - } - final MessageArchiveService.Query query = this.mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid")); - if (query!=null) { - query.incrementTotalCount(); - } - final Element forwarded = result.findChild("forwarded","urn:xmpp:forward:0"); - if (forwarded == null) { - return null; - } - final Element message = forwarded.findChild("message"); - if (message == null) { - return null; + @Override + public void onMessagePacketReceived(Account account, MessagePacket original) { + if (handleErrorMessage(account, original)) { + return; } - final Element body = message.findChild("body"); - if (body == null || message.hasChild("private","urn:xmpp:carbons:2") || message.hasChild("no-copy","urn:xmpp:hints")) { - return null; + final MessagePacket packet; + Long timestamp = null; + final boolean isForwarded; + String serverMsgId = null; + final Element fin = original.findChild("fin", "urn:xmpp:mam:0"); + if (fin != null) { + mXmppConnectionService.getMessageArchiveService().processFin(fin,original.getFrom()); + return; } - int encryption; - String content = getPgpBody(message); - if (content != null) { - encryption = Message.ENCRYPTION_PGP; + final Element result = original.findChild("result","urn:xmpp:mam:0"); + final MessageArchiveService.Query query = result == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid")); + if (query != null && query.validFrom(original.getFrom())) { + Pair<MessagePacket, Long> f = original.getForwardedMessagePacket("result", "urn:xmpp:mam:0"); + if (f == null) { + return; + } + timestamp = f.second; + packet = f.first; + isForwarded = true; + serverMsgId = result.getAttribute("id"); + query.incrementTotalCount(); + } else if (query != null) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": received mam result from invalid sender"); + return; + } else if (original.fromServer(account)) { + Pair<MessagePacket, Long> f; + f = original.getForwardedMessagePacket("received", "urn:xmpp:carbons:2"); + f = f == null ? original.getForwardedMessagePacket("sent", "urn:xmpp:carbons:2") : f; + packet = f != null ? f.first : original; + if (handleErrorMessage(account, packet)) { + return; + } + timestamp = f != null ? f.second : null; + isForwarded = f != null; } else { - encryption = Message.ENCRYPTION_NONE; - content = body.getContent(); + packet = original; + isForwarded = false; } - if (content == null) { - return null; + + if (timestamp == null) { + timestamp = AbstractParser.getTimestamp(packet, System.currentTimeMillis()); } - final long timestamp = getTimestamp(forwarded); - final Jid to = message.getAttributeAsJid("to"); - final Jid from = message.getAttributeAsJid("from"); - Jid counterpart; + final String body = packet.getBody(); + final String encrypted = packet.findChildContent("x", "jabber:x:encrypted"); + final Element mucUserElement = packet.findChild("x","http://jabber.org/protocol/muc#user"); int status; - Conversation conversation; - if (from!=null && to != null && from.toBareJid().equals(account.getJid().toBareJid())) { + final Jid counterpart; + final Jid to = packet.getTo(); + final Jid from = packet.getFrom(); + final String remoteMsgId = packet.getId(); + + if (from == null || to == null) { + Log.d(Config.LOGTAG,"no to or from in: "+packet.toString()); + return; + } + + boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT; + boolean isProperlyAddressed = !to.isBareJid() || account.countPresences() == 1; + boolean isMucStatusMessage = from.isBareJid() && mucUserElement != null && mucUserElement.hasChild("status"); + if (packet.fromAccount(account)) { status = Message.STATUS_SEND; - conversation = this.mXmppConnectionService.findOrCreateConversation(account,to.toBareJid(),false,query); counterpart = to; - } else if (from !=null && to != null) { + } else { status = Message.STATUS_RECEIVED; - conversation = this.mXmppConnectionService.findOrCreateConversation(account,from.toBareJid(),false,query); counterpart = from; - } else { - return null; - } - Message finishedMessage = new Message(conversation,content,encryption,status); - finishedMessage.setTime(timestamp); - finishedMessage.setCounterpart(counterpart); - finishedMessage.setRemoteMsgId(message.getAttribute("id")); - finishedMessage.setServerMsgId(result.getAttribute("id")); - if (conversation.hasDuplicateMessage(finishedMessage)) { - return null; } - if (query!=null) { - query.incrementMessageCount(); - } - return finishedMessage; - } - - private void parseError(final MessagePacket packet, final Account account) { - final Jid from = packet.getFrom(); - mXmppConnectionService.markMessage(account, from.toBareJid(), - packet.getId(), Message.STATUS_SEND_FAILED); - } - private void parseNonMessage(Element packet, Account account) { - final Jid from = packet.getAttributeAsJid("from"); - if (extractChatState(from == null ? null : mXmppConnectionService.find(account,from), packet)) { - mXmppConnectionService.updateConversationUi(); - } - Element invite = extractInvite(packet); - if (invite != null) { - Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, from, true); - if (!conversation.getMucOptions().online()) { - Element password = invite.findChild("password"); - conversation.getMucOptions().setPassword(password == null ? null : password.getContent()); - mXmppConnectionService.databaseBackend.updateConversation(conversation); - mXmppConnectionService.joinMuc(conversation); - mXmppConnectionService.updateConversationUi(); - } - } - if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) { - Element event = packet.findChild("event", - "http://jabber.org/protocol/pubsub#event"); - parseEvent(event, from, account); - } else if (from != null && packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) { - String id = packet - .findChild("displayed", "urn:xmpp:chat-markers:0") - .getAttribute("id"); - updateLastseen(packet, account, true); - final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), id, Message.STATUS_SEND_DISPLAYED); - Message message = displayedMessage == null ? null :displayedMessage.prev(); - while (message != null - && message.getStatus() == Message.STATUS_SEND_RECEIVED - && message.getTimeSent() < displayedMessage.getTimeSent()) { - mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED); - message = message.prev(); - } - } else if (from != null - && packet.hasChild("received", "urn:xmpp:chat-markers:0")) { - String id = packet.findChild("received", "urn:xmpp:chat-markers:0") - .getAttribute("id"); - updateLastseen(packet, account, false); - mXmppConnectionService.markMessage(account, from.toBareJid(), - id, Message.STATUS_SEND_RECEIVED); - } else if (from != null - && packet.hasChild("received", "urn:xmpp:receipts")) { - String id = packet.findChild("received", "urn:xmpp:receipts") - .getAttribute("id"); - updateLastseen(packet, account, false); - mXmppConnectionService.markMessage(account, from.toBareJid(), - id, Message.STATUS_SEND_RECEIVED); + Invite invite = extractInvite(packet); + if (invite != null && invite.execute(account)) { + return; } - } - private Element extractInvite(Element message) { - Element x = message.findChild("x","http://jabber.org/protocol/muc#user"); - if (x == null) { - x = message.findChild("x","jabber:x:conference"); - } - if (x != null && x.hasChild("invite")) { - return x; - } else { - return null; + if (extractChatState(mXmppConnectionService.find(account,from), packet)) { + mXmppConnectionService.updateConversationUi(); } - } - private void parseEvent(final Element event, final Jid from, final Account account) { - Element items = event.findChild("items"); - if (items == null) { - return; - } - String node = items.getAttribute("node"); - if (node == null) { - return; - } - if (node.equals("urn:xmpp:avatar:metadata")) { - Avatar avatar = Avatar.parseMetadata(items); - if (avatar != null) { - avatar.owner = from; - if (mXmppConnectionService.getFileBackend().isAvatarCached( - avatar)) { - if (account.getJid().toBareJid().equals(from)) { - if (account.setAvatar(avatar.getFilename())) { - mXmppConnectionService.databaseBackend - .updateAccount(account); - } - mXmppConnectionService.getAvatarService().clear( - account); - mXmppConnectionService.updateConversationUi(); - mXmppConnectionService.updateAccountUi(); + if ((body != null || encrypted != null) && !isMucStatusMessage) { + Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.toBareJid(), isTypeGroupChat); + if (isTypeGroupChat) { + if (counterpart.getResourcepart().equals(conversation.getMucOptions().getActualNick())) { + status = Message.STATUS_SEND_RECEIVED; + if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status)) { + return; } else { - Contact contact = account.getRoster().getContact( - from); - contact.setAvatar(avatar.getFilename()); - mXmppConnectionService.getAvatarService().clear( - contact); - mXmppConnectionService.updateConversationUi(); - mXmppConnectionService.updateRosterUi(); + Message message = conversation.findSentMessageWithBody(body); + if (message != null) { + message.setRemoteMsgId(remoteMsgId); + mXmppConnectionService.markMessage(message, status); + return; + } } } else { - mXmppConnectionService.fetchAvatar(account, avatar); + status = Message.STATUS_RECEIVED; } } - } else if (node.equals("http://jabber.org/protocol/nick")) { - Element item = items.findChild("item"); - if (item != null) { - Element nick = item.findChild("nick", - "http://jabber.org/protocol/nick"); - if (nick != null) { - if (from != null) { - Contact contact = account.getRoster().getContact( - from); - contact.setPresenceName(nick.getContent()); - mXmppConnectionService.getAvatarService().clear(account); - mXmppConnectionService.updateConversationUi(); - mXmppConnectionService.updateAccountUi(); + Message message; + if (body != null && body.startsWith("?OTR")) { + if (!isForwarded && !isTypeGroupChat && isProperlyAddressed) { + message = parseOtrChat(body, from, remoteMsgId, conversation); + if (message == null) { + return; } + } else { + message = new Message(conversation, body, Message.ENCRYPTION_NONE, status); } + } else if (encrypted != null) { + message = new Message(conversation, encrypted, Message.ENCRYPTION_PGP, status); + } else { + message = new Message(conversation, body, Message.ENCRYPTION_NONE, status); } - } - } - - private String getPgpBody(Element message) { - Element child = message.findChild("x", "jabber:x:encrypted"); - if (child == null) { - return null; - } else { - return child.getContent(); - } - } - - private boolean isMarkable(Element message) { - return message.hasChild("markable", "urn:xmpp:chat-markers:0"); - } - - @Override - public void onMessagePacketReceived(Account account, MessagePacket packet) { - Message message = null; - this.parseNick(packet, account); - if ((packet.getType() == MessagePacket.TYPE_CHAT || packet.getType() == MessagePacket.TYPE_NORMAL)) { - if ((packet.getBody() != null) - && (packet.getBody().startsWith("?OTR"))) { - message = this.parseOtrChat(packet, account); - if (message != null) { - message.markUnread(); + message.setCounterpart(counterpart); + message.setRemoteMsgId(remoteMsgId); + message.setServerMsgId(serverMsgId); + message.setTime(timestamp); + message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0"); + if (conversation.getMode() == Conversation.MODE_MULTI) { + message.setTrueCounterpart(conversation.getMucOptions().getTrueCounterpart(counterpart.getResourcepart())); + if (!isTypeGroupChat) { + message.setType(Message.TYPE_PRIVATE); } - } else if (packet.hasChild("body") && extractInvite(packet) == null) { - message = this.parseChat(packet, account); - if (message != null) { + } + updateLastseen(packet,account,true); + boolean checkForDuplicates = serverMsgId != null + || (isTypeGroupChat && packet.hasChild("delay","urn:xmpp:delay")) + || message.getType() == Message.TYPE_PRIVATE; + if (checkForDuplicates && conversation.hasDuplicateMessage(message)) { + Log.d(Config.LOGTAG,"skipping duplicate message from "+message.getCounterpart().toString()+" "+message.getBody()); + return; + } + if (query != null) { + query.incrementMessageCount(); + } + conversation.add(message); + if (serverMsgId == null) { + if (status == Message.STATUS_SEND || status == Message.STATUS_SEND_RECEIVED) { + mXmppConnectionService.markRead(conversation); + account.activateGracePeriod(); + } else { message.markUnread(); } - } else if (packet.hasChild("received", "urn:xmpp:carbons:2") - || (packet.hasChild("sent", "urn:xmpp:carbons:2"))) { - message = this.parseCarbonMessage(packet, account); - if (message != null) { - if (message.getStatus() == Message.STATUS_SEND) { - account.activateGracePeriod(); - mXmppConnectionService.markRead(message.getConversation()); - } else { - message.markUnread(); - } + mXmppConnectionService.updateConversationUi(); + } + + if (mXmppConnectionService.confirmMessages() && remoteMsgId != null && !isForwarded) { + if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) { + MessagePacket receipt = mXmppConnectionService + .getMessageGenerator().received(account, packet, "urn:xmpp:chat-markers:0"); + mXmppConnectionService.sendMessagePacket(account, receipt); } - } else if (packet.hasChild("result","urn:xmpp:mam:0")) { - message = parseMamMessage(packet, account); - if (message != null) { - Conversation conversation = message.getConversation(); - conversation.add(message); - mXmppConnectionService.databaseBackend.createMessage(message); + if (packet.hasChild("request", "urn:xmpp:receipts")) { + MessagePacket receipt = mXmppConnectionService + .getMessageGenerator().received(account, packet, "urn:xmpp:receipts"); + mXmppConnectionService.sendMessagePacket(account, receipt); } - return; - } else if (packet.hasChild("fin","urn:xmpp:mam:0")) { - Element fin = packet.findChild("fin","urn:xmpp:mam:0"); - mXmppConnectionService.getMessageArchiveService().processFin(fin); - } else { - parseNonMessage(packet, account); } - } else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) { - message = this.parseGroupchat(packet, account); - if (message != null) { - if (message.getStatus() == Message.STATUS_RECEIVED) { - message.markUnread(); - } else { - mXmppConnectionService.markRead(message.getConversation()); - account.activateGracePeriod(); + if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().advancedStreamFeaturesLoaded()) { + if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) { + mXmppConnectionService.updateConversation(conversation); } } - } else if (packet.getType() == MessagePacket.TYPE_ERROR) { - this.parseError(packet, account); - return; - } else if (packet.getType() == MessagePacket.TYPE_HEADLINE) { - this.parseHeadline(packet, account); - return; - } - if ((message == null) || (message.getBody() == null)) { - return; - } - if ((Settings.CONFIRM_MESSAGE_RECEIVED) - && ((packet.getId() != null))) { - if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) { - MessagePacket receipt = mXmppConnectionService - .getMessageGenerator().received(account, packet, - "urn:xmpp:chat-markers:0"); - mXmppConnectionService.sendMessagePacket(account, receipt); + + if (message.getStatus() == Message.STATUS_RECEIVED + && conversation.getOtrSession() != null + && !conversation.getOtrSession().getSessionID().getUserID() + .equals(message.getCounterpart().getResourcepart())) { + conversation.endOtrIfNeeded(); } - if (packet.hasChild("request", "urn:xmpp:receipts")) { - MessagePacket receipt = mXmppConnectionService - .getMessageGenerator().received(account, packet, - "urn:xmpp:receipts"); - mXmppConnectionService.sendMessagePacket(account, receipt); + + if (message.getEncryption() == Message.ENCRYPTION_NONE || mXmppConnectionService.saveEncryptedMessages()) { + mXmppConnectionService.databaseBackend.createMessage(message); } - } - Conversation conversation = message.getConversation(); - conversation.add(message); - if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().advancedStreamFeaturesLoaded()) { - if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) { - mXmppConnectionService.updateConversation(conversation); + final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager(); + if (message.trusted() && message.treatAsDownloadable() != Message.Decision.NEVER && manager.getAutoAcceptFileSize() > 0) { + manager.createNewDownloadConnection(message); + } else if (!message.isRead()) { + mXmppConnectionService.getNotificationService().push(message); } - } + } else { //no body + if (isTypeGroupChat) { + Conversation conversation = mXmppConnectionService.find(account, from.toBareJid()); + if (packet.hasChild("subject")) { + if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { + conversation.setHasMessagesLeftOnServer(conversation.countMessages() > 0); + conversation.getMucOptions().setSubject(packet.findChildContent("subject")); + mXmppConnectionService.updateConversationUi(); + return; + } + } - if (message.getStatus() == Message.STATUS_RECEIVED - && conversation.getOtrSession() != null - && !conversation.getOtrSession().getSessionID().getUserID() - .equals(message.getCounterpart().getResourcepart())) { - conversation.endOtrIfNeeded(); + if (conversation != null && isMucStatusMessage) { + for (Element child : mucUserElement.getChildren()) { + if (child.getName().equals("status") + && MucOptions.STATUS_CODE_ROOM_CONFIG_CHANGED.equals(child.getAttribute("code"))) { + mXmppConnectionService.fetchConferenceConfiguration(conversation); + } + } + } + } } - if (packet.getType() != MessagePacket.TYPE_ERROR) { - if (message.getEncryption() == Message.ENCRYPTION_NONE - || mXmppConnectionService.saveEncryptedMessages()) { - mXmppConnectionService.databaseBackend.createMessage(message); + Element received = packet.findChild("received", "urn:xmpp:chat-markers:0"); + if (received == null) { + received = packet.findChild("received", "urn:xmpp:receipts"); + } + if (received != null && !packet.fromAccount(account)) { + mXmppConnectionService.markMessage(account, from.toBareJid(), received.getAttribute("id"), Message.STATUS_SEND_RECEIVED); + } + Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0"); + if (displayed != null) { + if (packet.fromAccount(account)) { + Conversation conversation = mXmppConnectionService.find(account,counterpart.toBareJid()); + if (conversation != null) { + mXmppConnectionService.markRead(conversation); + } + } else { + updateLastseen(packet, account, true); + final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), displayed.getAttribute("id"), Message.STATUS_SEND_DISPLAYED); + Message message = displayedMessage == null ? null : displayedMessage.prev(); + while (message != null + && message.getStatus() == Message.STATUS_SEND_RECEIVED + && message.getTimeSent() < displayedMessage.getTimeSent()) { + mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED); + message = message.prev(); + } } } - final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager(); - if (message.trusted() - && Settings.DOWNLOAD_IMAGE_LINKS - && mXmppConnectionService.isDownloadAllowedInConnection() - && message.bodyContainsDownloadable() - && manager.getAutoAcceptFileSize() > 0) { - manager.createNewConnection(message); - } else if (!message.isRead()) { - mXmppConnectionService.getNotificationService().push(message); - } - mXmppConnectionService.updateConversationUi(); - } - private void parseHeadline(MessagePacket packet, Account account) { - if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) { - Element event = packet.findChild("event", - "http://jabber.org/protocol/pubsub#event"); - parseEvent(event, packet.getFrom(), account); + Element event = packet.findChild("event", "http://jabber.org/protocol/pubsub#event"); + if (event != null) { + parseEvent(event, from, account); } - } - private void parseNick(MessagePacket packet, Account account) { - Element nick = packet.findChild("nick", - "http://jabber.org/protocol/nick"); + String nick = packet.findChildContent("nick", "http://jabber.org/protocol/nick"); if (nick != null) { - if (packet.getFrom() != null) { - Contact contact = account.getRoster().getContact( - packet.getFrom()); - contact.setPresenceName(nick.getContent()); - } + Contact contact = account.getRoster().getContact(from); + contact.setPresenceName(nick); } } } diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java index 7505b091..d83347d8 100644 --- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java +++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java @@ -13,6 +13,7 @@ import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnPresencePacketReceived; import eu.siacs.conversations.xmpp.jid.Jid; +import eu.siacs.conversations.xmpp.pep.Avatar; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; public class PresenceParser extends AbstractParser implements @@ -44,38 +45,37 @@ public class PresenceParser extends AbstractParser implements } public void parseContactPresence(PresencePacket packet, Account account) { - PresenceGenerator mPresenceGenerator = mXmppConnectionService - .getPresenceGenerator(); - if (packet.getFrom() == null) { + PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator(); + final Jid from = packet.getFrom(); + if (from == null) { return; } - final Jid from = packet.getFrom(); - String type = packet.getAttribute("type"); - Contact contact = account.getRoster().getContact(packet.getFrom()); + final String type = packet.getAttribute("type"); + final Contact contact = account.getRoster().getContact(from); if (type == null) { - String presence; - if (!from.isBareJid()) { - presence = from.getResourcepart(); - } else { - presence = ""; + String presence = from.isBareJid() ? "" : from.getResourcepart(); + contact.setPresenceName(packet.findChildContent("nick", "http://jabber.org/protocol/nick")); + Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update")); + if (avatar != null && !contact.isSelf()) { + avatar.owner = from.toBareJid(); + if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { + if (contact.setAvatar(avatar)) { + mXmppConnectionService.getAvatarService().clear(contact); + mXmppConnectionService.updateConversationUi(); + mXmppConnectionService.updateRosterUi(); + } + } else { + mXmppConnectionService.fetchAvatar(account, avatar); + } } int sizeBefore = contact.getPresences().size(); - contact.updatePresence(presence, - Presences.parseShow(packet.findChild("show"))); + contact.updatePresence(presence, Presences.parseShow(packet.findChild("show"))); PgpEngine pgp = mXmppConnectionService.getPgpEngine(); - if (pgp != null) { - Element x = packet.findChild("x", "jabber:x:signed"); - if (x != null) { - Element status = packet.findChild("status"); - String msg; - if (status != null) { - msg = status.getContent(); - } else { - msg = ""; - } - contact.setPgpKeyId(pgp.fetchKeyId(account, msg, - x.getContent())); - } + Element x = packet.findChild("x", "jabber:x:signed"); + if (pgp != null && x != null) { + Element status = packet.findChild("status"); + String msg = status != null ? status.getContent() : ""; + contact.setPgpKeyId(pgp.fetchKeyId(account, msg, x.getContent())); } boolean online = sizeBefore < contact.getPresences().size(); updateLastseen(packet, account, false); @@ -86,8 +86,7 @@ public class PresenceParser extends AbstractParser implements } else { contact.removePresence(from.getResourcepart()); } - mXmppConnectionService.onContactStatusChanged - .onContactStatusChanged(contact, false); + mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, false); } else if (type.equals("subscribe")) { if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) { mXmppConnectionService.sendPresencePacket(account, @@ -96,11 +95,6 @@ public class PresenceParser extends AbstractParser implements contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST); } } - Element nick = packet.findChild("nick", - "http://jabber.org/protocol/nick"); - if (nick != null) { - contact.setPresenceName(nick.getContent()); - } mXmppConnectionService.updateRosterUi(); } diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index 28e1c47e..d11b02fa 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -4,11 +4,13 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Roster; +import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; import android.content.Context; @@ -16,13 +18,14 @@ import android.database.Cursor; import android.database.sqlite.SQLiteCantOpenDatabaseException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; public class DatabaseBackend extends SQLiteOpenHelper { private static DatabaseBackend instance = null; private static final String DATABASE_NAME = "history"; - private static final int DATABASE_VERSION = 13; + private static final int DATABASE_VERSION = 14; private static String CREATE_CONTATCS_STATEMENT = "create table " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " @@ -130,6 +133,88 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL("delete from "+Contact.TABLENAME); db.execSQL("update "+Account.TABLENAME+" set "+Account.ROSTERVERSION+" = NULL"); } + if (oldVersion < 14 && newVersion >= 14) { + // migrate db to new, canonicalized JID domainpart representation + + // Conversation table + Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME, new String[0]); + while(cursor.moveToNext()) { + String newJid; + try { + newJid = Jid.fromString( + cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID)) + ).toString(); + } catch (InvalidJidException ignored) { + Log.e(Config.LOGTAG, "Failed to migrate Conversation CONTACTJID " + +cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID)) + +": " + ignored +". Skipping..."); + continue; + } + + String updateArgs[] = { + newJid, + cursor.getString(cursor.getColumnIndex(Conversation.UUID)), + }; + db.execSQL("update " + Conversation.TABLENAME + + " set " + Conversation.CONTACTJID + " = ? " + + " where " + Conversation.UUID + " = ?", updateArgs); + } + cursor.close(); + + // Contact table + cursor = db.rawQuery("select * from " + Contact.TABLENAME, new String[0]); + while(cursor.moveToNext()) { + String newJid; + try { + newJid = Jid.fromString( + cursor.getString(cursor.getColumnIndex(Contact.JID)) + ).toString(); + } catch (InvalidJidException ignored) { + Log.e(Config.LOGTAG, "Failed to migrate Contact JID " + +cursor.getString(cursor.getColumnIndex(Contact.JID)) + +": " + ignored +". Skipping..."); + continue; + } + + String updateArgs[] = { + newJid, + cursor.getString(cursor.getColumnIndex(Contact.ACCOUNT)), + cursor.getString(cursor.getColumnIndex(Contact.JID)), + }; + db.execSQL("update " + Contact.TABLENAME + + " set " + Contact.JID + " = ? " + + " where " + Contact.ACCOUNT + " = ? " + + " AND " + Contact.JID + " = ?", updateArgs); + } + cursor.close(); + + // Account table + cursor = db.rawQuery("select * from " + Account.TABLENAME, new String[0]); + while(cursor.moveToNext()) { + String newServer; + try { + newServer = Jid.fromParts( + cursor.getString(cursor.getColumnIndex(Account.USERNAME)), + cursor.getString(cursor.getColumnIndex(Account.SERVER)), + "mobile" + ).getDomainpart(); + } catch (InvalidJidException ignored) { + Log.e(Config.LOGTAG, "Failed to migrate Account SERVER " + +cursor.getString(cursor.getColumnIndex(Account.SERVER)) + +": " + ignored +". Skipping..."); + continue; + } + + String updateArgs[] = { + newServer, + cursor.getString(cursor.getColumnIndex(Account.UUID)), + }; + db.execSQL("update " + Account.TABLENAME + + " set " + Account.SERVER + " = ? " + + " where " + Account.UUID + " = ?", updateArgs); + } + cursor.close(); + } } public static synchronized DatabaseBackend getInstance(Context context) { @@ -301,8 +386,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { SQLiteDatabase db = this.getReadableDatabase(); Cursor cursor; String args[] = { roster.getAccount().getUuid() }; - cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT + "=?", - args, null, null, null); + cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT + "=?", args, null, null, null); while (cursor.moveToNext()) { roster.initContact(Contact.fromCursor(cursor)); } diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index c499d499..ab191285 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.persistance; import java.io.ByteArrayOutputStream; +import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -12,6 +13,7 @@ import java.security.DigestOutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Date; import java.util.Locale; @@ -31,6 +33,7 @@ import android.webkit.MimeTypeMap; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; @@ -42,8 +45,7 @@ public class FileBackend { private static int IMAGE_SIZE = 1920; - private SimpleDateFormat imageDateFormat = new SimpleDateFormat( - "yyyyMMdd_HHmmssSSS", Locale.US); + private final SimpleDateFormat imageDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US); private XmppConnectionService mXmppConnectionService; @@ -78,10 +80,10 @@ public class FileBackend { if (path.startsWith("/")) { return new DownloadableFile(path); } else { - if (message.getType() == Message.TYPE_FILE) { + if (Arrays.asList(Transferable.VALID_IMAGE_EXTENSIONS).contains(extension)) { return new DownloadableFile(getConversationsFileDirectory() + path); } else { - return new DownloadableFile(getConversationsImageDirectory()+path); + return new DownloadableFile(getConversationsImageDirectory() + path); } } } @@ -110,9 +112,7 @@ public class FileBackend { scalledW = size; scalledH = (int) (h / ((double) w / size)); } - Bitmap scalledBitmap = Bitmap.createScaledBitmap(originalBitmap, - scalledW, scalledH, true); - return scalledBitmap; + return Bitmap.createScaledBitmap(originalBitmap, scalledW, scalledH, true); } else { return originalBitmap; } @@ -148,31 +148,35 @@ public class FileBackend { } public DownloadableFile copyFileToPrivateStorage(Message message, Uri uri) throws FileCopyException { + Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage"); + String mime = mXmppConnectionService.getContentResolver().getType(uri); + String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime); + message.setRelativeFilePath(message.getUuid() + "." + extension); + DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message); + file.getParentFile().mkdirs(); + OutputStream os = null; + InputStream is = null; try { - Log.d(Config.LOGTAG, "copy " + uri.toString() + " to private storage"); - String mime = mXmppConnectionService.getContentResolver().getType(uri); - String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime); - message.setRelativeFilePath(message.getUuid() + "." + extension); - DownloadableFile file = mXmppConnectionService.getFileBackend().getFile(message); - file.getParentFile().mkdirs(); file.createNewFile(); - OutputStream os = new FileOutputStream(file); - InputStream is = mXmppConnectionService.getContentResolver().openInputStream(uri); + os = new FileOutputStream(file); + is = mXmppConnectionService.getContentResolver().openInputStream(uri); byte[] buffer = new byte[1024]; - int length; - while ((length = is.read(buffer)) > 0) { + int length; + while ((length = is.read(buffer)) > 0) { os.write(buffer, 0, length); - } + } os.flush(); - os.close(); - is.close(); - Log.d(Config.LOGTAG, "output file name " + mXmppConnectionService.getFileBackend().getFile(message)); - return file; - } catch (FileNotFoundException e) { + } catch(FileNotFoundException e) { throw new FileCopyException(R.string.error_file_not_found); } catch (IOException e) { + e.printStackTrace(); throw new FileCopyException(R.string.error_io_exception); + } finally { + close(os); + close(is); } + Log.d(Config.LOGTAG, "output file name " + mXmppConnectionService.getFileBackend().getFile(message)); + return file; } public DownloadableFile copyImageToPrivateStorage(Message message, Uri image) @@ -182,49 +186,48 @@ public class FileBackend { private DownloadableFile copyImageToPrivateStorage(Message message, Uri image, int sampleSize) throws FileCopyException { + DownloadableFile file = getFile(message); + file.getParentFile().mkdirs(); + InputStream is = null; + OutputStream os = null; try { - InputStream is = mXmppConnectionService.getContentResolver() - .openInputStream(image); - DownloadableFile file = getFile(message); - file.getParentFile().mkdirs(); file.createNewFile(); + is = mXmppConnectionService.getContentResolver().openInputStream(image); + os = new FileOutputStream(file); + Bitmap originalBitmap; BitmapFactory.Options options = new BitmapFactory.Options(); int inSampleSize = (int) Math.pow(2, sampleSize); - Log.d(Config.LOGTAG, "reading bitmap with sample size " - + inSampleSize); + Log.d(Config.LOGTAG, "reading bitmap with sample size " + inSampleSize); options.inSampleSize = inSampleSize; originalBitmap = BitmapFactory.decodeStream(is, null, options); is.close(); if (originalBitmap == null) { throw new FileCopyException(R.string.error_not_an_image_file); } - Bitmap scalledBitmap = resize(originalBitmap, IMAGE_SIZE); - originalBitmap = null; + Bitmap scaledBitmap = resize(originalBitmap, IMAGE_SIZE); int rotation = getRotation(image); if (rotation > 0) { - scalledBitmap = rotate(scalledBitmap, rotation); + scaledBitmap = rotate(scaledBitmap, rotation); } - OutputStream os = new FileOutputStream(file); - boolean success = scalledBitmap.compress( - Bitmap.CompressFormat.WEBP, 75, os); + + boolean success = scaledBitmap.compress(Bitmap.CompressFormat.WEBP, 75, os); if (!success) { throw new FileCopyException(R.string.error_compressing_image); } os.flush(); - os.close(); long size = file.getSize(); - int width = scalledBitmap.getWidth(); - int height = scalledBitmap.getHeight(); - message.setBody(Long.toString(size) + ',' + width + ',' + height); + int width = scaledBitmap.getWidth(); + int height = scaledBitmap.getHeight(); + message.setBody(Long.toString(size) + '|' + width + '|' + height); return file; } catch (FileNotFoundException e) { throw new FileCopyException(R.string.error_file_not_found); } catch (IOException e) { + e.printStackTrace(); throw new FileCopyException(R.string.error_io_exception); } catch (SecurityException e) { - throw new FileCopyException( - R.string.error_security_exception_during_image_copy); + throw new FileCopyException(R.string.error_security_exception_during_image_copy); } catch (OutOfMemoryError e) { ++sampleSize; if (sampleSize <= 3) { @@ -232,23 +235,26 @@ public class FileBackend { } else { throw new FileCopyException(R.string.error_out_of_memory); } + } catch (NullPointerException e) { + throw new FileCopyException(R.string.error_io_exception); + } finally { + close(os); + close(is); } } private int getRotation(Uri image) { + InputStream is = null; try { - InputStream is = mXmppConnectionService.getContentResolver() - .openInputStream(image); + is = mXmppConnectionService.getContentResolver().openInputStream(image); return ExifHelper.getOrientation(is); } catch (FileNotFoundException e) { return 0; + } finally { + close(is); } } - public Bitmap getImageFromMessage(Message message) { - return BitmapFactory.decodeFile(getFile(message).getAbsolutePath()); - } - public Bitmap getThumbnail(Message message, int size, boolean cacheOnly) throws FileNotFoundException { Bitmap thumbnail = mXmppConnectionService.getBitmapCache().get( @@ -257,8 +263,7 @@ public class FileBackend { File file = getFile(message); BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = calcSampleSize(file, size); - Bitmap fullsize = BitmapFactory.decodeFile(file.getAbsolutePath(), - options); + Bitmap fullsize = BitmapFactory.decodeFile(file.getAbsolutePath(),options); if (fullsize == null) { throw new FileNotFoundException(); } @@ -271,13 +276,11 @@ public class FileBackend { public Uri getTakePhotoUri() { StringBuilder pathBuilder = new StringBuilder(); - pathBuilder.append(Environment - .getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)); + pathBuilder.append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)); pathBuilder.append('/'); pathBuilder.append("Camera"); pathBuilder.append('/'); - pathBuilder.append("IMG_" + this.imageDateFormat.format(new Date()) - + ".jpg"); + pathBuilder.append("IMG_" + this.imageDateFormat.format(new Date()) + ".jpg"); Uri uri = Uri.parse("file://" + pathBuilder.toString()); File file = new File(uri.toString()); file.getParentFile().mkdirs(); @@ -325,13 +328,13 @@ public class FileBackend { String filename = getAvatarPath(avatar.getFilename()); file = new File(filename + ".tmp"); file.getParentFile().mkdirs(); + OutputStream os = null; try { file.createNewFile(); - FileOutputStream mFileOutputStream = new FileOutputStream(file); + os = new FileOutputStream(file); MessageDigest digest = MessageDigest.getInstance("SHA-1"); digest.reset(); - DigestOutputStream mDigestOutputStream = new DigestOutputStream( - mFileOutputStream, digest); + DigestOutputStream mDigestOutputStream = new DigestOutputStream(os, digest); mDigestOutputStream.write(avatar.getImageAsBytes()); mDigestOutputStream.flush(); mDigestOutputStream.close(); @@ -349,6 +352,8 @@ public class FileBackend { return false; } catch (NoSuchAlgorithmException e) { return false; + } finally { + close(os); } } avatar.size = file.length(); @@ -356,8 +361,7 @@ public class FileBackend { } public String getAvatarPath(String avatar) { - return mXmppConnectionService.getFilesDir().getAbsolutePath() - + "/avatars/" + avatar; + return mXmppConnectionService.getFilesDir().getAbsolutePath()+ "/avatars/" + avatar; } public Uri getAvatarUri(String avatar) { @@ -368,10 +372,11 @@ public class FileBackend { if (image == null) { return null; } + InputStream is = null; try { BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = calcSampleSize(image, size); - InputStream is = mXmppConnectionService.getContentResolver().openInputStream(image); + is = mXmppConnectionService.getContentResolver().openInputStream(image); Bitmap input = BitmapFactory.decodeStream(is, null, options); if (input == null) { return null; @@ -384,6 +389,8 @@ public class FileBackend { } } catch (FileNotFoundException e) { return null; + } finally { + close(is); } } @@ -391,12 +398,15 @@ public class FileBackend { if (image == null) { return null; } + InputStream is = null; try { BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = calcSampleSize(image,Math.max(newHeight, newWidth)); - InputStream is = mXmppConnectionService.getContentResolver().openInputStream(image); + is = mXmppConnectionService.getContentResolver().openInputStream(image); Bitmap source = BitmapFactory.decodeStream(is, null, options); - + if (source == null) { + return null; + } int sourceWidth = source.getWidth(); int sourceHeight = source.getHeight(); float xScale = (float) newWidth / sourceWidth; @@ -408,14 +418,15 @@ public class FileBackend { float top = (newHeight - scaledHeight) / 2; RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight); - Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, source.getConfig()); + Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(dest); canvas.drawBitmap(source, null, targetRect, null); return dest; } catch (FileNotFoundException e) { return null; + } finally { + close(is); } - } public Bitmap cropCenterSquare(Bitmap input, int size) { @@ -430,7 +441,7 @@ public class FileBackend { float top = (size - outHeight) / 2; RectF target = new RectF(left, top, left + outWidth, top + outHeight); - Bitmap output = Bitmap.createBitmap(size, size, input.getConfig()); + Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(output); canvas.drawBitmap(input, null, target, null); return output; @@ -490,7 +501,11 @@ public class FileBackend { message.setBody(url.toString()+"|"+Long.toString(file.getSize()) + '|' + imageWidth + '|' + imageHeight); } } else { - message.setBody(Long.toString(file.getSize())); + if (url != null) { + message.setBody(url.toString()+"|"+Long.toString(file.getSize())); + } else { + message.setBody(Long.toString(file.getSize())); + } } } @@ -522,4 +537,13 @@ public class FileBackend { public boolean isFileAvailable(Message message) { return getFile(message).exists(); } + + public static void close(Closeable stream) { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + } + } + } } diff --git a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java index f97077c4..0bc428c8 100644 --- a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java +++ b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java @@ -166,12 +166,12 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { } } - public void processFin(Element fin) { + public void processFin(Element fin, Jid from) { if (fin == null) { return; } Query query = findQuery(fin.getAttribute("queryid")); - if (query == null) { + if (query == null || !query.validFrom(from)) { return; } boolean complete = fin.getAttributeAsBoolean("complete"); @@ -336,6 +336,14 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { return this.messageCount; } + public boolean validFrom(Jid from) { + if (muc()) { + return getWith().equals(from); + } else { + return (from == null) || account.getJid().toBareJid().equals(from.toBareJid()); + } + } + @Override public String toString() { StringBuilder builder = new StringBuilder(); diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index 642021bf..09d223b3 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -18,7 +18,6 @@ import android.support.v4.app.NotificationCompat.Builder; import android.support.v4.app.TaskStackBuilder; import android.text.Html; import android.util.DisplayMetrics; -import android.util.Log; import org.json.JSONArray; import org.json.JSONObject; @@ -43,7 +42,6 @@ import eu.siacs.conversations.ui.ManageAccountActivity; import eu.siacs.conversations.ui.TimePreference; import eu.siacs.conversations.utils.GeoHelper; import eu.siacs.conversations.utils.UIHelper; -import eu.siacs.conversations.xmpp.XmppConnection; public class NotificationService { @@ -86,7 +84,11 @@ public class NotificationService { i.putExtra("messageType", "PEBBLE_ALERT"); i.putExtra("sender", "Conversations"); /* XXX: Shouldn't be hardcoded, e.g., AbstractGenerator.APP_NAME); */ i.putExtra("notificationData", notificationData); - + // notify Pebble App + i.setPackage("com.getpebble.android"); + mXmppConnectionService.sendBroadcast(i); + // notify Gadgetbridge + i.setPackage("nodomain.freeyourgadget.gadgetbridge"); mXmppConnectionService.sendBroadcast(i); } @@ -131,6 +133,7 @@ public class NotificationService { } public void push(final Message message) { + mXmppConnectionService.updateUnreadCountBadge(); if (!notify(message)) { return; } @@ -176,7 +179,7 @@ public class NotificationService { } private void setNotificationColor(final Builder mBuilder) { - mBuilder.setColor(mXmppConnectionService.getResources().getColor(R.color.primary)); + mBuilder.setColor(mXmppConnectionService.getResources().getColor(R.color.green500)); } private void updateNotification(final boolean notify) { @@ -299,7 +302,7 @@ public class NotificationService { final ArrayList<Message> tmp = new ArrayList<>(); for (final Message msg : messages) { if (msg.getType() == Message.TYPE_TEXT - && msg.getDownloadable() == null) { + && msg.getTransferable() == null) { tmp.add(msg); } } @@ -331,7 +334,7 @@ public class NotificationService { private Message getImage(final Iterable<Message> messages) { for (final Message message : messages) { if (message.getType() == Message.TYPE_IMAGE - && message.getDownloadable() == null + && message.getTransferable() == null && message.getEncryption() != Message.ENCRYPTION_PGP) { return message; } @@ -342,7 +345,7 @@ public class NotificationService { private Message getFirstDownloadableMessage(final Iterable<Message> messages) { for (final Message message : messages) { if ((message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) && - message.getDownloadable() != null) { + message.getTransferable() != null) { return message; } } @@ -455,7 +458,7 @@ public class NotificationService { // nick (matched in case-insensitive manner), followed by optional // punctuation (for example "bob: i disagree" or "how are you alice?"), // followed by another word boundary. - return Pattern.compile("\\b" + nick + "\\p{Punct}?\\b", + return Pattern.compile("\\b" + Pattern.quote(nick) + "\\p{Punct}?\\b", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); } @@ -494,7 +497,7 @@ public class NotificationService { final int cancelIcon; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mBuilder.setCategory(Notification.CATEGORY_SERVICE); - mBuilder.setSmallIcon(R.drawable.ic_import_export_white_48dp); + mBuilder.setSmallIcon(R.drawable.ic_import_export_white_24dp); cancelIcon = R.drawable.ic_cancel_white_24dp; } else { mBuilder.setSmallIcon(R.drawable.ic_stat_communication_import_export); @@ -541,7 +544,7 @@ public class NotificationService { mBuilder.setOngoing(true); //mBuilder.setLights(0xffffffff, 2000, 4000); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - mBuilder.setSmallIcon(R.drawable.ic_warning_white_36dp); + mBuilder.setSmallIcon(R.drawable.ic_warning_white_24dp); } else { mBuilder.setSmallIcon(R.drawable.ic_stat_alert_warning); } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index fc670efd..27bc502a 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -28,6 +28,7 @@ import android.util.LruCache; import net.java.otr4j.OtrException; import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionID; +import net.java.otr4j.session.SessionImpl; import net.java.otr4j.session.SessionStatus; import org.openintents.openpgp.util.OpenPgpApi; @@ -36,10 +37,12 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection; import java.math.BigInteger; import java.security.SecureRandom; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -55,12 +58,11 @@ import eu.siacs.conversations.entities.Blockable; import eu.siacs.conversations.entities.Bookmark; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; -import eu.siacs.conversations.entities.Downloadable; -import eu.siacs.conversations.entities.DownloadablePlaceholder; +import eu.siacs.conversations.entities.Transferable; +import eu.siacs.conversations.entities.TransferablePlaceholder; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions.OnRenameListener; -import eu.siacs.conversations.entities.Presences; import eu.siacs.conversations.generator.IqGenerator; import eu.siacs.conversations.generator.MessageGenerator; import eu.siacs.conversations.generator.PresenceGenerator; @@ -76,6 +78,7 @@ import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener; import eu.siacs.conversations.utils.PRNGFixes; import eu.siacs.conversations.utils.PhoneHelper; +import eu.siacs.conversations.utils.SerialSingleThreadExecutor; import eu.siacs.conversations.utils.Xmlns; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnBindListener; @@ -99,6 +102,7 @@ import eu.siacs.conversations.xmpp.pep.Avatar; import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; +import me.leolin.shortcutbadger.ShortcutBadger; public class XmppConnectionService extends Service implements OnPhoneContactsLoadedListener { @@ -117,6 +121,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa startService(intent); } }; + + private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor(); + private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor(); + private final IBinder mBinder = new XmppConnectionBinder(); private final List<Conversation> conversations = new CopyOnWriteArrayList<>(); private final FileObserver fileObserver = new FileObserver( @@ -175,13 +183,22 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void onContactStatusChanged(Contact contact, boolean online) { Conversation conversation = find(getConversations(), contact); if (conversation != null) { - if (online && contact.getPresences().size() > 1) { + if (online) { conversation.endOtrIfNeeded(); + if (contact.getPresences().size() == 1) { + sendUnsentMessages(conversation); + } } else { - conversation.resetOtrSession(); - } - if (online && (contact.getPresences().size() == 1)) { - sendUnsentMessages(conversation); + if (contact.getPresences().size() >= 1) { + if (conversation.hasValidOtrSession()) { + String otrResource = conversation.getOtrSession().getSessionID().getUserID(); + if (!(Arrays.asList(contact.getPresences().asStringArray()).contains(otrResource))) { + conversation.endOtrIfNeeded(); + } + } + } else { + conversation.endOtrIfNeeded(); + } } } } @@ -193,6 +210,18 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private OnMessagePacketReceived mMessageParser = new MessageParser(this); private OnPresencePacketReceived mPresenceParser = new PresenceParser(this); private IqParser mIqParser = new IqParser(this); + private OnIqPacketReceived mDefaultIqHandler = new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.ERROR) { + Element error = packet.findChild("error"); + String text = error != null ? error.findChildContent("text") : null; + if (text != null) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": received iq error - "+text); + } + } + } + }; private MessageGenerator mMessageGenerator = new MessageGenerator(this); private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this); private List<Account> accounts; @@ -201,9 +230,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager( this); private AvatarService mAvatarService = new AvatarService(this); + private final List<String> mInProgressAvatarFetches = new ArrayList<>(); private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this); private OnConversationUpdate mOnConversationUpdate = null; - private Integer convChangedListenerCount = 0; + private int convChangedListenerCount = 0; + private OnShowErrorToast mOnShowErrorToast = null; + private int showErrorToastListenerCount = 0; + private int unreadCount = -1; private OnAccountUpdate mOnAccountUpdate = null; private OnStatusChanged statusListener = new OnStatusChanged() { @@ -319,7 +352,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa message.setCounterpart(conversation.getNextCounterpart()); } if (encryption == Message.ENCRYPTION_DECRYPTED) { - getPgpEngine().encrypt(message,callback); + getPgpEngine().encrypt(message, callback); } else { callback.success(message); } @@ -338,7 +371,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } message.setCounterpart(conversation.getNextCounterpart()); message.setType(Message.TYPE_FILE); - message.setStatus(Message.STATUS_OFFERED); String path = getFileBackend().getOriginalPath(uri); if (path!=null) { message.setRelativeFilePath(path); @@ -349,7 +381,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa callback.success(message); } } else { - new Thread(new Runnable() { + mFileAddingExecutor.execute(new Runnable() { @Override public void run() { try { @@ -361,11 +393,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa callback.success(message); } } catch (FileBackend.FileCopyException e) { - callback.error(e.getResId(),message); + callback.error(e.getResId(), message); } } - }).start(); - + }); } } @@ -381,8 +412,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } message.setCounterpart(conversation.getNextCounterpart()); message.setType(Message.TYPE_IMAGE); - message.setStatus(Message.STATUS_OFFERED); - new Thread(new Runnable() { + mFileAddingExecutor.execute(new Runnable() { @Override public void run() { @@ -397,7 +427,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa callback.error(e.getResId(), message); } } - }).start(); + }); } public Conversation find(Bookmark bookmark) { @@ -413,6 +443,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa final String action = intent == null ? null : intent.getAction(); if (action != null) { switch (action) { + case ConnectivityManager.CONNECTIVITY_ACTION: + if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) { + resetAllAttemptCounts(true); + } + break; case ACTION_MERGE_PHONE_CONTACTS: if (mRestoredFromDatabase) { PhoneHelper.loadPhoneContacts(getApplicationContext(), @@ -431,14 +466,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa toggleForegroundService(); break; case ACTION_TRY_AGAIN: - for(Account account : accounts) { - if (account.hasErrorStatus()) { - final XmppConnection connection = account.getXmppConnection(); - if (connection != null) { - connection.resetAttemptCount(); - } - } - } + resetAllAttemptCounts(false); break; case ACTION_DISABLE_ACCOUNT: try { @@ -475,9 +503,15 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa long lastSent = account.getXmppConnection().getLastPingSent(); long pingInterval = "ui".equals(action) ? Config.PING_MIN_INTERVAL * 1000 : Config.PING_MAX_INTERVAL * 1000; long msToNextPing = (Math.max(lastReceived,lastSent) + pingInterval) - SystemClock.elapsedRealtime(); - if (lastSent > lastReceived && (lastSent + Config.PING_TIMEOUT * 1000) < SystemClock.elapsedRealtime()) { - Log.d(Config.LOGTAG, account.getJid().toBareJid()+ ": ping timeout"); - this.reconnectAccount(account, true); + long pingTimeoutIn = (lastSent + Config.PING_TIMEOUT * 1000) - SystemClock.elapsedRealtime(); + if (lastSent > lastReceived) { + if (pingTimeoutIn < 0) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": ping timeout"); + this.reconnectAccount(account, true); + } else { + int secs = (int) (pingTimeoutIn / 1000); + this.scheduleWakeUpCall(secs,account.getUuid().hashCode()); + } } else if (msToNextPing <= 0) { account.getXmppConnection().sendPing(); Log.d(Config.LOGTAG, account.getJid().toBareJid()+" send ping"); @@ -520,6 +554,18 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa return START_STICKY; } + private void resetAllAttemptCounts(boolean reallyAll) { + Log.d(Config.LOGTAG,"resetting all attepmt counts"); + for(Account account : accounts) { + if (account.hasErrorStatus() || reallyAll) { + final XmppConnection connection = account.getXmppConnection(); + if (connection != null) { + connection.resetAttemptCount(); + } + } + } + } + public boolean hasInternetConnection() { ConnectivityManager cm = (ConnectivityManager) getApplicationContext() .getSystemService(Context.CONNECTIVITY_SERVICE); @@ -552,9 +598,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa ExceptionHelper.init(getApplicationContext()); PRNGFixes.apply(); this.mRandom = new SecureRandom(); - this.mMemorizingTrustManager = new MemorizingTrustManager( - getApplicationContext()); - + updateMemorizingTrustmanager(); final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); final int cacheSize = maxMemory / 8; this.mBitmapCache = new LruCache<String, Bitmap>(cacheSize) { @@ -568,7 +612,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa this.accounts = databaseBackend.getAccounts(); for (final Account account : this.accounts) { - account.initOtrEngine(this); + account.initAccountServices(this); } restoreFromDatabase(); @@ -580,6 +624,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa this.pm = (PowerManager) getSystemService(Context.POWER_SERVICE); this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"XmppConnectionService"); toggleForegroundService(); + updateUnreadCountBadge(); } public void toggleForegroundService() { @@ -607,7 +652,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } Context context = getApplicationContext(); AlarmManager alarmManager = (AlarmManager) context - .getSystemService(Context.ALARM_SERVICE); + .getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, EventReceiver.class); alarmManager.cancel(PendingIntent.getBroadcast(context, 0, intent, 0)); Log.d(Config.LOGTAG, "good bye"); @@ -615,7 +660,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } protected void scheduleWakeUpCall(int seconds, int requestCode) { - final long timeToWake = SystemClock.elapsedRealtime() + (seconds + 1) * 1000; + final long timeToWake = SystemClock.elapsedRealtime() + (seconds < 0 ? 1 : seconds + 1) * 1000; Context context = getApplicationContext(); AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); @@ -649,114 +694,138 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } + private void sendFileMessage(final Message message) { + Log.d(Config.LOGTAG, "send file message"); + final Account account = message.getConversation().getAccount(); + final XmppConnection connection = account.getXmppConnection(); + if (connection != null && connection.getFeatures().httpUpload()) { + mHttpConnectionManager.createNewUploadConnection(message); + } else { + mJingleConnectionManager.createNewConnection(message); + } + } + public void sendMessage(final Message message) { + sendMessage(message, false); + } + + private void sendMessage(final Message message, final boolean resend) { final Account account = message.getConversation().getAccount(); + final Conversation conversation = message.getConversation(); account.deactivateGracePeriod(); - final Conversation conv = message.getConversation(); MessagePacket packet = null; boolean saveInDb = true; - boolean send = false; - if (account.getStatus() == Account.State.ONLINE - && account.getXmppConnection() != null) { - if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) { - if (message.getCounterpart() != null) { - if (message.getEncryption() == Message.ENCRYPTION_OTR) { - if (!conv.hasValidOtrSession()) { - conv.startOtrSession(message.getCounterpart().getResourcepart(),true); - message.setStatus(Message.STATUS_WAITING); - } else if (conv.hasValidOtrSession() - && conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { - mJingleConnectionManager - .createNewConnection(message); - } + message.setStatus(Message.STATUS_WAITING); + + if (!resend && message.getEncryption() != Message.ENCRYPTION_OTR) { + message.getConversation().endOtrIfNeeded(); + message.getConversation().findUnsentMessagesWithOtrEncryption(new Conversation.OnMessageFound() { + @Override + public void onMessageFound(Message message) { + markMessage(message,Message.STATUS_SEND_FAILED); + } + }); + } + + if (account.isOnlineAndConnected()) { + switch (message.getEncryption()) { + case Message.ENCRYPTION_NONE: + if (message.needsUploading()) { + if (account.httpUploadAvailable() || message.fixCounterpart()) { + this.sendFileMessage(message); + } else { + break; + } } else { - mJingleConnectionManager.createNewConnection(message); - } - } else { - if (message.getEncryption() == Message.ENCRYPTION_OTR) { - conv.startOtrIfNeeded(); + packet = mMessageGenerator.generateChat(message,resend); } - message.setStatus(Message.STATUS_WAITING); - } - } else { - if (message.getEncryption() == Message.ENCRYPTION_OTR) { - if (!conv.hasValidOtrSession() && (message.getCounterpart() != null)) { - conv.startOtrSession(message.getCounterpart().getResourcepart(), true); - message.setStatus(Message.STATUS_WAITING); - } else if (conv.hasValidOtrSession()) { - if (conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { - packet = mMessageGenerator.generateOtrChat(message); - send = true; + break; + case Message.ENCRYPTION_PGP: + case Message.ENCRYPTION_DECRYPTED: + if (message.needsUploading()) { + if (account.httpUploadAvailable() || message.fixCounterpart()) { + this.sendFileMessage(message); } else { - message.setStatus(Message.STATUS_WAITING); - conv.startOtrIfNeeded(); + break; } } else { - message.setStatus(Message.STATUS_WAITING); + packet = mMessageGenerator.generatePgpChat(message,resend); } - } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { - message.getConversation().endOtrIfNeeded(); - message.getConversation().findUnsentMessagesWithOtrEncryption(new Conversation.OnMessageFound() { - @Override - public void onMessageFound(Message message) { - markMessage(message,Message.STATUS_SEND_FAILED); + break; + case Message.ENCRYPTION_OTR: + SessionImpl otrSession = conversation.getOtrSession(); + if (otrSession != null && otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) { + try { + message.setCounterpart(Jid.fromSessionID(otrSession.getSessionID())); + } catch (InvalidJidException e) { + break; } - }); - packet = mMessageGenerator.generatePgpChat(message); - send = true; - } else { - message.getConversation().endOtrIfNeeded(); - message.getConversation().findUnsentMessagesWithOtrEncryption(new Conversation.OnMessageFound() { - @Override - public void onMessageFound(Message message) { - markMessage(message,Message.STATUS_SEND_FAILED); + if (message.needsUploading()) { + mJingleConnectionManager.createNewConnection(message); + } else { + packet = mMessageGenerator.generateOtrChat(message,resend); } - }); - packet = mMessageGenerator.generateChat(message); - send = true; + } else if (otrSession == null) { + if (message.fixCounterpart()) { + conversation.startOtrSession(message.getCounterpart().getResourcepart(), true); + } else { + break; + } + } + break; + } + if (packet != null) { + if (account.getXmppConnection().getFeatures().sm() || conversation.getMode() == Conversation.MODE_MULTI) { + message.setStatus(Message.STATUS_UNSEND); + } else { + message.setStatus(Message.STATUS_SEND); } } - if (!account.getXmppConnection().getFeatures().sm() - && conv.getMode() != Conversation.MODE_MULTI) { - message.setStatus(Message.STATUS_SEND); - } } else { - message.setStatus(Message.STATUS_WAITING); - if (message.getType() == Message.TYPE_TEXT) { - if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) { - String pgpBody = message.getEncryptedBody(); - String decryptedBody = message.getBody(); - message.setBody(pgpBody); - message.setEncryption(Message.ENCRYPTION_PGP); - databaseBackend.createMessage(message); - saveInDb = false; - message.setBody(decryptedBody); - message.setEncryption(Message.ENCRYPTION_DECRYPTED); - } else if (message.getEncryption() == Message.ENCRYPTION_OTR) { - if (!conv.hasValidOtrSession() - && message.getCounterpart() != null) { - conv.startOtrSession(message.getCounterpart().getResourcepart(), false); - } - } + switch(message.getEncryption()) { + case Message.ENCRYPTION_DECRYPTED: + if (!message.needsUploading()) { + String pgpBody = message.getEncryptedBody(); + String decryptedBody = message.getBody(); + message.setBody(pgpBody); + message.setEncryption(Message.ENCRYPTION_PGP); + databaseBackend.createMessage(message); + saveInDb = false; + message.setBody(decryptedBody); + message.setEncryption(Message.ENCRYPTION_DECRYPTED); + } + break; + case Message.ENCRYPTION_OTR: + if (!conversation.hasValidOtrSession() && message.getCounterpart() != null) { + conversation.startOtrSession(message.getCounterpart().getResourcepart(), false); + } + break; } - } - conv.add(message); - if (saveInDb) { - if (message.getEncryption() == Message.ENCRYPTION_NONE - || saveEncryptedMessages()) { + + if (resend) { + if (packet != null) { + if (account.getXmppConnection().getFeatures().sm() || conversation.getMode() == Conversation.MODE_MULTI) { + markMessage(message,Message.STATUS_UNSEND); + } else { + markMessage(message,Message.STATUS_SEND); + } + } + } else { + conversation.add(message); + if (saveInDb && (message.getEncryption() == Message.ENCRYPTION_NONE || saveEncryptedMessages())) { databaseBackend.createMessage(message); - } + } + updateConversationUi(); } - if ((send) && (packet != null)) { - if (conv.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) { + if (packet != null) { + if (conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) { if (this.sendChatStates()) { - packet.addChild(ChatState.toElement(conv.getOutgoingChatState())); + packet.addChild(ChatState.toElement(conversation.getOutgoingChatState())); } } sendMessagePacket(account, packet); } - updateConversationUi(); } private void sendUnsentMessages(final Conversation conversation) { @@ -769,79 +838,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa }); } - private void resendMessage(final Message message) { - Account account = message.getConversation().getAccount(); - MessagePacket packet = null; - if (message.getEncryption() == Message.ENCRYPTION_OTR) { - Presences presences = message.getConversation().getContact() - .getPresences(); - if (!message.getConversation().hasValidOtrSession()) { - if ((message.getCounterpart() != null) - && (presences.has(message.getCounterpart().getResourcepart()))) { - message.getConversation().startOtrSession(message.getCounterpart().getResourcepart(), true); - } else { - if (presences.size() == 1) { - String presence = presences.asStringArray()[0]; - message.getConversation().startOtrSession(presence, true); - } - } - } else { - if (message.getConversation().getOtrSession() - .getSessionStatus() == SessionStatus.ENCRYPTED) { - try { - message.setCounterpart(Jid.fromSessionID(message.getConversation().getOtrSession().getSessionID())); - if (message.getType() == Message.TYPE_TEXT) { - packet = mMessageGenerator.generateOtrChat(message, - true); - } else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) { - mJingleConnectionManager.createNewConnection(message); - } - } catch (final InvalidJidException ignored) { - - } - } - } - } else if (message.getType() == Message.TYPE_TEXT) { - if (message.getEncryption() == Message.ENCRYPTION_NONE) { - packet = mMessageGenerator.generateChat(message, true); - } else if ((message.getEncryption() == Message.ENCRYPTION_DECRYPTED) - || (message.getEncryption() == Message.ENCRYPTION_PGP)) { - packet = mMessageGenerator.generatePgpChat(message, true); - } - } else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) { - Contact contact = message.getConversation().getContact(); - Presences presences = contact.getPresences(); - if ((message.getCounterpart() != null) - && (presences.has(message.getCounterpart().getResourcepart()))) { - markMessage(message, Message.STATUS_OFFERED); - mJingleConnectionManager.createNewConnection(message); - } else { - if (presences.size() == 1) { - String presence = presences.asStringArray()[0]; - try { - message.setCounterpart(Jid.fromParts(contact.getJid().getLocalpart(), contact.getJid().getDomainpart(), presence)); - } catch (InvalidJidException e) { - return; - } - markMessage(message, Message.STATUS_OFFERED); - mJingleConnectionManager.createNewConnection(message); - } - } - } - if (packet != null) { - if (!account.getXmppConnection().getFeatures().sm() - && message.getConversation().getMode() != Conversation.MODE_MULTI) { - markMessage(message, Message.STATUS_SEND); - } else { - markMessage(message, Message.STATUS_UNSEND); - } - if (message.getConversation().setOutgoingChatState(Config.DEFAULT_CHATSTATE)) { - if (this.sendChatStates()) { - packet.addChild(ChatState.toElement(message.getConversation().getOutgoingChatState())); - } - } - sendMessagePacket(account, packet); - } + public void resendMessage(final Message message) { + sendMessage(message, true); } public void fetchRosterFromServer(final Account account) { @@ -852,9 +850,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } else { Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster"); } - iqPacket.query(Xmlns.ROSTER).setAttribute("ver", - account.getRosterVersion()); - account.getXmppConnection().sendIqPacket(iqPacket, mIqParser); + iqPacket.query(Xmlns.ROSTER).setAttribute("ver",account.getRosterVersion()); + sendIqPacket(account,iqPacket,mIqParser); } public void fetchBookmarks(final Account account) { @@ -899,7 +896,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa for (Bookmark bookmark : account.getBookmarks()) { storage.addChild(bookmark); } - sendIqPacket(account, iqPacket, null); + sendIqPacket(account, iqPacket, mDefaultIqHandler); } public void onPhoneContactsLoaded(final List<Bundle> phoneContacts) { @@ -911,7 +908,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void run() { Log.d(Config.LOGTAG,"start merging phone contacts with roster"); for (Account account : accounts) { - account.getRoster().clearSystemAccounts(); + List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts(); for (Bundle phoneContact : phoneContacts) { if (Thread.interrupted()) { Log.d(Config.LOGTAG,"interrupted merging phone contacts"); @@ -928,9 +925,18 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa + "#" + phoneContact.getString("lookup"); contact.setSystemAccount(systemAccount); - contact.setPhotoUri(phoneContact.getString("photouri")); - getAvatarService().clear(contact); + if (contact.setPhotoUri(phoneContact.getString("photouri"))) { + getAvatarService().clear(contact); + } contact.setSystemName(phoneContact.getString("displayname")); + withSystemAccounts.remove(contact); + } + for(Contact contact : withSystemAccounts) { + contact.setSystemAccount(null); + contact.setSystemName(null); + if (contact.setPhotoUri(null)) { + getAvatarService().clear(contact); + } } } Log.d(Config.LOGTAG,"finished merging phone contacts"); @@ -951,7 +957,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa Account account = accountLookupTable.get(conversation.getAccountUuid()); conversation.setAccount(account); } - new Thread(new Runnable() { + Runnable runnable =new Runnable() { @Override public void run() { Log.d(Config.LOGTAG,"restoring roster"); @@ -972,7 +978,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa Log.d(Config.LOGTAG,"restored all messages"); updateConversationUi(); } - }).start(); + }; + mDatabaseExecutor.execute(runnable); } } @@ -986,7 +993,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa @Override public void onMessageFound(Message message) { if (!getFileBackend().isFileAvailable(message)) { - message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED)); + message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); } } }); @@ -997,7 +1004,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa Message message = conversation.findMessageWithFileAndUuid(uuid); if (message != null) { if (!getFileBackend().isFileAvailable(message)) { - message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED)); + message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); updateConversationUi(); } return; @@ -1009,13 +1016,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa populateWithOrderedConversations(list, true); } - public void populateWithOrderedConversations(final List<Conversation> list, boolean includeConferences) { + public void populateWithOrderedConversations(final List<Conversation> list, boolean includeNoFileUpload) { list.clear(); - if (includeConferences) { + if (includeNoFileUpload) { list.addAll(getConversations()); } else { for (Conversation conversation : getConversations()) { - if (conversation.getMode() == Conversation.MODE_SINGLE) { + if (conversation.getMode() == Conversation.MODE_SINGLE + || conversation.getAccount().httpUploadAvailable()) { list.add(conversation); } } @@ -1037,11 +1045,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void loadMoreMessages(final Conversation conversation, final long timestamp, final OnMoreMessagesLoaded callback) { - Log.d(Config.LOGTAG,"load more messages for "+conversation.getName() + " prior to "+MessageGenerator.getTimestamp(timestamp)); + Log.d(Config.LOGTAG, "load more messages for " + conversation.getName() + " prior to " + MessageGenerator.getTimestamp(timestamp)); if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation,callback)) { return; } - new Thread(new Runnable() { + Runnable runnable = new Runnable() { @Override public void run() { final Account account = conversation.getAccount(); @@ -1060,7 +1068,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa callback.informUser(R.string.fetching_history_from_server); } } - }).start(); + }; + mDatabaseExecutor.execute(runnable); } public List<Account> getAccounts() { @@ -1149,6 +1158,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void archiveConversation(Conversation conversation) { + getNotificationService().clear(conversation); conversation.setStatus(Conversation.STATUS_ARCHIVED); conversation.setNextEncryption(-1); synchronized (this.conversations) { @@ -1171,7 +1181,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void createAccount(final Account account) { - account.initOtrEngine(this); + account.initAccountServices(this); databaseBackend.createAccount(account); this.accounts.add(account); this.reconnectAccountInBackground(account); @@ -1251,6 +1261,32 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } + public void setOnShowErrorToastListener(OnShowErrorToast onShowErrorToast) { + synchronized (this) { + if (checkListeners()) { + switchToForeground(); + } + this.mOnShowErrorToast = onShowErrorToast; + if (this.showErrorToastListenerCount < 2) { + this.showErrorToastListenerCount++; + } + } + this.mOnShowErrorToast = onShowErrorToast; + } + + public void removeOnShowErrorToastListener() { + synchronized (this) { + this.showErrorToastListenerCount--; + if (this.showErrorToastListenerCount <= 0) { + this.showErrorToastListenerCount = 0; + this.mOnShowErrorToast = null; + if (checkListeners()) { + switchToBackground(); + } + } + } + } + public void setOnAccountListChangedListener(OnAccountUpdate listener) { synchronized (this) { if (checkListeners()) { @@ -1355,7 +1391,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa return (this.mOnAccountUpdate == null && this.mOnConversationUpdate == null && this.mOnRosterUpdate == null - && this.mOnUpdateBlocklist == null); + && this.mOnUpdateBlocklist == null + && this.mOnShowErrorToast == null); } private void switchToForeground() { @@ -1558,6 +1595,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa for (Jid invite : jids) { invite(conversation, invite); } + if (account.countPresences() > 1) { + directInvite(conversation, account.getJid().toBareJid()); + } if (callback != null) { callback.success(conversation); } @@ -1678,12 +1718,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void changeAffiliationsInConference(final Conversation conference, MucOptions.Affiliation before, MucOptions.Affiliation after) { List<Jid> jids = new ArrayList<>(); for (MucOptions.User user : conference.getMucOptions().getUsers()) { - if (user.getAffiliation() == before) { + if (user.getAffiliation() == before && user.getJid() != null) { jids.add(user.getJid()); } } IqPacket request = this.mIqGenerator.changeAffiliation(conference, jids, after.toString()); - sendIqPacket(conference.getAccount(), request, null); + sendIqPacket(conference.getAccount(), request, mDefaultIqHandler); } public void changeRoleInConference(final Conversation conference, final String nick, MucOptions.Role role, final OnRoleChanged callback) { @@ -1720,6 +1760,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } } + sendOfflinePresence(account); } account.getXmppConnection().disconnect(force); } @@ -1773,15 +1814,15 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } catch (InvalidJidException e) { return; } - if (message.getType() == Message.TYPE_TEXT) { + if (message.needsUploading()) { + mJingleConnectionManager.createNewConnection(message); + } else { MessagePacket outPacket = mMessageGenerator.generateOtrChat(message, true); if (outPacket != null) { message.setStatus(Message.STATUS_SEND); databaseBackend.updateMessage(message); sendMessagePacket(account, outPacket); } - } else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) { - mJingleConnectionManager.createNewConnection(message); } updateConversationUi(); } @@ -1826,7 +1867,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa && contact.getOption(Contact.Options.PREEMPTIVE_GRANT); final IqPacket iq = new IqPacket(IqPacket.TYPE.SET); iq.query(Xmlns.ROSTER).addChild(contact.asElement()); - account.getXmppConnection().sendIqPacket(iq, null); + account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler); if (sendUpdates) { sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact)); @@ -1874,6 +1915,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa IqPacket result) { if (result.getType() == IqPacket.TYPE.RESULT) { if (account.setAvatar(avatar.getFilename())) { + getAvatarService().clear(account); databaseBackend.updateAccount(account); } callback.success(avatar); @@ -1900,13 +1942,39 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa fetchAvatar(account, avatar, null); } - public void fetchAvatar(Account account, final Avatar avatar, - final UiCallback<Avatar> callback) { - IqPacket packet = this.mIqGenerator.retrieveAvatar(avatar); + private static String generateFetchKey(Account account, final Avatar avatar) { + return account.getJid().toBareJid()+"_"+avatar.owner+"_"+avatar.sha1sum; + } + + public void fetchAvatar(Account account, final Avatar avatar, final UiCallback<Avatar> callback) { + final String KEY = generateFetchKey(account, avatar); + synchronized(this.mInProgressAvatarFetches) { + if (this.mInProgressAvatarFetches.contains(KEY)) { + return; + } else { + switch (avatar.origin) { + case PEP: + this.mInProgressAvatarFetches.add(KEY); + fetchAvatarPep(account, avatar, callback); + break; + case VCARD: + this.mInProgressAvatarFetches.add(KEY); + fetchAvatarVcard(account, avatar, callback); + break; + } + } + } + } + + private void fetchAvatarPep(Account account, final Avatar avatar, final UiCallback<Avatar> callback) { + IqPacket packet = this.mIqGenerator.retrievePepAvatar(avatar); sendIqPacket(account, packet, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket result) { + synchronized (mInProgressAvatarFetches) { + mInProgressAvatarFetches.remove(generateFetchKey(account, avatar)); + } final String ERROR = account.getJid().toBareJid() + ": fetching avatar for " + avatar.owner + " failed "; if (result.getType() == IqPacket.TYPE.RESULT) { @@ -1923,7 +1991,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } else { Contact contact = account.getRoster() .getContact(avatar.owner); - contact.setAvatar(avatar.getFilename()); + contact.setAvatar(avatar); getAvatarService().clear(contact); updateConversationUi(); updateRosterUi(); @@ -1932,8 +2000,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa callback.success(avatar); } Log.d(Config.LOGTAG, account.getJid().toBareJid() - + ": succesfully fetched avatar for " - + avatar.owner); + + ": succesfuly fetched pep avatar for " + avatar.owner); return; } } else { @@ -1956,8 +2023,37 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa }); } - public void checkForAvatar(Account account, - final UiCallback<Avatar> callback) { + private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback<Avatar> callback) { + IqPacket packet = this.mIqGenerator.retrieveVcardAvatar(avatar); + this.sendIqPacket(account, packet, new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + synchronized (mInProgressAvatarFetches) { + mInProgressAvatarFetches.remove(generateFetchKey(account, avatar)); + } + if (packet.getType() == IqPacket.TYPE.RESULT) { + Element vCard = packet.findChild("vCard", "vcard-temp"); + Element photo = vCard != null ? vCard.findChild("PHOTO") : null; + String image = photo != null ? photo.findChildContent("BINVAL") : null; + if (image != null) { + avatar.image = image; + if (getFileBackend().save(avatar)) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + + ": successfully fetched vCard avatar for " + avatar.owner); + Contact contact = account.getRoster() + .getContact(avatar.owner); + contact.setAvatar(avatar); + getAvatarService().clear(contact); + updateConversationUi(); + updateRosterUi(); + } + } + } + } + }); + } + + public void checkForAvatar(Account account, final UiCallback<Avatar> callback) { IqPacket packet = this.mIqGenerator.retrieveAvatarMetaData(null); this.sendIqPacket(account, packet, new OnIqPacketReceived() { @@ -1979,7 +2075,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa getAvatarService().clear(account); callback.success(avatar); } else { - fetchAvatar(account, avatar, callback); + fetchAvatarPep(account, avatar, callback); } return; } @@ -2001,7 +2097,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa Element item = iq.query(Xmlns.ROSTER).addChild("item"); item.setAttribute("jid", contact.getJid().toString()); item.setAttribute("subscription", "remove"); - account.getXmppConnection().sendIqPacket(iq, null); + account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler); } } @@ -2015,6 +2111,16 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa disconnect(account, force); } 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 (account.getXmppConnection() == null) { account.setXmppConnection(createConnection(account)); } @@ -2038,10 +2144,16 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void invite(Conversation conversation, Jid contact) { + Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+": inviting "+contact+" to "+conversation.getJid().toBareJid()); MessagePacket packet = mMessageGenerator.invite(conversation, contact); sendMessagePacket(conversation.getAccount(), packet); } + public void directInvite(Conversation conversation, Jid jid) { + MessagePacket packet = mMessageGenerator.directInvite(conversation, jid); + sendMessagePacket(conversation.getAccount(),packet); + } + public void resetSendingToWaiting(Account account) { for (Conversation conversation : getConversations()) { if (conversation.getAccount() == account) { @@ -2106,6 +2218,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public boolean forceEncryption() { return getPreferences().getBoolean("force_encryption", false); } + public boolean confirmMessages() { + return getPreferences().getBoolean("confirm_messages", true); + } public boolean sendChatStates() { return getPreferences().getBoolean("chat_states", false); @@ -2127,6 +2242,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa return count; } + + public void showErrorToastInUi(int resId) { + if (mOnShowErrorToast != null) { + mOnShowErrorToast.onShowErrorToast(resId); + } + } + public void updateConversationUi() { if (mOnConversationUpdate != null) { mOnConversationUpdate.onConversationUpdate(); @@ -2178,6 +2300,20 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void markRead(final Conversation conversation) { mNotificationService.clear(conversation); conversation.markRead(); + updateUnreadCountBadge(); + } + + public synchronized void updateUnreadCountBadge() { + int count = unreadCount(); + if (unreadCount != count) { + Log.d(Config.LOGTAG, "update unread count to " + count); + if (count > 0) { + ShortcutBadger.with(getApplicationContext()).count(count); + } else { + ShortcutBadger.with(getApplicationContext()).remove(); + } + unreadCount = count; + } } public void sendReadMarker(final Conversation conversation) { @@ -2201,6 +2337,21 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa return this.mMemorizingTrustManager; } + public void setMemorizingTrustManager(MemorizingTrustManager trustManager) { + this.mMemorizingTrustManager = trustManager; + } + + public void updateMemorizingTrustmanager() { + final MemorizingTrustManager tm; + final boolean dontTrustSystemCAs = getPreferences().getBoolean("dont_trust_system_cas", false); + if (dontTrustSystemCAs) { + tm = new MemorizingTrustManager(getApplicationContext(), null); + } else { + tm = new MemorizingTrustManager(getApplicationContext()); + } + setMemorizingTrustManager(tm); + } + public PowerManager getPowerManager() { return this.pm; } @@ -2210,13 +2361,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void syncRosterToDisk(final Account account) { - new Thread(new Runnable() { + Runnable runnable = new Runnable() { @Override public void run() { databaseBackend.writeRoster(account.getRoster()); } - }).start(); + }; + mDatabaseExecutor.execute(runnable); } @@ -2276,6 +2428,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa sendPresencePacket(account, mPresenceGenerator.sendPresence(account)); } + public void sendOfflinePresence(final Account account) { + sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account)); + } + public MessageGenerator getMessageGenerator() { return this.mMessageGenerator; } @@ -2426,6 +2582,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void onPushFailed(); } + public interface OnShowErrorToast { + void onShowErrorToast(int resId); + } + public class XmppConnectionBinder extends Binder { public XmppConnectionService getService() { return XmppConnectionService.this; diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java index e4bfd6ff..07b8819d 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java @@ -237,6 +237,9 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark); MenuItem menuItemAdvancedMode = menu.findItem(R.id.action_advanced_mode); menuItemAdvancedMode.setChecked(mAdvancedMode); + if (mConversation == null) { + return true; + } Account account = mConversation.getAccount(); if (account.hasBookmarkFor(mConversation.getJid().toBareJid())) { menuItemSaveBookmark.setVisible(false); @@ -382,6 +385,10 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers @Override void onBackendConnected() { + if (mPendingConferenceInvite != null) { + mPendingConferenceInvite.execute(this); + mPendingConferenceInvite = null; + } if (getIntent().getAction().equals(ACTION_VIEW_MUC)) { this.uuid = getIntent().getExtras().getString("uuid"); } diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index 40a4587c..c190caed 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -10,6 +10,7 @@ import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; +import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Intents; @@ -126,14 +127,23 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd @Override public void onClick(View v) { - AlertDialog.Builder builder = new AlertDialog.Builder( - ContactDetailsActivity.this); - builder.setTitle(getString(R.string.action_add_phone_book)); - builder.setMessage(getString(R.string.add_phone_book_text, + if (contact.getSystemAccount() == null) { + AlertDialog.Builder builder = new AlertDialog.Builder( + ContactDetailsActivity.this); + builder.setTitle(getString(R.string.action_add_phone_book)); + builder.setMessage(getString(R.string.add_phone_book_text, contact.getJid())); - builder.setNegativeButton(getString(R.string.cancel), null); - builder.setPositiveButton(getString(R.string.add), addToPhonebook); - builder.create().show(); + builder.setNegativeButton(getString(R.string.cancel), null); + builder.setPositiveButton(getString(R.string.add), addToPhonebook); + builder.create().show(); + } else { + String[] systemAccount = contact.getSystemAccount().split("#"); + long id = Long.parseLong(systemAccount[0]); + Uri uri = ContactsContract.Contacts.getLookupUri(id, systemAccount[1]); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(uri); + startActivity(intent); + } } }; @@ -256,16 +266,19 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd MenuItem unblock = menu.findItem(R.id.action_unblock); MenuItem edit = menu.findItem(R.id.action_edit_contact); MenuItem delete = menu.findItem(R.id.action_delete_contact); + if (contact == null) { + return true; + } final XmppConnection connection = contact.getAccount().getXmppConnection(); if (connection != null && connection.getFeatures().blocking()) { if (this.contact.isBlocked()) { - menu.findItem(R.id.action_block).setVisible(false); + block.setVisible(false); } else { - menu.findItem(R.id.action_unblock).setVisible(false); + unblock.setVisible(false); } } else { - menu.findItem(R.id.action_unblock).setVisible(false); - menu.findItem(R.id.action_block).setVisible(false); + unblock.setVisible(false); + block.setVisible(false); } if (!contact.showInRoster()) { edit.setVisible(false); @@ -275,6 +288,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } private void populateView() { + invalidateOptionsMenu(); setTitle(contact.getDisplayName()); if (contact.showInRoster()) { send.setVisibility(View.VISIBLE); @@ -336,12 +350,9 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } else { contactJidTv.setText(contact.getJid().toString()); } - accountJidTv.setText(getString(R.string.using_account, contact - .getAccount().getJid().toBareJid())); - prepareContactBadge(badge, contact); - if (contact.getSystemAccount() == null) { - badge.setOnClickListener(onBadgeClick); - } + accountJidTv.setText(getString(R.string.using_account, contact.getAccount().getJid().toBareJid())); + badge.setImageBitmap(avatarService().get(contact, getPixel(72))); + badge.setOnClickListener(this.onBadgeClick); keys.removeAllViews(); boolean hasKeys = false; @@ -415,15 +426,6 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } } - private void prepareContactBadge(QuickContactBadge badge, Contact contact) { - if (contact.getSystemAccount() != null) { - String[] systemAccount = contact.getSystemAccount().split("#"); - long id = Long.parseLong(systemAccount[0]); - badge.assignContactUri(Contacts.getLookupUri(id, systemAccount[1])); - } - badge.setImageBitmap(avatarService().get(contact, getPixel(72))); - } - protected void confirmToDeleteFingerprint(final String fingerprint) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.delete_fingerprint); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java index 0fbaa479..0504ff34 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java @@ -5,6 +5,7 @@ import android.app.ActionBar; import android.app.AlertDialog; import android.app.FragmentTransaction; import android.app.PendingIntent; +import android.content.ClipData; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; @@ -22,21 +23,24 @@ import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.CheckBox; -import android.widget.ListView; import android.widget.PopupMenu; import android.widget.PopupMenu.OnMenuItemClickListener; import android.widget.Toast; import net.java.otr4j.session.SessionStatus; +import de.timroes.android.listview.EnhancedListView; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Blockable; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; +import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; @@ -46,7 +50,7 @@ import eu.siacs.conversations.xmpp.OnUpdateBlocklist; import github.ankushsachdeva.emojicon.EmojiconEditText; public class ConversationActivity extends XmppActivity - implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist { + implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist, XmppConnectionService.OnShowErrorToast { public static final String ACTION_DOWNLOAD = "eu.siacs.conversations.action.DOWNLOAD"; @@ -59,26 +63,27 @@ public class ConversationActivity extends XmppActivity public static final int REQUEST_SEND_MESSAGE = 0x0201; public static final int REQUEST_DECRYPT_PGP = 0x0202; public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207; - private static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301; - private static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302; - private static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303; - private static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304; - private static final int ATTACHMENT_CHOICE_LOCATION = 0x0305; + public static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301; + public static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302; + public static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303; + public static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304; + public static final int ATTACHMENT_CHOICE_LOCATION = 0x0305; private static final String STATE_OPEN_CONVERSATION = "state_open_conversation"; private static final String STATE_PANEL_OPEN = "state_panel_open"; private static final String STATE_PENDING_URI = "state_pending_uri"; private String mOpenConverstaion = null; private boolean mPanelOpen = true; - private Uri mPendingImageUri = null; - private Uri mPendingFileUri = null; + final private List<Uri> mPendingImageUris = new ArrayList<>(); + final private List<Uri> mPendingFileUris = new ArrayList<>(); private Uri mPendingGeoUri = null; private View mContentView; private List<Conversation> conversationList = new ArrayList<>(); + private Conversation swipedConversation = null; private Conversation mSelectedConversation = null; - private ListView listView; + private EnhancedListView listView; private ConversationFragment mConversationFragment; private ArrayAdapter<Conversation> listAdapter; @@ -142,12 +147,13 @@ public class ConversationActivity extends XmppActivity protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { - mOpenConverstaion = savedInstanceState.getString(STATE_OPEN_CONVERSATION, null); - mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true); - String pending = savedInstanceState.getString(STATE_PENDING_URI, null); - if (pending != null) { - mPendingImageUri = Uri.parse(pending); - } + mOpenConverstaion = savedInstanceState.getString(STATE_OPEN_CONVERSATION, null); + mPanelOpen = savedInstanceState.getBoolean(STATE_PANEL_OPEN, true); + String pending = savedInstanceState.getString(STATE_PENDING_URI, null); + if (pending != null) { + mPendingImageUris.clear(); + mPendingImageUris.add(Uri.parse(pending)); + } } setContentView(R.layout.fragment_conversations_overview); @@ -157,7 +163,7 @@ public class ConversationActivity extends XmppActivity transaction.replace(R.id.selected_conversation, this.mConversationFragment, "conversation"); transaction.commit(); - listView = (ListView) findViewById(R.id.list); + listView = (EnhancedListView) findViewById(R.id.list); this.listAdapter = new ConversationAdapter(this, conversationList); listView.setAdapter(this.listAdapter); @@ -179,6 +185,73 @@ public class ConversationActivity extends XmppActivity openConversation(); } }); + + listView.setDismissCallback(new EnhancedListView.OnDismissCallback() { + + @Override + public EnhancedListView.Undoable onDismiss(final EnhancedListView enhancedListView, final int position) { + + final int index = listView.getFirstVisiblePosition(); + View v = listView.getChildAt(0); + final int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop()); + + swipedConversation = listAdapter.getItem(position); + listAdapter.remove(swipedConversation); + swipedConversation.markRead(); + xmppConnectionService.getNotificationService().clear(swipedConversation); + + final boolean formerlySelected = (getSelectedConversation() == swipedConversation); + if (position == 0 && listAdapter.getCount() == 0) { + endConversation(swipedConversation, false, true); + return null; + } else if (formerlySelected) { + setSelectedConversation(listAdapter.getItem(0)); + ConversationActivity.this.mConversationFragment + .reInit(getSelectedConversation()); + } + + return new EnhancedListView.Undoable() { + + @Override + public void undo() { + listAdapter.insert(swipedConversation, position); + if (formerlySelected) { + setSelectedConversation(swipedConversation); + ConversationActivity.this.mConversationFragment + .reInit(getSelectedConversation()); + } + swipedConversation = null; + listView.setSelectionFromTop(index + (listView.getChildCount() < position ? 1 : 0), top); + } + + @Override + public void discard() { + if (!swipedConversation.isRead() + && swipedConversation.getMode() == Conversation.MODE_SINGLE) { + swipedConversation = null; + return; + } + endConversation(swipedConversation, false, false); + swipedConversation = null; + } + + @Override + public String getTitle() { + if (swipedConversation.getMode() == Conversation.MODE_MULTI) { + return getResources().getString(R.string.title_undo_swipe_out_muc); + } else { + return getResources().getString(R.string.title_undo_swipe_out_conversation); + } + } + }; + } + }); + listView.enableSwipeToDismiss(); + listView.setSwipingLayout(R.id.swipeable_item); + listView.setUndoStyle(EnhancedListView.UndoStyle.SINGLE_POPUP); + listView.setUndoHideDelay(5000); + listView.setRequireTouchBeforeDismiss(false); + mContentView = findViewById(R.id.content_view_spl); if (mContentView == null) { mContentView = findViewById(R.id.content_view_ll); @@ -205,6 +278,7 @@ public class ConversationActivity extends XmppActivity @Override public void onPanelClosed(View arg0) { + listView.discardUndo(); openConversation(); } @@ -301,17 +375,16 @@ public class ConversationActivity extends XmppActivity } else { menuAdd.setVisible(!isConversationsOverviewHideable()); if (this.getSelectedConversation() != null) { - if (this.getSelectedConversation().getLatestMessage() - .getEncryption() != Message.ENCRYPTION_NONE) { + if (this.getSelectedConversation().getNextEncryption(forceEncryption()) != Message.ENCRYPTION_NONE) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - menuSecure.setIcon(R.drawable.ic_lock_outline_white_48dp); + menuSecure.setIcon(R.drawable.ic_lock_white_24dp); } else { menuSecure.setIcon(R.drawable.ic_action_secure); } } if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) { menuContactDetails.setVisible(false); - menuAttach.setVisible(false); + menuAttach.setVisible(getSelectedConversation().getAccount().httpUploadAvailable()); menuInviteContact.setVisible(getSelectedConversation().getMucOptions().canInvite()); } else { menuMucDetails.setVisible(false); @@ -327,56 +400,93 @@ public class ConversationActivity extends XmppActivity } private void selectPresenceToAttachFile(final int attachmentChoice, final int encryption) { - if (attachmentChoice == ATTACHMENT_CHOICE_LOCATION && encryption != Message.ENCRYPTION_OTR) { - getSelectedConversation().setNextCounterpart(null); - Intent intent = new Intent("eu.siacs.conversations.location.request"); - startActivityForResult(intent,attachmentChoice); - } else { - selectPresence(getSelectedConversation(), new OnPresenceSelected() { + final Conversation conversation = getSelectedConversation(); + final Account account = conversation.getAccount(); + final OnPresenceSelected callback = new OnPresenceSelected() { - @Override - public void onPresenceSelected() { - Intent intent = new Intent(); - boolean chooser = false; - switch (attachmentChoice) { - case ATTACHMENT_CHOICE_CHOOSE_IMAGE: - intent.setAction(Intent.ACTION_GET_CONTENT); - intent.setType("image/*"); - chooser = true; - break; - case ATTACHMENT_CHOICE_TAKE_PHOTO: - mPendingImageUri = xmppConnectionService.getFileBackend().getTakePhotoUri(); - intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); - intent.putExtra(MediaStore.EXTRA_OUTPUT, mPendingImageUri); - break; - case ATTACHMENT_CHOICE_CHOOSE_FILE: - chooser = true; - intent.setType("*/*"); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setAction(Intent.ACTION_GET_CONTENT); - break; - case ATTACHMENT_CHOICE_RECORD_VOICE: - intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION); - break; - case ATTACHMENT_CHOICE_LOCATION: - intent.setAction("eu.siacs.conversations.location.request"); - break; - } - if (intent.resolveActivity(getPackageManager()) != null) { - if (chooser) { - startActivityForResult( - Intent.createChooser(intent, getString(R.string.perform_action_with)), - attachmentChoice); - } else { - startActivityForResult(intent, attachmentChoice); + @Override + public void onPresenceSelected() { + Intent intent = new Intent(); + boolean chooser = false; + String fallbackPackageId = null; + switch (attachmentChoice) { + case ATTACHMENT_CHOICE_CHOOSE_IMAGE: + intent.setAction(Intent.ACTION_GET_CONTENT); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE,true); } + intent.setType("image/*"); + chooser = true; + break; + case ATTACHMENT_CHOICE_TAKE_PHOTO: + Uri uri = xmppConnectionService.getFileBackend().getTakePhotoUri(); + intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); + intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + mPendingImageUris.clear(); + mPendingImageUris.add(uri); + break; + case ATTACHMENT_CHOICE_CHOOSE_FILE: + chooser = true; + intent.setType("*/*"); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setAction(Intent.ACTION_GET_CONTENT); + break; + case ATTACHMENT_CHOICE_RECORD_VOICE: + intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION); + fallbackPackageId = "eu.siacs.conversations.voicerecorder"; + break; + case ATTACHMENT_CHOICE_LOCATION: + intent.setAction("eu.siacs.conversations.location.request"); + fallbackPackageId = "eu.siacs.conversations.sharelocation"; + break; + } + if (intent.resolveActivity(getPackageManager()) != null) { + if (chooser) { + startActivityForResult( + Intent.createChooser(intent, getString(R.string.perform_action_with)), + attachmentChoice); + } else { + startActivityForResult(intent, attachmentChoice); } + } else if (fallbackPackageId != null) { + startActivity(getInstallApkIntent(fallbackPackageId)); } - }); + } + }; + if ((account.httpUploadAvailable() || attachmentChoice == ATTACHMENT_CHOICE_LOCATION) && encryption != Message.ENCRYPTION_OTR) { + conversation.setNextCounterpart(null); + callback.onPresenceSelected(); + } else { + selectPresence(conversation, callback); + } + } + + private Intent getInstallApkIntent(final String packageId) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("market://details?id="+packageId)); + if (intent.resolveActivity(getPackageManager()) != null) { + return intent; + } else { + intent.setData(Uri.parse("http://play.google.com/store/apps/details?id=" + packageId)); + return intent; } } - private void attachFile(final int attachmentChoice) { + public void attachFile(final int attachmentChoice) { + switch (attachmentChoice) { + case ATTACHMENT_CHOICE_LOCATION: + getPreferences().edit().putString("recently_used_quick_action","location").apply(); + break; + case ATTACHMENT_CHOICE_RECORD_VOICE: + getPreferences().edit().putString("recently_used_quick_action","voice").apply(); + break; + case ATTACHMENT_CHOICE_TAKE_PHOTO: + getPreferences().edit().putString("recently_used_quick_action","photo").apply(); + break; + case ATTACHMENT_CHOICE_CHOOSE_IMAGE: + getPreferences().edit().putString("recently_used_quick_action","picture").apply(); + break; + } final Conversation conversation = getSelectedConversation(); final int encryption = conversation.getNextEncryption(forceEncryption()); if (encryption == Message.ENCRYPTION_PGP) { @@ -425,7 +535,7 @@ public class ConversationActivity extends XmppActivity showInstallPgpDialog(); } } else { - selectPresenceToAttachFile(attachmentChoice,encryption); + selectPresenceToAttachFile(attachmentChoice, encryption); } } @@ -486,13 +596,21 @@ public class ConversationActivity extends XmppActivity } public void endConversation(Conversation conversation) { - showConversationsOverview(); + endConversation(conversation, true, true); + } + + public void endConversation(Conversation conversation, boolean showOverview, boolean reinit) { + if (showOverview) { + showConversationsOverview(); + } xmppConnectionService.archiveConversation(conversation); - if (conversationList.size() > 0) { - setSelectedConversation(conversationList.get(0)); - this.mConversationFragment.reInit(getSelectedConversation()); - } else { - setSelectedConversation(null); + if (reinit) { + if (conversationList.size() > 0) { + setSelectedConversation(conversationList.get(0)); + this.mConversationFragment.reInit(getSelectedConversation()); + } else { + setSelectedConversation(null); + } } } @@ -582,13 +700,13 @@ public class ConversationActivity extends XmppActivity intent.putExtra("account", conversation.getAccount().getJid().toBareJid().toString()); switch (menuItem.getItemId()) { case R.id.scan_fingerprint: - intent.putExtra("mode",VerifyOTRActivity.MODE_SCAN_FINGERPRINT); + intent.putExtra("mode", VerifyOTRActivity.MODE_SCAN_FINGERPRINT); break; case R.id.ask_question: - intent.putExtra("mode",VerifyOTRActivity.MODE_ASK_QUESTION); + intent.putExtra("mode", VerifyOTRActivity.MODE_ASK_QUESTION); break; case R.id.manual_verification: - intent.putExtra("mode",VerifyOTRActivity.MODE_MANUAL_VERIFICATION); + intent.putExtra("mode", VerifyOTRActivity.MODE_MANUAL_VERIFICATION); break; } startActivity(intent); @@ -622,14 +740,11 @@ public class ConversationActivity extends XmppActivity break; case R.id.encryption_choice_pgp: if (hasPgp()) { - if (conversation.getAccount().getKeys() - .has("pgp_signature")) { - conversation - .setNextEncryption(Message.ENCRYPTION_PGP); + if (conversation.getAccount().getKeys().has("pgp_signature")) { + conversation.setNextEncryption(Message.ENCRYPTION_PGP); item.setChecked(true); } else { - announcePgp(conversation.getAccount(), - conversation); + announcePgp(conversation.getAccount(),conversation); } } else { showInstallPgpDialog(); @@ -639,16 +754,16 @@ public class ConversationActivity extends XmppActivity conversation.setNextEncryption(Message.ENCRYPTION_NONE); break; } - xmppConnectionService.databaseBackend - .updateConversation(conversation); + xmppConnectionService.databaseBackend.updateConversation(conversation); fragment.updateChatMsgHint(); + invalidateOptionsMenu(); return true; } }); popup.inflate(R.menu.encryption_choices); MenuItem otr = popup.getMenu().findItem(R.id.encryption_choice_otr); - MenuItem none = popup.getMenu().findItem( - R.id.encryption_choice_none); + MenuItem none = popup.getMenu().findItem(R.id.encryption_choice_none); + MenuItem pgp = popup.getMenu().findItem(R.id.encryption_choice_pgp); if (conversation.getMode() == Conversation.MODE_MULTI) { otr.setEnabled(false); } else { @@ -664,12 +779,10 @@ public class ConversationActivity extends XmppActivity otr.setChecked(true); break; case Message.ENCRYPTION_PGP: - popup.getMenu().findItem(R.id.encryption_choice_pgp) - .setChecked(true); + pgp.setChecked(true); break; default: - popup.getMenu().findItem(R.id.encryption_choice_none) - .setChecked(true); + none.setChecked(true); break; } popup.show(); @@ -745,6 +858,7 @@ public class ConversationActivity extends XmppActivity @Override public void onPause() { + listView.discardUndo(); super.onPause(); this.mActivityPaused = true; if (this.xmppConnectionServiceBound) { @@ -780,8 +894,8 @@ public class ConversationActivity extends XmppActivity } savedInstanceState.putBoolean(STATE_PANEL_OPEN, isConversationsOverviewVisable()); - if (this.mPendingImageUri != null) { - savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUri.toString()); + if (this.mPendingImageUris.size() >= 1) { + savedInstanceState.putString(STATE_PENDING_URI, this.mPendingImageUris.get(0).toString()); } super.onSaveInstanceState(savedInstanceState); } @@ -790,6 +904,12 @@ public class ConversationActivity extends XmppActivity void onBackendConnected() { this.xmppConnectionService.getNotificationService().setIsInForeground(true); updateConversationList(); + + if (mPendingConferenceInvite != null) { + mPendingConferenceInvite.execute(this); + mPendingConferenceInvite = null; + } + if (xmppConnectionService.getAccounts().size() == 0) { if (!mRedirected) { this.mRedirected = true; @@ -816,25 +936,25 @@ public class ConversationActivity extends XmppActivity } this.mConversationFragment.reInit(getSelectedConversation()); mOpenConverstaion = null; - } else if (getSelectedConversation() != null) { - this.mConversationFragment.reInit(getSelectedConversation()); - } else { + } else if (getSelectedConversation() == null) { showConversationsOverview(); - mPendingImageUri = null; - mPendingFileUri = null; + mPendingImageUris.clear(); + mPendingFileUris.clear(); mPendingGeoUri = null; setSelectedConversation(conversationList.get(0)); this.mConversationFragment.reInit(getSelectedConversation()); } - if (mPendingImageUri != null) { - attachImageToConversation(getSelectedConversation(),mPendingImageUri); - mPendingImageUri = null; - } else if (mPendingFileUri != null) { - attachFileToConversation(getSelectedConversation(),mPendingFileUri); - mPendingFileUri = null; - } else if (mPendingGeoUri != null) { - attachLocationToConversation(getSelectedConversation(),mPendingGeoUri); + for(Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) { + attachImageToConversation(getSelectedConversation(),i.next()); + } + + for(Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) { + attachFileToConversation(getSelectedConversation(),i.next()); + } + + if (mPendingGeoUri != null) { + attachLocationToConversation(getSelectedConversation(), mPendingGeoUri); mPendingGeoUri = null; } ExceptionHelper.checkForCrash(this, this.xmppConnectionService); @@ -842,10 +962,10 @@ public class ConversationActivity extends XmppActivity } private void handleViewConversationIntent(final Intent intent) { - final String uuid = (String) intent.getExtras().get(CONVERSATION); - final String downloadUuid = (String) intent.getExtras().get(MESSAGE); - final String text = intent.getExtras().getString(TEXT, ""); - final String nick = intent.getExtras().getString(NICK, null); + final String uuid = intent.getStringExtra(CONVERSATION); + final String downloadUuid = intent.getStringExtra(MESSAGE); + final String text = intent.getStringExtra(TEXT); + final String nick = intent.getStringExtra(NICK); if (selectConversationByUuid(uuid)) { this.mConversationFragment.reInit(getSelectedConversation()); if (nick != null) { @@ -886,6 +1006,21 @@ public class ConversationActivity extends XmppActivity xmppConnectionService.getNotificationService().setOpenConversation(null); } + @SuppressLint("NewApi") + private static List<Uri> extractUriFromIntent(final Intent intent) { + List<Uri> uris = new ArrayList<>(); + Uri uri = intent.getData(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && uri == null) { + ClipData clipData = intent.getClipData(); + for(int i = 0; i < clipData.getItemCount(); ++i) { + uris.add(clipData.getItemAt(i).getUri()); + } + } else { + uris.add(uri); + } + return uris; + } + @Override protected void onActivityResult(int requestCode, int resultCode, final Intent data) { @@ -895,25 +1030,34 @@ public class ConversationActivity extends XmppActivity mConversationFragment.hideSnackbar(); mConversationFragment.updateMessages(); } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_IMAGE) { - mPendingImageUri = data.getData(); + mPendingImageUris.clear(); + mPendingImageUris.addAll(extractUriFromIntent(data)); if (xmppConnectionServiceBound) { - attachImageToConversation(getSelectedConversation(),mPendingImageUri); - mPendingImageUri = null; + for(Iterator<Uri> i = mPendingImageUris.iterator(); i.hasNext(); i.remove()) { + attachImageToConversation(getSelectedConversation(),i.next()); + } } } else if (requestCode == ATTACHMENT_CHOICE_CHOOSE_FILE || requestCode == ATTACHMENT_CHOICE_RECORD_VOICE) { - mPendingFileUri = data.getData(); + mPendingFileUris.clear(); + mPendingFileUris.addAll(extractUriFromIntent(data)); if (xmppConnectionServiceBound) { - attachFileToConversation(getSelectedConversation(),mPendingFileUri); - mPendingFileUri = null; + for(Iterator<Uri> i = mPendingFileUris.iterator(); i.hasNext(); i.remove()) { + attachFileToConversation(getSelectedConversation(), i.next()); + } } - } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO && mPendingImageUri != null) { - if (xmppConnectionServiceBound) { - attachImageToConversation(getSelectedConversation(),mPendingImageUri); - mPendingImageUri = null; + } else if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) { + if (mPendingImageUris.size() == 1) { + Uri uri = mPendingImageUris.get(0); + if (xmppConnectionServiceBound) { + attachImageToConversation(getSelectedConversation(), uri); + mPendingImageUris.clear(); + } + Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + intent.setData(uri); + sendBroadcast(intent); + } else { + mPendingImageUris.clear(); } - Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); - intent.setData(mPendingImageUri); - sendBroadcast(intent); } else if (requestCode == ATTACHMENT_CHOICE_LOCATION) { double latitude = data.getDoubleExtra("latitude",0); double longitude = data.getDoubleExtra("longitude",0); @@ -924,13 +1068,15 @@ public class ConversationActivity extends XmppActivity } } } else { - if (requestCode == ATTACHMENT_CHOICE_TAKE_PHOTO) { - mPendingImageUri = null; - } + mPendingImageUris.clear(); + mPendingFileUris.clear(); } } private void attachLocationToConversation(Conversation conversation, Uri uri) { + if (conversation == null) { + return; + } xmppConnectionService.attachLocationToConversation(conversation,uri, new UiCallback<Message>() { @Override @@ -951,10 +1097,12 @@ public class ConversationActivity extends XmppActivity } private void attachFileToConversation(Conversation conversation, Uri uri) { - prepareFileToast = Toast.makeText(getApplicationContext(), - getText(R.string.preparing_file), Toast.LENGTH_LONG); + if (conversation == null) { + return; + } + prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_file), Toast.LENGTH_LONG); prepareFileToast.show(); - xmppConnectionService.attachFileToConversation(conversation,uri, new UiCallback<Message>() { + xmppConnectionService.attachFileToConversation(conversation, uri, new UiCallback<Message>() { @Override public void success(Message message) { hidePrepareFileToast(); @@ -974,8 +1122,10 @@ public class ConversationActivity extends XmppActivity } private void attachImageToConversation(Conversation conversation, Uri uri) { - prepareFileToast = Toast.makeText(getApplicationContext(), - getText(R.string.preparing_image), Toast.LENGTH_LONG); + if (conversation == null) { + return; + } + prepareFileToast = Toast.makeText(getApplicationContext(),getText(R.string.preparing_image), Toast.LENGTH_LONG); prepareFileToast.show(); xmppConnectionService.attachImageToConversation(conversation, uri, new UiCallback<Message>() { @@ -1014,6 +1164,13 @@ public class ConversationActivity extends XmppActivity public void updateConversationList() { xmppConnectionService .populateWithOrderedConversations(conversationList); + if (swipedConversation != null) { + if (swipedConversation.isRead()) { + conversationList.remove(swipedConversation); + } else { + listView.discardUndo(); + } + } listAdapter.notifyDataSetChanged(); } @@ -1053,6 +1210,10 @@ public class ConversationActivity extends XmppActivity return getPreferences().getBoolean("force_encryption", false); } + public boolean useSendButtonToIndicateStatus() { + return getPreferences().getBoolean("send_button_status", false); + } + public boolean indicateReceived() { return getPreferences().getBoolean("indicate_received", false); } @@ -1113,4 +1274,14 @@ public class ConversationActivity extends XmppActivity public boolean enterIsSend() { return getPreferences().getBoolean("enter_is_send",false); } + + @Override + public void onShowErrorToast(final int resId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(ConversationActivity.this,resId,Toast.LENGTH_SHORT).show(); + } + }); + } } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 4f359d9c..5a672520 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -3,12 +3,12 @@ package eu.siacs.conversations.ui; import android.app.AlertDialog; import android.app.Fragment; import android.app.PendingIntent; +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.net.Uri; import android.os.Bundle; import android.text.InputType; import android.view.ContextMenu; @@ -37,7 +37,6 @@ import android.widget.Toast; import net.java.otr4j.session.SessionStatus; -import java.net.URLConnection; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; @@ -50,9 +49,9 @@ import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; -import eu.siacs.conversations.entities.Downloadable; +import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.DownloadableFile; -import eu.siacs.conversations.entities.DownloadablePlaceholder; +import eu.siacs.conversations.entities.TransferablePlaceholder; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.Presences; @@ -63,6 +62,7 @@ import eu.siacs.conversations.ui.adapter.MessageAdapter; import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureClicked; import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClicked; import eu.siacs.conversations.utils.GeoHelper; +import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.chatstate.ChatState; import eu.siacs.conversations.xmpp.jid.Jid; import github.ankushsachdeva.emojicon.EmojiconEditText; @@ -128,16 +128,37 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } + private int getIndexOf(String uuid, List<Message> messages) { + if (uuid == null) { + return 0; + } + for(int i = 0; i < messages.size(); ++i) { + if (uuid.equals(messages.get(i).getUuid())) { + return i; + } else { + Message next = messages.get(i); + while(next != null && next.wasMergedIntoPrevious()) { + if (uuid.equals(next.getUuid())) { + return i; + } + next = next.next(); + } + + } + } + return 0; + } + @Override public void onScroll(AbsListView view, int firstVisibleItem, - int visibleItemCount, int totalItemCount) { + int visibleItemCount, int totalItemCount) { synchronized (ConversationFragment.this.messageList) { if (firstVisibleItem < 5 && messagesLoaded && messageList.size() > 0) { long timestamp = ConversationFragment.this.messageList.get(0).getTimeSent(); messagesLoaded = false; activity.xmppConnectionService.loadMoreMessages(conversation, timestamp, new XmppConnectionService.OnMoreMessagesLoaded() { @Override - public void onMoreMessagesLoaded(final int count, Conversation conversation) { + public void onMoreMessagesLoaded(final int c, Conversation conversation) { if (ConversationFragment.this.conversation != conversation) { return; } @@ -145,29 +166,18 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa @Override public void run() { final int oldPosition = messagesView.getFirstVisiblePosition(); + Message message = messageList.get(oldPosition); + String uuid = message != null ? message.getUuid() : null; View v = messagesView.getChildAt(0); final int pxOffset = (v == null) ? 0 : v.getTop(); ConversationFragment.this.conversation.populateWithMessages(ConversationFragment.this.messageList); updateStatusMessages(); messageListAdapter.notifyDataSetChanged(); - if (count != 0) { - final int newPosition = oldPosition + count; - int offset = 0; - try { - Message tmpMessage = messageList.get(newPosition); - - while(tmpMessage.wasMergedIntoPrevious()) { - offset++; - tmpMessage = tmpMessage.prev(); - } - } catch (final IndexOutOfBoundsException ignored) { - - } - messagesView.setSelectionFromTop(newPosition - offset, pxOffset); - messagesLoaded = true; - if (messageLoaderToast != null) { - messageLoaderToast.cancel(); - } + int pos = getIndexOf(uuid,messageList); + messagesView.setSelectionFromTop(pos, pxOffset); + messagesLoaded = true; + if (messageLoaderToast != null) { + messageLoaderToast.cancel(); } } }); @@ -185,7 +195,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (ConversationFragment.this.conversation != conversation) { return; } - messageLoaderToast = Toast.makeText(activity,resId,Toast.LENGTH_LONG); + messageLoaderToast = Toast.makeText(activity, resId, Toast.LENGTH_LONG); messageLoaderToast.show(); } }); @@ -219,7 +229,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa @Override public void onClick(View v) { - activity.verifyOtrSessionDialog(conversation,v); + activity.verifyOtrSessionDialog(conversation, v); } }; private ConcurrentLinkedQueue<Message> mEncryptedMessages = new ConcurrentLinkedQueue<>(); @@ -230,7 +240,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_SEND) { InputMethodManager imm = (InputMethodManager) v.getContext() - .getSystemService(Context.INPUT_METHOD_SERVICE); + .getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(v.getWindowToken(), 0); sendMessage(); return true; @@ -243,15 +253,42 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa @Override public void onClick(View v) { - sendMessage(); + Object tag = v.getTag(); + if (tag instanceof SendButtonAction) { + SendButtonAction action = (SendButtonAction) tag; + switch (action) { + case TAKE_PHOTO: + activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_TAKE_PHOTO); + break; + case SEND_LOCATION: + activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_LOCATION); + break; + case RECORD_VOICE: + activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_RECORD_VOICE); + break; + case CHOOSE_PICTURE: + activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_CHOOSE_IMAGE); + break; + case CANCEL: + if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { + conversation.setNextCounterpart(null); + updateChatMsgHint(); + updateSendButton(); + } + break; + default: + sendMessage(); + } + } else { + sendMessage(); + } } }; private OnClickListener clickToMuc = new OnClickListener() { @Override public void onClick(View v) { - Intent intent = new Intent(getActivity(), - ConferenceDetailsActivity.class); + Intent intent = new Intent(getActivity(), ConferenceDetailsActivity.class); intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC); intent.putExtra("uuid", conversation.getUuid()); startActivity(intent); @@ -261,24 +298,15 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa private Message selectedMessage; private void sendMessage() { - if (this.conversation == null) { + final String body = mEditMessage.getText().toString(); + if (body.length() == 0 || this.conversation == null) { return; } - if (mEditMessage.getText().length() < 1) { - if (this.conversation.getMode() == Conversation.MODE_MULTI) { - conversation.setNextCounterpart(null); - updateChatMsgHint(); - } - return; - } - Message message = new Message(conversation, mEditMessage.getText() - .toString(), conversation.getNextEncryption(activity - .forceEncryption())); + Message message = new Message(conversation, body, conversation.getNextEncryption(activity.forceEncryption())); if (conversation.getMode() == Conversation.MODE_MULTI) { if (conversation.getNextCounterpart() != null) { message.setCounterpart(conversation.getNextCounterpart()); message.setType(Message.TYPE_PRIVATE); - conversation.setNextCounterpart(null); } } if (conversation.getNextEncryption(activity.forceEncryption()) == Message.ENCRYPTION_OTR) { @@ -294,13 +322,13 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (conversation.getMode() == Conversation.MODE_MULTI && conversation.getNextCounterpart() != null) { this.mEditMessage.setHint(getString( - R.string.send_private_message_to, - conversation.getNextCounterpart().getResourcepart())); + R.string.send_private_message_to, + conversation.getNextCounterpart().getResourcepart())); } else { switch (conversation.getNextEncryption(activity.forceEncryption())) { case Message.ENCRYPTION_NONE: mEditMessage - .setHint(getString(R.string.send_plain_text_message)); + .setHint(getString(R.string.send_plain_text_message)); break; case Message.ENCRYPTION_OTR: mEditMessage.setHint(getString(R.string.send_otr_message)); @@ -316,7 +344,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } private void setupIme() { - if (((ConversationActivity)getActivity()).usingEnterKey()) { + if (((ConversationActivity) getActivity()).usingEnterKey()) { mEditMessage.setInputType(mEditMessage.getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE)); } else { mEditMessage.setInputType(mEditMessage.getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE); @@ -325,9 +353,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa @Override public View onCreateView(final LayoutInflater inflater, - ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.fragment_conversation, - container, false); + ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.fragment_conversation, container, false); + view.setOnClickListener(null); mEditMessage = (EditMessage) view.findViewById(R.id.textinput); setupIme(); mEditMessage.setOnClickListener(new OnClickListener() { @@ -486,21 +514,21 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } }); messageListAdapter - .setOnContactPictureLongClicked(new OnContactPictureLongClicked() { - - @Override - public void onContactPictureLongClicked(Message message) { - if (message.getStatus() <= Message.STATUS_RECEIVED) { - if (message.getConversation().getMode() == Conversation.MODE_MULTI) { - if (message.getCounterpart() != null) { - privateMessageWith(message.getCounterpart()); + .setOnContactPictureLongClicked(new OnContactPictureLongClicked() { + + @Override + public void onContactPictureLongClicked(Message message) { + if (message.getStatus() <= Message.STATUS_RECEIVED) { + if (message.getConversation().getMode() == Conversation.MODE_MULTI) { + if (message.getCounterpart() != null) { + privateMessageWith(message.getCounterpart()); + } } + } else { + activity.showQrCode(); } - } else { - activity.showQrCode(); } - } - }); + }); messagesView.setAdapter(messageListAdapter); registerForContextMenu(messagesView); @@ -510,7 +538,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa @Override public void onCreateContextMenu(ContextMenu menu, View v, - ContextMenuInfo menuInfo) { + ContextMenuInfo menuInfo) { synchronized (this.messageList) { super.onCreateContextMenu(menu, v, menuInfo); AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo; @@ -528,35 +556,37 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa MenuItem shareWith = menu.findItem(R.id.share_with); MenuItem sendAgain = menu.findItem(R.id.send_again); MenuItem copyUrl = menu.findItem(R.id.copy_url); - MenuItem downloadImage = menu.findItem(R.id.download_image); + 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.getDownloadable() != null || GeoHelper.isGeoUri(m.getBody())) { - copyText.setVisible(false); + if ((m.getType() == Message.TYPE_TEXT || m.getType() == Message.TYPE_PRIVATE) + && m.getTransferable() == null + && !GeoHelper.isGeoUri(m.getBody()) + && m.treatAsDownloadable() != Message.Decision.MUST) { + copyText.setVisible(true); } - if ((m.getType() == Message.TYPE_TEXT - || m.getType() == Message.TYPE_PRIVATE - || m.getDownloadable() != null) - && (!GeoHelper.isGeoUri(m.getBody()))) { - shareWith.setVisible(false); + if ((m.getType() != Message.TYPE_TEXT + && m.getType() != Message.TYPE_PRIVATE + && m.getTransferable() == null) + || (GeoHelper.isGeoUri(m.getBody()))) { + shareWith.setVisible(true); } - if (m.getStatus() != Message.STATUS_SEND_FAILED) { - sendAgain.setVisible(false); + if (m.getStatus() == Message.STATUS_SEND_FAILED) { + sendAgain.setVisible(true); + } + if (m.hasFileOnRemoteHost() + || GeoHelper.isGeoUri(m.getBody()) + || m.treatAsDownloadable() == Message.Decision.MUST) { + copyUrl.setVisible(true); + } + if (m.getType() == Message.TYPE_TEXT && m.getTransferable() == null && m.treatAsDownloadable() != Message.Decision.NEVER) { + downloadFile.setVisible(true); + downloadFile.setTitle(activity.getString(R.string.download_x_file,UIHelper.getFileDescriptionString(activity, m))); + } + if ((m.getTransferable() != null && !(m.getTransferable() instanceof TransferablePlaceholder)) + || (m.isFileOrImage() && (m.getStatus() == Message.STATUS_WAITING + || m.getStatus() == Message.STATUS_OFFERED))) { + cancelTransmission.setVisible(true); } - if (((m.getType() != Message.TYPE_IMAGE && m.getDownloadable() == null) - || m.getImageParams().url == null) && !GeoHelper.isGeoUri(m.getBody())) { - copyUrl.setVisible(false); - } - if (m.getType() != Message.TYPE_TEXT - || m.getDownloadable() != null - || !m.bodyContainsDownloadable()) { - downloadImage.setVisible(false); - } - if (!((m.getDownloadable() != null && !(m.getDownloadable() instanceof DownloadablePlaceholder)) - || (m.isFileOrImage() && (m.getStatus() == Message.STATUS_WAITING - || m.getStatus() == Message.STATUS_OFFERED)))) { - cancelTransmission.setVisible(false); - } } } @@ -575,8 +605,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa case R.id.copy_url: copyUrl(selectedMessage); return true; - case R.id.download_image: - downloadImage(selectedMessage); + case R.id.download_file: + downloadFile(selectedMessage); return true; case R.id.cancel_transmission: cancelTransmission(selectedMessage); @@ -597,19 +627,23 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa activity.xmppConnectionService.getFileBackend() .getJingleFileUri(message)); shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - String path = message.getRelativeFilePath(); - String mime = path == null ? null : URLConnection.guessContentTypeFromName(path); + String mime = message.getMimeType(); if (mime == null) { mime = "image/webp"; } shareIntent.setType(mime); } - activity.startActivity(Intent.createChooser(shareIntent,getText(R.string.share_with))); + try { + activity.startActivity(Intent.createChooser(shareIntent, getText(R.string.share_with))); + } catch (ActivityNotFoundException e) { + //This should happen only on faulty androids because normally chooser is always available + Toast.makeText(activity,R.string.no_application_found_to_open_file,Toast.LENGTH_SHORT).show(); + } } private void copyText(Message message) { if (activity.copyTextToClipboard(message.getMergedBody(), - R.string.message_text)) { + R.string.message_text)) { Toast.makeText(activity, R.string.message_copied_to_clipboard, Toast.LENGTH_SHORT).show(); } @@ -619,8 +653,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (message.getType() == Message.TYPE_FILE || message.getType() == Message.TYPE_IMAGE) { DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message); if (!file.exists()) { - Toast.makeText(activity,R.string.file_deleted,Toast.LENGTH_SHORT).show(); - message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED)); + Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show(); + message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_DELETED)); return; } } @@ -633,27 +667,30 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (GeoHelper.isGeoUri(message.getBody())) { resId = R.string.location; url = message.getBody(); + } else if (message.hasFileOnRemoteHost()) { + resId = R.string.file_url; + url = message.getFileParams().url.toString(); } else { - resId = R.string.image_url; - url = message.getImageParams().url.toString(); + url = message.getBody().trim(); + resId = R.string.file_url; } if (activity.copyTextToClipboard(url, resId)) { Toast.makeText(activity, R.string.url_copied_to_clipboard, Toast.LENGTH_SHORT).show(); - } + } } - private void downloadImage(Message message) { + private void downloadFile(Message message) { activity.xmppConnectionService.getHttpConnectionManager() - .createNewConnection(message); + .createNewDownloadConnection(message); } private void cancelTransmission(Message message) { - Downloadable downloadable = message.getDownloadable(); - if (downloadable!=null) { - downloadable.cancel(); + Transferable transferable = message.getTransferable(); + if (transferable != null) { + transferable.cancel(); } else { - activity.xmppConnectionService.markMessage(message,Message.STATUS_SEND_FAILED); + activity.xmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED); } } @@ -661,6 +698,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa this.mEditMessage.setText(""); this.conversation.setNextCounterpart(counterpart); updateChatMsgHint(); + updateSendButton(); } protected void highlightInConference(String nick) { @@ -669,9 +707,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa mEditMessage.getText().insert(0, nick + ": "); } else { if (mEditMessage.getText().charAt( - mEditMessage.getSelectionStart() - 1) != ' ') { + mEditMessage.getSelectionStart() - 1) != ' ') { nick = " " + nick; - } + } mEditMessage.getText().insert(mEditMessage.getSelectionStart(), nick + " "); } @@ -684,7 +722,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (this.conversation != null) { final String msg = mEditMessage.getText().toString(); this.conversation.setNextMessage(msg); - updateChatState(this.conversation,msg); + updateChatState(this.conversation, msg); } } @@ -707,7 +745,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa final String msg = mEditMessage.getText().toString(); this.conversation.setNextMessage(msg); if (this.conversation != conversation) { - updateChatState(this.conversation,msg); + updateChatState(this.conversation, msg); } this.conversation.trim(); } @@ -753,7 +791,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa @Override public void onClick(View v) { - final Contact contact = conversation == null ? null :conversation.getContact(); + final Contact contact = conversation == null ? null : conversation.getContact(); if (contact != null) { activity.xmppConnectionService.createContact(contact); activity.switchToContactDetails(contact); @@ -776,7 +814,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa intent.setAction(VerifyOTRActivity.ACTION_VERIFY_CONTACT); intent.putExtra("contact", conversation.getContact().getJid().toBareJid().toString()); intent.putExtra("account", conversation.getAccount().getJid().toBareJid().toString()); - intent.putExtra("mode",VerifyOTRActivity.MODE_ANSWER_QUESTION); + intent.putExtra("mode", VerifyOTRActivity.MODE_ANSWER_QUESTION); startActivity(intent); } }; @@ -786,11 +824,11 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa final Contact contact = conversation.getContact(); final int mode = conversation.getMode(); if (conversation.isBlocked()) { - showSnackbar(R.string.contact_blocked, R.string.unblock,this.mUnblockClickListener); + 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); + showSnackbar(R.string.contact_added_you, R.string.add_back, this.mAddBackClickListener); } else if (mode == Conversation.MODE_MULTI - &&!conversation.getMucOptions().online() + && !conversation.getMucOptions().online() && account.getStatus() == Account.State.ONLINE) { switch (conversation.getMucOptions().getError()) { case MucOptions.ERROR_NICK_IN_USE: @@ -814,18 +852,18 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa default: break; } - } else if (askForPassphraseIntent != null ) { - showSnackbar(R.string.openpgp_messages_found,R.string.decrypt, clickToDecryptListener); + } else if (askForPassphraseIntent != null) { + showSnackbar(R.string.openpgp_messages_found, R.string.decrypt, clickToDecryptListener); } else if (mode == Conversation.MODE_SINGLE && conversation.smpRequested()) { - showSnackbar(R.string.smp_requested, R.string.verify,this.mAnswerSmpClickListener); + showSnackbar(R.string.smp_requested, R.string.verify, this.mAnswerSmpClickListener); } else if (mode == Conversation.MODE_SINGLE - &&conversation.hasValidOtrSession() + && conversation.hasValidOtrSession() && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!conversation.isOtrFingerprintVerified())) { showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, clickToVerify); } else if (conversation.isMuted()) { - showSnackbar(R.string.notifications_disabled, R.string.enable,this.mUnmuteClickListener); + showSnackbar(R.string.notifications_disabled, R.string.enable, this.mUnmuteClickListener); } else { hideSnackbar(); } @@ -839,31 +877,16 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa final ConversationActivity activity = (ConversationActivity) getActivity(); if (this.conversation != null) { updateSnackBar(this.conversation); - final Contact contact = this.conversation.getContact(); - if (this.conversation.isBlocked()) { - - } else if (!contact.showInRoster() - && contact - .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { - - } else if (conversation.getMode() == Conversation.MODE_SINGLE) { - makeFingerprintWarning(); - } else if (!conversation.getMucOptions().online() - && conversation.getAccount().getStatus() == Account.State.ONLINE) { - - } else if (this.conversation.isMuted()) { - - } conversation.populateWithMessages(ConversationFragment.this.messageList); for (final Message message : this.messageList) { if (message.getEncryption() == Message.ENCRYPTION_PGP && (message.getStatus() == Message.STATUS_RECEIVED || message - .getStatus() >= Message.STATUS_SEND) - && message.getDownloadable() == null) { + .getStatus() >= Message.STATUS_SEND) + && message.getTransferable() == null) { if (!mEncryptedMessages.contains(message)) { mEncryptedMessages.add(message); } - } + } } decryptNext(); updateStatusMessages(); @@ -900,6 +923,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } catch (final NoSuchElementException ignored) { } + askForPassphraseIntent = null; activity.xmppConnectionService.updateMessage(message); } @@ -925,53 +949,149 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa updateChatMsgHint(); } - public void updateSendButton() { - Conversation c = this.conversation; - if (Settings.SHOW_ONLINE_STATUS && c != null - && c.getAccount().getStatus() == Account.State.ONLINE) { - if (c.getMode() == Conversation.MODE_SINGLE) { - switch (c.getContact().getMostAvailableStatus()) { + enum SendButtonAction {TEXT, TAKE_PHOTO, SEND_LOCATION, RECORD_VOICE, CANCEL, CHOOSE_PICTURE} + + private int getSendButtonImageResource(SendButtonAction action, int status) { + switch (action) { + case TEXT: + switch (status) { case Presences.CHAT: - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_online); - break; case Presences.ONLINE: - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_online); - break; + return R.drawable.ic_send_text_online; case Presences.AWAY: - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_away); - break; + return R.drawable.ic_send_text_away; case Presences.XA: - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_away); - break; case Presences.DND: - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_dnd); - break; + return R.drawable.ic_send_text_dnd; default: - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_offline); - break; + return R.drawable.ic_send_text_offline; + } + case TAKE_PHOTO: + switch (status) { + case Presences.CHAT: + case Presences.ONLINE: + return R.drawable.ic_send_photo_online; + case Presences.AWAY: + return R.drawable.ic_send_photo_away; + case Presences.XA: + case Presences.DND: + return R.drawable.ic_send_photo_dnd; + default: + return R.drawable.ic_send_photo_offline; + } + case RECORD_VOICE: + switch (status) { + case Presences.CHAT: + case Presences.ONLINE: + return R.drawable.ic_send_voice_online; + case Presences.AWAY: + return R.drawable.ic_send_voice_away; + case Presences.XA: + case Presences.DND: + return R.drawable.ic_send_voice_dnd; + default: + return R.drawable.ic_send_voice_offline; + } + case SEND_LOCATION: + switch (status) { + case Presences.CHAT: + case Presences.ONLINE: + return R.drawable.ic_send_location_online; + case Presences.AWAY: + return R.drawable.ic_send_location_away; + case Presences.XA: + case Presences.DND: + return R.drawable.ic_send_location_dnd; + default: + return R.drawable.ic_send_location_offline; + } + case CANCEL: + switch (status) { + case Presences.CHAT: + case Presences.ONLINE: + return R.drawable.ic_send_cancel_online; + case Presences.AWAY: + return R.drawable.ic_send_cancel_away; + case Presences.XA: + case Presences.DND: + return R.drawable.ic_send_cancel_dnd; + default: + return R.drawable.ic_send_cancel_offline; } - } else if (c.getMode() == Conversation.MODE_MULTI) { - if (c.getMucOptions().online()) { - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_online); + case CHOOSE_PICTURE: + switch (status) { + case Presences.CHAT: + case Presences.ONLINE: + return R.drawable.ic_send_picture_online; + case Presences.AWAY: + return R.drawable.ic_send_picture_away; + case Presences.XA: + case Presences.DND: + return R.drawable.ic_send_picture_dnd; + default: + return R.drawable.ic_send_picture_offline; + } + } + return R.drawable.ic_send_text_offline; + } + + public void updateSendButton() { + final Conversation c = this.conversation; + final SendButtonAction action; + final int status; + final boolean empty = this.mEditMessage == null || this.mEditMessage.getText().length() == 0; + final boolean conference = c.getMode() == Conversation.MODE_MULTI; + if (conference && !c.getAccount().httpUploadAvailable()) { + if (empty && c.getNextCounterpart() != null) { + action = SendButtonAction.CANCEL; + } else { + action = SendButtonAction.TEXT; + } + } else { + if (empty) { + if (conference && c.getNextCounterpart() != null) { + action = SendButtonAction.CANCEL; } else { - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_offline); + String setting = activity.getPreferences().getString("quick_action", "recent"); + if (!setting.equals("none") && UIHelper.receivedLocationQuestion(conversation.getLatestMessage())) { + setting = "location"; + } else if (setting.equals("recent")) { + setting = activity.getPreferences().getString("recently_used_quick_action", "text"); + } + switch (setting) { + case "photo": + action = SendButtonAction.TAKE_PHOTO; + break; + case "location": + action = SendButtonAction.SEND_LOCATION; + break; + case "voice": + action = SendButtonAction.RECORD_VOICE; + break; + case "picture": + action = SendButtonAction.CHOOSE_PICTURE; + break; + default: + action = SendButtonAction.TEXT; + break; + } } } else { - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_offline); + action = SendButtonAction.TEXT; + } + } + if (activity.useSendButtonToIndicateStatus() && c != null + && c.getAccount().getStatus() == Account.State.ONLINE) { + if (c.getMode() == Conversation.MODE_SINGLE) { + status = c.getContact().getMostAvailableStatus(); + } else { + status = c.getMucOptions().online() ? Presences.ONLINE : Presences.OFFLINE; } } else { - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_offline); + status = Presences.OFFLINE; } + this.mSendButton.setTag(action); + this.mSendButton.setImageResource(getSendButtonImageResource(action, status)); } protected void updateStatusMessages() { @@ -999,12 +1119,8 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } } - protected void makeFingerprintWarning() { - - } - protected void showSnackbar(final int message, final int action, - final OnClickListener clickListener) { + final OnClickListener clickListener) { snackbar.setVisibility(View.VISIBLE); snackbar.setOnClickListener(null); snackbarMessage.setText(message); @@ -1036,7 +1152,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa @Override public void userInputRequried(PendingIntent pi, - Contact contact) { + Contact contact) { activity.runIntent( pi, ConversationActivity.REQUEST_ENCRYPT_MESSAGE); @@ -1060,11 +1176,11 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa @Override public void onClick(DialogInterface dialog, - int which) { + int which) { conversation - .setNextEncryption(Message.ENCRYPTION_NONE); + .setNextEncryption(Message.ENCRYPTION_NONE); xmppService.databaseBackend - .updateConversation(conversation); + .updateConversation(conversation); message.setEncryption(Message.ENCRYPTION_NONE); xmppService.sendMessage(message); messageSent(); @@ -1075,9 +1191,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (conversation.getMucOptions().pgpKeysInUse()) { if (!conversation.getMucOptions().everybodyHasKeys()) { Toast warning = Toast - .makeText(getActivity(), - R.string.missing_public_keys, - Toast.LENGTH_LONG); + .makeText(getActivity(), + R.string.missing_public_keys, + Toast.LENGTH_LONG); warning.setGravity(Gravity.CENTER_VERTICAL, 0, 0); warning.show(); } @@ -1089,12 +1205,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa @Override public void onClick(DialogInterface dialog, - int which) { + int which) { conversation - .setNextEncryption(Message.ENCRYPTION_NONE); + .setNextEncryption(Message.ENCRYPTION_NONE); message.setEncryption(Message.ENCRYPTION_NONE); xmppService.databaseBackend - .updateConversation(conversation); + .updateConversation(conversation); xmppService.sendMessage(message); messageSent(); } @@ -1107,7 +1223,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } public void showNoPGPKeyDialog(boolean plural, - DialogInterface.OnClickListener listener) { + DialogInterface.OnClickListener listener) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setIconAttribute(android.R.attr.alertDialogIcon); if (plural) { @@ -1139,6 +1255,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } public void appendText(String text) { + if (text == null) { + return; + } String previous = this.mEditMessage.getText().toString(); if (previous.length() != 0 && !previous.endsWith(" ")) { text = " " + text; @@ -1162,6 +1281,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.COMPOSING)) { activity.xmppConnectionService.sendChatState(conversation); } + updateSendButton(); } @Override @@ -1178,6 +1298,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (status == Account.State.ONLINE && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) { activity.xmppConnectionService.sendChatState(conversation); } + updateSendButton(); } private void changeEmojiKeyboardIcon(ImageView iconToBeChanged, int drawableResourceId){ diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index 27dfc492..908c29d2 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -67,7 +67,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate @Override public void onClick(final View v) { - if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED) { + if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited()) { mAccount.setOption(Account.OPTION_DISABLED, false); xmppConnectionService.updateAccount(mAccount); return; @@ -123,11 +123,11 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount); xmppConnectionService.createAccount(mAccount); } - if (jidToEdit != null) { + if (jidToEdit != null && !mAccount.isOptionSet(Account.OPTION_DISABLED)) { finish(); } else { updateSaveButton(); - updateAccountInformation(); + updateAccountInformation(true); } } @@ -163,7 +163,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate updateSaveButton(); } if (mAccount != null) { - updateAccountInformation(); + updateAccountInformation(false); } } }); @@ -223,7 +223,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate if (avatar != null) { intent = new Intent(getApplicationContext(), StartConversationActivity.class); - intent.putExtra("init",true); + if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) { + intent.putExtra("init", true); + } } else { intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class); @@ -237,7 +239,11 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } protected void updateSaveButton() { - if (mAccount != null && (mAccount.getStatus() == Account.State.CONNECTING || mFetchingAvatar)) { + if (accountInfoEdited() && jidToEdit != null) { + this.mSaveButton.setText(R.string.save); + this.mSaveButton.setEnabled(true); + this.mSaveButton.setTextColor(getPrimaryTextColor()); + } else if (mAccount != null && (mAccount.getStatus() == Account.State.CONNECTING || mFetchingAvatar)) { this.mSaveButton.setEnabled(false); this.mSaveButton.setTextColor(getSecondaryTextColor()); this.mSaveButton.setText(R.string.account_status_connecting); @@ -265,9 +271,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } protected boolean accountInfoEdited() { - return (!this.mAccount.getJid().toBareJid().toString().equals( - this.mAccountJid.getText().toString())) - || (!this.mAccount.getPassword().equals( + return this.mAccount != null && (!this.mAccount.getJid().toBareJid().toString().equals( + this.mAccountJid.getText().toString()) + || !this.mAccount.getPassword().equals( this.mPassword.getText().toString())); } @@ -378,7 +384,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate xmppConnectionService.getKnownHosts()); if (this.jidToEdit != null) { this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit); - updateAccountInformation(); + updateAccountInformation(true); } else if (this.xmppConnectionService.getAccounts().size() == 0) { if (getActionBar() != null) { getActionBar().setDisplayHomeAsUpEnabled(false); @@ -413,9 +419,11 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate return super.onOptionsItemSelected(item); } - private void updateAccountInformation() { - this.mAccountJid.setText(this.mAccount.getJid().toBareJid().toString()); - this.mPassword.setText(this.mAccount.getPassword()); + private void updateAccountInformation(boolean init) { + if (init) { + this.mAccountJid.setText(this.mAccount.getJid().toBareJid().toString()); + this.mPassword.setText(this.mAccount.getPassword()); + } if (this.jidToEdit != null) { this.mAvatar.setVisibility(View.VISIBLE); this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(72))); @@ -464,7 +472,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } else { this.mServerInfoSm.setText(R.string.server_info_unavailable); } - if (features.pubsub()) { + if (features.pep()) { this.mServerInfoPep.setText(R.string.server_info_available); } else { this.mServerInfoPep.setText(R.string.server_info_unavailable); @@ -495,7 +503,9 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate } else { if (this.mAccount.errorStatus()) { this.mAccountJid.setError(getString(this.mAccount.getStatus().getReadableId())); - this.mAccountJid.requestFocus(); + if (init || !accountInfoEdited()) { + this.mAccountJid.requestFocus(); + } } else { this.mAccountJid.setError(null); } diff --git a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java index b2d5ddfd..56dbc55e 100644 --- a/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -168,6 +168,14 @@ public class ManageAccountActivity extends XmppActivity implements OnAccountUpda } } + public void onClickTglAccountState(Account account, boolean enable) { + if (enable) { + enableAccount(account); + } else { + disableAccount(account); + } + } + private void publishAvatar(Account account) { Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class); diff --git a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java index 3f72b723..4333dbdb 100644 --- a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java @@ -116,7 +116,9 @@ public class PublishProfilePictureActivity extends XmppActivity { if (mInitialAccountSetup) { Intent intent = new Intent(getApplicationContext(), StartConversationActivity.class); - intent.putExtra("init",true); + if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) { + intent.putExtra("init", true); + } startActivity(intent); } finish(); @@ -163,8 +165,7 @@ public class PublishProfilePictureActivity extends XmppActivity { if (jid != null) { this.account = xmppConnectionService.findAccountByJid(jid); if (this.account.getXmppConnection() != null) { - this.support = this.account.getXmppConnection() - .getFeatures().pubsub(); + this.support = this.account.getXmppConnection().getFeatures().pep(); } if (this.avatarUri == null) { if (this.account.getAvatar() != null diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java index 2115b23b..f3d5d8bd 100644 --- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java @@ -1,18 +1,30 @@ package eu.siacs.conversations.ui; +import java.security.KeyStoreException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Locale; import de.tzur.conversations.Settings; +import de.duenndns.ssl.MemorizingTrustManager; + +import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.xmpp.XmppConnection; +import android.app.AlertDialog; +import android.app.Fragment; +import android.app.FragmentManager; +import android.content.DialogInterface; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Build; import android.os.Bundle; import android.preference.ListPreference; +import android.preference.Preference; import android.preference.PreferenceManager; +import android.widget.Toast; public class SettingsActivity extends XmppActivity implements OnSharedPreferenceChangeListener { @@ -21,9 +33,12 @@ public class SettingsActivity extends XmppActivity implements @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mSettingsFragment = new SettingsFragment(); - getFragmentManager().beginTransaction() - .replace(android.R.id.content, mSettingsFragment).commit(); + FragmentManager fm = getFragmentManager(); + mSettingsFragment = (SettingsFragment) fm.findFragmentById(android.R.id.content); + if (mSettingsFragment == null || !mSettingsFragment.getClass().equals(SettingsFragment.class)) { + mSettingsFragment = new SettingsFragment(); + fm.beginTransaction().replace(android.R.id.content, mSettingsFragment).commit(); + } } @Override @@ -34,19 +49,78 @@ public class SettingsActivity extends XmppActivity implements @Override public void onStart() { super.onStart(); - PreferenceManager.getDefaultSharedPreferences(this) - .registerOnSharedPreferenceChangeListener(this); - ListPreference resources = (ListPreference) mSettingsFragment - .findPreference("resource"); + PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this); + ListPreference resources = (ListPreference) mSettingsFragment.findPreference("resource"); if (resources != null) { - ArrayList<CharSequence> entries = new ArrayList<CharSequence>( - Arrays.asList(resources.getEntries())); - entries.add(0, Build.MODEL); - resources.setEntries(entries.toArray(new CharSequence[entries - .size()])); - resources.setEntryValues(entries.toArray(new CharSequence[entries - .size()])); + ArrayList<CharSequence> entries = new ArrayList<>(Arrays.asList(resources.getEntries())); + if (!entries.contains(Build.MODEL)) { + entries.add(0, Build.MODEL); + resources.setEntries(entries.toArray(new CharSequence[entries.size()])); + resources.setEntryValues(entries.toArray(new CharSequence[entries.size()])); + } } + + 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); + } + } + }); + + dialogBuilder.setPositiveButton( + getResources().getString(R.string.dialog_manage_certs_positivebutton), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + int count = selectedItems.size(); + if (count > 0) { + for (int i = 0; i < count; i++) { + try { + Integer item = Integer.valueOf(selectedItems.get(i).toString()); + String alias = aliases.get(item); + mtm.deleteCertificate(alias); + } catch (KeyStoreException e) { + e.printStackTrace(); + displayToast("Error: " + e.getLocalizedMessage()); + } + } + if (xmppConnectionServiceBound) { + reconnectAccounts(); + } + displayToast(getResources().getQuantityString(R.plurals.toast_delete_certificates, count, count)); + } + } + }); + dialogBuilder.setNegativeButton(getResources().getString(R.string.dialog_manage_certs_negativebutton), null); + AlertDialog removeCertsDialog = dialogBuilder.create(); + removeCertsDialog.show(); + removeCertsDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); + return true; + } + }); } @Override @@ -69,6 +143,10 @@ public class SettingsActivity extends XmppActivity implements for (Account account : xmppConnectionService.getAccounts()) { account.setResource(resource); if (!account.isOptionSet(Account.OPTION_DISABLED)) { + XmppConnection connection = account.getXmppConnection(); + if (connection != null) { + connection.resetStreamId(); + } xmppConnectionService.reconnectAccountInBackground(account); } } @@ -77,7 +155,7 @@ public class SettingsActivity extends XmppActivity implements case "keep_foreground_service": xmppConnectionService.toggleForegroundService(); break; - case "confirm_messages_list": + case "confirm_messages": if (xmppConnectionServiceBound) { for (Account account : xmppConnectionService.getAccounts()) { if (!account.isOptionSet(Account.OPTION_DISABLED)) { @@ -86,7 +164,28 @@ public class SettingsActivity extends XmppActivity implements } } break; - } + case "dont_trust_system_cas": + xmppConnectionService.updateMemorizingTrustmanager(); + reconnectAccounts(); + break; + } + } + + private void displayToast(final String msg) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show(); + } + }); + } + + private void reconnectAccounts() { + for (Account account : xmppConnectionService.getAccounts()) { + if (!account.isOptionSet(Account.OPTION_DISABLED)) { + xmppConnectionService.reconnectAccountInBackground(account); + } + } } } diff --git a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java index 6be238dc..351f1dfc 100644 --- a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java @@ -4,7 +4,6 @@ import android.app.PendingIntent; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -13,11 +12,9 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; import android.widget.Toast; -import java.io.UnsupportedEncodingException; import java.net.URLConnection; -import java.net.URLDecoder; -import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import eu.siacs.conversations.Config; @@ -32,7 +29,7 @@ import eu.siacs.conversations.xmpp.jid.Jid; public class ShareWithActivity extends XmppActivity { private class Share { - public Uri uri; + public List<Uri> uris = new ArrayList<>(); public boolean image; public String account; public String contact; @@ -65,18 +62,17 @@ public class ShareWithActivity extends XmppActivity { } }; - protected void onActivityResult(int requestCode, int resultCode, - final Intent data) { + protected void onActivityResult(int requestCode, int resultCode, final Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_START_NEW_CONVERSATION && resultCode == RESULT_OK) { share.contact = data.getStringExtra("contact"); share.account = data.getStringExtra("account"); - Log.d(Config.LOGTAG, "contact: " + share.contact + " account:" - + share.account); } - if (xmppConnectionServiceBound && share != null - && share.contact != null && share.account != null) { + if (xmppConnectionServiceBound + && share != null + && share.contact != null + && share.account != null) { share(); } } @@ -100,13 +96,8 @@ public class ShareWithActivity extends XmppActivity { mListView.setOnItemClickListener(new OnItemClickListener() { @Override - public void onItemClick(AdapterView<?> arg0, View arg1, - int position, long arg3) { - Conversation conversation = mConversations.get(position); - if (conversation.getMode() == Conversation.MODE_SINGLE - || share.uri == null) { - share(mConversations.get(position)); - } + public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) { + share(mConversations.get(position)); } }); @@ -122,29 +113,42 @@ public class ShareWithActivity extends XmppActivity { @Override public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { - case R.id.action_add: - final Intent intent = new Intent(getApplicationContext(), - ChooseContactActivity.class); - startActivityForResult(intent, REQUEST_START_NEW_CONVERSATION); - return true; + case R.id.action_add: + final Intent intent = new Intent(getApplicationContext(), ChooseContactActivity.class); + startActivityForResult(intent, REQUEST_START_NEW_CONVERSATION); + return true; } return super.onOptionsItemSelected(item); } @Override public void onStart() { - final String type = getIntent().getType(); - final Uri uri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM); - if (type != null && uri != null && !type.equalsIgnoreCase("text/plain")) { - this.share.uri = uri; - this.share.image = type.startsWith("image/") || isImage(uri); - } else { - this.share.text = getIntent().getStringExtra(Intent.EXTRA_TEXT); + super.onStart(); + Intent intent = getIntent(); + if (intent == null) { + return; + } + final String type = intent.getType(); + if (Intent.ACTION_SEND.equals(intent.getAction())) { + final Uri uri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM); + if (type != null && uri != null && !type.equalsIgnoreCase("text/plain")) { + this.share.uris.add(uri); + this.share.image = type.startsWith("image/") || isImage(uri); + } else { + this.share.text = getIntent().getStringExtra(Intent.EXTRA_TEXT); + } + } else if (Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())) { + this.share.image = type != null && type.startsWith("image/"); + if (!this.share.image) { + return; + } + + this.share.uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); } if (xmppConnectionServiceBound) { - xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uri == null); + xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0); } - super.onStart(); + } protected boolean isImage(Uri uri) { @@ -164,54 +168,60 @@ public class ShareWithActivity extends XmppActivity { return; } xmppConnectionService.populateWithOrderedConversations(mConversations, - this.share != null && this.share.uri == null); + this.share != null && this.share.uris.size() == 0); } private void share() { - Account account; - try { - account = xmppConnectionService.findAccountByJid(Jid.fromString(share.account)); - } catch (final InvalidJidException e) { - account = null; - } - if (account == null) { + Account account; + try { + account = xmppConnectionService.findAccountByJid(Jid.fromString(share.account)); + } catch (final InvalidJidException e) { + account = null; + } + if (account == null) { + return; + } + final Conversation conversation; + try { + conversation = xmppConnectionService + .findOrCreateConversation(account, Jid.fromString(share.contact), false); + } catch (final InvalidJidException e) { return; } - final Conversation conversation; - try { - conversation = xmppConnectionService - .findOrCreateConversation(account, Jid.fromString(share.contact), false); - } catch (final InvalidJidException e) { - return; - } - share(conversation); + share(conversation); } private void share(final Conversation conversation) { - if (share.uri != null) { - selectPresence(conversation, new OnPresenceSelected() { + if (share.uris.size() != 0) { + OnPresenceSelected callback = new OnPresenceSelected() { @Override public void onPresenceSelected() { if (share.image) { Toast.makeText(getApplicationContext(), getText(R.string.preparing_image), Toast.LENGTH_LONG).show(); - ShareWithActivity.this.xmppConnectionService - .attachImageToConversation(conversation, share.uri, - attachFileCallback); + for (Iterator<Uri> i = share.uris.iterator(); i.hasNext(); i.remove()) { + ShareWithActivity.this.xmppConnectionService + .attachImageToConversation(conversation, i.next(), + attachFileCallback); + } } else { Toast.makeText(getApplicationContext(), getText(R.string.preparing_file), Toast.LENGTH_LONG).show(); ShareWithActivity.this.xmppConnectionService - .attachFileToConversation(conversation, share.uri, - attachFileCallback); + .attachFileToConversation(conversation, share.uris.get(0), + attachFileCallback); } switchToConversation(conversation, null, true); finish(); } - }); - + }; + if (conversation.getAccount().httpUploadAvailable()) { + callback.onPresenceSelected(); + } else { + selectPresence(conversation, callback); + } } else { switchToConversation(conversation, this.share.text, true); finish(); diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index a556b8b7..68e77af4 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -42,6 +42,7 @@ import android.widget.Checkable; import android.widget.EditText; import android.widget.ListView; import android.widget.Spinner; +import android.widget.Toast; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; @@ -65,6 +66,7 @@ import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.ui.adapter.ListItemAdapter; import eu.siacs.conversations.utils.XmppUri; import eu.siacs.conversations.xmpp.OnUpdateBlocklist; +import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; @@ -262,9 +264,12 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU protected void openConversationForBookmark(int position) { Bookmark bookmark = (Bookmark) conferences.get(position); - Conversation conversation = xmppConnectionService - .findOrCreateConversation(bookmark.getAccount(), - bookmark.getJid(), true); + Jid jid = bookmark.getJid(); + if (jid == null) { + Toast.makeText(this,R.string.invalid_jid,Toast.LENGTH_SHORT).show(); + return; + } + Conversation conversation = xmppConnectionService.findOrCreateConversation(bookmark.getAccount(),jid, true); conversation.setBookmark(bookmark); if (!conversation.getMucOptions().online()) { xmppConnectionService.joinMuc(conversation); @@ -757,14 +762,16 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU } else { activity.contact_context_id = acmi.position; final Blockable contact = (Contact) activity.contacts.get(acmi.position); - final MenuItem blockUnblockItem = menu.findItem(R.id.context_contact_block_unblock); - if (blockUnblockItem != null) { + XmppConnection xmpp = contact.getAccount().getXmppConnection(); + if (xmpp != null && xmpp.getFeatures().blocking()) { if (contact.isBlocked()) { blockUnblockItem.setTitle(R.string.unblock_contact); } else { blockUnblockItem.setTitle(R.string.block_contact); } + } else { + blockUnblockItem.setVisible(false); } } } diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index 62f62b9a..4157035b 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -91,6 +91,7 @@ public abstract class XmppActivity extends Activity { protected int mPrimaryTextColor; protected int mSecondaryTextColor; + protected int mPrimaryBackgroundColor; protected int mSecondaryBackgroundColor; protected int mColorRed; protected int mColorOrange; @@ -113,6 +114,8 @@ public abstract class XmppActivity extends Activity { } }; + protected ConferenceInvite mPendingConferenceInvite = null; + protected void refreshUi() { final long diff = SystemClock.elapsedRealtime() - mLastUiRefresh; @@ -282,6 +285,9 @@ public abstract class XmppActivity extends Activity { if (this instanceof OnUpdateBlocklist) { this.xmppConnectionService.setOnUpdateBlocklistListener((OnUpdateBlocklist) this); } + if (this instanceof XmppConnectionService.OnShowErrorToast) { + this.xmppConnectionService.setOnShowErrorToastListener((XmppConnectionService.OnShowErrorToast) this); + } } protected void unregisterListeners() { @@ -300,6 +306,9 @@ public abstract class XmppActivity extends Activity { if (this instanceof OnUpdateBlocklist) { this.xmppConnectionService.removeOnUpdateBlocklistListener(); } + if (this instanceof XmppConnectionService.OnShowErrorToast) { + this.xmppConnectionService.removeOnShowErrorToastListener(); + } } @Override @@ -326,13 +335,14 @@ public abstract class XmppActivity extends Activity { super.onCreate(savedInstanceState); metrics = getResources().getDisplayMetrics(); ExceptionHelper.init(getApplicationContext()); - mPrimaryTextColor = getResources().getColor(R.color.primarytext); - mSecondaryTextColor = getResources().getColor(R.color.secondarytext); - mColorRed = getResources().getColor(R.color.red); - mColorOrange = getResources().getColor(R.color.orange); - mColorGreen = getResources().getColor(R.color.green); - mPrimaryColor = getResources().getColor(R.color.primary); - mSecondaryBackgroundColor = getResources().getColor(R.color.secondarybackground); + mPrimaryTextColor = getResources().getColor(R.color.black87); + mSecondaryTextColor = getResources().getColor(R.color.black54); + mColorRed = getResources().getColor(R.color.red500); + mColorOrange = getResources().getColor(R.color.orange500); + mColorGreen = getResources().getColor(R.color.green500); + mPrimaryColor = getResources().getColor(R.color.green500); + mPrimaryBackgroundColor = getResources().getColor(R.color.grey50); + mSecondaryBackgroundColor = getResources().getColor(R.color.grey200); this.mTheme = findTheme(); setTheme(this.mTheme); this.mUsingEnterKey = usingEnterKey(); @@ -369,7 +379,7 @@ public abstract class XmppActivity extends Activity { } public void highlightInMuc(Conversation conversation, String nick) { - switchToConversation(conversation,null,nick,false); + switchToConversation(conversation, null, nick, false); } private void switchToConversation(Conversation conversation, String text, String nick, boolean newTask) { @@ -437,7 +447,7 @@ public abstract class XmppActivity extends Activity { @Override public void userInputRequried(PendingIntent pi, - Account account) { + Account account) { try { startIntentSenderForResult(pi.getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0); @@ -447,14 +457,11 @@ public abstract class XmppActivity extends Activity { @Override public void success(Account account) { - xmppConnectionService.databaseBackend - .updateAccount(account); + xmppConnectionService.databaseBackend.updateAccount(account); xmppConnectionService.sendPresence(account); if (conversation != null) { - conversation - .setNextEncryption(Message.ENCRYPTION_PGP); - xmppConnectionService.databaseBackend - .updateConversation(conversation); + conversation.setNextEncryption(Message.ENCRYPTION_PGP); + xmppConnectionService.databaseBackend.updateConversation(conversation); } } @@ -667,32 +674,11 @@ public abstract class XmppActivity extends Activity { protected void onActivityResult(int requestCode, int resultCode, final Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_INVITE_TO_CONVERSATION - && resultCode == RESULT_OK) { - try { - String conversationUuid = data.getStringExtra("conversation"); - Conversation conversation = xmppConnectionService - .findConversationByUuid(conversationUuid); - List<Jid> jids = new ArrayList<Jid>(); - if (data.getBooleanExtra("multiple", false)) { - String[] toAdd = data.getStringArrayExtra("contacts"); - for (String item : toAdd) { - jids.add(Jid.fromString(item)); - } - } else { - jids.add(Jid.fromString(data.getStringExtra("contact"))); - } - - if (conversation.getMode() == Conversation.MODE_MULTI) { - for (Jid jid : jids) { - xmppConnectionService.invite(conversation, jid); - } - } else { - jids.add(conversation.getJid().toBareJid()); - xmppConnectionService.createAdhocConference(conversation.getAccount(), jids, adhocCallback); - } - } catch (final InvalidJidException ignored) { - + if (requestCode == REQUEST_INVITE_TO_CONVERSATION && resultCode == RESULT_OK) { + mPendingConferenceInvite = ConferenceInvite.parse(data); + if (xmppConnectionServiceBound && mPendingConferenceInvite != null) { + mPendingConferenceInvite.execute(this); + mPendingConferenceInvite = null; } } } @@ -737,14 +723,14 @@ public abstract class XmppActivity extends Activity { return this.mColorRed; } - public int getPrimaryColor() { - return this.mPrimaryColor; - } - public int getOnlineColor() { return this.mColorGreen; } - + + public int getPrimaryBackgroundColor() { + return this.mPrimaryBackgroundColor; + } + public int getSecondaryBackgroundColor() { return this.mSecondaryBackgroundColor; } @@ -853,6 +839,48 @@ public abstract class XmppActivity extends Activity { } } + public static class ConferenceInvite { + private String uuid; + private List<Jid> jids = new ArrayList<>(); + + public static ConferenceInvite parse(Intent data) { + ConferenceInvite invite = new ConferenceInvite(); + invite.uuid = data.getStringExtra("conversation"); + if (invite.uuid == null) { + return null; + } + try { + if (data.getBooleanExtra("multiple", false)) { + String[] toAdd = data.getStringArrayExtra("contacts"); + for (String item : toAdd) { + invite.jids.add(Jid.fromString(item)); + } + } else { + invite.jids.add(Jid.fromString(data.getStringExtra("contact"))); + } + } catch (final InvalidJidException ignored) { + return null; + } + return invite; + } + + public void execute(XmppActivity activity) { + XmppConnectionService service = activity.xmppConnectionService; + Conversation conversation = service.findConversationByUuid(this.uuid); + if (conversation == null) { + return; + } + if (conversation.getMode() == Conversation.MODE_MULTI) { + for (Jid jid : jids) { + service.invite(conversation, jid); + } + } else { + jids.add(conversation.getJid().toBareJid()); + service.createAdhocConference(conversation.getAccount(), jids, activity.adhocCallback); + } + } + } + public AvatarService avatarService() { return xmppConnectionService.getAvatarService(); } diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java index 29730914..782a1231 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java @@ -5,13 +5,16 @@ import java.util.List; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.ui.XmppActivity; +import eu.siacs.conversations.ui.ManageAccountActivity; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.TextView; +import android.widget.Switch; public class AccountAdapter extends ArrayAdapter<Account> { @@ -24,7 +27,7 @@ public class AccountAdapter extends ArrayAdapter<Account> { @Override public View getView(int position, View view, ViewGroup parent) { - Account account = getItem(position); + final Account account = getItem(position); if (view == null) { LayoutInflater inflater = (LayoutInflater) getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -34,21 +37,32 @@ public class AccountAdapter extends ArrayAdapter<Account> { jid.setText(account.getJid().toBareJid().toString()); TextView statusView = (TextView) view.findViewById(R.id.account_status); ImageView imageView = (ImageView) view.findViewById(R.id.account_image); - imageView.setImageBitmap(activity.avatarService().get(account, - activity.getPixel(48))); - statusView.setText(getContext().getString(account.getStatus().getReadableId())); - switch (account.getStatus()) { - case ONLINE: - statusView.setTextColor(activity.getOnlineColor()); - break; - case DISABLED: - case CONNECTING: - statusView.setTextColor(activity.getSecondaryTextColor()); - break; - default: - statusView.setTextColor(activity.getWarningTextColor()); - break; - } + imageView.setImageBitmap(activity.avatarService().get(account, activity.getPixel(48))); + statusView.setText(getContext().getString(account.getStatus().getReadableId())); + switch (account.getStatus()) { + case ONLINE: + statusView.setTextColor(activity.getOnlineColor()); + break; + case DISABLED: + case CONNECTING: + statusView.setTextColor(activity.getSecondaryTextColor()); + break; + default: + statusView.setTextColor(activity.getWarningTextColor()); + break; + } + final Switch tglAccountState = (Switch) view.findViewById(R.id.tgl_account_status); + final boolean isDisabled = (account.getStatus() == Account.State.DISABLED); + tglAccountState.setOnCheckedChangeListener(null); + tglAccountState.setChecked(!isDisabled); + tglAccountState.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + if (b == isDisabled && activity instanceof ManageAccountActivity) { + ((ManageAccountActivity) activity).onClickTglAccountState(account,b); + } + } + }); return view; } } diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java index b6f88356..c42dd305 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java @@ -3,7 +3,6 @@ package eu.siacs.conversations.ui.adapter; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.Color; import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; @@ -25,7 +24,7 @@ import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; -import eu.siacs.conversations.entities.Downloadable; +import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Presences; import eu.siacs.conversations.ui.ConversationActivity; @@ -63,17 +62,10 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { } Conversation conversation = getItem(position); if (this.activity instanceof ConversationActivity) { - ConversationActivity activity = (ConversationActivity) this.activity; - if (!activity.isConversationsOverviewHideable()) { - if (conversation == activity.getSelectedConversation()) { - view.setBackgroundColor(activity - .getSecondaryBackgroundColor()); - } else { - view.setBackgroundColor(Color.TRANSPARENT); - } - } else { - view.setBackgroundColor(Color.TRANSPARENT); - } + View swipeableItem = view.findViewById(R.id.swipeable_item); + ConversationActivity a = (ConversationActivity) this.activity; + int c = !a.isConversationsOverviewHideable() && conversation == a.getSelectedConversation() ? a.getSecondaryBackgroundColor() : a.getPrimaryBackgroundColor(); + swipeableItem.setBackgroundColor(c); } TextView convName = (TextView) view.findViewById(R.id.conversation_name); if (conversation.getMode() == Conversation.MODE_SINGLE || activity.useSubjectToIdentifyConference()) { @@ -117,9 +109,9 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { convName.setTypeface(null, Typeface.NORMAL); } - if (message.getImageParams().width > 0 - && (message.getDownloadable() == null - || message.getDownloadable().getStatus() != Downloadable.STATUS_DELETED)) { + if (message.getFileParams().width > 0 + && (message.getTransferable() == null + || message.getTransferable().getStatus() != Transferable.STATUS_DELETED)) { mLastMessage.setVisibility(View.GONE); imagePreview.setVisibility(View.VISIBLE); activity.loadBitmap(message, imagePreview); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java index 10500bfc..4187ec60 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java @@ -4,6 +4,7 @@ import java.lang.ref.WeakReference; import java.util.List; import java.util.concurrent.RejectedExecutionException; +import de.tzur.conversations.Settings; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.ui.XmppActivity; diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index 933ca1f8..b7ac3092 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -8,10 +8,11 @@ import android.net.Uri; import android.preference.PreferenceManager; import android.text.Spannable; import android.text.SpannableString; +import android.text.Spanned; import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; import android.text.style.StyleSpan; import android.util.DisplayMetrics; -import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; @@ -31,10 +32,10 @@ import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; -import eu.siacs.conversations.entities.Downloadable; +import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.DownloadableFile; import eu.siacs.conversations.entities.Message; -import eu.siacs.conversations.entities.Message.ImageParams; +import eu.siacs.conversations.entities.Message.FileParams; import eu.siacs.conversations.ui.ConversationActivity; import eu.siacs.conversations.utils.GeoHelper; import eu.siacs.conversations.utils.UIHelper; @@ -45,7 +46,6 @@ public class MessageAdapter extends ArrayAdapter<Message> { private static final int SENT = 0; private static final int RECEIVED = 1; private static final int STATUS = 2; - private static final int NULL = 3; private ConversationActivity activity; @@ -80,14 +80,12 @@ public class MessageAdapter extends ArrayAdapter<Message> { @Override public int getViewTypeCount() { - return 4; + return 3; } @Override public int getItemViewType(int position) { - if (getItem(position).wasMergedIntoPrevious()) { - return NULL; - } else if (getItem(position).getType() == Message.TYPE_STATUS) { + if (getItem(position).getType() == Message.TYPE_STATUS) { return STATUS; } else if (getItem(position).getStatus() <= Message.STATUS_RECEIVED) { return RECEIVED; @@ -105,14 +103,14 @@ public class MessageAdapter extends ArrayAdapter<Message> { } boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI && message.getMergedStatus() <= Message.STATUS_RECEIVED; - if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getDownloadable() != null) { - ImageParams params = message.getImageParams(); + if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getTransferable() != null) { + FileParams params = message.getFileParams(); if (params.size > (1.5 * 1024 * 1024)) { filesize = params.size / (1024 * 1024)+ " MiB"; } else if (params.size > 0) { filesize = params.size / 1024 + " KiB"; } - if (message.getDownloadable() != null && message.getDownloadable().getStatus() == Downloadable.STATUS_FAILED) { + if (message.getTransferable() != null && message.getTransferable().getStatus() == Transferable.STATUS_FAILED) { error = true; } } @@ -121,7 +119,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { info = getContext().getString(R.string.waiting); break; case Message.STATUS_UNSEND: - Downloadable d = message.getDownloadable(); + Transferable d = message.getTransferable(); if (d!=null) { info = getContext().getString(R.string.sending_file,d.getProgress()); } else { @@ -166,7 +164,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { message.getMergedTimeSent()); if (message.getStatus() <= Message.STATUS_RECEIVED) { if ((filesize != null) && (info != null)) { - viewHolder.time.setText(filesize + " \u00B7 " + info); + viewHolder.time.setText(formatedTime + " \u00B7 " + filesize +" \u00B7 " + info); } else if ((filesize == null) && (info != null)) { viewHolder.time.setText(formatedTime + " \u00B7 " + info); } else if ((filesize != null) && (info == null)) { @@ -210,22 +208,42 @@ public class MessageAdapter extends ArrayAdapter<Message> { viewHolder.image.setVisibility(View.GONE); viewHolder.messageBody.setVisibility(View.VISIBLE); viewHolder.messageBody.setText(getContext().getString( - R.string.decryption_failed)); + R.string.decryption_failed)); viewHolder.messageBody.setTextColor(activity.getWarningTextColor()); viewHolder.messageBody.setTypeface(null, Typeface.NORMAL); viewHolder.messageBody.setTextIsSelectable(false); } + private void displayHeartMessage(final ViewHolder viewHolder, final String body) { + if (viewHolder.download_button != null) { + viewHolder.download_button.setVisibility(View.GONE); + } + viewHolder.image.setVisibility(View.GONE); + viewHolder.messageBody.setVisibility(View.VISIBLE); + viewHolder.messageBody.setIncludeFontPadding(false); + Spannable span = new SpannableString(body); + span.setSpan(new RelativeSizeSpan(4.0f), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + span.setSpan(new ForegroundColorSpan(activity.getWarningTextColor()), 0, body.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + viewHolder.messageBody.setText(span); + } + private void displayTextMessage(final ViewHolder viewHolder, final Message message) { if (viewHolder.download_button != null) { viewHolder.download_button.setVisibility(View.GONE); } viewHolder.image.setVisibility(View.GONE); viewHolder.messageBody.setVisibility(View.VISIBLE); + viewHolder.messageBody.setIncludeFontPadding(true); if (message.getBody() != null) { final String nick = UIHelper.getMessageDisplayName(message); - final String formattedBody = message.getMergedBody().replaceAll("^" + Message.ME_COMMAND, - nick + " "); + final String body = message.getMergedBody().replaceAll("^" + Message.ME_COMMAND,nick + " "); + final SpannableString formattedBody = new SpannableString(body); + int i = body.indexOf(Message.MERGE_SEPARATOR); + while(i >= 0) { + final int end = i + Message.MERGE_SEPARATOR.length(); + formattedBody.setSpan(new RelativeSizeSpan(0.3f),i,end,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + i = body.indexOf(Message.MERGE_SEPARATOR,end); + } if (message.getType() != Message.TYPE_PRIVATE) { if (message.hasMeCommand()) { @@ -237,7 +255,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { boolean parseEmoticons = Settings.PARSE_EMOTICONS; viewHolder.messageBody.setText(parseEmoticons ? UIHelper .transformAsciiEmoticons(getContext(), message.getMergedBody()) - : message.getMergedBody()); + : formattedBody); } } else { String privateMarker; @@ -296,7 +314,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { viewHolder.image.setVisibility(View.GONE); viewHolder.messageBody.setVisibility(View.GONE); viewHolder.download_button.setVisibility(View.VISIBLE); - viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity,message))); + viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message))); viewHolder.download_button.setOnClickListener(new OnClickListener() { @Override @@ -329,7 +347,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { } viewHolder.messageBody.setVisibility(View.GONE); viewHolder.image.setVisibility(View.VISIBLE); - ImageParams params = message.getImageParams(); + FileParams params = message.getFileParams(); double target = metrics.density * 288; int scalledW; int scalledH; @@ -341,7 +359,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { scalledH = (int) (params.height / ((double) params.width / target)); } viewHolder.image.setLayoutParams(new LinearLayout.LayoutParams( - scalledW, scalledH)); + scalledW, scalledH)); activity.loadBitmap(message, viewHolder.image); viewHolder.image.setOnClickListener(new OnClickListener() { @@ -366,10 +384,6 @@ public class MessageAdapter extends ArrayAdapter<Message> { if (view == null) { viewHolder = new ViewHolder(); switch (type) { - case NULL: - view = activity.getLayoutInflater().inflate( - R.layout.message_null, parent, false); - break; case SENT: view = activity.getLayoutInflater().inflate( R.layout.message_sent, parent, false); @@ -436,25 +450,6 @@ public class MessageAdapter extends ArrayAdapter<Message> { viewHolder.status_message.setText(message.getBody()); } return view; - } else if (type == NULL) { - if (viewHolder.message_box != null) { - Log.e(Config.LOGTAG, "detected type=NULL but with wrong cached view"); - view = activity.getLayoutInflater().inflate(R.layout.message_null, parent, false); - view.setTag(new ViewHolder()); - } - if (position == getCount() - 1) { - view.getLayoutParams().height = 1; - } else { - view.getLayoutParams().height = 0; - - } - view.setLayoutParams(view.getLayoutParams()); - return view; - } else if (message.wasMergedIntoPrevious()) { - Log.e(Config.LOGTAG,"detected wasMergedIntoPrevious with wrong type"); - return view; - } else if (viewHolder.messageBody == null || viewHolder.image == null) { - return view; //avoiding weird platform bugs } else if (type == RECEIVED) { Contact contact = message.getContact(); if (contact != null) { @@ -495,19 +490,19 @@ public class MessageAdapter extends ArrayAdapter<Message> { } }); - final Downloadable downloadable = message.getDownloadable(); - if (downloadable != null && downloadable.getStatus() != Downloadable.STATUS_UPLOADING) { - if (downloadable.getStatus() == Downloadable.STATUS_OFFER) { + final Transferable transferable = message.getTransferable(); + if (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING) { + if (transferable.getStatus() == Transferable.STATUS_OFFER) { displayDownloadableMessage(viewHolder,message,activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message))); - } else if (downloadable.getStatus() == Downloadable.STATUS_OFFER_CHECK_FILESIZE) { - displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_image_filesize)); + } else if (transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) { + displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message))); } else { displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first); } } else if (message.getType() == Message.TYPE_IMAGE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) { displayImageMessage(viewHolder, message); } else if (message.getType() == Message.TYPE_FILE && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) { - if (message.getImageParams().width > 0) { + if (message.getFileParams().width > 0) { displayImageMessage(viewHolder,message); } else { displayOpenableMessage(viewHolder, message); @@ -534,6 +529,10 @@ public class MessageAdapter extends ArrayAdapter<Message> { } else { if (GeoHelper.isGeoUri(message.getBody())) { displayLocationMessage(viewHolder,message); + } else if (message.bodyIsHeart()) { + displayHeartMessage(viewHolder, message.getBody().trim()); + } else if (message.treatAsDownloadable() == Message.Decision.MUST) { + displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message))); } else { displayTextMessage(viewHolder, message); } @@ -545,12 +544,14 @@ public class MessageAdapter extends ArrayAdapter<Message> { } public void startDownloadable(Message message) { - Downloadable downloadable = message.getDownloadable(); - if (downloadable != null) { - if (!downloadable.start()) { + Transferable transferable = message.getTransferable(); + if (transferable != null) { + if (!transferable.start()) { Toast.makeText(activity, R.string.not_connected_try_again, Toast.LENGTH_SHORT).show(); } + } else if (message.treatAsDownloadable() != Message.Decision.NEVER) { + activity.xmppConnectionService.getHttpConnectionManager().createNewDownloadConnection(message); } } diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java index eb7e2c3c..2dec203d 100644 --- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java @@ -91,6 +91,11 @@ public final class CryptoHelper { } public static String prettifyFingerprint(String fingerprint) { + if (fingerprint==null) { + return ""; + } else if (fingerprint.length() < 40) { + return fingerprint; + } StringBuilder builder = new StringBuilder(fingerprint); builder.insert(8, " "); builder.insert(17, " "); diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java index bcb2ca44..5a47bb3c 100644 --- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java @@ -20,11 +20,19 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Random; import java.util.TreeMap; +import java.util.regex.Pattern; import android.os.Bundle; import android.util.Log; public class DNSHelper { + + public static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z"); + public static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z"); + public static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z"); + public static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z"); + public static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z"); + protected static Client client = new Client(); public static Bundle getSRVRecord(final Jid jid) throws IOException { @@ -37,9 +45,6 @@ public class DNSHelper { Bundle b = queryDNS(host, ip); if (b.containsKey("values")) { return b; - } else if (b.containsKey("error") - && "nosrv".equals(b.getString("error", null))) { - return b; } } } @@ -50,113 +55,96 @@ public class DNSHelper { Bundle bundle = new Bundle(); try { String qname = "_xmpp-client._tcp." + host; - Log.d(Config.LOGTAG, - "using dns server: " + dnsServer.getHostAddress() - + " to look up " + host); - DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, - dnsServer.getHostAddress()); - - // How should we handle priorities and weight? - // Wikipedia has a nice article about priorities vs. weights: - // https://en.wikipedia.org/wiki/SRV_record#Provisioning_for_high_service_availability - - // we bucket the SRV records based on priority, pick per priority - // a random order respecting the weight, and dump that priority by - // priority + Log.d(Config.LOGTAG, "using dns server: " + dnsServer.getHostAddress() + " to look up " + host); + DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServer.getHostAddress()); TreeMap<Integer, ArrayList<SRV>> priorities = new TreeMap<>(); TreeMap<String, ArrayList<String>> ips4 = new TreeMap<>(); TreeMap<String, ArrayList<String>> ips6 = new TreeMap<>(); - for (Record[] rrset : new Record[][] { message.getAnswers(), - message.getAdditionalResourceRecords() }) { + for (Record[] rrset : new Record[][] { message.getAnswers(), message.getAdditionalResourceRecords() }) { for (Record rr : rrset) { Data d = rr.getPayload(); - if (d instanceof SRV - && NameUtil.idnEquals(qname, rr.getName())) { + if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) { SRV srv = (SRV) d; if (!priorities.containsKey(srv.getPriority())) { - priorities.put(srv.getPriority(), - new ArrayList<SRV>(2)); + priorities.put(srv.getPriority(),new ArrayList<SRV>()); } priorities.get(srv.getPriority()).add(srv); } if (d instanceof A) { - A arecord = (A) d; + A a = (A) d; if (!ips4.containsKey(rr.getName())) { - ips4.put(rr.getName(), new ArrayList<String>(3)); + ips4.put(rr.getName(), new ArrayList<String>()); } - ips4.get(rr.getName()).add(arecord.toString()); + ips4.get(rr.getName()).add(a.toString()); } if (d instanceof AAAA) { AAAA aaaa = (AAAA) d; if (!ips6.containsKey(rr.getName())) { - ips6.put(rr.getName(), new ArrayList<String>(3)); + ips6.put(rr.getName(), new ArrayList<String>()); } ips6.get(rr.getName()).add("[" + aaaa.toString() + "]"); } } } - Random rnd = new Random(); - ArrayList<SRV> result = new ArrayList<>( - priorities.size() * 2 + 1); + ArrayList<SRV> result = new ArrayList<>(); for (ArrayList<SRV> s : priorities.values()) { - - // trivial case - if (s.size() <= 1) { - result.addAll(s); - continue; - } - - long totalweight = 0l; - for (SRV srv : s) { - totalweight += srv.getWeight(); - } - - while (totalweight > 0l && s.size() > 0) { - long p = (rnd.nextLong() & 0x7fffffffffffffffl) - % totalweight; - int i = 0; - while (p > 0) { - p -= s.get(i++).getPriority(); - } - i--; - // remove is expensive, but we have only a few entries - // anyway - SRV srv = s.remove(i); - totalweight -= srv.getWeight(); - result.add(srv); - } - - Collections.shuffle(s, rnd); result.addAll(s); - } + ArrayList<Bundle> values = new ArrayList<>(); if (result.size() == 0) { - bundle.putString("error", "nosrv"); + DNSMessage response; + response = client.query(host, TYPE.A, CLASS.IN, dnsServer.getHostAddress()); + for(int i = 0; i < response.getAnswers().length; ++i) { + values.add(createNamePortBundle(host,5222,response.getAnswers()[i].getPayload())); + } + response = client.query(host, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress()); + for(int i = 0; i < response.getAnswers().length; ++i) { + values.add(createNamePortBundle(host,5222,response.getAnswers()[i].getPayload())); + } + values.add(createNamePortBundle(host,5222)); + bundle.putParcelableArrayList("values", values); return bundle; } - ArrayList<Bundle> values = new ArrayList<>(); for (SRV srv : result) { if (ips6.containsKey(srv.getName())) { values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips6)); + } else { + DNSMessage response = client.query(srv.getName(), TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress()); + for(int i = 0; i < response.getAnswers().length; ++i) { + values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[i].getPayload())); + } } if (ips4.containsKey(srv.getName())) { values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips4)); + } else { + DNSMessage response = client.query(srv.getName(), TYPE.A, CLASS.IN, dnsServer.getHostAddress()); + for(int i = 0; i < response.getAnswers().length; ++i) { + values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[i].getPayload())); + } } - values.add(createNamePortBundle(srv.getName(),srv.getPort(),null)); + values.add(createNamePortBundle(srv.getName(), srv.getPort())); } bundle.putParcelableArrayList("values", values); } catch (SocketTimeoutException e) { bundle.putString("error", "timeout"); } catch (Exception e) { + e.printStackTrace(); bundle.putString("error", "unhandled"); } return bundle; } + private static Bundle createNamePortBundle(String name, int port) { + Bundle namePort = new Bundle(); + namePort.putString("name", name); + namePort.putInt("port", port); + return namePort; + } + private static Bundle createNamePortBundle(String name, int port, TreeMap<String, ArrayList<String>> ips) { Bundle namePort = new Bundle(); namePort.putString("name", name); @@ -169,15 +157,23 @@ public class DNSHelper { return namePort; } - final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); - - public static String bytesToHex(byte[] bytes) { - char[] hexChars = new char[bytes.length * 2]; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + private static Bundle createNamePortBundle(String name, int port, Data data) { + Bundle namePort = new Bundle(); + namePort.putString("name", name); + namePort.putInt("port", port); + if (data instanceof A) { + namePort.putString("ip", data.toString()); + } else if (data instanceof AAAA) { + namePort.putString("ip","["+data.toString()+"]"); } - return new String(hexChars); + return namePort; + } + + public static boolean isIp(final String server) { + return PATTERN_IPV4.matcher(server).matches() + || PATTERN_IPV6.matcher(server).matches() + || PATTERN_IPV6_6HEX4DEC.matcher(server).matches() + || PATTERN_IPV6_HEX4DECCOMPRESSED.matcher(server).matches() + || PATTERN_IPV6_HEXCOMPRESSED.matcher(server).matches(); } } diff --git a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java index f7dda936..74f91a98 100644 --- a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java @@ -20,7 +20,7 @@ public class GeoHelper { } public static ArrayList<Intent> createGeoIntentsFromMessage(Message message) { - final ArrayList<Intent> intents = new ArrayList(); + final ArrayList<Intent> intents = new ArrayList<>(); Matcher matcher = GEO_URI.matcher(message.getBody()); if (!matcher.matches()) { return intents; @@ -54,8 +54,14 @@ public class GeoHelper { Intent locationPluginIntent = new Intent("eu.siacs.conversations.location.show"); locationPluginIntent.putExtra("latitude",latitude); locationPluginIntent.putExtra("longitude",longitude); - if (conversation.getMode() == Conversation.MODE_SINGLE && message.getStatus() == Message.STATUS_RECEIVED) { - locationPluginIntent.putExtra("name",conversation.getName()); + if (conversation.getMode() == Conversation.MODE_SINGLE) { + if (message.getStatus() == Message.STATUS_RECEIVED) { + locationPluginIntent.putExtra("name",conversation.getName()); + locationPluginIntent.putExtra("jid",message.getCounterpart().toString()); + } + else { + locationPluginIntent.putExtra("jid",conversation.getAccount().getJid().toString()); + } } intents.add(locationPluginIntent); diff --git a/src/main/java/eu/siacs/conversations/utils/MimeUtils.java b/src/main/java/eu/siacs/conversations/utils/MimeUtils.java new file mode 100644 index 00000000..a9e89d1b --- /dev/null +++ b/src/main/java/eu/siacs/conversations/utils/MimeUtils.java @@ -0,0 +1,487 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package eu.siacs.conversations.utils; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +/** + * Utilities for dealing with MIME types. + * Used to implement java.net.URLConnection and android.webkit.MimeTypeMap. + */ +public final class MimeUtils { + private static final Map<String, String> mimeTypeToExtensionMap = new HashMap<String, String>(); + private static final Map<String, String> extensionToMimeTypeMap = new HashMap<String, String>(); + static { + // The following table is based on /etc/mime.types data minus + // chemical/* MIME types and MIME types that don't map to any + // file extensions. We also exclude top-level domain names to + // deal with cases like: + // + // mail.google.com/a/google.com + // + // and "active" MIME types (due to potential security issues). + // Note that this list is _not_ in alphabetical order and must not be sorted. + // The "most popular" extension must come first, so that it's the one returned + // by guessExtensionFromMimeType. + add("application/andrew-inset", "ez"); + add("application/dsptype", "tsp"); + add("application/hta", "hta"); + add("application/mac-binhex40", "hqx"); + add("application/mathematica", "nb"); + add("application/msaccess", "mdb"); + add("application/oda", "oda"); + add("application/ogg", "ogg"); + add("application/ogg", "oga"); + add("application/pdf", "pdf"); + add("application/pgp-keys", "key"); + add("application/pgp-signature", "pgp"); + add("application/pics-rules", "prf"); + add("application/pkix-cert", "cer"); + add("application/rar", "rar"); + add("application/rdf+xml", "rdf"); + add("application/rss+xml", "rss"); + add("application/zip", "zip"); + add("application/vnd.android.package-archive", "apk"); + add("application/vnd.cinderella", "cdy"); + add("application/vnd.ms-pki.stl", "stl"); + add("application/vnd.oasis.opendocument.database", "odb"); + add("application/vnd.oasis.opendocument.formula", "odf"); + add("application/vnd.oasis.opendocument.graphics", "odg"); + add("application/vnd.oasis.opendocument.graphics-template", "otg"); + add("application/vnd.oasis.opendocument.image", "odi"); + add("application/vnd.oasis.opendocument.spreadsheet", "ods"); + add("application/vnd.oasis.opendocument.spreadsheet-template", "ots"); + add("application/vnd.oasis.opendocument.text", "odt"); + add("application/vnd.oasis.opendocument.text-master", "odm"); + add("application/vnd.oasis.opendocument.text-template", "ott"); + add("application/vnd.oasis.opendocument.text-web", "oth"); + add("application/vnd.google-earth.kml+xml", "kml"); + add("application/vnd.google-earth.kmz", "kmz"); + add("application/msword", "doc"); + add("application/msword", "dot"); + add("application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx"); + add("application/vnd.openxmlformats-officedocument.wordprocessingml.template", "dotx"); + add("application/vnd.ms-excel", "xls"); + add("application/vnd.ms-excel", "xlt"); + add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx"); + add("application/vnd.openxmlformats-officedocument.spreadsheetml.template", "xltx"); + add("application/vnd.ms-powerpoint", "ppt"); + add("application/vnd.ms-powerpoint", "pot"); + add("application/vnd.ms-powerpoint", "pps"); + add("application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx"); + add("application/vnd.openxmlformats-officedocument.presentationml.template", "potx"); + add("application/vnd.openxmlformats-officedocument.presentationml.slideshow", "ppsx"); + add("application/vnd.rim.cod", "cod"); + add("application/vnd.smaf", "mmf"); + add("application/vnd.stardivision.calc", "sdc"); + add("application/vnd.stardivision.draw", "sda"); + add("application/vnd.stardivision.impress", "sdd"); + add("application/vnd.stardivision.impress", "sdp"); + add("application/vnd.stardivision.math", "smf"); + add("application/vnd.stardivision.writer", "sdw"); + add("application/vnd.stardivision.writer", "vor"); + add("application/vnd.stardivision.writer-global", "sgl"); + add("application/vnd.sun.xml.calc", "sxc"); + add("application/vnd.sun.xml.calc.template", "stc"); + add("application/vnd.sun.xml.draw", "sxd"); + add("application/vnd.sun.xml.draw.template", "std"); + add("application/vnd.sun.xml.impress", "sxi"); + add("application/vnd.sun.xml.impress.template", "sti"); + add("application/vnd.sun.xml.math", "sxm"); + add("application/vnd.sun.xml.writer", "sxw"); + add("application/vnd.sun.xml.writer.global", "sxg"); + add("application/vnd.sun.xml.writer.template", "stw"); + add("application/vnd.visio", "vsd"); + add("application/x-abiword", "abw"); + add("application/x-apple-diskimage", "dmg"); + add("application/x-bcpio", "bcpio"); + add("application/x-bittorrent", "torrent"); + add("application/x-cdf", "cdf"); + add("application/x-cdlink", "vcd"); + add("application/x-chess-pgn", "pgn"); + add("application/x-cpio", "cpio"); + add("application/x-debian-package", "deb"); + add("application/x-debian-package", "udeb"); + add("application/x-director", "dcr"); + add("application/x-director", "dir"); + add("application/x-director", "dxr"); + add("application/x-dms", "dms"); + add("application/x-doom", "wad"); + add("application/x-dvi", "dvi"); + add("application/x-font", "pfa"); + add("application/x-font", "pfb"); + add("application/x-font", "gsf"); + add("application/x-font", "pcf"); + add("application/x-font", "pcf.Z"); + add("application/x-freemind", "mm"); + // application/futuresplash isn't IANA, so application/x-futuresplash should come first. + add("application/x-futuresplash", "spl"); + add("application/futuresplash", "spl"); + add("application/x-gnumeric", "gnumeric"); + add("application/x-go-sgf", "sgf"); + add("application/x-graphing-calculator", "gcf"); + add("application/x-gtar", "tgz"); + add("application/x-gtar", "gtar"); + add("application/x-gtar", "taz"); + add("application/x-hdf", "hdf"); + add("application/x-ica", "ica"); + add("application/x-internet-signup", "ins"); + add("application/x-internet-signup", "isp"); + add("application/x-iphone", "iii"); + add("application/x-iso9660-image", "iso"); + add("application/x-jmol", "jmz"); + add("application/x-kchart", "chrt"); + add("application/x-killustrator", "kil"); + add("application/x-koan", "skp"); + add("application/x-koan", "skd"); + add("application/x-koan", "skt"); + add("application/x-koan", "skm"); + add("application/x-kpresenter", "kpr"); + add("application/x-kpresenter", "kpt"); + add("application/x-kspread", "ksp"); + add("application/x-kword", "kwd"); + add("application/x-kword", "kwt"); + add("application/x-latex", "latex"); + add("application/x-lha", "lha"); + add("application/x-lzh", "lzh"); + add("application/x-lzx", "lzx"); + add("application/x-maker", "frm"); + add("application/x-maker", "maker"); + add("application/x-maker", "frame"); + add("application/x-maker", "fb"); + add("application/x-maker", "book"); + add("application/x-maker", "fbdoc"); + add("application/x-mif", "mif"); + add("application/x-ms-wmd", "wmd"); + add("application/x-ms-wmz", "wmz"); + add("application/x-msi", "msi"); + add("application/x-ns-proxy-autoconfig", "pac"); + add("application/x-nwc", "nwc"); + add("application/x-object", "o"); + add("application/x-oz-application", "oza"); + add("application/x-pem-file", "pem"); + add("application/x-pkcs12", "p12"); + add("application/x-pkcs12", "pfx"); + add("application/x-pkcs7-certreqresp", "p7r"); + add("application/x-pkcs7-crl", "crl"); + add("application/x-quicktimeplayer", "qtl"); + add("application/x-shar", "shar"); + add("application/x-shockwave-flash", "swf"); + add("application/x-stuffit", "sit"); + add("application/x-sv4cpio", "sv4cpio"); + add("application/x-sv4crc", "sv4crc"); + add("application/x-tar", "tar"); + add("application/x-texinfo", "texinfo"); + add("application/x-texinfo", "texi"); + add("application/x-troff", "t"); + add("application/x-troff", "roff"); + add("application/x-troff-man", "man"); + add("application/x-ustar", "ustar"); + add("application/x-wais-source", "src"); + add("application/x-wingz", "wz"); + add("application/x-webarchive", "webarchive"); + add("application/x-webarchive-xml", "webarchivexml"); + add("application/x-x509-ca-cert", "crt"); + add("application/x-x509-user-cert", "crt"); + add("application/x-x509-server-cert", "crt"); + add("application/x-xcf", "xcf"); + add("application/x-xfig", "fig"); + add("application/xhtml+xml", "xhtml"); + add("audio/3gpp", "3gpp"); + add("audio/aac", "aac"); + add("audio/aac-adts", "aac"); + add("audio/amr", "amr"); + add("audio/amr-wb", "awb"); + add("audio/basic", "snd"); + add("audio/flac", "flac"); + add("application/x-flac", "flac"); + add("audio/imelody", "imy"); + add("audio/midi", "mid"); + add("audio/midi", "midi"); + add("audio/midi", "ota"); + add("audio/midi", "kar"); + add("audio/midi", "rtttl"); + add("audio/midi", "xmf"); + add("audio/mobile-xmf", "mxmf"); + // add ".mp3" first so it will be the default for guessExtensionFromMimeType + add("audio/mpeg", "mp3"); + add("audio/mpeg", "mpga"); + add("audio/mpeg", "mpega"); + add("audio/mpeg", "mp2"); + add("audio/mpeg", "m4a"); + add("audio/mpegurl", "m3u"); + add("audio/prs.sid", "sid"); + add("audio/x-aiff", "aif"); + add("audio/x-aiff", "aiff"); + add("audio/x-aiff", "aifc"); + add("audio/x-gsm", "gsm"); + add("audio/x-matroska", "mka"); + add("audio/x-mpegurl", "m3u"); + add("audio/x-ms-wma", "wma"); + add("audio/x-ms-wax", "wax"); + add("audio/x-pn-realaudio", "ra"); + add("audio/x-pn-realaudio", "rm"); + add("audio/x-pn-realaudio", "ram"); + add("audio/x-realaudio", "ra"); + add("audio/x-scpls", "pls"); + add("audio/x-sd2", "sd2"); + add("audio/x-wav", "wav"); + // image/bmp isn't IANA, so image/x-ms-bmp should come first. + add("image/x-ms-bmp", "bmp"); + add("image/bmp", "bmp"); + add("image/gif", "gif"); + // image/ico isn't IANA, so image/x-icon should come first. + add("image/x-icon", "ico"); + add("image/ico", "cur"); + add("image/ico", "ico"); + add("image/ief", "ief"); + // add ".jpg" first so it will be the default for guessExtensionFromMimeType + add("image/jpeg", "jpg"); + add("image/jpeg", "jpeg"); + add("image/jpeg", "jpe"); + add("image/pcx", "pcx"); + add("image/png", "png"); + add("image/svg+xml", "svg"); + add("image/svg+xml", "svgz"); + add("image/tiff", "tiff"); + add("image/tiff", "tif"); + add("image/vnd.djvu", "djvu"); + add("image/vnd.djvu", "djv"); + add("image/vnd.wap.wbmp", "wbmp"); + add("image/webp", "webp"); + add("image/x-cmu-raster", "ras"); + add("image/x-coreldraw", "cdr"); + add("image/x-coreldrawpattern", "pat"); + add("image/x-coreldrawtemplate", "cdt"); + add("image/x-corelphotopaint", "cpt"); + add("image/x-jg", "art"); + add("image/x-jng", "jng"); + add("image/x-photoshop", "psd"); + add("image/x-portable-anymap", "pnm"); + add("image/x-portable-bitmap", "pbm"); + add("image/x-portable-graymap", "pgm"); + add("image/x-portable-pixmap", "ppm"); + add("image/x-rgb", "rgb"); + add("image/x-xbitmap", "xbm"); + add("image/x-xpixmap", "xpm"); + add("image/x-xwindowdump", "xwd"); + add("model/iges", "igs"); + add("model/iges", "iges"); + add("model/mesh", "msh"); + add("model/mesh", "mesh"); + add("model/mesh", "silo"); + add("text/calendar", "ics"); + add("text/calendar", "icz"); + add("text/comma-separated-values", "csv"); + add("text/css", "css"); + add("text/html", "htm"); + add("text/html", "html"); + add("text/h323", "323"); + add("text/iuls", "uls"); + add("text/mathml", "mml"); + // add ".txt" first so it will be the default for guessExtensionFromMimeType + add("text/plain", "txt"); + add("text/plain", "asc"); + add("text/plain", "text"); + add("text/plain", "diff"); + add("text/plain", "po"); // reserve "pot" for vnd.ms-powerpoint + add("text/richtext", "rtx"); + add("text/rtf", "rtf"); + add("text/text", "phps"); + add("text/tab-separated-values", "tsv"); + add("text/xml", "xml"); + add("text/x-bibtex", "bib"); + add("text/x-boo", "boo"); + add("text/x-c++hdr", "hpp"); + add("text/x-c++hdr", "h++"); + add("text/x-c++hdr", "hxx"); + add("text/x-c++hdr", "hh"); + add("text/x-c++src", "cpp"); + add("text/x-c++src", "c++"); + add("text/x-c++src", "cc"); + add("text/x-c++src", "cxx"); + add("text/x-chdr", "h"); + add("text/x-component", "htc"); + add("text/x-csh", "csh"); + add("text/x-csrc", "c"); + add("text/x-dsrc", "d"); + add("text/x-haskell", "hs"); + add("text/x-java", "java"); + add("text/x-literate-haskell", "lhs"); + add("text/x-moc", "moc"); + add("text/x-pascal", "p"); + add("text/x-pascal", "pas"); + add("text/x-pcs-gcd", "gcd"); + add("text/x-setext", "etx"); + add("text/x-tcl", "tcl"); + add("text/x-tex", "tex"); + add("text/x-tex", "ltx"); + add("text/x-tex", "sty"); + add("text/x-tex", "cls"); + add("text/x-vcalendar", "vcs"); + add("text/x-vcard", "vcf"); + add("video/3gpp", "3gpp"); + add("video/3gpp", "3gp"); + add("video/3gpp2", "3gpp2"); + add("video/3gpp2", "3g2"); + add("video/avi", "avi"); + add("video/dl", "dl"); + add("video/dv", "dif"); + add("video/dv", "dv"); + add("video/fli", "fli"); + add("video/m4v", "m4v"); + add("video/mp2ts", "ts"); + add("video/mpeg", "mpeg"); + add("video/mpeg", "mpg"); + add("video/mpeg", "mpe"); + add("video/mp4", "mp4"); + add("video/mpeg", "VOB"); + add("video/quicktime", "qt"); + add("video/quicktime", "mov"); + add("video/vnd.mpegurl", "mxu"); + add("video/webm", "webm"); + add("video/x-la-asf", "lsf"); + add("video/x-la-asf", "lsx"); + add("video/x-matroska", "mkv"); + add("video/x-mng", "mng"); + add("video/x-ms-asf", "asf"); + add("video/x-ms-asf", "asx"); + add("video/x-ms-wm", "wm"); + add("video/x-ms-wmv", "wmv"); + add("video/x-ms-wmx", "wmx"); + add("video/x-ms-wvx", "wvx"); + add("video/x-sgi-movie", "movie"); + add("video/x-webex", "wrf"); + add("x-conference/x-cooltalk", "ice"); + add("x-epoc/x-sisx-app", "sisx"); + applyOverrides(); + } + private static void add(String mimeType, String extension) { + // If we have an existing x -> y mapping, we do not want to + // override it with another mapping x -> y2. + // If a mime type maps to several extensions + // the first extension added is considered the most popular + // so we do not want to overwrite it later. + if (!mimeTypeToExtensionMap.containsKey(mimeType)) { + mimeTypeToExtensionMap.put(mimeType, extension); + } + if (!extensionToMimeTypeMap.containsKey(extension)) { + extensionToMimeTypeMap.put(extension, mimeType); + } + } + private static InputStream getContentTypesPropertiesStream() { + // User override? + String userTable = System.getProperty("content.types.user.table"); + if (userTable != null) { + File f = new File(userTable); + if (f.exists()) { + try { + return new FileInputStream(f); + } catch (IOException ignored) { + } + } + } + // Standard location? + File f = new File(System.getProperty("java.home"), "lib" + File.separator + "content-types.properties"); + if (f.exists()) { + try { + return new FileInputStream(f); + } catch (IOException ignored) { + } + } + return null; + } + /** + * This isn't what the RI does. The RI doesn't have hard-coded defaults, so supplying your + * own "content.types.user.table" means you don't get any of the built-ins, and the built-ins + * come from "$JAVA_HOME/lib/content-types.properties". + */ + private static void applyOverrides() { + // Get the appropriate InputStream to read overrides from, if any. + InputStream stream = getContentTypesPropertiesStream(); + if (stream == null) { + return; + } + try { + try { + // Read the properties file... + Properties overrides = new Properties(); + overrides.load(stream); + // And translate its mapping to ours... + for (Map.Entry<Object, Object> entry : overrides.entrySet()) { + String extension = (String) entry.getKey(); + String mimeType = (String) entry.getValue(); + add(mimeType, extension); + } + } finally { + stream.close(); + } + } catch (IOException ignored) { + } + } + private MimeUtils() { + } + /** + * Returns true if the given MIME type has an entry in the map. + * @param mimeType A MIME type (i.e. text/plain) + * @return True iff there is a mimeType entry in the map. + */ + public static boolean hasMimeType(String mimeType) { + if (mimeType == null || mimeType.isEmpty()) { + return false; + } + return mimeTypeToExtensionMap.containsKey(mimeType); + } + /** + * Returns the MIME type for the given extension. + * @param extension A file extension without the leading '.' + * @return The MIME type for the given extension or null iff there is none. + */ + public static String guessMimeTypeFromExtension(String extension) { + if (extension == null || extension.isEmpty()) { + return null; + } + return extensionToMimeTypeMap.get(extension); + } + /** + * Returns true if the given extension has a registered MIME type. + * @param extension A file extension without the leading '.' + * @return True iff there is an extension entry in the map. + */ + public static boolean hasExtension(String extension) { + if (extension == null || extension.isEmpty()) { + return false; + } + return extensionToMimeTypeMap.containsKey(extension); + } + /** + * Returns the registered extension for the given MIME type. Note that some + * MIME types map to multiple extensions. This call will return the most + * common extension for the given MIME type. + * @param mimeType A MIME type (i.e. text/plain) + * @return The extension for the given MIME type or null iff there is none. + */ + public static String guessExtensionFromMimeType(String mimeType) { + if (mimeType == null || mimeType.isEmpty()) { + return null; + } + return mimeTypeToExtensionMap.get(mimeType); + } +} diff --git a/src/main/java/eu/siacs/conversations/utils/SerialSingleThreadExecutor.java b/src/main/java/eu/siacs/conversations/utils/SerialSingleThreadExecutor.java new file mode 100644 index 00000000..bfb4668d --- /dev/null +++ b/src/main/java/eu/siacs/conversations/utils/SerialSingleThreadExecutor.java @@ -0,0 +1,34 @@ +package eu.siacs.conversations.utils; + +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +public class SerialSingleThreadExecutor implements Executor { + + final Executor executor = Executors.newSingleThreadExecutor(); + final Queue<Runnable> tasks = new ArrayDeque(); + Runnable active; + + public synchronized void execute(final Runnable r) { + tasks.offer(new Runnable() { + public void run() { + try { + r.run(); + } finally { + scheduleNext(); + } + } + }); + if (active == null) { + scheduleNext(); + } + } + + protected synchronized void scheduleNext() { + if ((active = tasks.poll()) != null) { + executor.execute(active); + } + } +}
\ No newline at end of file diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java index 82e950ad..de669ea6 100644 --- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.utils; import java.util.ArrayList; +import java.util.Arrays; import java.net.URLConnection; import java.util.Calendar; import java.util.Date; @@ -16,7 +17,8 @@ import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; -import eu.siacs.conversations.entities.Downloadable; +import eu.siacs.conversations.entities.Presences; +import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.xmpp.jid.Jid; @@ -29,6 +31,33 @@ import android.text.Spannable; import android.util.Pair; public class UIHelper { + + private static String BLACK_HEART_SUIT = "\u2665"; + private static String HEAVY_BLACK_HEART_SUIT = "\u2764"; + private static String WHITE_HEART_SUIT = "\u2661"; + + public static final ArrayList<String> HEARTS = new ArrayList<>(Arrays.asList(BLACK_HEART_SUIT,HEAVY_BLACK_HEART_SUIT,WHITE_HEART_SUIT)); + + private static final ArrayList<String> LOCATION_QUESTIONS = new ArrayList<>(Arrays.asList( + "where are you", //en + "where are you now", //en + "where are you right now", //en + "whats your 20", //en + "what is your 20", //en + "what's your 20", //en + "whats your twenty", //en + "what is your twenty", //en + "what's your twenty", //en + "wo bist du", //de + "wo bist du jetzt", //de + "wo bist du gerade", //de + "wo seid ihr", //de + "wo seid ihr jetzt", //de + "wo seid ihr gerade", //de + "dónde estás", //es + "donde estas" //es + )); + private static final int SHORT_DATE_FLAGS = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR | DateUtils.FORMAT_ABBREV_ALL; private static final int FULL_DATE_FLAGS = DateUtils.FORMAT_SHOW_TIME @@ -253,24 +282,25 @@ public class UIHelper { } public static Pair<String,Boolean> getMessagePreview(final Context context, final Message message) { - final Downloadable d = message.getDownloadable(); + final Transferable d = message.getTransferable(); if (d != null ) { switch (d.getStatus()) { - case Downloadable.STATUS_CHECKING: - return new Pair<>(context.getString(R.string.checking_image),true); - case Downloadable.STATUS_DOWNLOADING: + case Transferable.STATUS_CHECKING: + return new Pair<>(context.getString(R.string.checking_x, + getFileDescriptionString(context,message)),true); + case Transferable.STATUS_DOWNLOADING: return new Pair<>(context.getString(R.string.receiving_x_file, getFileDescriptionString(context,message), d.getProgress()),true); - case Downloadable.STATUS_OFFER: - case Downloadable.STATUS_OFFER_CHECK_FILESIZE: + case Transferable.STATUS_OFFER: + case Transferable.STATUS_OFFER_CHECK_FILESIZE: return new Pair<>(context.getString(R.string.x_file_offered_for_download, getFileDescriptionString(context,message)),true); - case Downloadable.STATUS_DELETED: + case Transferable.STATUS_DELETED: return new Pair<>(context.getString(R.string.file_deleted),true); - case Downloadable.STATUS_FAILED: + case Transferable.STATUS_FAILED: return new Pair<>(context.getString(R.string.file_transmission_failed),true); - case Downloadable.STATUS_UPLOADING: + case Transferable.STATUS_UPLOADING: if (message.getStatus() == Message.STATUS_OFFERED) { return new Pair<>(context.getString(R.string.offering_x_file, getFileDescriptionString(context, message)), true); @@ -310,16 +340,7 @@ public class UIHelper { if (message.getType() == Message.TYPE_IMAGE) { return context.getString(R.string.image); } - final String path = message.getRelativeFilePath(); - if (path == null) { - return ""; - } - final String mime; - try { - mime = URLConnection.guessContentTypeFromName(path.replace("#","")); - } catch (final StringIndexOutOfBoundsException ignored) { - return context.getString(R.string.file); - } + final String mime = message.getMimeType(); if (mime == null) { return context.getString(R.string.file); } else if (mime.startsWith("audio/")) { @@ -341,10 +362,14 @@ public class UIHelper { public static String getMessageDisplayName(final Message message) { if (message.getStatus() == Message.STATUS_RECEIVED) { + final Contact contact = message.getContact(); if (message.getConversation().getMode() == Conversation.MODE_MULTI) { - return getDisplayedMucCounterpart(message.getCounterpart()); + if (contact != null) { + return contact.getDisplayName(); + } else { + return getDisplayedMucCounterpart(message.getCounterpart()); + } } else { - final Contact contact = message.getContact(); return contact != null ? contact.getDisplayName() : ""; } } else { @@ -380,4 +405,15 @@ public class UIHelper { return counterpart.toString().trim(); } } + + public static boolean receivedLocationQuestion(Message message) { + if (message == null + || message.getStatus() != Message.STATUS_RECEIVED + || message.getType() != Message.TYPE_TEXT) { + return false; + } + String body = message.getBody() == null ? null : message.getBody().trim().toLowerCase(Locale.getDefault()); + body = body.replace("?","").replace("¿",""); + return LOCATION_QUESTIONS.contains(body); + } } diff --git a/src/main/java/eu/siacs/conversations/utils/Xmlns.java b/src/main/java/eu/siacs/conversations/utils/Xmlns.java index 17fd2d26..868566d9 100644 --- a/src/main/java/eu/siacs/conversations/utils/Xmlns.java +++ b/src/main/java/eu/siacs/conversations/utils/Xmlns.java @@ -5,4 +5,5 @@ public final class Xmlns { public static final String ROSTER = "jabber:iq:roster"; public static final String REGISTER = "jabber:iq:register"; public static final String BYTE_STREAMS = "http://jabber.org/protocol/bytestreams"; + public static final String HTTP_UPLOAD = "eu:siacs:conversations:http:upload"; } diff --git a/src/main/java/eu/siacs/conversations/xml/Element.java b/src/main/java/eu/siacs/conversations/xml/Element.java index 51708759..32657c66 100644 --- a/src/main/java/eu/siacs/conversations/xml/Element.java +++ b/src/main/java/eu/siacs/conversations/xml/Element.java @@ -57,6 +57,11 @@ public class Element { return null; } + public String findChildContent(String name) { + Element element = findChild(name); + return element == null ? null : element.getContent(); + } + public Element findChild(String name, String xmlns) { for (Element child : this.children) { if (child.getName().equals(name) @@ -67,6 +72,11 @@ public class Element { return null; } + public String findChildContent(String name, String xmlns) { + Element element = findChild(name,xmlns); + return element == null ? null : element.getContent(); + } + public boolean hasChild(final String name) { return findChild(name) != null; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 48dc2150..35c89b45 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -26,6 +26,7 @@ import java.net.IDN; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; +import java.net.SocketAddress; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; @@ -90,7 +91,7 @@ public class XmppConnection implements Runnable { private boolean shouldBind = true; private boolean shouldAuthenticate = true; private Element streamFeatures; - private final HashMap<String, List<String>> disco = new HashMap<>(); + private final HashMap<Jid, Info> disco = new HashMap<>(); private String streamId = null; private int smVersion = 3; @@ -155,56 +156,62 @@ public class XmppConnection implements Runnable { tagWriter = new TagWriter(); packetCallbacks.clear(); this.changeStatus(Account.State.CONNECTING); - final Bundle result = DNSHelper.getSRVRecord(account.getServer()); - final ArrayList<Parcelable> values = result.getParcelableArrayList("values"); - if ("timeout".equals(result.getString("error"))) { - throw new IOException("timeout in dns"); - } else if (values != null) { - int i = 0; - boolean socketError = true; - while (socketError && values.size() > i) { - final Bundle namePort = (Bundle) values.get(i); - try { - String srvRecordServer; + if (DNSHelper.isIp(account.getServer().toString())) { + socket = new Socket(); + try { + socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000); + } catch (IOException e) { + throw new UnknownHostException(); + } + } else { + final Bundle result = DNSHelper.getSRVRecord(account.getServer()); + final ArrayList<Parcelable> values = result.getParcelableArrayList("values"); + if ("timeout".equals(result.getString("error"))) { + throw new IOException("timeout in dns"); + } else if (values != null) { + int i = 0; + boolean socketError = true; + while (socketError && values.size() > i) { + final Bundle namePort = (Bundle) values.get(i); try { - srvRecordServer=IDN.toASCII(namePort.getString("name")); - } catch (final IllegalArgumentException e) { - // TODO: Handle me?` - srvRecordServer = ""; - } - final int srvRecordPort = namePort.getInt("port"); - final String srvIpServer = namePort.getString("ip"); - final InetSocketAddress addr; - if (srvIpServer != null) { - addr = new InetSocketAddress(srvIpServer, srvRecordPort); - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() - + ": using values from dns " + srvRecordServer - + "[" + srvIpServer + "]:" + srvRecordPort); - } else { - addr = new InetSocketAddress(srvRecordServer, srvRecordPort); - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() - + ": using values from dns " - + srvRecordServer + ":" + srvRecordPort); + String srvRecordServer; + try { + srvRecordServer = IDN.toASCII(namePort.getString("name")); + } catch (final IllegalArgumentException e) { + // TODO: Handle me?` + srvRecordServer = ""; + } + final int srvRecordPort = namePort.getInt("port"); + final String srvIpServer = namePort.getString("ip"); + final InetSocketAddress addr; + if (srvIpServer != null) { + addr = new InetSocketAddress(srvIpServer, srvRecordPort); + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + + ": using values from dns " + srvRecordServer + + "[" + srvIpServer + "]:" + srvRecordPort); + } else { + addr = new InetSocketAddress(srvRecordServer, srvRecordPort); + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + + ": using values from dns " + + srvRecordServer + ":" + srvRecordPort); + } + socket = new Socket(); + socket.connect(addr, Config.SOCKET_TIMEOUT * 1000); + socketError = false; + } catch (final UnknownHostException e) { + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); + i++; + } catch (final IOException e) { + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); + i++; } - socket = new Socket(); - socket.connect(addr, 20000); - socketError = false; - } catch (final UnknownHostException e) { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); - i++; - } catch (final IOException e) { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); - i++; } + if (socketError) { + throw new UnknownHostException(); + } + } else { + throw new IOException("unhandled exception in DNS resolver"); } - if (socketError) { - throw new UnknownHostException(); - } - } else if (result.containsKey("error") - && "nosrv".equals(result.getString("error", null))) { - socket = new Socket(account.getServer().getDomainpart(), 5222); - } else { - throw new IOException("timeout in dns"); } final OutputStream out = socket.getOutputStream(); tagWriter.setOutputStream(out); @@ -224,6 +231,12 @@ public class XmppConnection implements Runnable { if (socket.isConnected()) { socket.close(); } + } catch (final IncompatibleServerException e) { + this.changeStatus(Account.State.INCOMPATIBLE_SERVER); + } catch (final SecurityException e) { + this.changeStatus(Account.State.SECURITY_ERROR); + } catch (final UnauthorizedException e) { + this.changeStatus(Account.State.UNAUTHORIZED); } catch (final UnknownHostException | ConnectException e) { this.changeStatus(Account.State.SERVER_NOT_FOUND); } catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) { @@ -231,6 +244,13 @@ public class XmppConnection implements Runnable { this.changeStatus(Account.State.OFFLINE); this.attempt--; //don't count attempt when reconnecting instantly anyway } finally { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + + } + } if (wakeLock.isHeld()) { try { wakeLock.release(); @@ -279,8 +299,7 @@ public class XmppConnection implements Runnable { processStream(tagReader.readTag()); break; } else if (nextTag.isStart("failure")) { - tagReader.readElement(nextTag); - changeStatus(Account.State.UNAUTHORIZED); + throw new UnauthorizedException(); } else if (nextTag.isStart("challenge")) { final String challenge = tagReader.readElement(nextTag).getContent(); final Element response = new Element("response"); @@ -334,23 +353,34 @@ public class XmppConnection implements Runnable { } catch (final NumberFormatException ignored) { } sendServiceDiscoveryInfo(account.getServer()); + sendServiceDiscoveryInfo(account.getJid().toBareJid()); sendServiceDiscoveryItems(account.getServer()); sendInitialPing(); } else if (nextTag.isStart("r")) { tagReader.readElement(nextTag); + if (Config.EXTENDED_SM_LOGGING) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": acknowledging stanza #" + this.stanzasReceived); + } final AckPacket ack = new AckPacket(this.stanzasReceived, smVersion); tagWriter.writeStanzaAsync(ack); } else if (nextTag.isStart("a")) { final Element ack = tagReader.readElement(nextTag); lastPacketReceived = SystemClock.elapsedRealtime(); - final int serverSequence = Integer.parseInt(ack.getAttribute("h")); - final String msgId = this.messageReceipts.get(serverSequence); - if (msgId != null) { - if (this.acknowledgedListener != null) { - this.acknowledgedListener.onMessageAcknowledged( - account, msgId); + try { + final int serverSequence = Integer.parseInt(ack.getAttribute("h")); + if (Config.EXTENDED_SM_LOGGING) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": server acknowledged stanza #" + serverSequence); } - this.messageReceipts.remove(serverSequence); + final String msgId = this.messageReceipts.get(serverSequence); + if (msgId != null) { + if (this.acknowledgedListener != null) { + this.acknowledgedListener.onMessageAcknowledged( + account, msgId); + } + this.messageReceipts.remove(serverSequence); + } + } catch (NumberFormatException e) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": server send ack without sequence number"); } } else if (nextTag.isStart("failed")) { tagReader.readElement(nextTag); @@ -430,6 +460,10 @@ public class XmppConnection implements Runnable { throw new IOException("interrupted mid tag"); } } + if (stanzasReceived == Integer.MAX_VALUE) { + resetStreamId(); + throw new IOException("time to restart the session. cant handle >2 billion pcks"); + } ++stanzasReceived; lastPacketReceived = SystemClock.elapsedRealtime(); return element; @@ -531,8 +565,7 @@ public class XmppConnection implements Runnable { if (!verifier.verify(account.getServer().getDomainpart(),sslSocket.getSession())) { Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed"); - disconnect(true); - changeStatus(Account.State.SECURITY_ERROR); + throw new SecurityException(); } tagReader.setInputStream(sslSocket.getInputStream()); tagWriter.setOutputStream(sslSocket.getOutputStream()); @@ -543,8 +576,7 @@ public class XmppConnection implements Runnable { sslSocket.close(); } catch (final NoSuchAlgorithmException | KeyManagementException e1) { Log.d(Config.LOGTAG,account.getJid().toBareJid()+": TLS certificate verification failed"); - disconnect(true); - changeStatus(Account.State.SECURITY_ERROR); + throw new SecurityException(); } } @@ -574,31 +606,36 @@ public class XmppConnection implements Runnable { } else if (mechanisms.contains("DIGEST-MD5")) { saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG()); } - final JSONObject keys = account.getKeys(); - try { - if (keys.has(Account.PINNED_MECHANISM_KEY) && - keys.getInt(Account.PINNED_MECHANISM_KEY) > saslMechanism.getPriority() ) { - Log.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() + - " has lower priority (" + String.valueOf(saslMechanism.getPriority()) + - ") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) + - "). Possible downgrade attack?"); - disconnect(true); - changeStatus(Account.State.SECURITY_ERROR); - } - } catch (final JSONException e) { - Log.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism"); - } - Log.d(Config.LOGTAG,account.getJid().toString()+": Authenticating with " + saslMechanism.getMechanism()); - auth.setAttribute("mechanism", saslMechanism.getMechanism()); - if (!saslMechanism.getClientFirstMessage().isEmpty()) { - auth.setContent(saslMechanism.getClientFirstMessage()); + if (saslMechanism != null) { + final JSONObject keys = account.getKeys(); + try { + if (keys.has(Account.PINNED_MECHANISM_KEY) && + keys.getInt(Account.PINNED_MECHANISM_KEY) > saslMechanism.getPriority()) { + Log.e(Config.LOGTAG, "Auth failed. Authentication mechanism " + saslMechanism.getMechanism() + + " has lower priority (" + String.valueOf(saslMechanism.getPriority()) + + ") than pinned priority (" + keys.getInt(Account.PINNED_MECHANISM_KEY) + + "). Possible downgrade attack?"); + throw new SecurityException(); + } + } catch (final JSONException e) { + Log.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism"); + } + Log.d(Config.LOGTAG, account.getJid().toString() + ": Authenticating with " + saslMechanism.getMechanism()); + auth.setAttribute("mechanism", saslMechanism.getMechanism()); + if (!saslMechanism.getClientFirstMessage().isEmpty()) { + auth.setContent(saslMechanism.getClientFirstMessage()); + } + tagWriter.writeElement(auth); + } else { + throw new IncompatibleServerException(); } - tagWriter.writeElement(auth); } else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" + smVersion) && streamId != null) { - final ResumePacket resume = new ResumePacket(this.streamId, - stanzasReceived, smVersion); + if (Config.EXTENDED_SM_LOGGING) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": resuming after stanza #"+stanzasReceived); + } + final ResumePacket resume = new ResumePacket(this.streamId, stanzasReceived, smVersion); this.tagWriter.writeStanzaAsync(resume); } else if (this.streamFeatures.hasChild("bind") && shouldBind) { sendBindRequest(); @@ -629,10 +666,8 @@ public class XmppConnection implements Runnable { if (packet.query().hasChild("username") && (packet.query().hasChild("password"))) { final IqPacket register = new IqPacket(IqPacket.TYPE.SET); - final Element username = new Element("username") - .setContent(account.getUsername()); - final Element password = new Element("password") - .setContent(account.getPassword()); + final Element username = new Element("username").setContent(account.getUsername()); + final Element password = new Element("password").setContent(account.getPassword()); register.query("jabber:iq:register").addChild(username); register.query().addChild(password); sendIqPacket(register, new OnIqPacketReceived() { @@ -645,7 +680,7 @@ public class XmppConnection implements Runnable { changeStatus(Account.State.REGISTRATION_SUCCESSFUL); } else if (packet.hasChild("error") && (packet.findChild("error") - .hasChild("conflict"))) { + .hasChild("conflict"))) { changeStatus(Account.State.REGISTRATION_CONFLICT); } else { changeStatus(Account.State.REGISTRATION_FAILED); @@ -659,7 +694,7 @@ public class XmppConnection implements Runnable { disconnect(true); Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not register. instructions are" - + instructions.getContent()); + + (instructions != null ? instructions.getContent() : "")); } } }); @@ -674,7 +709,7 @@ public class XmppConnection implements Runnable { } final IqPacket iq = new IqPacket(IqPacket.TYPE.SET); iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind") - .addChild("resource").setContent(account.getResource()); + .addChild("resource").setContent(account.getResource()); this.sendUnmodifiedIqPacket(iq, new OnIqPacketReceived() { @Override public void onIqPacketReceived(final Account account, final IqPacket packet) { @@ -734,6 +769,7 @@ public class XmppConnection implements Runnable { features.blockListRequested = false; disco.clear(); sendServiceDiscoveryInfo(account.getServer()); + sendServiceDiscoveryInfo(account.getJid().toBareJid()); sendServiceDiscoveryItems(account.getServer()); if (bindListener != null) { bindListener.onBind(account); @@ -741,34 +777,35 @@ public class XmppConnection implements Runnable { sendInitialPing(); } - private void sendServiceDiscoveryInfo(final Jid server) { - if (disco.containsKey(server.toDomainJid().toString())) { - if (account.getServer().equals(server.toDomainJid())) { + private void sendServiceDiscoveryInfo(final Jid jid) { + if (disco.containsKey(jid)) { + if (account.getServer().equals(jid)) { enableAdvancedStreamFeatures(); } } else { final IqPacket iq = new IqPacket(IqPacket.TYPE.GET); - iq.setTo(server.toDomainJid()); + iq.setTo(jid); iq.query("http://jabber.org/protocol/disco#info"); this.sendIqPacket(iq, new OnIqPacketReceived() { @Override public void onIqPacketReceived(final Account account, final IqPacket packet) { final List<Element> elements = packet.query().getChildren(); - final List<String> features = new ArrayList<>(); + final Info info = new Info(); for (final Element element : elements) { if (element.getName().equals("identity")) { - if ("irc".equals(element.getAttribute("type"))) { - //add fake feature to not confuse irc and real muc - features.add("siacs:no:muc"); + String type = element.getAttribute("type"); + String category = element.getAttribute("category"); + if (type != null && category != null) { + info.identities.add(new Pair<>(category,type)); } } else if (element.getName().equals("feature")) { - features.add(element.getAttribute("var")); + info.features.add(element.getAttribute("var")); } } - disco.put(server.toDomainJid().toString(), features); + disco.put(jid, info); - if (account.getServer().equals(server.toDomainJid())) { + if (account.getServer().equals(jid)) { enableAdvancedStreamFeatures(); for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) { listener.onAdvancedStreamFeaturesAvailable(account); @@ -784,7 +821,7 @@ public class XmppConnection implements Runnable { sendEnableCarbons(); } if (getFeatures().blocking() && !features.blockListRequested) { - Log.d(Config.LOGTAG, "Requesting block list"); + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": Requesting block list"); this.sendIqPacket(getIqGenerator().generateGetBlockList(), mXmppConnectionService.getIqParser()); } } @@ -885,13 +922,20 @@ public class XmppConnection implements Runnable { } private synchronized void sendPacket(final AbstractStanza packet) { + if (stanzasSent == Integer.MAX_VALUE) { + resetStreamId(); + disconnect(true); + return; + } final String name = packet.getName(); if (name.equals("iq") || name.equals("message") || name.equals("presence")) { ++stanzasSent; } tagWriter.writeStanzaAsync(packet); - if (packet instanceof MessagePacket && packet.getId() != null && this.streamId != null) { - Log.d(Config.LOGTAG, "request delivery report for stanza " + stanzasSent); + if (packet instanceof MessagePacket && packet.getId() != null && getFeatures().sm()) { + if (Config.EXTENDED_SM_LOGGING) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": requesting ack for message stanza #" + stanzasSent); + } this.messageReceipts.put(stanzasSent, packet.getId()); tagWriter.writeStanzaAsync(new RequestPacket(this.smVersion)); } @@ -981,18 +1025,22 @@ public class XmppConnection implements Runnable { } } - public List<String> findDiscoItemsByFeature(final String feature) { - final List<String> items = new ArrayList<>(); - for (final Entry<String, List<String>> cursor : disco.entrySet()) { - if (cursor.getValue().contains(feature)) { + public void resetStreamId() { + this.streamId = null; + } + + public List<Jid> findDiscoItemsByFeature(final String feature) { + final List<Jid> items = new ArrayList<>(); + for (final Entry<Jid, Info> cursor : disco.entrySet()) { + if (cursor.getValue().features.contains(feature)) { items.add(cursor.getKey()); } } return items; } - public String findDiscoItemByFeature(final String feature) { - final List<String> items = findDiscoItemsByFeature(feature); + public Jid findDiscoItemByFeature(final String feature) { + final List<Jid> items = findDiscoItemsByFeature(feature); if (items.size() >= 1) { return items.get(0); } @@ -1004,10 +1052,12 @@ public class XmppConnection implements Runnable { } public String getMucServer() { - for (final Entry<String, List<String>> cursor : disco.entrySet()) { - final List<String> value = cursor.getValue(); - if (value.contains("http://jabber.org/protocol/muc") && !value.contains("jabber:iq:gateway") && !value.contains("siacs:no:muc")) { - return cursor.getKey(); + for (final Entry<Jid, Info> cursor : disco.entrySet()) { + final Info value = cursor.getValue(); + if (value.features.contains("http://jabber.org/protocol/muc") + && !value.features.contains("jabber:iq:gateway") + && !value.identities.contains(new Pair<>("conference","irc"))) { + return cursor.getKey().toString(); } } return null; @@ -1062,6 +1112,23 @@ public class XmppConnection implements Runnable { this.lastConnect = 0; } + private class Info { + public final ArrayList<String> features = new ArrayList<>(); + public final ArrayList<Pair<String,String>> identities = new ArrayList<>(); + } + + private class UnauthorizedException extends IOException { + + } + + private class SecurityException extends IOException { + + } + + private class IncompatibleServerException extends IOException { + + } + public class Features { XmppConnection connection; private boolean carbonsEnabled = false; @@ -1073,8 +1140,8 @@ public class XmppConnection implements Runnable { } private boolean hasDiscoFeature(final Jid server, final String feature) { - return connection.disco.containsKey(server.toDomainJid().toString()) && - connection.disco.get(server.toDomainJid().toString()).contains(feature); + return connection.disco.containsKey(server) && + connection.disco.get(server).features.contains(feature); } public boolean carbons() { @@ -1090,24 +1157,35 @@ public class XmppConnection implements Runnable { } public boolean sm() { - return streamId != null; + return streamId != null + || (connection.streamFeatures != null && connection.streamFeatures.hasChild("sm")); } public boolean csi() { return connection.streamFeatures != null && connection.streamFeatures.hasChild("csi", "urn:xmpp:csi:0"); } - public boolean pubsub() { - return hasDiscoFeature(account.getServer(), - "http://jabber.org/protocol/pubsub#publish"); + public boolean pep() { + final Pair<String,String> needle = new Pair<>("pubsub","pep"); + Info info = disco.get(account.getServer()); + if (info != null && info.identities.contains(needle)) { + return true; + } else { + info = disco.get(account.getJid().toBareJid()); + return info != null && info.identities.contains(needle); + } } public boolean mam() { - return hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0"); + if (hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:mam:0")) { + return true; + } else { + return hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0"); + } } public boolean advancedStreamFeaturesLoaded() { - return disco.containsKey(account.getServer().toString()); + return disco.containsKey(account.getServer()); } public boolean rosterVersioning() { @@ -1117,6 +1195,10 @@ public class XmppConnection implements Runnable { public void setBlockListRequested(boolean value) { this.blockListRequested = value; } + + public boolean httpUpload() { + return findDiscoItemsByFeature(Xmlns.HTTP_UPLOAD).size() > 0; + } } private IqGenerator getIqGenerator() { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java index 295e067a..f989c0c2 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jid/Jid.java @@ -130,12 +130,19 @@ public final class Jid { if (resourcepart.isEmpty() || resourcepart.length() > 1023) { throw new InvalidJidException(InvalidJidException.INVALID_PART_LENGTH); } - dp = IDN.toUnicode(jid.substring(domainpartStart, slashLoc), IDN.USE_STD3_ASCII_RULES); + try { + dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, slashLoc)), IDN.USE_STD3_ASCII_RULES); + } catch (final StringprepException e) { + throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e); + } finaljid = finaljid + dp + "/" + rp; } else { resourcepart = ""; - dp = IDN.toUnicode(jid.substring(domainpartStart, jid.length()), - IDN.USE_STD3_ASCII_RULES); + try{ + dp = IDN.toUnicode(Stringprep.nameprep(jid.substring(domainpartStart, jid.length())), IDN.USE_STD3_ASCII_RULES); + } catch (final StringprepException e) { + throw new InvalidJidException(InvalidJidException.STRINGPREP_FAIL, e); + } finaljid = finaljid + dp; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index 0db0deee..2c56488d 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -1,6 +1,5 @@ package eu.siacs.conversations.xmpp.jingle; -import java.net.URLConnection; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; @@ -16,9 +15,9 @@ import android.util.Log; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Conversation; -import eu.siacs.conversations.entities.Downloadable; +import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.DownloadableFile; -import eu.siacs.conversations.entities.DownloadablePlaceholder; +import eu.siacs.conversations.entities.TransferablePlaceholder; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; @@ -29,7 +28,7 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.jingle.stanzas.Reason; import eu.siacs.conversations.xmpp.stanzas.IqPacket; -public class JingleConnection implements Downloadable { +public class JingleConnection implements Transferable { private JingleConnectionManager mJingleConnectionManager; private XmppConnectionService mXmppConnectionService; @@ -40,10 +39,10 @@ public class JingleConnection implements Downloadable { protected static final int JINGLE_STATUS_TRANSMITTING = 5; protected static final int JINGLE_STATUS_FAILED = 99; - private int ibbBlockSize = 4096; + private int ibbBlockSize = 8192; private int mJingleStatus = -1; - private int mStatus = Downloadable.STATUS_UNKNOWN; + private int mStatus = Transferable.STATUS_UNKNOWN; private Message message; private String sessionId; private Account account; @@ -99,7 +98,7 @@ public class JingleConnection implements Downloadable { file.delete(); } } - Log.d(Config.LOGTAG,"sucessfully transmitted file:" + file.getAbsolutePath()); + Log.d(Config.LOGTAG,"successfully transmitted file:" + file.getAbsolutePath()+" ("+file.getSha1Sum()+")"); if (message.getEncryption() != Message.ENCRYPTION_PGP) { Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intent.setData(Uri.fromFile(file)); @@ -192,15 +191,15 @@ public class JingleConnection implements Downloadable { } else { response = packet.generateResponse(IqPacket.TYPE.ERROR); } - account.getXmppConnection().sendIqPacket(response, null); + mXmppConnectionService.sendIqPacket(account,response,null); } public void init(Message message) { this.contentCreator = "initiator"; this.contentName = this.mJingleConnectionManager.nextRandomId(); this.message = message; - this.message.setDownloadable(this); - this.mStatus = Downloadable.STATUS_UPLOADING; + this.message.setTransferable(this); + this.mStatus = Transferable.STATUS_UPLOADING; this.account = message.getConversation().getAccount(); this.initiator = this.account.getJid(); this.responder = this.message.getCounterpart(); @@ -213,7 +212,7 @@ public class JingleConnection implements Downloadable { @Override public void onPrimaryCandidateFound(boolean success, - final JingleCandidate candidate) { + final JingleCandidate candidate) { if (success) { final JingleSocks5Transport socksConnection = new JingleSocks5Transport( JingleConnection.this, candidate); @@ -256,8 +255,8 @@ public class JingleConnection implements Downloadable { packet.getFrom().toBareJid(), false); this.message = new Message(conversation, "", Message.ENCRYPTION_NONE); this.message.setStatus(Message.STATUS_RECEIVED); - this.mStatus = Downloadable.STATUS_OFFER; - this.message.setDownloadable(this); + this.mStatus = Transferable.STATUS_OFFER; + this.message.setTransferable(this); final Jid from = packet.getFrom(); this.message.setCounterpart(from); this.account = account; @@ -271,6 +270,9 @@ public class JingleConnection implements Downloadable { this.mergeCandidates(JingleCandidate.parse(content.socks5transport() .getChildren())); this.fileOffer = packet.getJingleContent().getFileOffer(); + + mXmppConnectionService.sendIqPacket(account,packet.generateResponse(IqPacket.TYPE.RESULT),null); + if (fileOffer != null) { Element fileSize = fileOffer.findChild("size"); Element fileNameElement = fileOffer.findChild("name"); @@ -366,7 +368,10 @@ public class JingleConnection implements Downloadable { message, false); if (message.getEncryption() == Message.ENCRYPTION_OTR) { Conversation conversation = this.message.getConversation(); - this.mXmppConnectionService.renewSymmetricKey(conversation); + if (!this.mXmppConnectionService.renewSymmetricKey(conversation)) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not set symmetric key"); + cancel(); + } content.setFileOffer(this.file, true); this.file.setKey(conversation.getSymmetricKey()); } else { @@ -381,6 +386,7 @@ public class JingleConnection implements Downloadable { @Override public void onIqPacketReceived(Account account, IqPacket packet) { if (packet.getType() != IqPacket.TYPE.ERROR) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": other party received offer"); mJingleStatus = JINGLE_STATUS_INITIATED; mXmppConnectionService.markMessage(message, Message.STATUS_OFFERED); } else { @@ -395,14 +401,16 @@ public class JingleConnection implements Downloadable { private List<Element> getCandidatesAsElements() { List<Element> elements = new ArrayList<>(); for (JingleCandidate c : this.candidates) { - elements.add(c.toElement()); + if (c.isOurs()) { + elements.add(c.toElement()); + } } return elements; } private void sendAccept() { mJingleStatus = JINGLE_STATUS_ACCEPTED; - this.mStatus = Downloadable.STATUS_DOWNLOADING; + this.mStatus = Transferable.STATUS_DOWNLOADING; mXmppConnectionService.updateConversationUi(); this.mJingleConnectionManager.getPrimaryCandidate(this.account, new OnPrimaryCandidateFound() { @Override @@ -459,11 +467,11 @@ public class JingleConnection implements Downloadable { } private void sendJinglePacket(JinglePacket packet) { - account.getXmppConnection().sendIqPacket(packet, responseListener); + mXmppConnectionService.sendIqPacket(account,packet,responseListener); } private void sendJinglePacket(JinglePacket packet, OnIqPacketReceived callback) { - account.getXmppConnection().sendIqPacket(packet,callback); + mXmppConnectionService.sendIqPacket(account,packet,callback); } private boolean receiveAccept(JinglePacket packet) { @@ -556,7 +564,7 @@ public class JingleConnection implements Downloadable { .setAttribute("sid", this.getSessionId()); activation.query().addChild("activate") .setContent(this.getCounterPart().toString()); - this.account.getXmppConnection().sendIqPacket(activation, + mXmppConnectionService.sendIqPacket(account,activation, new OnIqPacketReceived() { @Override @@ -633,7 +641,7 @@ public class JingleConnection implements Downloadable { this.disconnectSocks5Connections(); this.mJingleStatus = JINGLE_STATUS_FINISHED; this.message.setStatus(Message.STATUS_RECEIVED); - this.message.setDownloadable(null); + this.message.setTransferable(null); this.mXmppConnectionService.updateMessage(message); this.mJingleConnectionManager.finishConnection(this); } @@ -710,7 +718,7 @@ public class JingleConnection implements Downloadable { if (this.transport != null && this.transport instanceof JingleInbandTransport) { this.transport.disconnect(); } - this.message.setDownloadable(null); + this.message.setTransferable(null); this.mJingleConnectionManager.finishConnection(this); } @@ -722,7 +730,7 @@ public class JingleConnection implements Downloadable { this.sendCancel(); this.mJingleConnectionManager.finishConnection(this); if (this.responder.equals(account.getJid())) { - this.message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_FAILED)); + this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED)); if (this.file!=null) { file.delete(); } @@ -730,7 +738,7 @@ public class JingleConnection implements Downloadable { } else { this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_FAILED); - this.message.setDownloadable(null); + this.message.setTransferable(null); } } @@ -742,7 +750,7 @@ public class JingleConnection implements Downloadable { } if (this.message != null) { if (this.responder.equals(account.getJid())) { - this.message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_FAILED)); + this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED)); if (this.file!=null) { file.delete(); } @@ -750,7 +758,7 @@ public class JingleConnection implements Downloadable { } else { this.mXmppConnectionService.markMessage(this.message, Message.STATUS_SEND_FAILED); - this.message.setDownloadable(null); + this.message.setTransferable(null); } } this.mJingleConnectionManager.finishConnection(this); @@ -948,24 +956,4 @@ public class JingleConnection implements Downloadable { public int getProgress() { return this.mProgress; } - - @Override - public String getMimeType() { - if (this.message.getType() == Message.TYPE_FILE) { - String mime = null; - String path = this.message.getRelativeFilePath(); - if (path != null && !this.message.getRelativeFilePath().isEmpty()) { - mime = URLConnection.guessContentTypeFromName(this.message.getRelativeFilePath()); - if (mime!=null) { - return mime; - } else { - return ""; - } - } else { - return ""; - } - } else { - return "image/webp"; - } - } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 5dfa3ff4..cadf9df3 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -9,13 +9,13 @@ import android.annotation.SuppressLint; import android.util.Log; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.AbstractConnectionManager; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.Xmlns; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnIqPacketReceived; -import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket; @@ -58,7 +58,12 @@ public class JingleConnectionManager extends AbstractConnectionManager { } public JingleConnection createNewConnection(Message message) { + Transferable old = message.getTransferable(); + if (old != null) { + old.cancel(); + } JingleConnection connection = new JingleConnection(this); + mXmppConnectionService.markMessage(message,Message.STATUS_WAITING); connection.init(message); this.connections.add(connection); return connection; @@ -81,10 +86,10 @@ public class JingleConnectionManager extends AbstractConnectionManager { return; } if (!this.primaryCandidates.containsKey(account.getJid().toBareJid())) { - final String proxy = account.getXmppConnection().findDiscoItemByFeature(Xmlns.BYTE_STREAMS); + final Jid proxy = account.getXmppConnection().findDiscoItemByFeature(Xmlns.BYTE_STREAMS); if (proxy != null) { IqPacket iq = new IqPacket(IqPacket.TYPE.GET); - iq.setAttribute("to", proxy); + iq.setTo(proxy); iq.query(Xmlns.BYTE_STREAMS); account.getXmppConnection().sendIqPacket(iq,new OnIqPacketReceived() { @@ -99,11 +104,11 @@ public class JingleConnectionManager extends AbstractConnectionManager { candidate.setHost(host); candidate.setPort(Integer.parseInt(port)); candidate.setType(JingleCandidate.TYPE_PROXY); - candidate.setJid(Jid.fromString(proxy)); + candidate.setJid(proxy); candidate.setPriority(655360 + 65535); primaryCandidates.put(account.getJid().toBareJid(),candidate); listener.onPrimaryCandidateFound(true,candidate); - } catch (final NumberFormatException | InvalidJidException e) { + } catch (final NumberFormatException e) { listener.onPrimaryCandidateFound(false,null); return; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java index 174f70fa..9a02ee7a 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java @@ -8,9 +8,12 @@ import java.security.NoSuchAlgorithmException; import java.util.Arrays; import android.util.Base64; +import android.util.Log; +import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.DownloadableFile; +import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnIqPacketReceived; @@ -22,7 +25,6 @@ public class JingleInbandTransport extends JingleTransport { private Account account; private Jid counterpart; private int blockSize; - private int bufferSize; private int seq = 0; private String sessionId; @@ -55,7 +57,6 @@ public class JingleInbandTransport extends JingleTransport { this.account = connection.getAccount(); this.counterpart = connection.getCounterPart(); this.blockSize = blocksize; - this.bufferSize = blocksize / 4; this.sessionId = sid; } @@ -94,11 +95,13 @@ public class JingleInbandTransport extends JingleTransport { file.createNewFile(); this.fileOutputStream = file.createOutputStream(); if (this.fileOutputStream == null) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not create output stream"); callback.onFileTransferAborted(); return; } this.remainingSize = this.fileSize = file.getExpectedSize(); } catch (final NoSuchAlgorithmException | IOException e) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+" "+e.getMessage()); callback.onFileTransferAborted(); } } @@ -109,12 +112,17 @@ public class JingleInbandTransport extends JingleTransport { this.onFileTransmissionStatusChanged = callback; this.file = file; try { - this.remainingSize = this.file.getSize(); + if (this.file.getKey() != null) { + this.remainingSize = (this.file.getSize() / 16 + 1) * 16; + } else { + this.remainingSize = this.file.getSize(); + } this.fileSize = this.remainingSize; this.digest = MessageDigest.getInstance("SHA-1"); this.digest.reset(); fileInputStream = this.file.createInputStream(); if (fileInputStream == null) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could no create input stream"); callback.onFileTransferAborted(); return; } @@ -123,6 +131,7 @@ public class JingleInbandTransport extends JingleTransport { } } catch (NoSuchAlgorithmException e) { callback.onFileTransferAborted(); + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage()); } } @@ -146,32 +155,42 @@ public class JingleInbandTransport extends JingleTransport { } private void sendNextBlock() { - byte[] buffer = new byte[this.bufferSize]; + byte[] buffer = new byte[this.blockSize]; try { int count = fileInputStream.read(buffer); if (count == -1) { file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest())); - fileInputStream.close(); this.onFileTransmissionStatusChanged.onFileTransmitted(file); - } else { - this.remainingSize -= count; - this.digest.update(buffer); - String base64 = Base64.encodeToString(buffer, Base64.NO_WRAP); - IqPacket iq = new IqPacket(IqPacket.TYPE.SET); - iq.setTo(this.counterpart); - Element data = iq.addChild("data", - "http://jabber.org/protocol/ibb"); - data.setAttribute("seq", Integer.toString(this.seq)); - data.setAttribute("block-size", - Integer.toString(this.blockSize)); - data.setAttribute("sid", this.sessionId); - data.setContent(base64); - this.account.getXmppConnection().sendIqPacket(iq, - this.onAckReceived); - this.seq++; + fileInputStream.close(); + return; + } else if (count != buffer.length) { + int rem = fileInputStream.read(buffer,count,buffer.length-count); + if (rem > 0) { + count += rem; + } + } + this.remainingSize -= count; + this.digest.update(buffer,0,count); + String base64 = Base64.encodeToString(buffer,0,count, Base64.NO_WRAP); + IqPacket iq = new IqPacket(IqPacket.TYPE.SET); + iq.setTo(this.counterpart); + Element data = iq.addChild("data", "http://jabber.org/protocol/ibb"); + data.setAttribute("seq", Integer.toString(this.seq)); + data.setAttribute("block-size", Integer.toString(this.blockSize)); + data.setAttribute("sid", this.sessionId); + data.setContent(base64); + this.account.getXmppConnection().sendIqPacket(iq, this.onAckReceived); + this.seq++; + if (this.remainingSize > 0) { connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); + } else { + file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest())); + this.onFileTransmissionStatusChanged.onFileTransmitted(file); + fileInputStream.close(); } } catch (IOException e) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage()); + FileBackend.close(fileInputStream); this.onFileTransmissionStatusChanged.onFileTransferAborted(); } } @@ -180,14 +199,10 @@ public class JingleInbandTransport extends JingleTransport { try { byte[] buffer = Base64.decode(data, Base64.NO_WRAP); if (this.remainingSize < buffer.length) { - buffer = Arrays - .copyOfRange(buffer, 0, (int) this.remainingSize); + buffer = Arrays.copyOfRange(buffer, 0, (int) this.remainingSize); } this.remainingSize -= buffer.length; - - this.fileOutputStream.write(buffer); - this.digest.update(buffer); if (this.remainingSize <= 0) { file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest())); @@ -198,6 +213,8 @@ public class JingleInbandTransport extends JingleTransport { connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); } } catch (IOException e) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage()); + FileBackend.close(fileOutputStream); this.onFileTransmissionStatusChanged.onFileTransferAborted(); } } @@ -207,6 +224,7 @@ public class JingleInbandTransport extends JingleTransport { if (!established) { established = true; connected = true; + this.receiveNextBlock(""); this.account.getXmppConnection().sendIqPacket( packet.generateResponse(IqPacket.TYPE.RESULT), null); } else { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java index c3419580..8d74f44e 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -1,16 +1,22 @@ package eu.siacs.conversations.xmpp.jingle; +import android.util.Log; + import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.InetSocketAddress; import java.net.Socket; +import java.net.SocketAddress; import java.net.UnknownHostException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.DownloadableFile; +import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.utils.CryptoHelper; public class JingleSocks5Transport extends JingleTransport { @@ -52,8 +58,9 @@ public class JingleSocks5Transport extends JingleTransport { @Override public void run() { try { - socket = new Socket(candidate.getHost(), - candidate.getPort()); + socket = new Socket(); + SocketAddress address = new InetSocketAddress(candidate.getHost(),candidate.getPort()); + socket.connect(address,Config.SOCKET_TIMEOUT * 1000); inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); byte[] login = { 0x05, 0x01, 0x00 }; @@ -101,6 +108,7 @@ public class JingleSocks5Transport extends JingleTransport { digest.reset(); fileInputStream = file.createInputStream(); if (fileInputStream == null) { + Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create input stream"); callback.onFileTransferAborted(); return; } @@ -120,31 +128,28 @@ public class JingleSocks5Transport extends JingleTransport { callback.onFileTransmitted(file); } } catch (FileNotFoundException e) { + Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); callback.onFileTransferAborted(); } catch (IOException e) { + Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); callback.onFileTransferAborted(); } catch (NoSuchAlgorithmException e) { + Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); callback.onFileTransferAborted(); } finally { - try { - if (fileInputStream != null) { - fileInputStream.close(); - } - } catch (IOException e) { - callback.onFileTransferAborted(); - } + FileBackend.close(fileInputStream); } } }).start(); } - public void receive(final DownloadableFile file, - final OnFileTransmissionStatusChanged callback) { + public void receive(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) { new Thread(new Runnable() { @Override public void run() { + OutputStream fileOutputStream = null; try { MessageDigest digest = MessageDigest.getInstance("SHA-1"); digest.reset(); @@ -152,9 +157,10 @@ public class JingleSocks5Transport extends JingleTransport { socket.setSoTimeout(30000); file.getParentFile().mkdirs(); file.createNewFile(); - OutputStream fileOutputStream = file.createOutputStream(); + fileOutputStream = file.createOutputStream(); if (fileOutputStream == null) { callback.onFileTransferAborted(); + Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": could not create output stream"); return; } double size = file.getExpectedSize(); @@ -165,6 +171,7 @@ public class JingleSocks5Transport extends JingleTransport { count = inputStream.read(buffer); if (count == -1) { callback.onFileTransferAborted(); + Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": file ended prematurely with "+remainingSize+" bytes remaining"); return; } else { fileOutputStream.write(buffer, 0, count); @@ -178,11 +185,16 @@ public class JingleSocks5Transport extends JingleTransport { file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest())); callback.onFileTransmitted(file); } catch (FileNotFoundException e) { + Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); callback.onFileTransferAborted(); } catch (IOException e) { + Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); callback.onFileTransferAborted(); } catch (NoSuchAlgorithmException e) { + Log.d(Config.LOGTAG, connection.getAccount().getJid().toBareJid() + ": "+e.getMessage()); callback.onFileTransferAborted(); + } finally { + FileBackend.close(fileOutputStream); } } }).start(); diff --git a/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java b/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java index 9f5ac988..74da6a9b 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java +++ b/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java @@ -6,6 +6,9 @@ import eu.siacs.conversations.xmpp.jid.Jid; import android.util.Base64; public class Avatar { + + public enum Origin { PEP, VCARD }; + public String type; public String sha1sum; public String image; @@ -13,21 +16,14 @@ public class Avatar { public int width; public long size; public Jid owner; + public Origin origin = Origin.PEP; //default to maintain compat public byte[] getImageAsBytes() { return Base64.decode(image, Base64.DEFAULT); } public String getFilename() { - if (type == null) { - return sha1sum; - } else if (type.equalsIgnoreCase("image/webp")) { - return sha1sum + ".webp"; - } else if (type.equalsIgnoreCase("image/png")) { - return sha1sum + ".png"; - } else { - return sha1sum; - } + return sha1sum; } public static Avatar parseMetadata(Element items) { @@ -64,10 +60,43 @@ public class Avatar { return null; } avatar.type = child.getAttribute("type"); - avatar.sha1sum = child.getAttribute("id"); + String hash = child.getAttribute("id"); + if (!isValidSHA1(hash)) { + return null; + } + avatar.sha1sum = hash; + avatar.origin = Origin.PEP; return avatar; } } return null; } + + @Override + public boolean equals(Object object) { + if (object != null && object instanceof Avatar) { + Avatar other = (Avatar) object; + return other.getFilename().equals(this.getFilename()); + } else { + return false; + } + } + + public static Avatar parsePresence(Element x) { + String hash = x == null ? null : x.findChildContent("photo"); + if (hash == null) { + return null; + } + if (!isValidSHA1(hash)) { + return null; + } + Avatar avatar = new Avatar(); + avatar.sha1sum = hash; + avatar.origin = Origin.VCARD; + return avatar; + } + + private static boolean isValidSHA1(String s) { + return s != null && s.matches("[a-fA-F0-9]{40}"); + } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java index 55256ece..bd706b57 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java +++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java @@ -51,4 +51,8 @@ public class AbstractStanza extends Element { || getTo().equals(account.getJid().toBareJid()) || getTo().equals(account.getJid()); } + + public boolean fromAccount(final Account account) { + return getFrom() != null && getFrom().toBareJid().equals(account.getJid().toBareJid()); + } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java index 93aaa68c..e32811af 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java +++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java @@ -1,5 +1,10 @@ package eu.siacs.conversations.xmpp.stanzas; +import android.util.Pair; + +import java.text.ParseException; + +import eu.siacs.conversations.parser.AbstractParser; import eu.siacs.conversations.xml.Element; public class MessagePacket extends AbstractStanza { @@ -14,12 +19,7 @@ public class MessagePacket extends AbstractStanza { } public String getBody() { - Element body = this.findChild("body"); - if (body != null) { - return body.getContent(); - } else { - return null; - } + return findChildContent("body"); } public void setBody(String text) { @@ -66,4 +66,31 @@ public class MessagePacket extends AbstractStanza { return TYPE_NORMAL; } } + + public Pair<MessagePacket,Long> getForwardedMessagePacket(String name, String namespace) { + Element wrapper = findChild(name, namespace); + if (wrapper == null) { + return null; + } + Element forwarded = wrapper.findChild("forwarded", "urn:xmpp:forward:0"); + if (forwarded == null) { + return null; + } + MessagePacket packet = create(forwarded.findChild("message")); + if (packet == null) { + return null; + } + Long timestamp = AbstractParser.getTimestamp(forwarded,null); + return new Pair(packet,timestamp); + } + + public static MessagePacket create(Element element) { + if (element == null) { + return null; + } + MessagePacket packet = new MessagePacket(); + packet.setAttributes(element.getAttributes()); + packet.setChildren(element.getChildren()); + return packet; + } } diff --git a/src/main/res/drawable-hdpi/ic_action_send_now_away.png b/src/main/res/drawable-hdpi/ic_action_send_now_away.png Binary files differdeleted file mode 100644 index 505cbe63..00000000 --- a/src/main/res/drawable-hdpi/ic_action_send_now_away.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_action_send_now_dnd.png b/src/main/res/drawable-hdpi/ic_action_send_now_dnd.png Binary files differdeleted file mode 100644 index a376524d..00000000 --- a/src/main/res/drawable-hdpi/ic_action_send_now_dnd.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_action_send_now_offline.png b/src/main/res/drawable-hdpi/ic_action_send_now_offline.png Binary files differdeleted file mode 100644 index d4d2d510..00000000 --- a/src/main/res/drawable-hdpi/ic_action_send_now_offline.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_action_send_now_online.png b/src/main/res/drawable-hdpi/ic_action_send_now_online.png Binary files differdeleted file mode 100644 index 48676f7b..00000000 --- a/src/main/res/drawable-hdpi/ic_action_send_now_online.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_add_white_24dp.png b/src/main/res/drawable-hdpi/ic_add_white_24dp.png Binary files differnew file mode 100644 index 00000000..481643ec --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_add_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_attach_file_white_24dp.png b/src/main/res/drawable-hdpi/ic_attach_file_white_24dp.png Binary files differnew file mode 100644 index 00000000..66299b88 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_attach_file_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_chat_white_48dp.png b/src/main/res/drawable-hdpi/ic_chat_white_48dp.png Binary files differdeleted file mode 100644 index 71cac75a..00000000 --- a/src/main/res/drawable-hdpi/ic_chat_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_content_copy_grey600_36dp.png b/src/main/res/drawable-hdpi/ic_content_copy_grey600_24dp.png Binary files differindex 5592d5c8..5592d5c8 100644 --- a/src/main/res/drawable-mdpi/ic_content_copy_grey600_36dp.png +++ b/src/main/res/drawable-hdpi/ic_content_copy_grey600_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_content_copy_grey600_36dp.png b/src/main/res/drawable-hdpi/ic_content_copy_grey600_36dp.png Binary files differdeleted file mode 100644 index bd2c60b8..00000000 --- a/src/main/res/drawable-hdpi/ic_content_copy_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_delete_grey600_24dp.png b/src/main/res/drawable-hdpi/ic_delete_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..b72a9f3c --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_delete_grey600_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_delete_white_24dp.png b/src/main/res/drawable-hdpi/ic_delete_white_24dp.png Binary files differnew file mode 100644 index 00000000..a9eac0ca --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_delete_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_edit_grey600_36dp.png b/src/main/res/drawable-hdpi/ic_edit_grey600_24dp.png Binary files differindex b5f88c80..b5f88c80 100644 --- a/src/main/res/drawable-mdpi/ic_edit_grey600_36dp.png +++ b/src/main/res/drawable-hdpi/ic_edit_grey600_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_edit_grey600_36dp.png b/src/main/res/drawable-hdpi/ic_edit_grey600_36dp.png Binary files differdeleted file mode 100644 index f8970cb2..00000000 --- a/src/main/res/drawable-hdpi/ic_edit_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_edit_white_24dp.png b/src/main/res/drawable-hdpi/ic_edit_white_24dp.png Binary files differnew file mode 100644 index 00000000..730416c9 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_edit_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_group_add_white_24dp.png b/src/main/res/drawable-hdpi/ic_group_add_white_24dp.png Binary files differnew file mode 100644 index 00000000..d96c584c --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_group_add_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_group_white_24dp.png b/src/main/res/drawable-hdpi/ic_group_white_24dp.png Binary files differnew file mode 100644 index 00000000..dada448f --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_group_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_import_export_white_24dp.png b/src/main/res/drawable-hdpi/ic_import_export_white_24dp.png Binary files differnew file mode 100644 index 00000000..705a4cc7 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_import_export_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_launcher.png b/src/main/res/drawable-hdpi/ic_launcher.png Binary files differindex bffc1c65..25fc8591 100644 --- a/src/main/res/drawable-hdpi/ic_launcher.png +++ b/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/src/main/res/drawable-hdpi/ic_lock_open_white_24dp.png b/src/main/res/drawable-hdpi/ic_lock_open_white_24dp.png Binary files differnew file mode 100644 index 00000000..1f582254 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_lock_open_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_lock_outline_white_48dp.png b/src/main/res/drawable-hdpi/ic_lock_outline_white_48dp.png Binary files differdeleted file mode 100644 index d0c25290..00000000 --- a/src/main/res/drawable-hdpi/ic_lock_outline_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_lock_white_24dp.png b/src/main/res/drawable-hdpi/ic_lock_white_24dp.png Binary files differnew file mode 100644 index 00000000..b94735ec --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_lock_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_notification.png b/src/main/res/drawable-hdpi/ic_notification.png Binary files differindex c466a7b1..31c0ee1a 100644 --- a/src/main/res/drawable-hdpi/ic_notification.png +++ b/src/main/res/drawable-hdpi/ic_notification.png diff --git a/src/main/res/drawable-hdpi/ic_person_add_white_24dp.png b/src/main/res/drawable-hdpi/ic_person_add_white_24dp.png Binary files differnew file mode 100644 index 00000000..53341908 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_person_add_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_received_indicator.png b/src/main/res/drawable-hdpi/ic_received_indicator.png Binary files differindex b1e3f274..4d2eab56 100644 --- a/src/main/res/drawable-hdpi/ic_received_indicator.png +++ b/src/main/res/drawable-hdpi/ic_received_indicator.png diff --git a/src/main/res/drawable-hdpi/ic_refresh_white_48dp.png b/src/main/res/drawable-hdpi/ic_refresh_white_48dp.png Binary files differdeleted file mode 100644 index 72128fe6..00000000 --- a/src/main/res/drawable-hdpi/ic_refresh_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_remove_grey600_36dp.png b/src/main/res/drawable-hdpi/ic_remove_grey600_36dp.png Binary files differdeleted file mode 100644 index aeb1cea9..00000000 --- a/src/main/res/drawable-hdpi/ic_remove_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_search_white_24dp.png b/src/main/res/drawable-hdpi/ic_search_white_24dp.png Binary files differnew file mode 100644 index 00000000..a2fc5b2e --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_search_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_send_cancel_away.png b/src/main/res/drawable-hdpi/ic_send_cancel_away.png Binary files differnew file mode 100644 index 00000000..9525c040 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_cancel_away.png diff --git a/src/main/res/drawable-hdpi/ic_send_cancel_dnd.png b/src/main/res/drawable-hdpi/ic_send_cancel_dnd.png Binary files differnew file mode 100644 index 00000000..83a7b94b --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_cancel_dnd.png diff --git a/src/main/res/drawable-hdpi/ic_send_cancel_offline.png b/src/main/res/drawable-hdpi/ic_send_cancel_offline.png Binary files differnew file mode 100644 index 00000000..2cb4f4f3 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_cancel_offline.png diff --git a/src/main/res/drawable-hdpi/ic_send_cancel_online.png b/src/main/res/drawable-hdpi/ic_send_cancel_online.png Binary files differnew file mode 100644 index 00000000..cb3bc3b5 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_cancel_online.png diff --git a/src/main/res/drawable-hdpi/ic_send_location_away.png b/src/main/res/drawable-hdpi/ic_send_location_away.png Binary files differnew file mode 100644 index 00000000..d139818b --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_location_away.png diff --git a/src/main/res/drawable-hdpi/ic_send_location_dnd.png b/src/main/res/drawable-hdpi/ic_send_location_dnd.png Binary files differnew file mode 100644 index 00000000..a9c51317 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_location_dnd.png diff --git a/src/main/res/drawable-hdpi/ic_send_location_offline.png b/src/main/res/drawable-hdpi/ic_send_location_offline.png Binary files differnew file mode 100644 index 00000000..4aec18af --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_location_offline.png diff --git a/src/main/res/drawable-hdpi/ic_send_location_online.png b/src/main/res/drawable-hdpi/ic_send_location_online.png Binary files differnew file mode 100644 index 00000000..19ddc51e --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_location_online.png diff --git a/src/main/res/drawable-hdpi/ic_send_photo_away.png b/src/main/res/drawable-hdpi/ic_send_photo_away.png Binary files differnew file mode 100644 index 00000000..f6beb23c --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_photo_away.png diff --git a/src/main/res/drawable-hdpi/ic_send_photo_dnd.png b/src/main/res/drawable-hdpi/ic_send_photo_dnd.png Binary files differnew file mode 100644 index 00000000..15b1eb7a --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_photo_dnd.png diff --git a/src/main/res/drawable-hdpi/ic_send_photo_offline.png b/src/main/res/drawable-hdpi/ic_send_photo_offline.png Binary files differnew file mode 100644 index 00000000..046f989b --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_photo_offline.png diff --git a/src/main/res/drawable-hdpi/ic_send_photo_online.png b/src/main/res/drawable-hdpi/ic_send_photo_online.png Binary files differnew file mode 100644 index 00000000..3fd20d10 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_photo_online.png diff --git a/src/main/res/drawable-hdpi/ic_send_picture_away.png b/src/main/res/drawable-hdpi/ic_send_picture_away.png Binary files differnew file mode 100644 index 00000000..09d1c7b7 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_picture_away.png diff --git a/src/main/res/drawable-hdpi/ic_send_picture_dnd.png b/src/main/res/drawable-hdpi/ic_send_picture_dnd.png Binary files differnew file mode 100644 index 00000000..77964b5b --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_picture_dnd.png diff --git a/src/main/res/drawable-hdpi/ic_send_picture_offline.png b/src/main/res/drawable-hdpi/ic_send_picture_offline.png Binary files differnew file mode 100644 index 00000000..28135e25 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_picture_offline.png diff --git a/src/main/res/drawable-hdpi/ic_send_picture_online.png b/src/main/res/drawable-hdpi/ic_send_picture_online.png Binary files differnew file mode 100644 index 00000000..feb926c6 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_picture_online.png diff --git a/src/main/res/drawable-hdpi/ic_send_text_away.png b/src/main/res/drawable-hdpi/ic_send_text_away.png Binary files differnew file mode 100644 index 00000000..d9ef99c5 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_text_away.png diff --git a/src/main/res/drawable-hdpi/ic_send_text_dnd.png b/src/main/res/drawable-hdpi/ic_send_text_dnd.png Binary files differnew file mode 100644 index 00000000..b43428de --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_text_dnd.png diff --git a/src/main/res/drawable-hdpi/ic_send_text_offline.png b/src/main/res/drawable-hdpi/ic_send_text_offline.png Binary files differnew file mode 100644 index 00000000..0f23fdbb --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_text_offline.png diff --git a/src/main/res/drawable-hdpi/ic_send_text_online.png b/src/main/res/drawable-hdpi/ic_send_text_online.png Binary files differnew file mode 100644 index 00000000..305f1ec2 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_text_online.png diff --git a/src/main/res/drawable-hdpi/ic_send_voice_away.png b/src/main/res/drawable-hdpi/ic_send_voice_away.png Binary files differnew file mode 100644 index 00000000..e87d9751 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_voice_away.png diff --git a/src/main/res/drawable-hdpi/ic_send_voice_dnd.png b/src/main/res/drawable-hdpi/ic_send_voice_dnd.png Binary files differnew file mode 100644 index 00000000..26a89e8e --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_voice_dnd.png diff --git a/src/main/res/drawable-hdpi/ic_send_voice_offline.png b/src/main/res/drawable-hdpi/ic_send_voice_offline.png Binary files differnew file mode 100644 index 00000000..68ce48b8 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_voice_offline.png diff --git a/src/main/res/drawable-hdpi/ic_send_voice_online.png b/src/main/res/drawable-hdpi/ic_send_voice_online.png Binary files differnew file mode 100644 index 00000000..bfd7dfa7 --- /dev/null +++ b/src/main/res/drawable-hdpi/ic_send_voice_online.png diff --git a/src/main/res/drawable-mdpi/ic_settings_grey600_36dp.png b/src/main/res/drawable-hdpi/ic_settings_grey600_24dp.png Binary files differindex 20d2b66e..20d2b66e 100644 --- a/src/main/res/drawable-mdpi/ic_settings_grey600_36dp.png +++ b/src/main/res/drawable-hdpi/ic_settings_grey600_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_settings_grey600_36dp.png b/src/main/res/drawable-hdpi/ic_settings_grey600_36dp.png Binary files differdeleted file mode 100644 index cff84744..00000000 --- a/src/main/res/drawable-hdpi/ic_settings_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_warning_white_36dp.png b/src/main/res/drawable-hdpi/ic_warning_white_24dp.png Binary files differindex 30eee6c4..30eee6c4 100644 --- a/src/main/res/drawable-mdpi/ic_warning_white_36dp.png +++ b/src/main/res/drawable-hdpi/ic_warning_white_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_warning_white_36dp.png b/src/main/res/drawable-hdpi/ic_warning_white_36dp.png Binary files differdeleted file mode 100644 index c8b7140a..00000000 --- a/src/main/res/drawable-hdpi/ic_warning_white_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_action_send_now_away.png b/src/main/res/drawable-mdpi/ic_action_send_now_away.png Binary files differdeleted file mode 100644 index 0fdca901..00000000 --- a/src/main/res/drawable-mdpi/ic_action_send_now_away.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_action_send_now_dnd.png b/src/main/res/drawable-mdpi/ic_action_send_now_dnd.png Binary files differdeleted file mode 100644 index c0aef36c..00000000 --- a/src/main/res/drawable-mdpi/ic_action_send_now_dnd.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_action_send_now_offline.png b/src/main/res/drawable-mdpi/ic_action_send_now_offline.png Binary files differdeleted file mode 100644 index 7723f4aa..00000000 --- a/src/main/res/drawable-mdpi/ic_action_send_now_offline.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_action_send_now_online.png b/src/main/res/drawable-mdpi/ic_action_send_now_online.png Binary files differdeleted file mode 100644 index 39d00ee4..00000000 --- a/src/main/res/drawable-mdpi/ic_action_send_now_online.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_add_white_24dp.png b/src/main/res/drawable-mdpi/ic_add_white_24dp.png Binary files differnew file mode 100644 index 00000000..977dd342 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_add_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_attach_file_white_24dp.png b/src/main/res/drawable-mdpi/ic_attach_file_white_24dp.png Binary files differnew file mode 100644 index 00000000..fc62a5bf --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_attach_file_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_chat_white_48dp.png b/src/main/res/drawable-mdpi/ic_chat_white_48dp.png Binary files differdeleted file mode 100644 index 526ebec6..00000000 --- a/src/main/res/drawable-mdpi/ic_chat_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_content_copy_grey600_24dp.png b/src/main/res/drawable-mdpi/ic_content_copy_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..bab9b4d6 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_content_copy_grey600_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_delete_grey600_24dp.png b/src/main/res/drawable-mdpi/ic_delete_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..e757fdb0 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_delete_grey600_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_delete_white_24dp.png b/src/main/res/drawable-mdpi/ic_delete_white_24dp.png Binary files differnew file mode 100644 index 00000000..e4ea52ef --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_delete_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_edit_grey600_24dp.png b/src/main/res/drawable-mdpi/ic_edit_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..bae3480c --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_edit_grey600_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_edit_white_24dp.png b/src/main/res/drawable-mdpi/ic_edit_white_24dp.png Binary files differnew file mode 100644 index 00000000..85cff0b9 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_edit_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_group_add_white_24dp.png b/src/main/res/drawable-mdpi/ic_group_add_white_24dp.png Binary files differnew file mode 100644 index 00000000..d0ef7ee8 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_group_add_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_group_white_24dp.png b/src/main/res/drawable-mdpi/ic_group_white_24dp.png Binary files differnew file mode 100644 index 00000000..ff2a916b --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_group_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_import_export_white_24dp.png b/src/main/res/drawable-mdpi/ic_import_export_white_24dp.png Binary files differnew file mode 100644 index 00000000..5f6e11bc --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_import_export_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_launcher.png b/src/main/res/drawable-mdpi/ic_launcher.png Binary files differindex 063ee15d..733e9615 100644 --- a/src/main/res/drawable-mdpi/ic_launcher.png +++ b/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/src/main/res/drawable-mdpi/ic_lock_open_white_24dp.png b/src/main/res/drawable-mdpi/ic_lock_open_white_24dp.png Binary files differnew file mode 100644 index 00000000..ab9efde0 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_lock_open_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_lock_outline_white_48dp.png b/src/main/res/drawable-mdpi/ic_lock_outline_white_48dp.png Binary files differdeleted file mode 100644 index 2d2c9417..00000000 --- a/src/main/res/drawable-mdpi/ic_lock_outline_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_lock_white_24dp.png b/src/main/res/drawable-mdpi/ic_lock_white_24dp.png Binary files differnew file mode 100644 index 00000000..381b6a11 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_lock_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_notification.png b/src/main/res/drawable-mdpi/ic_notification.png Binary files differindex fa35b7c1..aafc54f5 100644 --- a/src/main/res/drawable-mdpi/ic_notification.png +++ b/src/main/res/drawable-mdpi/ic_notification.png diff --git a/src/main/res/drawable-mdpi/ic_person_add_white_24dp.png b/src/main/res/drawable-mdpi/ic_person_add_white_24dp.png Binary files differnew file mode 100644 index 00000000..d0857a49 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_person_add_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_received_indicator.png b/src/main/res/drawable-mdpi/ic_received_indicator.png Binary files differindex 88ff1efb..2ba92b69 100644 --- a/src/main/res/drawable-mdpi/ic_received_indicator.png +++ b/src/main/res/drawable-mdpi/ic_received_indicator.png diff --git a/src/main/res/drawable-mdpi/ic_refresh_white_48dp.png b/src/main/res/drawable-mdpi/ic_refresh_white_48dp.png Binary files differdeleted file mode 100644 index 5f89fc25..00000000 --- a/src/main/res/drawable-mdpi/ic_refresh_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_remove_grey600_36dp.png b/src/main/res/drawable-mdpi/ic_remove_grey600_36dp.png Binary files differdeleted file mode 100644 index efc9560e..00000000 --- a/src/main/res/drawable-mdpi/ic_remove_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_search_white_24dp.png b/src/main/res/drawable-mdpi/ic_search_white_24dp.png Binary files differnew file mode 100644 index 00000000..dff1e3a8 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_search_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_send_cancel_away.png b/src/main/res/drawable-mdpi/ic_send_cancel_away.png Binary files differnew file mode 100644 index 00000000..bbc86b45 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_cancel_away.png diff --git a/src/main/res/drawable-mdpi/ic_send_cancel_dnd.png b/src/main/res/drawable-mdpi/ic_send_cancel_dnd.png Binary files differnew file mode 100644 index 00000000..75006446 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_cancel_dnd.png diff --git a/src/main/res/drawable-mdpi/ic_send_cancel_offline.png b/src/main/res/drawable-mdpi/ic_send_cancel_offline.png Binary files differnew file mode 100644 index 00000000..604dfefa --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_cancel_offline.png diff --git a/src/main/res/drawable-mdpi/ic_send_cancel_online.png b/src/main/res/drawable-mdpi/ic_send_cancel_online.png Binary files differnew file mode 100644 index 00000000..1b0d1811 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_cancel_online.png diff --git a/src/main/res/drawable-mdpi/ic_send_location_away.png b/src/main/res/drawable-mdpi/ic_send_location_away.png Binary files differnew file mode 100644 index 00000000..821e80d2 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_location_away.png diff --git a/src/main/res/drawable-mdpi/ic_send_location_dnd.png b/src/main/res/drawable-mdpi/ic_send_location_dnd.png Binary files differnew file mode 100644 index 00000000..3f4d6aa4 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_location_dnd.png diff --git a/src/main/res/drawable-mdpi/ic_send_location_offline.png b/src/main/res/drawable-mdpi/ic_send_location_offline.png Binary files differnew file mode 100644 index 00000000..ff11a080 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_location_offline.png diff --git a/src/main/res/drawable-mdpi/ic_send_location_online.png b/src/main/res/drawable-mdpi/ic_send_location_online.png Binary files differnew file mode 100644 index 00000000..a0eb4018 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_location_online.png diff --git a/src/main/res/drawable-mdpi/ic_send_photo_away.png b/src/main/res/drawable-mdpi/ic_send_photo_away.png Binary files differnew file mode 100644 index 00000000..d9c1f266 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_photo_away.png diff --git a/src/main/res/drawable-mdpi/ic_send_photo_dnd.png b/src/main/res/drawable-mdpi/ic_send_photo_dnd.png Binary files differnew file mode 100644 index 00000000..08033f63 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_photo_dnd.png diff --git a/src/main/res/drawable-mdpi/ic_send_photo_offline.png b/src/main/res/drawable-mdpi/ic_send_photo_offline.png Binary files differnew file mode 100644 index 00000000..f3e6e1fa --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_photo_offline.png diff --git a/src/main/res/drawable-mdpi/ic_send_photo_online.png b/src/main/res/drawable-mdpi/ic_send_photo_online.png Binary files differnew file mode 100644 index 00000000..0aaab38d --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_photo_online.png diff --git a/src/main/res/drawable-mdpi/ic_send_picture_away.png b/src/main/res/drawable-mdpi/ic_send_picture_away.png Binary files differnew file mode 100644 index 00000000..d3ebca53 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_picture_away.png diff --git a/src/main/res/drawable-mdpi/ic_send_picture_dnd.png b/src/main/res/drawable-mdpi/ic_send_picture_dnd.png Binary files differnew file mode 100644 index 00000000..1d293f20 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_picture_dnd.png diff --git a/src/main/res/drawable-mdpi/ic_send_picture_offline.png b/src/main/res/drawable-mdpi/ic_send_picture_offline.png Binary files differnew file mode 100644 index 00000000..95d5621e --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_picture_offline.png diff --git a/src/main/res/drawable-mdpi/ic_send_picture_online.png b/src/main/res/drawable-mdpi/ic_send_picture_online.png Binary files differnew file mode 100644 index 00000000..be4194d3 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_picture_online.png diff --git a/src/main/res/drawable-mdpi/ic_send_text_away.png b/src/main/res/drawable-mdpi/ic_send_text_away.png Binary files differnew file mode 100644 index 00000000..ddd983b5 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_text_away.png diff --git a/src/main/res/drawable-mdpi/ic_send_text_dnd.png b/src/main/res/drawable-mdpi/ic_send_text_dnd.png Binary files differnew file mode 100644 index 00000000..c3d9e79a --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_text_dnd.png diff --git a/src/main/res/drawable-mdpi/ic_send_text_offline.png b/src/main/res/drawable-mdpi/ic_send_text_offline.png Binary files differnew file mode 100644 index 00000000..72b9da27 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_text_offline.png diff --git a/src/main/res/drawable-mdpi/ic_send_text_online.png b/src/main/res/drawable-mdpi/ic_send_text_online.png Binary files differnew file mode 100644 index 00000000..86d1e330 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_text_online.png diff --git a/src/main/res/drawable-mdpi/ic_send_voice_away.png b/src/main/res/drawable-mdpi/ic_send_voice_away.png Binary files differnew file mode 100644 index 00000000..943f690f --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_voice_away.png diff --git a/src/main/res/drawable-mdpi/ic_send_voice_dnd.png b/src/main/res/drawable-mdpi/ic_send_voice_dnd.png Binary files differnew file mode 100644 index 00000000..aaf8ff32 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_voice_dnd.png diff --git a/src/main/res/drawable-mdpi/ic_send_voice_offline.png b/src/main/res/drawable-mdpi/ic_send_voice_offline.png Binary files differnew file mode 100644 index 00000000..e6b2355f --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_voice_offline.png diff --git a/src/main/res/drawable-mdpi/ic_send_voice_online.png b/src/main/res/drawable-mdpi/ic_send_voice_online.png Binary files differnew file mode 100644 index 00000000..bd0e1f87 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_send_voice_online.png diff --git a/src/main/res/drawable-mdpi/ic_settings_grey600_24dp.png b/src/main/res/drawable-mdpi/ic_settings_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..5a1b41f0 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_settings_grey600_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_warning_white_24dp.png b/src/main/res/drawable-mdpi/ic_warning_white_24dp.png Binary files differnew file mode 100644 index 00000000..385c0f68 --- /dev/null +++ b/src/main/res/drawable-mdpi/ic_warning_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_action_send_now_away.png b/src/main/res/drawable-xhdpi/ic_action_send_now_away.png Binary files differdeleted file mode 100644 index bb999d85..00000000 --- a/src/main/res/drawable-xhdpi/ic_action_send_now_away.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_action_send_now_dnd.png b/src/main/res/drawable-xhdpi/ic_action_send_now_dnd.png Binary files differdeleted file mode 100644 index a0bf5561..00000000 --- a/src/main/res/drawable-xhdpi/ic_action_send_now_dnd.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_action_send_now_offline.png b/src/main/res/drawable-xhdpi/ic_action_send_now_offline.png Binary files differdeleted file mode 100644 index 6da9ff7b..00000000 --- a/src/main/res/drawable-xhdpi/ic_action_send_now_offline.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_action_send_now_online.png b/src/main/res/drawable-xhdpi/ic_action_send_now_online.png Binary files differdeleted file mode 100644 index 348ba657..00000000 --- a/src/main/res/drawable-xhdpi/ic_action_send_now_online.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_add_white_48dp.png b/src/main/res/drawable-xhdpi/ic_add_white_24dp.png Binary files differindex 67042105..67042105 100644 --- a/src/main/res/drawable-mdpi/ic_add_white_48dp.png +++ b/src/main/res/drawable-xhdpi/ic_add_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_attach_file_white_48dp.png b/src/main/res/drawable-xhdpi/ic_attach_file_white_24dp.png Binary files differindex db23fd65..db23fd65 100644 --- a/src/main/res/drawable-mdpi/ic_attach_file_white_48dp.png +++ b/src/main/res/drawable-xhdpi/ic_attach_file_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_chat_white_48dp.png b/src/main/res/drawable-xhdpi/ic_chat_white_48dp.png Binary files differdeleted file mode 100644 index 1230ab39..00000000 --- a/src/main/res/drawable-xhdpi/ic_chat_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_content_copy_grey600_24dp.png b/src/main/res/drawable-xhdpi/ic_content_copy_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..68ccece6 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_content_copy_grey600_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_delete_grey600_24dp.png b/src/main/res/drawable-xhdpi/ic_delete_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..c6bb43e8 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_delete_grey600_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_delete_white_48dp.png b/src/main/res/drawable-xhdpi/ic_delete_white_24dp.png Binary files differindex cdb230c2..cdb230c2 100644 --- a/src/main/res/drawable-mdpi/ic_delete_white_48dp.png +++ b/src/main/res/drawable-xhdpi/ic_delete_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_edit_grey600_24dp.png b/src/main/res/drawable-xhdpi/ic_edit_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..4c95bd57 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_edit_grey600_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_edit_white_48dp.png b/src/main/res/drawable-xhdpi/ic_edit_white_24dp.png Binary files differindex 7f0ea51b..7f0ea51b 100644 --- a/src/main/res/drawable-mdpi/ic_edit_white_48dp.png +++ b/src/main/res/drawable-xhdpi/ic_edit_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_group_add_white_48dp.png b/src/main/res/drawable-xhdpi/ic_group_add_white_24dp.png Binary files differindex 6055325e..6055325e 100644 --- a/src/main/res/drawable-mdpi/ic_group_add_white_48dp.png +++ b/src/main/res/drawable-xhdpi/ic_group_add_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_group_white_48dp.png b/src/main/res/drawable-xhdpi/ic_group_white_24dp.png Binary files differindex 0c5fd333..0c5fd333 100644 --- a/src/main/res/drawable-mdpi/ic_group_white_48dp.png +++ b/src/main/res/drawable-xhdpi/ic_group_white_24dp.png diff --git a/src/main/res/drawable-mdpi/ic_import_export_white_48dp.png b/src/main/res/drawable-xhdpi/ic_import_export_white_24dp.png Binary files differindex fb82f420..fb82f420 100644 --- a/src/main/res/drawable-mdpi/ic_import_export_white_48dp.png +++ b/src/main/res/drawable-xhdpi/ic_import_export_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_launcher.png b/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differindex fd9937f1..c9e48859 100644 --- a/src/main/res/drawable-xhdpi/ic_launcher.png +++ b/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/src/main/res/drawable-mdpi/ic_lock_open_white_48dp.png b/src/main/res/drawable-xhdpi/ic_lock_open_white_24dp.png Binary files differindex 6e1321e4..6e1321e4 100644 --- a/src/main/res/drawable-mdpi/ic_lock_open_white_48dp.png +++ b/src/main/res/drawable-xhdpi/ic_lock_open_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_lock_outline_white_48dp.png b/src/main/res/drawable-xhdpi/ic_lock_outline_white_48dp.png Binary files differdeleted file mode 100644 index 3de35f9b..00000000 --- a/src/main/res/drawable-xhdpi/ic_lock_outline_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png b/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png Binary files differnew file mode 100644 index 00000000..c5e9d0b4 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_lock_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_notification.png b/src/main/res/drawable-xhdpi/ic_notification.png Binary files differindex 43daff65..042d2cda 100644 --- a/src/main/res/drawable-xhdpi/ic_notification.png +++ b/src/main/res/drawable-xhdpi/ic_notification.png diff --git a/src/main/res/drawable-mdpi/ic_person_add_white_48dp.png b/src/main/res/drawable-xhdpi/ic_person_add_white_24dp.png Binary files differindex c402e8f0..c402e8f0 100644 --- a/src/main/res/drawable-mdpi/ic_person_add_white_48dp.png +++ b/src/main/res/drawable-xhdpi/ic_person_add_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_received_indicator.png b/src/main/res/drawable-xhdpi/ic_received_indicator.png Binary files differindex 2c871933..cf7c2bb8 100644 --- a/src/main/res/drawable-xhdpi/ic_received_indicator.png +++ b/src/main/res/drawable-xhdpi/ic_received_indicator.png diff --git a/src/main/res/drawable-xhdpi/ic_refresh_white_48dp.png b/src/main/res/drawable-xhdpi/ic_refresh_white_48dp.png Binary files differdeleted file mode 100644 index d271d8e0..00000000 --- a/src/main/res/drawable-xhdpi/ic_refresh_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_remove_grey600_36dp.png b/src/main/res/drawable-xhdpi/ic_remove_grey600_36dp.png Binary files differdeleted file mode 100644 index 3d5136fe..00000000 --- a/src/main/res/drawable-xhdpi/ic_remove_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-mdpi/ic_search_white_48dp.png b/src/main/res/drawable-xhdpi/ic_search_white_24dp.png Binary files differindex 043759ac..043759ac 100644 --- a/src/main/res/drawable-mdpi/ic_search_white_48dp.png +++ b/src/main/res/drawable-xhdpi/ic_search_white_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_send_cancel_away.png b/src/main/res/drawable-xhdpi/ic_send_cancel_away.png Binary files differnew file mode 100644 index 00000000..44b5f096 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_cancel_away.png diff --git a/src/main/res/drawable-xhdpi/ic_send_cancel_dnd.png b/src/main/res/drawable-xhdpi/ic_send_cancel_dnd.png Binary files differnew file mode 100644 index 00000000..dde1b707 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_cancel_dnd.png diff --git a/src/main/res/drawable-xhdpi/ic_send_cancel_offline.png b/src/main/res/drawable-xhdpi/ic_send_cancel_offline.png Binary files differnew file mode 100644 index 00000000..53a9ac90 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_cancel_offline.png diff --git a/src/main/res/drawable-xhdpi/ic_send_cancel_online.png b/src/main/res/drawable-xhdpi/ic_send_cancel_online.png Binary files differnew file mode 100644 index 00000000..23a5f263 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_cancel_online.png diff --git a/src/main/res/drawable-xhdpi/ic_send_location_away.png b/src/main/res/drawable-xhdpi/ic_send_location_away.png Binary files differnew file mode 100644 index 00000000..0a5f3d54 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_location_away.png diff --git a/src/main/res/drawable-xhdpi/ic_send_location_dnd.png b/src/main/res/drawable-xhdpi/ic_send_location_dnd.png Binary files differnew file mode 100644 index 00000000..99c8ce36 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_location_dnd.png diff --git a/src/main/res/drawable-xhdpi/ic_send_location_offline.png b/src/main/res/drawable-xhdpi/ic_send_location_offline.png Binary files differnew file mode 100644 index 00000000..114ce01b --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_location_offline.png diff --git a/src/main/res/drawable-xhdpi/ic_send_location_online.png b/src/main/res/drawable-xhdpi/ic_send_location_online.png Binary files differnew file mode 100644 index 00000000..17204eea --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_location_online.png diff --git a/src/main/res/drawable-xhdpi/ic_send_photo_away.png b/src/main/res/drawable-xhdpi/ic_send_photo_away.png Binary files differnew file mode 100644 index 00000000..7ac674ea --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_photo_away.png diff --git a/src/main/res/drawable-xhdpi/ic_send_photo_dnd.png b/src/main/res/drawable-xhdpi/ic_send_photo_dnd.png Binary files differnew file mode 100644 index 00000000..fc69cb41 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_photo_dnd.png diff --git a/src/main/res/drawable-xhdpi/ic_send_photo_offline.png b/src/main/res/drawable-xhdpi/ic_send_photo_offline.png Binary files differnew file mode 100644 index 00000000..6ef1e16a --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_photo_offline.png diff --git a/src/main/res/drawable-xhdpi/ic_send_photo_online.png b/src/main/res/drawable-xhdpi/ic_send_photo_online.png Binary files differnew file mode 100644 index 00000000..f585ef98 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_photo_online.png diff --git a/src/main/res/drawable-xhdpi/ic_send_picture_away.png b/src/main/res/drawable-xhdpi/ic_send_picture_away.png Binary files differnew file mode 100644 index 00000000..f9aa21dc --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_picture_away.png diff --git a/src/main/res/drawable-xhdpi/ic_send_picture_dnd.png b/src/main/res/drawable-xhdpi/ic_send_picture_dnd.png Binary files differnew file mode 100644 index 00000000..95e4acce --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_picture_dnd.png diff --git a/src/main/res/drawable-xhdpi/ic_send_picture_offline.png b/src/main/res/drawable-xhdpi/ic_send_picture_offline.png Binary files differnew file mode 100644 index 00000000..75ff2fc4 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_picture_offline.png diff --git a/src/main/res/drawable-xhdpi/ic_send_picture_online.png b/src/main/res/drawable-xhdpi/ic_send_picture_online.png Binary files differnew file mode 100644 index 00000000..0f68d5f5 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_picture_online.png diff --git a/src/main/res/drawable-xhdpi/ic_send_text_away.png b/src/main/res/drawable-xhdpi/ic_send_text_away.png Binary files differnew file mode 100644 index 00000000..41f223f6 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_text_away.png diff --git a/src/main/res/drawable-xhdpi/ic_send_text_dnd.png b/src/main/res/drawable-xhdpi/ic_send_text_dnd.png Binary files differnew file mode 100644 index 00000000..8b93ec10 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_text_dnd.png diff --git a/src/main/res/drawable-xhdpi/ic_send_text_offline.png b/src/main/res/drawable-xhdpi/ic_send_text_offline.png Binary files differnew file mode 100644 index 00000000..d0a98e5d --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_text_offline.png diff --git a/src/main/res/drawable-xhdpi/ic_send_text_online.png b/src/main/res/drawable-xhdpi/ic_send_text_online.png Binary files differnew file mode 100644 index 00000000..91d240d2 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_text_online.png diff --git a/src/main/res/drawable-xhdpi/ic_send_voice_away.png b/src/main/res/drawable-xhdpi/ic_send_voice_away.png Binary files differnew file mode 100644 index 00000000..34f8ea86 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_voice_away.png diff --git a/src/main/res/drawable-xhdpi/ic_send_voice_dnd.png b/src/main/res/drawable-xhdpi/ic_send_voice_dnd.png Binary files differnew file mode 100644 index 00000000..66b0c677 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_voice_dnd.png diff --git a/src/main/res/drawable-xhdpi/ic_send_voice_offline.png b/src/main/res/drawable-xhdpi/ic_send_voice_offline.png Binary files differnew file mode 100644 index 00000000..fc4cff1f --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_voice_offline.png diff --git a/src/main/res/drawable-xhdpi/ic_send_voice_online.png b/src/main/res/drawable-xhdpi/ic_send_voice_online.png Binary files differnew file mode 100644 index 00000000..d2f03dd5 --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_send_voice_online.png diff --git a/src/main/res/drawable-xhdpi/ic_settings_grey600_24dp.png b/src/main/res/drawable-xhdpi/ic_settings_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..2251d2bb --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_settings_grey600_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_warning_white_24dp.png b/src/main/res/drawable-xhdpi/ic_warning_white_24dp.png Binary files differnew file mode 100644 index 00000000..6eb7943c --- /dev/null +++ b/src/main/res/drawable-xhdpi/ic_warning_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_action_send_now_away.png b/src/main/res/drawable-xxhdpi/ic_action_send_now_away.png Binary files differdeleted file mode 100644 index 12ec4d33..00000000 --- a/src/main/res/drawable-xxhdpi/ic_action_send_now_away.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_action_send_now_dnd.png b/src/main/res/drawable-xxhdpi/ic_action_send_now_dnd.png Binary files differdeleted file mode 100644 index 7719f81a..00000000 --- a/src/main/res/drawable-xxhdpi/ic_action_send_now_dnd.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_action_send_now_offline.png b/src/main/res/drawable-xxhdpi/ic_action_send_now_offline.png Binary files differdeleted file mode 100644 index 18895813..00000000 --- a/src/main/res/drawable-xxhdpi/ic_action_send_now_offline.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_action_send_now_online.png b/src/main/res/drawable-xxhdpi/ic_action_send_now_online.png Binary files differdeleted file mode 100644 index 29bde36e..00000000 --- a/src/main/res/drawable-xxhdpi/ic_action_send_now_online.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_add_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png Binary files differindex 72cedcad..72cedcad 100644 --- a/src/main/res/drawable-hdpi/ic_add_white_48dp.png +++ b/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_add_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_add_white_48dp.png Binary files differdeleted file mode 100644 index b12e040e..00000000 --- a/src/main/res/drawable-xxhdpi/ic_add_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_attach_file_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_attach_file_white_24dp.png Binary files differindex 7256ca3d..7256ca3d 100644 --- a/src/main/res/drawable-hdpi/ic_attach_file_white_48dp.png +++ b/src/main/res/drawable-xxhdpi/ic_attach_file_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_attach_file_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_attach_file_white_48dp.png Binary files differdeleted file mode 100644 index b50e5161..00000000 --- a/src/main/res/drawable-xxhdpi/ic_attach_file_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_chat_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_chat_white_48dp.png Binary files differdeleted file mode 100644 index bb39d157..00000000 --- a/src/main/res/drawable-xxhdpi/ic_chat_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_content_copy_grey600_36dp.png b/src/main/res/drawable-xxhdpi/ic_content_copy_grey600_24dp.png Binary files differindex 2fdbbea1..2fdbbea1 100644 --- a/src/main/res/drawable-xhdpi/ic_content_copy_grey600_36dp.png +++ b/src/main/res/drawable-xxhdpi/ic_content_copy_grey600_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_content_copy_grey600_36dp.png b/src/main/res/drawable-xxhdpi/ic_content_copy_grey600_36dp.png Binary files differdeleted file mode 100644 index 72b3474b..00000000 --- a/src/main/res/drawable-xxhdpi/ic_content_copy_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_delete_grey600_24dp.png b/src/main/res/drawable-xxhdpi/ic_delete_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..4886ab1e --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_delete_grey600_24dp.png diff --git a/src/main/res/drawable-hdpi/ic_delete_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.png Binary files differindex 0e95e9b1..0e95e9b1 100644 --- a/src/main/res/drawable-hdpi/ic_delete_white_48dp.png +++ b/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_delete_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_delete_white_48dp.png Binary files differdeleted file mode 100644 index a8d8ca84..00000000 --- a/src/main/res/drawable-xxhdpi/ic_delete_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_edit_grey600_36dp.png b/src/main/res/drawable-xxhdpi/ic_edit_grey600_24dp.png Binary files differindex 6ed4351c..6ed4351c 100644 --- a/src/main/res/drawable-xhdpi/ic_edit_grey600_36dp.png +++ b/src/main/res/drawable-xxhdpi/ic_edit_grey600_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_edit_grey600_36dp.png b/src/main/res/drawable-xxhdpi/ic_edit_grey600_36dp.png Binary files differdeleted file mode 100644 index 21b617a9..00000000 --- a/src/main/res/drawable-xxhdpi/ic_edit_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_edit_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_edit_white_24dp.png Binary files differindex 34ec7092..34ec7092 100644 --- a/src/main/res/drawable-hdpi/ic_edit_white_48dp.png +++ b/src/main/res/drawable-xxhdpi/ic_edit_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_edit_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_edit_white_48dp.png Binary files differdeleted file mode 100644 index fe5bd13f..00000000 --- a/src/main/res/drawable-xxhdpi/ic_edit_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_group_add_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_group_add_white_24dp.png Binary files differindex a5ad0a29..a5ad0a29 100644 --- a/src/main/res/drawable-hdpi/ic_group_add_white_48dp.png +++ b/src/main/res/drawable-xxhdpi/ic_group_add_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_group_add_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_group_add_white_48dp.png Binary files differdeleted file mode 100644 index 7ff60d6f..00000000 --- a/src/main/res/drawable-xxhdpi/ic_group_add_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_group_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_group_white_24dp.png Binary files differindex a6ed594f..a6ed594f 100644 --- a/src/main/res/drawable-hdpi/ic_group_white_48dp.png +++ b/src/main/res/drawable-xxhdpi/ic_group_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_group_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_group_white_48dp.png Binary files differdeleted file mode 100644 index fffa6a6a..00000000 --- a/src/main/res/drawable-xxhdpi/ic_group_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_import_export_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_import_export_white_24dp.png Binary files differindex c3497037..c3497037 100644 --- a/src/main/res/drawable-hdpi/ic_import_export_white_48dp.png +++ b/src/main/res/drawable-xxhdpi/ic_import_export_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_import_export_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_import_export_white_48dp.png Binary files differdeleted file mode 100644 index cabd50c0..00000000 --- a/src/main/res/drawable-xxhdpi/ic_import_export_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_launcher.png b/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differindex 0e06656f..e69b9c8d 100644 --- a/src/main/res/drawable-xxhdpi/ic_launcher.png +++ b/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/src/main/res/drawable-hdpi/ic_lock_open_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_lock_open_white_24dp.png Binary files differindex b90a5980..b90a5980 100644 --- a/src/main/res/drawable-hdpi/ic_lock_open_white_48dp.png +++ b/src/main/res/drawable-xxhdpi/ic_lock_open_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_lock_open_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_lock_open_white_48dp.png Binary files differdeleted file mode 100644 index 031eb06e..00000000 --- a/src/main/res/drawable-xxhdpi/ic_lock_open_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_lock_outline_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_lock_outline_white_48dp.png Binary files differdeleted file mode 100644 index f57082d8..00000000 --- a/src/main/res/drawable-xxhdpi/ic_lock_outline_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png Binary files differnew file mode 100644 index 00000000..0dcada81 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_lock_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_notification.png b/src/main/res/drawable-xxhdpi/ic_notification.png Binary files differindex c2ee5dec..42c62d32 100644 --- a/src/main/res/drawable-xxhdpi/ic_notification.png +++ b/src/main/res/drawable-xxhdpi/ic_notification.png diff --git a/src/main/res/drawable-hdpi/ic_person_add_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_person_add_white_24dp.png Binary files differindex 074db2d3..074db2d3 100644 --- a/src/main/res/drawable-hdpi/ic_person_add_white_48dp.png +++ b/src/main/res/drawable-xxhdpi/ic_person_add_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_person_add_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_person_add_white_48dp.png Binary files differdeleted file mode 100644 index ba242487..00000000 --- a/src/main/res/drawable-xxhdpi/ic_person_add_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_received_indicator.png b/src/main/res/drawable-xxhdpi/ic_received_indicator.png Binary files differindex 039a9ef9..5d1c9b87 100644 --- a/src/main/res/drawable-xxhdpi/ic_received_indicator.png +++ b/src/main/res/drawable-xxhdpi/ic_received_indicator.png diff --git a/src/main/res/drawable-xxhdpi/ic_refresh_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_refresh_white_48dp.png Binary files differdeleted file mode 100644 index 87ab2d64..00000000 --- a/src/main/res/drawable-xxhdpi/ic_refresh_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_remove_grey600_36dp.png b/src/main/res/drawable-xxhdpi/ic_remove_grey600_36dp.png Binary files differdeleted file mode 100644 index 4b1003db..00000000 --- a/src/main/res/drawable-xxhdpi/ic_remove_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-hdpi/ic_search_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png Binary files differindex 0bbeab15..0bbeab15 100644 --- a/src/main/res/drawable-hdpi/ic_search_white_48dp.png +++ b/src/main/res/drawable-xxhdpi/ic_search_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_search_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_search_white_48dp.png Binary files differdeleted file mode 100644 index 75d9aa69..00000000 --- a/src/main/res/drawable-xxhdpi/ic_search_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxhdpi/ic_send_cancel_away.png b/src/main/res/drawable-xxhdpi/ic_send_cancel_away.png Binary files differnew file mode 100644 index 00000000..07113f02 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_cancel_away.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_cancel_dnd.png b/src/main/res/drawable-xxhdpi/ic_send_cancel_dnd.png Binary files differnew file mode 100644 index 00000000..e68a912d --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_cancel_dnd.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_cancel_offline.png b/src/main/res/drawable-xxhdpi/ic_send_cancel_offline.png Binary files differnew file mode 100644 index 00000000..10412fab --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_cancel_offline.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_cancel_online.png b/src/main/res/drawable-xxhdpi/ic_send_cancel_online.png Binary files differnew file mode 100644 index 00000000..6fa18239 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_cancel_online.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_location_away.png b/src/main/res/drawable-xxhdpi/ic_send_location_away.png Binary files differnew file mode 100644 index 00000000..4fb370ff --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_location_away.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_location_dnd.png b/src/main/res/drawable-xxhdpi/ic_send_location_dnd.png Binary files differnew file mode 100644 index 00000000..1773e62d --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_location_dnd.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_location_offline.png b/src/main/res/drawable-xxhdpi/ic_send_location_offline.png Binary files differnew file mode 100644 index 00000000..b4317aae --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_location_offline.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_location_online.png b/src/main/res/drawable-xxhdpi/ic_send_location_online.png Binary files differnew file mode 100644 index 00000000..10dfed81 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_location_online.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_photo_away.png b/src/main/res/drawable-xxhdpi/ic_send_photo_away.png Binary files differnew file mode 100644 index 00000000..78eea39e --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_photo_away.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_photo_dnd.png b/src/main/res/drawable-xxhdpi/ic_send_photo_dnd.png Binary files differnew file mode 100644 index 00000000..fe33a1d0 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_photo_dnd.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_photo_offline.png b/src/main/res/drawable-xxhdpi/ic_send_photo_offline.png Binary files differnew file mode 100644 index 00000000..6b41c3bd --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_photo_offline.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_photo_online.png b/src/main/res/drawable-xxhdpi/ic_send_photo_online.png Binary files differnew file mode 100644 index 00000000..1c8992c5 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_photo_online.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_picture_away.png b/src/main/res/drawable-xxhdpi/ic_send_picture_away.png Binary files differnew file mode 100644 index 00000000..7898ed4f --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_picture_away.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_picture_dnd.png b/src/main/res/drawable-xxhdpi/ic_send_picture_dnd.png Binary files differnew file mode 100644 index 00000000..ccffabbe --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_picture_dnd.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_picture_offline.png b/src/main/res/drawable-xxhdpi/ic_send_picture_offline.png Binary files differnew file mode 100644 index 00000000..7b5687e4 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_picture_offline.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_picture_online.png b/src/main/res/drawable-xxhdpi/ic_send_picture_online.png Binary files differnew file mode 100644 index 00000000..82eab70c --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_picture_online.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_text_away.png b/src/main/res/drawable-xxhdpi/ic_send_text_away.png Binary files differnew file mode 100644 index 00000000..2b2b0793 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_text_away.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_text_dnd.png b/src/main/res/drawable-xxhdpi/ic_send_text_dnd.png Binary files differnew file mode 100644 index 00000000..32f5e29c --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_text_dnd.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_text_offline.png b/src/main/res/drawable-xxhdpi/ic_send_text_offline.png Binary files differnew file mode 100644 index 00000000..6bd9c414 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_text_offline.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_text_online.png b/src/main/res/drawable-xxhdpi/ic_send_text_online.png Binary files differnew file mode 100644 index 00000000..cb6a2dba --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_text_online.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_voice_away.png b/src/main/res/drawable-xxhdpi/ic_send_voice_away.png Binary files differnew file mode 100644 index 00000000..b8b9e807 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_voice_away.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_voice_dnd.png b/src/main/res/drawable-xxhdpi/ic_send_voice_dnd.png Binary files differnew file mode 100644 index 00000000..4a5b4104 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_voice_dnd.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_voice_offline.png b/src/main/res/drawable-xxhdpi/ic_send_voice_offline.png Binary files differnew file mode 100644 index 00000000..3d58f699 --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_voice_offline.png diff --git a/src/main/res/drawable-xxhdpi/ic_send_voice_online.png b/src/main/res/drawable-xxhdpi/ic_send_voice_online.png Binary files differnew file mode 100644 index 00000000..600371eb --- /dev/null +++ b/src/main/res/drawable-xxhdpi/ic_send_voice_online.png diff --git a/src/main/res/drawable-xhdpi/ic_settings_grey600_36dp.png b/src/main/res/drawable-xxhdpi/ic_settings_grey600_24dp.png Binary files differindex 6a70402b..6a70402b 100644 --- a/src/main/res/drawable-xhdpi/ic_settings_grey600_36dp.png +++ b/src/main/res/drawable-xxhdpi/ic_settings_grey600_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_settings_grey600_36dp.png b/src/main/res/drawable-xxhdpi/ic_settings_grey600_36dp.png Binary files differdeleted file mode 100644 index d47d042e..00000000 --- a/src/main/res/drawable-xxhdpi/ic_settings_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_warning_white_36dp.png b/src/main/res/drawable-xxhdpi/ic_warning_white_24dp.png Binary files differindex 3f401317..3f401317 100644 --- a/src/main/res/drawable-xhdpi/ic_warning_white_36dp.png +++ b/src/main/res/drawable-xxhdpi/ic_warning_white_24dp.png diff --git a/src/main/res/drawable-xxhdpi/ic_warning_white_36dp.png b/src/main/res/drawable-xxhdpi/ic_warning_white_36dp.png Binary files differdeleted file mode 100644 index a6e5788b..00000000 --- a/src/main/res/drawable-xxhdpi/ic_warning_white_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_add_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png Binary files differindex 2bef0595..2bef0595 100644 --- a/src/main/res/drawable-xhdpi/ic_add_white_48dp.png +++ b/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_add_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_add_white_48dp.png Binary files differdeleted file mode 100644 index cd32f0a8..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_add_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_attach_file_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_attach_file_white_24dp.png Binary files differindex caaee37a..caaee37a 100644 --- a/src/main/res/drawable-xhdpi/ic_attach_file_white_48dp.png +++ b/src/main/res/drawable-xxxhdpi/ic_attach_file_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_attach_file_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_attach_file_white_48dp.png Binary files differdeleted file mode 100644 index 55464b7e..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_attach_file_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_chat_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_chat_white_48dp.png Binary files differdeleted file mode 100644 index 68f14af6..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_chat_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_content_copy_grey600_24dp.png b/src/main/res/drawable-xxxhdpi/ic_content_copy_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..1eb62d42 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_content_copy_grey600_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_content_copy_grey600_36dp.png b/src/main/res/drawable-xxxhdpi/ic_content_copy_grey600_36dp.png Binary files differdeleted file mode 100644 index 435c087a..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_content_copy_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_delete_grey600_24dp.png b/src/main/res/drawable-xxxhdpi/ic_delete_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..e4e21812 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_delete_grey600_24dp.png diff --git a/src/main/res/drawable-xhdpi/ic_delete_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.png Binary files differindex ccf8c716..ccf8c716 100644 --- a/src/main/res/drawable-xhdpi/ic_delete_white_48dp.png +++ b/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_delete_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_delete_white_48dp.png Binary files differdeleted file mode 100644 index 47fef5aa..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_delete_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_edit_grey600_24dp.png b/src/main/res/drawable-xxxhdpi/ic_edit_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..0c0fd76f --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_edit_grey600_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_edit_grey600_36dp.png b/src/main/res/drawable-xxxhdpi/ic_edit_grey600_36dp.png Binary files differdeleted file mode 100644 index 1361eedc..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_edit_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_edit_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_edit_white_24dp.png Binary files differindex 9380370f..9380370f 100644 --- a/src/main/res/drawable-xhdpi/ic_edit_white_48dp.png +++ b/src/main/res/drawable-xxxhdpi/ic_edit_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_edit_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_edit_white_48dp.png Binary files differdeleted file mode 100644 index 73680649..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_edit_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_group_add_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_group_add_white_24dp.png Binary files differindex 0ac3d4a8..0ac3d4a8 100644 --- a/src/main/res/drawable-xhdpi/ic_group_add_white_48dp.png +++ b/src/main/res/drawable-xxxhdpi/ic_group_add_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_group_add_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_group_add_white_48dp.png Binary files differdeleted file mode 100644 index 7e9d67f6..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_group_add_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_group_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_group_white_24dp.png Binary files differindex a62b5677..a62b5677 100644 --- a/src/main/res/drawable-xhdpi/ic_group_white_48dp.png +++ b/src/main/res/drawable-xxxhdpi/ic_group_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_group_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_group_white_48dp.png Binary files differdeleted file mode 100644 index 2a27316b..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_group_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_import_export_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_import_export_white_24dp.png Binary files differindex 06b27ea1..06b27ea1 100644 --- a/src/main/res/drawable-xhdpi/ic_import_export_white_48dp.png +++ b/src/main/res/drawable-xxxhdpi/ic_import_export_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_import_export_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_import_export_white_48dp.png Binary files differdeleted file mode 100644 index ea80bc00..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_import_export_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_launcher.png b/src/main/res/drawable-xxxhdpi/ic_launcher.png Binary files differindex b6dcb0b9..668504df 100644 --- a/src/main/res/drawable-xxxhdpi/ic_launcher.png +++ b/src/main/res/drawable-xxxhdpi/ic_launcher.png diff --git a/src/main/res/drawable-xhdpi/ic_lock_open_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_lock_open_white_24dp.png Binary files differindex 8f35034c..8f35034c 100644 --- a/src/main/res/drawable-xhdpi/ic_lock_open_white_48dp.png +++ b/src/main/res/drawable-xxxhdpi/ic_lock_open_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_lock_open_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_lock_open_white_48dp.png Binary files differdeleted file mode 100644 index f7652e60..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_lock_open_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_lock_outline_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_lock_outline_white_48dp.png Binary files differdeleted file mode 100644 index 440070a1..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_lock_outline_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.png Binary files differnew file mode 100644 index 00000000..a70c55b7 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_lock_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_notification.png b/src/main/res/drawable-xxxhdpi/ic_notification.png Binary files differindex ee2f3a43..c3439f1a 100644 --- a/src/main/res/drawable-xxxhdpi/ic_notification.png +++ b/src/main/res/drawable-xxxhdpi/ic_notification.png diff --git a/src/main/res/drawable-xhdpi/ic_person_add_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_person_add_white_24dp.png Binary files differindex 4e2121e5..4e2121e5 100644 --- a/src/main/res/drawable-xhdpi/ic_person_add_white_48dp.png +++ b/src/main/res/drawable-xxxhdpi/ic_person_add_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_person_add_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_person_add_white_48dp.png Binary files differdeleted file mode 100644 index 89c1aef2..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_person_add_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_received_indicator.png b/src/main/res/drawable-xxxhdpi/ic_received_indicator.png Binary files differindex 86db9890..f35c8b44 100644 --- a/src/main/res/drawable-xxxhdpi/ic_received_indicator.png +++ b/src/main/res/drawable-xxxhdpi/ic_received_indicator.png diff --git a/src/main/res/drawable-xxxhdpi/ic_refresh_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_refresh_white_48dp.png Binary files differdeleted file mode 100644 index fe0ae13a..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_refresh_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_remove_grey600_36dp.png b/src/main/res/drawable-xxxhdpi/ic_remove_grey600_36dp.png Binary files differdeleted file mode 100644 index 524cc0a0..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_remove_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-xhdpi/ic_search_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png Binary files differindex 70c21baf..70c21baf 100644 --- a/src/main/res/drawable-xhdpi/ic_search_white_48dp.png +++ b/src/main/res/drawable-xxxhdpi/ic_search_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_search_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_search_white_48dp.png Binary files differdeleted file mode 100644 index 7caf6c9b..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_search_white_48dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_send_cancel_away.png b/src/main/res/drawable-xxxhdpi/ic_send_cancel_away.png Binary files differnew file mode 100644 index 00000000..a9340a88 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_cancel_away.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_cancel_dnd.png b/src/main/res/drawable-xxxhdpi/ic_send_cancel_dnd.png Binary files differnew file mode 100644 index 00000000..307ca8a0 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_cancel_dnd.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_cancel_offline.png b/src/main/res/drawable-xxxhdpi/ic_send_cancel_offline.png Binary files differnew file mode 100644 index 00000000..abd3af2c --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_cancel_offline.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_cancel_online.png b/src/main/res/drawable-xxxhdpi/ic_send_cancel_online.png Binary files differnew file mode 100644 index 00000000..80be8edf --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_cancel_online.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_location_away.png b/src/main/res/drawable-xxxhdpi/ic_send_location_away.png Binary files differnew file mode 100644 index 00000000..0fab2554 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_location_away.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_location_dnd.png b/src/main/res/drawable-xxxhdpi/ic_send_location_dnd.png Binary files differnew file mode 100644 index 00000000..e7f6fde7 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_location_dnd.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_location_offline.png b/src/main/res/drawable-xxxhdpi/ic_send_location_offline.png Binary files differnew file mode 100644 index 00000000..2af75cde --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_location_offline.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_location_online.png b/src/main/res/drawable-xxxhdpi/ic_send_location_online.png Binary files differnew file mode 100644 index 00000000..2e54ef89 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_location_online.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_photo_away.png b/src/main/res/drawable-xxxhdpi/ic_send_photo_away.png Binary files differnew file mode 100644 index 00000000..ba171ce1 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_photo_away.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_photo_dnd.png b/src/main/res/drawable-xxxhdpi/ic_send_photo_dnd.png Binary files differnew file mode 100644 index 00000000..8a9b0700 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_photo_dnd.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_photo_offline.png b/src/main/res/drawable-xxxhdpi/ic_send_photo_offline.png Binary files differnew file mode 100644 index 00000000..e94e930d --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_photo_offline.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_photo_online.png b/src/main/res/drawable-xxxhdpi/ic_send_photo_online.png Binary files differnew file mode 100644 index 00000000..1bf680eb --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_photo_online.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_picture_away.png b/src/main/res/drawable-xxxhdpi/ic_send_picture_away.png Binary files differnew file mode 100644 index 00000000..1daa8ecc --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_picture_away.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_picture_dnd.png b/src/main/res/drawable-xxxhdpi/ic_send_picture_dnd.png Binary files differnew file mode 100644 index 00000000..d8257aad --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_picture_dnd.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_picture_offline.png b/src/main/res/drawable-xxxhdpi/ic_send_picture_offline.png Binary files differnew file mode 100644 index 00000000..d487709b --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_picture_offline.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_picture_online.png b/src/main/res/drawable-xxxhdpi/ic_send_picture_online.png Binary files differnew file mode 100644 index 00000000..c095d795 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_picture_online.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_text_away.png b/src/main/res/drawable-xxxhdpi/ic_send_text_away.png Binary files differnew file mode 100644 index 00000000..afcfa89d --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_text_away.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_text_dnd.png b/src/main/res/drawable-xxxhdpi/ic_send_text_dnd.png Binary files differnew file mode 100644 index 00000000..b11cd6b6 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_text_dnd.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_text_offline.png b/src/main/res/drawable-xxxhdpi/ic_send_text_offline.png Binary files differnew file mode 100644 index 00000000..b9122e45 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_text_offline.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_text_online.png b/src/main/res/drawable-xxxhdpi/ic_send_text_online.png Binary files differnew file mode 100644 index 00000000..abec2e0b --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_text_online.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_voice_away.png b/src/main/res/drawable-xxxhdpi/ic_send_voice_away.png Binary files differnew file mode 100644 index 00000000..de1375e2 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_voice_away.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_voice_dnd.png b/src/main/res/drawable-xxxhdpi/ic_send_voice_dnd.png Binary files differnew file mode 100644 index 00000000..4ad9d389 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_voice_dnd.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_voice_offline.png b/src/main/res/drawable-xxxhdpi/ic_send_voice_offline.png Binary files differnew file mode 100644 index 00000000..eec3d8f2 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_voice_offline.png diff --git a/src/main/res/drawable-xxxhdpi/ic_send_voice_online.png b/src/main/res/drawable-xxxhdpi/ic_send_voice_online.png Binary files differnew file mode 100644 index 00000000..fcdfcb43 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_send_voice_online.png diff --git a/src/main/res/drawable-xxxhdpi/ic_settings_grey600_24dp.png b/src/main/res/drawable-xxxhdpi/ic_settings_grey600_24dp.png Binary files differnew file mode 100644 index 00000000..5eba9e8e --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_settings_grey600_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_settings_grey600_36dp.png b/src/main/res/drawable-xxxhdpi/ic_settings_grey600_36dp.png Binary files differdeleted file mode 100644 index 8fea47f6..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_settings_grey600_36dp.png +++ /dev/null diff --git a/src/main/res/drawable-xxxhdpi/ic_warning_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_warning_white_24dp.png Binary files differnew file mode 100644 index 00000000..b80446e7 --- /dev/null +++ b/src/main/res/drawable-xxxhdpi/ic_warning_white_24dp.png diff --git a/src/main/res/drawable-xxxhdpi/ic_warning_white_36dp.png b/src/main/res/drawable-xxxhdpi/ic_warning_white_36dp.png Binary files differdeleted file mode 100644 index abc5c7a5..00000000 --- a/src/main/res/drawable-xxxhdpi/ic_warning_white_36dp.png +++ /dev/null diff --git a/src/main/res/drawable/es_slidingpane_shadow.xml b/src/main/res/drawable/es_slidingpane_shadow.xml index 44ffd4ea..de96e08f 100644 --- a/src/main/res/drawable/es_slidingpane_shadow.xml +++ b/src/main/res/drawable/es_slidingpane_shadow.xml @@ -2,7 +2,7 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" > <gradient - android:endColor="@color/divider" + android:endColor="@color/black12" android:startColor="@android:color/transparent" /> <size diff --git a/src/main/res/drawable/grey.xml b/src/main/res/drawable/grey.xml index 96e03d2d..26986dbe 100644 --- a/src/main/res/drawable/grey.xml +++ b/src/main/res/drawable/grey.xml @@ -2,6 +2,6 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > - <solid android:color="@color/divider" /> + <solid android:color="@color/black12" /> </shape>
\ No newline at end of file diff --git a/src/main/res/drawable/infocard_border.xml b/src/main/res/drawable/infocard_border.xml index 7c7ded57..e01e1442 100644 --- a/src/main/res/drawable/infocard_border.xml +++ b/src/main/res/drawable/infocard_border.xml @@ -1,13 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > - <solid android:color="@color/primarybackground" /> + <solid android:color="@color/grey50" /> <corners android:radius="2dp" /> <stroke android:width="0.5dp" - android:color="@color/divider" > + android:color="@color/black12" > </stroke> <padding diff --git a/src/main/res/drawable/message_border.xml b/src/main/res/drawable/message_border.xml index b463d788..990d0288 100644 --- a/src/main/res/drawable/message_border.xml +++ b/src/main/res/drawable/message_border.xml @@ -10,6 +10,6 @@ android:right="1.5dp" android:top="1.5dp" /> - <solid android:color="@color/divider" /> + <solid android:color="@color/black12" /> </shape>
\ No newline at end of file diff --git a/src/main/res/drawable/snackbar.xml b/src/main/res/drawable/snackbar.xml index 951d7aee..2645b136 100644 --- a/src/main/res/drawable/snackbar.xml +++ b/src/main/res/drawable/snackbar.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > - <solid android:color="@color/darkbackground" /> + <solid android:color="@color/grey800" /> <corners android:radius="8dip" /> diff --git a/src/main/res/layout-w960dp/fragment_conversations_overview.xml b/src/main/res/layout-w945dp/fragment_conversations_overview.xml index 2744f38e..7ae1788d 100644 --- a/src/main/res/layout-w960dp/fragment_conversations_overview.xml +++ b/src/main/res/layout-w945dp/fragment_conversations_overview.xml @@ -9,15 +9,15 @@ android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" - android:background="@color/primarybackground" + android:background="@color/grey50" android:orientation="vertical" > - <ListView + <de.timroes.android.listview.EnhancedListView android:id="@+id/list" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:background="@color/primarybackground" - android:divider="@color/divider" + android:background="@color/grey50" + android:divider="@color/black12" android:dividerHeight="1dp" /> </LinearLayout> diff --git a/src/main/res/layout/account_row.xml b/src/main/res/layout/account_row.xml index 60b69090..06716a10 100644 --- a/src/main/res/layout/account_row.xml +++ b/src/main/res/layout/account_row.xml @@ -3,7 +3,9 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?android:attr/activatedBackgroundIndicator" - android:padding="8dp" > + android:paddingLeft="8dp" + android:paddingBottom="8dp" + android:paddingTop="8dp"> <ImageView android:id="@+id/account_image" @@ -20,7 +22,9 @@ android:layout_centerVertical="true" android:layout_toRightOf="@+id/account_image" android:orientation="vertical" - android:paddingLeft="8dp" > + android:paddingLeft="8dp" + android:layout_toLeftOf="@+id/tgl_account_status" + android:layout_toStartOf="@+id/tgl_account_status"> <TextView android:id="@+id/account_jid" @@ -28,7 +32,7 @@ android:layout_height="wrap_content" android:scrollHorizontally="false" android:singleLine="true" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeHeadline" /> <TextView @@ -36,9 +40,18 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/account_status_unknown" - android:textColor="@color/secondarytext" + android:textColor="@color/black54" android:textSize="?attr/TextSizeBody" android:textStyle="bold" /> </LinearLayout> + <Switch + android:id="@+id/tgl_account_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:padding="8dp" + android:focusable="false"/> + </RelativeLayout>
\ No newline at end of file diff --git a/src/main/res/layout/actionview_search.xml b/src/main/res/layout/actionview_search.xml index cc5fc9d7..8b657f4a 100644 --- a/src/main/res/layout/actionview_search.xml +++ b/src/main/res/layout/actionview_search.xml @@ -14,6 +14,8 @@ android:layout_height="wrap_content" android:focusable="true" android:inputType="textEmailAddress|textNoSuggestions" - android:textColor="@color/ondarktext" /> + android:textColor="@color/white" + android:textColorHint="@color/white70" + android:hint="@string/search_for_contacts_or_groups"/> </RelativeLayout>
\ No newline at end of file diff --git a/src/main/res/layout/activity_about.xml b/src/main/res/layout/activity_about.xml index ab0e34eb..d7d23f0f 100644 --- a/src/main/res/layout/activity_about.xml +++ b/src/main/res/layout/activity_about.xml @@ -1,7 +1,7 @@ <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:context="eu.siacs.conversations.ui.AboutActivity" - android:background="@color/primarybackground" + android:background="@color/grey50" android:layout_width="fill_parent" android:layout_height="fill_parent"> @@ -15,7 +15,7 @@ android:layout_marginRight="@dimen/activity_horizontal_margin" android:layout_marginTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" android:typeface="monospace"/> </ScrollView> diff --git a/src/main/res/layout/activity_change_password.xml b/src/main/res/layout/activity_change_password.xml index 28d531c2..1a4d00d8 100644 --- a/src/main/res/layout/activity_change_password.xml +++ b/src/main/res/layout/activity_change_password.xml @@ -2,7 +2,7 @@ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/primarybackground"> + android:background="@color/grey50"> <ScrollView android:layout_width="fill_parent" @@ -22,7 +22,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/current_password" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody"/> <EditText @@ -32,15 +32,15 @@ android:layout_marginBottom="8dp" android:hint="@string/password" android:inputType="textPassword" - android:textColor="@color/primarytext" - android:textColorHint="@color/secondarytext" + android:textColor="@color/black87" + android:textColorHint="@color/black54" android:textSize="?attr/TextSizeBody"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/new_password" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody"/> <EditText @@ -50,15 +50,15 @@ android:layout_marginBottom="8dp" android:hint="@string/password" android:inputType="textPassword" - android:textColor="@color/primarytext" - android:textColorHint="@color/secondarytext" + android:textColor="@color/black87" + android:textColorHint="@color/black54" android:textSize="?attr/TextSizeBody"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/account_settings_confirm_password" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody"/> <EditText @@ -67,8 +67,8 @@ android:layout_height="wrap_content" android:hint="@string/password" android:inputType="textPassword" - android:textColor="@color/primarytext" - android:textColorHint="@color/secondarytext" + android:textColor="@color/black87" + android:textColorHint="@color/black54" android:textSize="?attr/TextSizeBody"/> </LinearLayout> </ScrollView> @@ -94,7 +94,7 @@ android:layout_height="fill_parent" android:layout_marginBottom="7dp" android:layout_marginTop="7dp" - android:background="@color/divider"/> + android:background="@color/black12"/> <Button android:id="@+id/right_button" diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml index 1254a3ab..5f6a2dd0 100644 --- a/src/main/res/layout/activity_contact_details.xml +++ b/src/main/res/layout/activity_contact_details.xml @@ -2,7 +2,7 @@ <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" - android:background="@color/secondarybackground" > + android:background="@color/grey200" > <LinearLayout android:layout_width="fill_parent" @@ -39,7 +39,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/account_settings_example_jabber_id" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeHeadline" android:textStyle="bold" /> @@ -61,7 +61,7 @@ android:id="@+id/details_lastseen" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/secondarytext" + android:textColor="@color/black54" android:textSize="?attr/TextSizeBody" /> </LinearLayout> @@ -78,7 +78,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/send_presence_updates" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <CheckBox @@ -86,7 +86,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/receive_presence_updates" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> </LinearLayout> @@ -98,7 +98,7 @@ android:layout_below="@+id/details_jidbox" android:layout_marginTop="32dp" android:text="@string/using_account" - android:textColor="@color/secondarytext" + android:textColor="@color/black54" android:textSize="?attr/TextSizeInfo" /> </RelativeLayout> diff --git a/src/main/res/layout/activity_edit_account.xml b/src/main/res/layout/activity_edit_account.xml index 750c28e0..98de84f5 100644 --- a/src/main/res/layout/activity_edit_account.xml +++ b/src/main/res/layout/activity_edit_account.xml @@ -3,7 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/secondarybackground" > + android:background="@color/grey200" > <ScrollView android:layout_width="fill_parent" @@ -42,7 +42,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/account_settings_jabber_id" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <AutoCompleteTextView @@ -51,8 +51,8 @@ android:layout_height="wrap_content" android:hint="@string/account_settings_example_jabber_id" android:inputType="textEmailAddress" - android:textColor="@color/primarytext" - android:textColorHint="@color/secondarytext" + android:textColor="@color/black87" + android:textColorHint="@color/black54" android:textSize="?attr/TextSizeBody" /> <TextView @@ -60,7 +60,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/account_settings_password" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <EditText @@ -69,8 +69,8 @@ android:layout_height="wrap_content" android:hint="@string/password" android:inputType="textPassword" - android:textColor="@color/primarytext" - android:textColorHint="@color/secondarytext" + android:textColor="@color/black87" + android:textColorHint="@color/black54" android:textSize="?attr/TextSizeBody" /> <CheckBox @@ -79,7 +79,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/register_account" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <TextView @@ -87,7 +87,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/account_settings_confirm_password" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" android:visibility="gone" /> @@ -99,8 +99,8 @@ android:hint="@string/confirm_password" android:inputType="textPassword" android:visibility="gone" - android:textColor="@color/primarytext" - android:textColorHint="@color/secondarytext" + android:textColor="@color/black87" + android:textColorHint="@color/black54" android:textSize="?attr/TextSizeBody" /> </LinearLayout> </RelativeLayout> @@ -132,7 +132,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_session_established" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <TextView @@ -140,7 +140,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -161,7 +161,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_pep" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <TextView @@ -169,7 +169,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -182,7 +182,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_blocking" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <TextView @@ -190,7 +190,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -203,7 +203,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_stream_management" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <TextView @@ -211,7 +211,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -224,7 +224,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_roster_version" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <TextView @@ -232,7 +232,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -245,7 +245,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_carbon_messages" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <TextView @@ -253,7 +253,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -266,7 +266,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_mam" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <TextView @@ -274,7 +274,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -287,7 +287,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/server_info_csi" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <TextView @@ -295,7 +295,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" tools:ignore="RtlHardcoded"/> </TableRow> @@ -318,14 +318,14 @@ android:id="@+id/otr_fingerprint" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" android:typeface="monospace" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/secondarytext" + android:textColor="@color/black54" android:textSize="?attr/TextSizeInfo" android:text="@string/otr_fingerprint"/> </LinearLayout> @@ -337,7 +337,7 @@ android:layout_alignParentRight="true" android:layout_centerVertical="true" android:background="?android:selectableItemBackground" - android:padding="8dp" + android:padding="@dimen/image_button_padding" android:src="?attr/icon_copy" android:visibility="visible" android:contentDescription="@string/copy_otr_clipboard_description"/> @@ -363,14 +363,14 @@ android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/cancel" - android:textColor="@color/primarytext" /> + android:textColor="@color/black87" /> <View android:layout_width="1dp" android:layout_height="fill_parent" android:layout_marginBottom="7dp" android:layout_marginTop="7dp" - android:background="@color/divider" /> + android:background="@color/black12" /> <Button android:id="@+id/save_button" @@ -380,7 +380,7 @@ android:layout_weight="1" android:enabled="false" android:text="@string/save" - android:textColor="@color/secondarytext" /> + android:textColor="@color/black54" /> </LinearLayout> </RelativeLayout> diff --git a/src/main/res/layout/activity_muc_details.xml b/src/main/res/layout/activity_muc_details.xml index 36873654..b89c9945 100644 --- a/src/main/res/layout/activity_muc_details.xml +++ b/src/main/res/layout/activity_muc_details.xml @@ -2,7 +2,7 @@ <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" - android:background="@color/secondarybackground"> + android:background="@color/grey200"> <LinearLayout android:layout_width="fill_parent" @@ -26,7 +26,7 @@ android:layout_height="wrap_content" android:layout_marginBottom="16dp" android:text="@string/account_settings_example_jabber_id" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeHeadline" android:textStyle="bold"/> @@ -56,7 +56,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeHeadline"/> <TextView @@ -64,7 +64,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody"/> </LinearLayout> @@ -75,7 +75,7 @@ android:layout_alignParentRight="true" android:layout_centerVertical="true" android:background="?android:selectableItemBackground" - android:padding="8dp" + android:padding="@dimen/image_button_padding" android:src="?attr/icon_edit_dark"/> </RelativeLayout> @@ -88,7 +88,7 @@ android:layout_height="wrap_content" android:text="@string/private_conference" android:layout_centerVertical="true" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" android:layout_alignParentLeft="true" android:layout_toLeftOf="@+id/change_conference_button" @@ -102,7 +102,7 @@ android:layout_alignParentRight="true" android:layout_centerVertical="true" android:background="?android:selectableItemBackground" - android:padding="8dp" + android:padding="@dimen/image_button_padding" android:src="?attr/icon_settings"/> </RelativeLayout> @@ -113,7 +113,7 @@ android:layout_gravity="right" android:layout_marginTop="32dp" android:text="@string/using_account" - android:textColor="@color/secondarytext" + android:textColor="@color/black54" android:textSize="?attr/TextSizeInfo"/> </LinearLayout> diff --git a/src/main/res/layout/activity_publish_profile_picture.xml b/src/main/res/layout/activity_publish_profile_picture.xml index fac499bc..4b249745 100644 --- a/src/main/res/layout/activity_publish_profile_picture.xml +++ b/src/main/res/layout/activity_publish_profile_picture.xml @@ -2,7 +2,7 @@ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/primarybackground" > + android:background="@color/grey50" > <LinearLayout android:id="@+id/account_image_wrapper" @@ -27,7 +27,7 @@ android:layout_below="@id/account_image_wrapper" android:layout_centerHorizontal="true" android:text="@string/touch_to_choose_picture" - android:textColor="@color/secondarytext" /> + android:textColor="@color/black54" /> <TextView android:id="@+id/secondary_hint" @@ -36,7 +36,7 @@ android:layout_below="@id/hint" android:layout_centerHorizontal="true" android:text="@string/or_long_press_for_default" - android:textColor="@color/secondarytext" /> + android:textColor="@color/black54" /> <LinearLayout android:id="@+id/button_bar" @@ -53,14 +53,14 @@ android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/cancel" - android:textColor="@color/primarytext" /> + android:textColor="@color/black87" /> <View android:layout_width="1dp" android:layout_height="fill_parent" android:layout_marginBottom="7dp" android:layout_marginTop="7dp" - android:background="@color/divider" /> + android:background="@color/black12" /> <Button android:id="@+id/publish_button" @@ -70,7 +70,7 @@ android:layout_weight="1" android:enabled="false" android:text="@string/publish" - android:textColor="@color/secondarytext" /> + android:textColor="@color/black54" /> </LinearLayout> <LinearLayout @@ -89,7 +89,7 @@ android:id="@+id/account" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeHeadline" /> <TextView @@ -99,7 +99,7 @@ android:layout_marginTop="8dp" android:minLines="3" android:text="@string/publish_avatar_explanation" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> </LinearLayout> diff --git a/src/main/res/layout/activity_start_conversation.xml b/src/main/res/layout/activity_start_conversation.xml index f9c98529..d55ec196 100644 --- a/src/main/res/layout/activity_start_conversation.xml +++ b/src/main/res/layout/activity_start_conversation.xml @@ -3,6 +3,6 @@ android:id="@+id/start_conversation_view_pager" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/primarybackground" > + android:background="@color/grey50" > </android.support.v4.view.ViewPager>
\ No newline at end of file diff --git a/src/main/res/layout/activity_verify_otr.xml b/src/main/res/layout/activity_verify_otr.xml index 54f9a542..ab21c693 100644 --- a/src/main/res/layout/activity_verify_otr.xml +++ b/src/main/res/layout/activity_verify_otr.xml @@ -2,7 +2,7 @@ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/primarybackground"> + android:background="@color/grey50"> <ScrollView android:layout_width="fill_parent" @@ -34,7 +34,7 @@ android:id="@+id/your_fingerprint" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" android:typeface="monospace"/> @@ -42,7 +42,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/your_fingerprint" - android:textColor="@color/secondarytext" + android:textColor="@color/black54" android:textSize="?attr/TextSizeInfo"/> <TextView @@ -50,7 +50,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" android:typeface="monospace"/> @@ -59,7 +59,7 @@ android:layout_height="wrap_content" android:layout_marginBottom="20dp" android:text="@string/remote_fingerprint" - android:textColor="@color/secondarytext" + android:textColor="@color/black54" android:textSize="?attr/TextSizeInfo"/> </LinearLayout> @@ -77,7 +77,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="@string/verified" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeHeadline" android:textStyle="bold" android:visibility="gone"/> @@ -87,7 +87,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" android:textStyle="bold" android:visibility="gone"/> @@ -99,8 +99,8 @@ android:layout_marginBottom="8dp" android:hint="@string/shared_secret_hint" android:inputType="textAutoComplete" - android:textColor="@color/primarytext" - android:textColorHint="@color/secondarytext" + android:textColor="@color/black87" + android:textColorHint="@color/black54" android:textSize="?attr/TextSizeBody"/> <EditText @@ -110,8 +110,8 @@ android:layout_marginTop="8dp" android:hint="@string/shared_secret_secret" android:inputType="textPassword" - android:textColor="@color/primarytext" - android:textColorHint="@color/secondarytext" + android:textColor="@color/black87" + android:textColorHint="@color/black54" android:textSize="?attr/TextSizeBody"/> </LinearLayout> </LinearLayout> @@ -137,7 +137,7 @@ android:layout_height="fill_parent" android:layout_marginBottom="7dp" android:layout_marginTop="7dp" - android:background="@color/divider"/> + android:background="@color/black12"/> <Button android:id="@+id/right_button" diff --git a/src/main/res/layout/contact.xml b/src/main/res/layout/contact.xml index f0ef5d94..8a8e311b 100644 --- a/src/main/res/layout/contact.xml +++ b/src/main/res/layout/contact.xml @@ -34,7 +34,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeHeadline" /> <TextView @@ -42,7 +42,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <LinearLayout android:id="@+id/tags" @@ -55,7 +55,7 @@ android:id="@+id/key" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeHeadline" android:typeface="monospace" android:visibility="gone" /> diff --git a/src/main/res/layout/contact_key.xml b/src/main/res/layout/contact_key.xml index 75572877..933b72b4 100644 --- a/src/main/res/layout/contact_key.xml +++ b/src/main/res/layout/contact_key.xml @@ -15,7 +15,7 @@ android:id="@+id/key" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" android:typeface="monospace" /> @@ -23,7 +23,7 @@ android:id="@+id/key_type" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/secondarytext" + android:textColor="@color/black54" android:textSize="?attr/TextSizeInfo"/> </LinearLayout> @@ -34,7 +34,7 @@ android:layout_alignParentRight="true" android:layout_centerVertical="true" android:background="?android:selectableItemBackground" - android:padding="8dp" + android:padding="@dimen/image_button_padding" android:src="?attr/icon_remove" android:visibility="invisible" /> diff --git a/src/main/res/layout/conversation_list_row.xml b/src/main/res/layout/conversation_list_row.xml index e3f8f844..c79628d4 100644 --- a/src/main/res/layout/conversation_list_row.xml +++ b/src/main/res/layout/conversation_list_row.xml @@ -1,81 +1,101 @@ -<RelativeLayout +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:emojicon="http://schemas.android.com/apk/res-auto" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:padding="8dp" > + android:padding="8dp" + android:descendantFocusability="blocksDescendants"> - <ImageView - android:id="@+id/conversation_image" - android:layout_width="56dp" - android:layout_height="56dp" - android:layout_alignParentLeft="true" - android:scaleType="centerCrop" /> + <View + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@color/red500"/> - <TextView - android:layout_width="56dp" - android:layout_height="4dp" - android:textAppearance="?android:attr/textAppearanceSmall" - android:id="@+id/status" - android:layout_below="@+id/conversation_image" - android:paddingTop="8dp" - android:paddingRight="8dp"/> + <FrameLayout + android:id="@+id/swipeable_item" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@color/grey50"> - <RelativeLayout - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_toRightOf="@+id/conversation_image" - android:paddingLeft="8dp" - android:id="@+id/relativeLayout"> + <RelativeLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:background="?android:selectableItemBackground" + android:orientation="horizontal" + android:padding="8dp" > - <TextView - android:id="@+id/conversation_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignLeft="@+id/conversation_lastwrapper" - android:layout_toLeftOf="@+id/conversation_lastupdate" - android:singleLine="true" - android:textColor="@color/primarytext" - android:textSize="?attr/TextSizeHeadline" - android:typeface="sans" /> + <ImageView + android:id="@+id/conversation_image" + android:layout_width="56dp" + android:layout_height="56dp" + android:layout_alignParentLeft="true" + android:scaleType="centerCrop" /> - <LinearLayout - android:id="@+id/conversation_lastwrapper" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_below="@id/conversation_name" - android:orientation="vertical" - android:paddingTop="3dp" > + <TextView + android:layout_width="56dp" + android:layout_height="4dp" + android:textAppearance="?android:attr/textAppearanceSmall" + android:id="@+id/status" + android:layout_below="@+id/conversation_image" + android:paddingTop="8dp" + android:paddingRight="8dp"/> - <github.ankushsachdeva.emojicon.EmojiconTextView - android:id="@+id/conversation_lastmsg" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:scrollHorizontally="false" - android:singleLine="true" - android:textColor="@color/primarytext" - android:textSize="?attr/TextSizeBody" - emojicon:emojiconSize="20sp" /> + <RelativeLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_toRightOf="@+id/conversation_image" + android:paddingLeft="8dp" > - <ImageView - android:id="@+id/conversation_lastimage" - android:layout_width="fill_parent" - android:layout_height="36dp" - android:background="@color/primarytext" - android:scaleType="centerCrop" /> - </LinearLayout> - <TextView - android:id="@+id/conversation_lastupdate" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignBaseline="@+id/conversation_name" - android:layout_alignParentRight="true" - android:gravity="right" - android:textColor="@color/secondarytext" - android:textSize="?attr/TextSizeInfo" /> - </RelativeLayout> + <TextView + android:id="@+id/conversation_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignLeft="@+id/conversation_lastwrapper" + android:layout_toLeftOf="@+id/conversation_lastupdate" + android:singleLine="true" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeHeadline" + android:typeface="sans" /> -</RelativeLayout>
\ No newline at end of file + <LinearLayout + android:id="@+id/conversation_lastwrapper" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/conversation_name" + android:orientation="vertical" + android:paddingTop="3dp" > + + <github.ankushsachdeva.emojicon.EmojiconTextView + android:id="@+id/conversation_lastmsg" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:scrollHorizontally="false" + android:singleLine="true" + android:textColor="@color/black87" + android:textSize="?attr/TextSizeBody" + emojicon:emojiconSize="20sp" /> + + <ImageView + android:id="@+id/conversation_lastimage" + android:layout_width="fill_parent" + android:layout_height="36dp" + android:background="@color/black87" + android:scaleType="centerCrop" /> + </LinearLayout> + + <TextView + android:id="@+id/conversation_lastupdate" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBaseline="@+id/conversation_name" + android:layout_alignParentRight="true" + android:gravity="right" + android:textColor="@color/black54" + android:textSize="?attr/TextSizeInfo" /> + </RelativeLayout> + </RelativeLayout> + </FrameLayout> +</FrameLayout> diff --git a/src/main/res/layout/create_contact_dialog.xml b/src/main/res/layout/create_contact_dialog.xml index 1ab4b686..6333e340 100644 --- a/src/main/res/layout/create_contact_dialog.xml +++ b/src/main/res/layout/create_contact_dialog.xml @@ -10,7 +10,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/your_account" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <Spinner @@ -24,7 +24,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/account_settings_jabber_id" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <AutoCompleteTextView @@ -33,7 +33,7 @@ android:layout_height="wrap_content" android:hint="@string/account_settings_example_jabber_id" android:inputType="textEmailAddress" - android:textColor="@color/primarytext" - android:textColorHint="@color/secondarytext" /> + android:textColor="@color/black87" + android:textColorHint="@color/black54" /> </LinearLayout>
\ No newline at end of file diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml index b343781b..3fd80aa2 100644 --- a/src/main/res/layout/fragment_conversation.xml +++ b/src/main/res/layout/fragment_conversation.xml @@ -3,7 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/secondarybackground" > + android:background="@color/grey200" > <ListView android:id="@+id/messages_view" @@ -12,7 +12,7 @@ android:layout_above="@+id/snackbar" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" - android:background="@color/secondarybackground" + android:background="@color/grey200" android:divider="@null" android:dividerHeight="0dp" android:listSelector="@android:color/transparent" @@ -27,7 +27,7 @@ android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" - android:background="@color/primarybackground" > + android:background="@color/grey50" > <eu.siacs.conversations.ui.EditMessage android:id="@+id/textinput" @@ -35,7 +35,7 @@ android:layout_height="wrap_content" android:layout_toLeftOf="@+id/textSendButton" android:layout_toRightOf="@+id/emoji_btn" - android:background="@color/primarybackground" + android:background="@color/grey50" android:ems="10" android:imeOptions="flagNoExtractUi|actionSend" android:inputType="textShortMessage|textMultiLine|textCapSentences" @@ -45,7 +45,7 @@ android:paddingLeft="8dp" android:paddingRight="8dp" android:paddingTop="12dp" - android:textColor="@color/primarytext" > + android:textColor="@color/black87" > <requestFocus /> </eu.siacs.conversations.ui.EditMessage> @@ -57,7 +57,7 @@ android:layout_alignParentRight="true" android:layout_centerVertical="true" android:background="?android:selectableItemBackground" - android:src="@drawable/ic_action_send_now_offline" /> + android:src="@drawable/ic_send_text_offline" /> <ImageView android:layout_width="48dp" @@ -89,7 +89,7 @@ android:layout_centerVertical="true" android:layout_toLeftOf="@+id/snackbar_action" android:paddingLeft="24dp" - android:textColor="@color/ondarktext" + android:textColor="@color/white" android:textSize="?attr/TextSizeBody" /> <TextView @@ -103,7 +103,7 @@ android:paddingRight="24dp" android:paddingTop="16dp" android:textAllCaps="true" - android:textColor="@color/ondarktext" + android:textColor="@color/white" android:textSize="?attr/TextSizeBody" android:textStyle="bold" /> </RelativeLayout> diff --git a/src/main/res/layout/fragment_conversations_overview.xml b/src/main/res/layout/fragment_conversations_overview.xml index 69570028..2223219a 100644 --- a/src/main/res/layout/fragment_conversations_overview.xml +++ b/src/main/res/layout/fragment_conversations_overview.xml @@ -7,15 +7,15 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="@dimen/conversations_overview_width" android:layout_height="match_parent" - android:background="@color/primarybackground" + android:background="@color/grey50" android:orientation="vertical" > - <ListView + <de.timroes.android.listview.EnhancedListView android:id="@+id/list" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:background="@color/primarybackground" - android:divider="@color/divider" + android:background="@color/grey50" + android:divider="@color/black12" android:dividerHeight="1dp" /> </LinearLayout> diff --git a/src/main/res/layout/join_conference_dialog.xml b/src/main/res/layout/join_conference_dialog.xml index 95c9d24c..7609dd0d 100644 --- a/src/main/res/layout/join_conference_dialog.xml +++ b/src/main/res/layout/join_conference_dialog.xml @@ -10,7 +10,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/your_account" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <Spinner @@ -24,7 +24,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="@string/conference_address" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" /> <AutoCompleteTextView @@ -33,8 +33,8 @@ android:layout_height="wrap_content" android:hint="@string/conference_address_example" android:inputType="textEmailAddress" - android:textColor="@color/primarytext" - android:textColorHint="@color/secondarytext" /> + android:textColor="@color/black87" + android:textColorHint="@color/black54" /> <CheckBox android:id="@+id/bookmark" diff --git a/src/main/res/layout/list_item_tag.xml b/src/main/res/layout/list_item_tag.xml index 7a77f710..1a6a5576 100644 --- a/src/main/res/layout/list_item_tag.xml +++ b/src/main/res/layout/list_item_tag.xml @@ -7,7 +7,7 @@ android:paddingLeft="4dp" android:paddingRight="4dp" android:textSize="?attr/TextSizeInfo" - android:textColor="@color/ondarktext" + android:textColor="@color/white" android:textAllCaps="true" android:layout_marginRight="8dp" />
\ No newline at end of file diff --git a/src/main/res/layout/manage_accounts.xml b/src/main/res/layout/manage_accounts.xml index 11ce35b2..90ca7de0 100644 --- a/src/main/res/layout/manage_accounts.xml +++ b/src/main/res/layout/manage_accounts.xml @@ -3,13 +3,13 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" - android:background="@color/primarybackground" > + android:background="@color/grey50" > <ListView android:id="@+id/account_list" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:divider="@color/divider" + android:divider="@color/black12" android:dividerHeight="1dp" > </ListView> diff --git a/src/main/res/layout/message_null.xml b/src/main/res/layout/message_null.xml deleted file mode 100644 index 0e0f1c92..00000000 --- a/src/main/res/layout/message_null.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="0dp" - android:background="#00000000"> - -</RelativeLayout>
\ No newline at end of file diff --git a/src/main/res/layout/message_received.xml b/src/main/res/layout/message_received.xml index 23997028..68ed5711 100644 --- a/src/main/res/layout/message_received.xml +++ b/src/main/res/layout/message_received.xml @@ -23,7 +23,7 @@ <LinearLayout android:layout_width="wrap_content" android:layout_height="fill_parent" - android:background="@color/primarybackground" + android:background="@color/grey50" android:gravity="center_vertical" android:orientation="vertical" android:paddingBottom="4dp" @@ -36,7 +36,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:adjustViewBounds="true" - android:background="@color/primarytext" + android:background="@color/black87" android:paddingBottom="2dp" android:scaleType="centerCrop" /> @@ -45,7 +45,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:autoLink="web" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" emojicon:emojiconSize="28sp" /> @@ -54,7 +54,6 @@ style="?android:attr/buttonStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/download_image" android:visibility="gone" /> <LinearLayout @@ -80,7 +79,7 @@ android:layout_gravity="center_vertical" android:gravity="center_vertical" android:text="@string/sending" - android:textColor="@color/secondarytext" + android:textColor="@color/black54" android:textSize="?attr/TextSizeInfo" /> </LinearLayout> </LinearLayout> diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml index e91c9230..563e2e29 100644 --- a/src/main/res/layout/message_sent.xml +++ b/src/main/res/layout/message_sent.xml @@ -23,7 +23,7 @@ <LinearLayout android:layout_width="wrap_content" android:layout_height="fill_parent" - android:background="@color/primarybackground" + android:background="@color/grey50" android:gravity="center_vertical" android:orientation="vertical" android:paddingBottom="4dp" @@ -36,7 +36,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:adjustViewBounds="true" - android:background="@color/primarytext" + android:background="@color/black87" android:paddingBottom="2dp" android:scaleType="centerCrop" /> @@ -45,7 +45,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:autoLink="web" - android:textColor="@color/primarytext" + android:textColor="@color/black87" android:textSize="?attr/TextSizeBody" emojicon:emojiconSize="28sp" /> @@ -54,7 +54,6 @@ style="?android:attr/buttonStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/download_image" android:visibility="gone" /> <LinearLayout @@ -71,7 +70,7 @@ android:layout_gravity="center_vertical" android:gravity="center_vertical" android:text="@string/sending" - android:textColor="@color/secondarytext" + android:textColor="@color/black54" android:textSize="?attr/TextSizeInfo" /> <ImageView diff --git a/src/main/res/layout/message_status.xml b/src/main/res/layout/message_status.xml index 20544cd5..2816f475 100644 --- a/src/main/res/layout/message_status.xml +++ b/src/main/res/layout/message_status.xml @@ -28,7 +28,7 @@ android:layout_toEndOf="@+id/message_photo" android:layout_toRightOf="@+id/message_photo" android:text="@string/contact_has_read_up_to_this_point" - android:textColor="@color/secondarytext" + android:textColor="@color/black54" android:textSize="?attr/TextSizeInfo" android:textStyle="italic"/> diff --git a/src/main/res/layout/quickedit.xml b/src/main/res/layout/quickedit.xml index 20a2868a..ff6b0413 100644 --- a/src/main/res/layout/quickedit.xml +++ b/src/main/res/layout/quickedit.xml @@ -11,7 +11,7 @@ android:layout_height="wrap_content" android:ems="10" android:inputType="textPersonName" - android:textColor="@color/primarytext" > + android:textColor="@color/black87" > <requestFocus /> </EditText> diff --git a/src/main/res/menu/message_context.xml b/src/main/res/menu/message_context.xml index 21659266..4e1316d3 100644 --- a/src/main/res/menu/message_context.xml +++ b/src/main/res/menu/message_context.xml @@ -3,21 +3,27 @@ <item android:id="@+id/copy_text" - android:title="@string/copy_text"/> + android:title="@string/copy_text" + android:visible="false"/> <item android:id="@+id/share_with" - android:title="@string/share_with"/> + android:title="@string/share_with" + android:visible="false"/> <item android:id="@+id/copy_url" - android:title="@string/copy_original_url"/> + android:title="@string/copy_original_url" + android:visible="false"/> <item android:id="@+id/send_again" - android:title="@string/send_again"/> + android:title="@string/send_again" + android:visible="false"/> <item - android:id="@+id/download_image" - android:title="@string/download_image"/> + android:id="@+id/download_file" + android:title="@string/download_x_file" + android:visible="false"/> <item android:id="@+id/cancel_transmission" - android:title="@string/cancel_transmission" /> + android:title="@string/cancel_transmission" + android:visible="false"/> </menu>
\ No newline at end of file diff --git a/src/main/res/values-ar-rEG/strings.xml b/src/main/res/values-ar-rEG/strings.xml index 9db20be6..79590fd6 100644 --- a/src/main/res/values-ar-rEG/strings.xml +++ b/src/main/res/values-ar-rEG/strings.xml @@ -80,7 +80,6 @@ <string name="send_otr_message">OTRارساله رساله مشفره عبر</string> <string name="send_pgp_message">OpenPGPارساله رساله مشفره عبر</string> <string name="your_nick_has_been_changed">تم تغيير لقبك بنجاح</string> - <string name="download_image">تنزيل الصورة</string> <string name="send_unencrypted">إرسال بدون تشفير</string> <string name="decryption_failed">فشل فك التشفير. ربما لم يكن لديك المفتاح الخاص الصحيح.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -212,7 +211,6 @@ <string name="conference_members_only">الغرفة للأعضاء فقط</string> <string name="conference_kicked">تم طردك من الغرفة</string> <string name="not_connected_try_again">انقطع الإتصال .. حاول مرة أخرى</string> - <string name="check_image_filesize">فحص حجم الصورة</string> <string name="message_options">خيارات الرساله</string> <string name="copy_text">نسخ النص</string> <string name="message_text">نص الرسالة</string> @@ -260,7 +258,6 @@ <string name="could_not_change_password">لايمكن تغيير كلمة السر</string> <string name="otr_session_not_started">ارسل رساله لبدأ محادثة مشفّرة</string> <string name="ask_question">أسال سؤال</string> - <string name="smp_explain_question">إذا كنت و جهة اتصالك لديكم سر مشترك غير معروف لأحد آخر (مثل جملة مكرره بينكم او تاريخ ما يخصكم كتاريخ آخر لقاء بينكم ) استخدماه للتحقق من بصمات الأصابع \n\n عليك توفير تلميحا أو سؤال لجهة الإتصال بك للحصول على تلك الإجابه وبدأ التواصل</string> <string name="smp_explain_answer">سترغب جهة الإتصال بتأكيد بصمتك عبر السر المشترك بينكما لذلك أخبره تلميحا أو إسأله سؤالا يذكره بالسر ليكتبه برده</string> <string name="shared_secret_hint_should_not_be_empty">التلميح الذي يراه صديقك لا يمكن ان يكون فارغ</string> <string name="shared_secret_can_not_be_empty">السر المشترك بينكما لا يمكن ان يترك فارغا !!</string> diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml index 2c444785..73e64331 100644 --- a/src/main/res/values-bg/strings.xml +++ b/src/main/res/values-bg/strings.xml @@ -80,7 +80,6 @@ <string name="send_otr_message">Изпращане на съобщение, шифровано чрез OTP</string> <string name="send_pgp_message">Изпращане на съобщение, шифровано чрез OpenPGP</string> <string name="your_nick_has_been_changed">Псевдонимът Ви беше променен</string> - <string name="download_image">Изтегляне на изображението</string> <string name="send_unencrypted">Изпращане нешифровано</string> <string name="decryption_failed">Неуспешно дешифроване. Възможно е да нямате правилния частен ключ.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -289,15 +288,14 @@ <string name="conference_members_only">Тази беседа е само за членове</string> <string name="conference_kicked">Бяхте изритан от тази конференция</string> <string name="using_account">използвайки профила %s</string> - <string name="checking_image">Проверяване на изображението на HTTP сървъра</string> - <string name="image_file_deleted">Изображението е изтрито</string> + <string name="checking_x">Проверяване на %s на HTTP сървъра</string> <string name="not_connected_try_again">Не сте свързани. Опитайте отново по-късно</string> - <string name="check_image_filesize">Проверка на размера на файла с изображението</string> + <string name="check_x_filesize">Проверете размера на %s</string> <string name="message_options">Настройки за съобщенята</string> <string name="copy_text">Копиране на текста</string> <string name="copy_original_url">Копиране на оригиналния адрес</string> <string name="send_again">Повторно изпращане</string> - <string name="image_url">Адрес на изображението</string> + <string name="file_url">Адрес на файла</string> <string name="message_text">Текст на съобщението</string> <string name="url_copied_to_clipboard">Адресът е копиран</string> <string name="message_copied_to_clipboard">Съобщението е копирано</string> @@ -359,7 +357,7 @@ <string name="could_not_change_password">Неуспешна промяна на паролата</string> <string name="otr_session_not_started">Изпратете съобщение, за да започнете нешифрован разговор</string> <string name="ask_question">Задаване на въпрос</string> - <string name="smp_explain_question">Ако Вие и контакта Ви имате някаква тайна информация, която никой друг не знае (като някаква шега или пък просто какво сте обядвали, когато сте се срещнали за последно), можете да я използвате, за да проверите отпечатъците си един на друг.\n\nМожете да подсигурите подсказка или въпрос, на който контакта Ви да отговори, като има предвид, че главните и малките букви се броят за различни.</string> + <string name="smp_explain_question">Ако Вие и контактът Ви имате някаква тайна информация, която никой друг не знае (като някаква шега или пък просто какво сте обядвали, когато сте се срещнали за последно), можете да я използвате, за да проверите отпечатъците си един на друг.\n\nМожете да подсигурите подсказка или въпрос, на който контактът Ви да отговори, като има предвид, че главните и малките букви се броят за различни.</string> <string name="smp_explain_answer">Вашият контакт би искал да провери отпечатъка Ви, като Ви попита за обща тайна информация. Контактът Ви предостави следната подсказка или въпрос, който да Ви насочи към тази тайна.</string> <string name="shared_secret_hint_should_not_be_empty">Подсказката Ви не трябва да е празна</string> <string name="shared_secret_can_not_be_empty">Общата Ви тайна не може да е празна</string> @@ -427,8 +425,30 @@ <string name="no_application_found_to_display_location">Няма намерено приложение за показване на местоположението</string> <string name="location">Местоположение</string> <string name="received_location">Получено местоположение</string> + <string name="title_undo_swipe_out_conversation">Conversation се затвори</string> + <string name="title_undo_swipe_out_muc">Напуснахте беседата</string> + <string name="pref_certificate_options">Настройки на сертификата</string> + <string name="pref_dont_trust_system_cas_title">Да не се вярва на системните сертификати</string> + <string name="pref_dont_trust_system_cas_summary">Всички сертификати трябва да бъдат одобрени на ръка</string> + <string name="pref_remove_trusted_certificates_title">Премахване на сертификатите</string> + <string name="pref_remove_trusted_certificates_summary">Изтриване на сертификатите, одобрени на ръка</string> + <string name="toast_no_trusted_certs">Няма сертификати, одобрени на ръка</string> + <string name="dialog_manage_certs_title">Премахване на сертификатите</string> + <string name="dialog_manage_certs_positivebutton">Изтриване на избраните</string> + <string name="dialog_manage_certs_negativebutton">Отказ</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">%d сертификат е изтрит</item> + <item quantity="other">%d сертификата са изтрити</item> + </plurals> <plurals name="select_contact"> <item quantity="one">Изберете %d контакт</item> <item quantity="other">Изберете %d контакта</item> </plurals> + <string name="pref_quick_action_summary">Замяна на бутона за изпращане с бързо действие</string> + <string name="pref_quick_action">Бързо действие</string> + <string name="none">Нищо</string> + <string name="recently_used">Използвани наскоро</string> + <string name="choose_quick_action">Изберете бързо действие</string> + <string name="file_not_found_on_remote_host">Файлът не е открит на отдалечения сървър</string> + <string name="search_for_contacts_or_groups">Търсене на контакти или групи</string> </resources> diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index 911c42d7..6b673071 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -24,8 +24,8 @@ <string name="title_activity_choose_contact">Escollir un contacte</string> <string name="title_activity_block_list">LLista bloqueix</string> <string name="just_now">Ara</string> - <string name="minute_ago">1 min avans</string> - <string name="minutes_ago">%de minuts avans</string> + <string name="minute_ago">1 min abans</string> + <string name="minutes_ago">%de minuts abans</string> <string name="unread_conversations">Converses sense llegir o no llegides</string> <string name="sending">enviant…</string> <string name="encrypted_message">Desxifrant missatge. Espera si us plau…</string> @@ -80,7 +80,6 @@ <string name="send_otr_message">Enviar missatge xifrat amb OTR</string> <string name="send_pgp_message">Enviar missatge xifrat amb OpenPGP</string> <string name="your_nick_has_been_changed">El teu sobrenom s\'ha modificat</string> - <string name="download_image">Descarregar imatge</string> <string name="send_unencrypted">Enviar sense xifrar</string> <string name="decryption_failed">Ha fallat el desxiframent. Potser no tinguis la clau privada apropiada.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -289,15 +288,11 @@ <string name="conference_members_only">La sala es nomès per membres</string> <string name="conference_kicked">Estàs expulsat d\'aquesta sala</string> <string name="using_account">Utlitzant el compte %s</string> - <string name="checking_image">Comprovant l\'imatge en el client HTTP</string> - <string name="image_file_deleted">L\'arxiu de l\'imatge ha sigut eliminada</string> <string name="not_connected_try_again">No estàs connectat. Intenta-ho més tard</string> - <string name="check_image_filesize">Comprobant el tamany de l\'imatge</string> <string name="message_options">Opcions del missatge</string> <string name="copy_text">Copiar el text</string> <string name="copy_original_url">Copiar la URL original</string> <string name="send_again">Envia una altra vegada</string> - <string name="image_url">Imatge URL</string> <string name="message_text">Missatge de text</string> <string name="url_copied_to_clipboard">URL copiada al portapapers</string> <string name="message_copied_to_clipboard">Missatge copiat al portapapers</string> @@ -359,7 +354,7 @@ <string name="could_not_change_password">No s\'ha pogut canviar la contrasenya</string> <string name="otr_session_not_started">Començar a enviar un missatge de conversació xifrat</string> <string name="ask_question">Fer una pregunta</string> - <string name="smp_explain_question">Si vosté i el seu contacte tenen un secret en comú que ningú més sap (com una broma o simplement el que tenia per dinar l\'última vegada que es va trobar) pot utilitzar aquest secret per comprovar les empremtes dactilars de cadascú. \ n\ n Proporcionarás una pista o una pregunta per a la seu contacte que respondrà amb una resposta entre majúscules i minúscules.</string> + <string name="smp_explain_question">Si vosté i el seu contacte tenen un secret en comú que ningú més sap (com una broma o simplement el que vau dinar l\'última vegada que es van trobar) pot utilitzar aquest secret per comprovar les empremtes de cadascú.\n\nProporcionaràs una pista o una pregunta a la que el seu contacte donarà una resposta, que distingeix entre majúscules i minúscules.</string> <string name="smp_explain_answer">El seu contacte l\'hi agradaria verificar la seva empremta digital per un repte amb un secret compartit.El seu contacte proporciona el següent suggeriment o pregunta per aquest secret.</string> <string name="shared_secret_hint_should_not_be_empty">El seu suggeriment no pot estar buit</string> <string name="shared_secret_can_not_be_empty">El teu secret compartit no pot estar buit</string> @@ -417,4 +412,33 @@ <string name="sending_x_file">Enviant %s</string> <string name="offering_x_file">Oferint %s</string> <string name="hide_offline">Amaga el fora de línia</string> + <string name="disable_account">Deshabilita el compte</string> + <string name="contact_is_typing">%s està escrivint...</string> + <string name="contact_has_stopped_typing">%s ha deixat d\'escriure</string> + <string name="pref_chat_states">Notificacions d\'escriptura</string> + <string name="pref_chat_states_summary">Permet el teu contacte saber quan estàs escrivint un missatge nou</string> + <string name="send_location">Enviar localització</string> + <string name="show_location">Mostrar localització</string> + <string name="no_application_found_to_display_location">No s\'ha trobat cap aplicació per mostrar la localització</string> + <string name="location">Localització</string> + <string name="received_location">Localització rebuda</string> + <string name="title_undo_swipe_out_conversation">Conversa tancada</string> + <string name="title_undo_swipe_out_muc">S\'ha sortit de la conferència</string> + <string name="pref_certificate_options">Opcions de certificats</string> + <string name="pref_dont_trust_system_cas_title">No confiar en les CAs del sistema</string> + <string name="pref_dont_trust_system_cas_summary">Tots els certificats han de ser aprovats manualment</string> + <string name="pref_remove_trusted_certificates_title">Eliminar certificats</string> + <string name="pref_remove_trusted_certificates_summary">Esborrar certificats aprovats manualment</string> + <string name="toast_no_trusted_certs">No hi ha certificats aprovats manualment</string> + <string name="dialog_manage_certs_title">Esborrar certificats</string> + <string name="dialog_manage_certs_positivebutton">Esborrar selecció</string> + <string name="dialog_manage_certs_negativebutton">Cancel·lar</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">%d certificat esborrat</item> + <item quantity="other">%d certificats esborrats</item> + </plurals> + <plurals name="select_contact"> + <item quantity="one">Seleccionar %d contacte</item> + <item quantity="other">Seleccionar %d contactes</item> + </plurals> </resources> diff --git a/src/main/res/values-ca@valencia/strings.xml b/src/main/res/values-ca@valencia/strings.xml deleted file mode 100644 index c757504a..00000000 --- a/src/main/res/values-ca@valencia/strings.xml +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<resources/> diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index 03b18a71..816abba8 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -80,7 +80,6 @@ <string name="send_otr_message">Poslat OTR šifrovanou zprávu</string> <string name="send_pgp_message">Poslat OpenPGP šifrovanou zprávu</string> <string name="your_nick_has_been_changed">Přezdívka byla změněna</string> - <string name="download_image">Stáhnout obrázek</string> <string name="send_unencrypted">Poslat nešifrované</string> <string name="decryption_failed">Zašifrování se nezdařilo. Možná nemáte správný privátní klíč.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -289,15 +288,14 @@ <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="using_account">za použití účtu %s</string> - <string name="checking_image">Ověřuji obrázek na HTTP hostiteli</string> - <string name="image_file_deleted">Obrázek byl smazán</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> - <string name="check_image_filesize">Ověřit velikost obrázku</string> + <string name="check_x_filesize">Ověřit %s velikost</string> <string name="message_options">Možnosti zpráv</string> <string name="copy_text">Zkopírovat text</string> <string name="copy_original_url">Kopírovat originální URL</string> <string name="send_again">Poslat znovu</string> - <string name="image_url">URL obrázku</string> + <string name="file_url">URL souboru</string> <string name="message_text">Text zprávy</string> <string name="url_copied_to_clipboard">URL zkopírováno do schránky</string> <string name="message_copied_to_clipboard">Zpráva zkopírována do schránky</string> @@ -427,9 +425,32 @@ <string name="no_application_found_to_display_location">Nebyla nalezena aplikace pro zobrazení pozice</string> <string name="location">Pozice</string> <string name="received_location">Přijmout pozici</string> + <string name="title_undo_swipe_out_conversation">Conversation zavřena</string> + <string name="title_undo_swipe_out_muc">Opustil(a) konferenci</string> + <string name="pref_certificate_options">Nastavení certifikátu</string> + <string name="pref_dont_trust_system_cas_title">Nedůvěřovat systémovým CA</string> + <string name="pref_dont_trust_system_cas_summary">Všechny certifikáty musí být schváleny ručně</string> + <string name="pref_remove_trusted_certificates_title">Odstranit certifikáty</string> + <string name="pref_remove_trusted_certificates_summary">Smazat ručně povolené certifikáty</string> + <string name="toast_no_trusted_certs">Žádné ručně povolené certifikáty</string> + <string name="dialog_manage_certs_title">Odstranit certifikáty</string> + <string name="dialog_manage_certs_positivebutton">Smazat výběr</string> + <string name="dialog_manage_certs_negativebutton">Zrušit</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">%d certifikát smazán</item> + <item quantity="few">%d certifikáty smazány</item> + <item quantity="other">%d certifikátů smazáno</item> + </plurals> <plurals name="select_contact"> <item quantity="one">Vybrat %d kontakt</item> <item quantity="few">Vybrat %d kontakty</item> <item quantity="other">Vybrat %d kontaktů</item> </plurals> + <string name="pref_quick_action_summary">Nahradit tlačítko odeslání rychlou akcí</string> + <string name="pref_quick_action">Rychlá akce</string> + <string name="none">Žádná</string> + <string name="recently_used">Naposledy použitá</string> + <string name="choose_quick_action">Vybrat rychlou akci</string> + <string name="file_not_found_on_remote_host">Soubor nenalezen na vzdáleném serveru</string> + <string name="search_for_contacts_or_groups">Hledat kontakty či skupiny</string> </resources> diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index ccb3c990..2d5337be 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -257,38 +257,38 @@ <string name="account_already_exists">Das Konto existiert bereits</string> <string name="next">Weiter</string> <string name="server_info_session_established">Sitzung wiederhergestellt</string> - <string name="additional_information">Zusätzliche Informationen</string> - <string name="skip">Überspringen</string> - <string name="disable_notifications">Benachrichtigungen deaktivieren</string> - <string name="disable_notifications_for_this_conversation">Benachrichtigungen für diese Unterhaltung deaktivieren</string> - <string name="notifications_disabled">Benachrichtigungen sind deaktiviert</string> - <string name="enable">Aktivieren</string> - <string name="conference_requires_password">Konferenz ist passwortgeschützt</string> - <string name="enter_password">Passwort eingeben</string> - <string name="missing_presence_updates">Fehlender Online-Status vom Kontakt</string> - <string name="request_presence_updates">Bitte erst Anwesenheitsbenachrichtigungen vom Kontakt anfordern.\n\n</string> - <string name="request_now">Jetzt anfordern</string> - <string name="delete_fingerprint">Fingerabdruck löschen</string> - <string name="sure_delete_fingerprint">Soll dieser Fingerabdruck gelöscht werden?</string> - <string name="ignore">Ignorieren</string> - <string name="without_mutual_presence_updates"><b>Achtung:</b> Es kann zu unerwarteten Problemen führen, dies ohne gegenseitige Anwesenheitsbenachrichtigungen abzusenden.\n\n<small>Bitte die Online-Status-Abonnements in den Kontaktdetails prüfen.</small></string> - <string name="pref_encryption_settings">Verschlüsselungs-Einstellungen</string> - <string name="pref_force_encryption">Ende-zu-Ende-Verschlüsselung erzwingen</string> - <string name="pref_force_encryption_summary">Nachrichten immer verschlüsseln (außer für Konferenzen)</string> - <string name="pref_dont_save_encrypted">Verschlüsselte Nachrichten nicht speichern</string> - <string name="pref_dont_save_encrypted_summary">Achtung: kann zu Nachrichtenverlust führen</string> - <string name="pref_expert_options">Einstellungen für Experten</string> - <string name="pref_expert_options_summary">Hier bitte vorsichtig sein</string> - <string name="title_activity_about">Über Conversations</string> - <string name="pref_about_conversations_summary">Versions- und Lizenzinformationen</string> - <string name="title_pref_quiet_hours">Ruhige Stunden</string> - <string name="title_pref_quiet_hours_start_time">Beginn</string> - <string name="title_pref_quiet_hours_end_time">Ende</string> - <string name="title_pref_enable_quiet_hours">Aktiviere ruhige Stunden</string> - <string name="pref_quiet_hours_summary">Benachrichtigungen sind während der ruhigen Stunden stumm.</string> - <string name="pref_use_larger_font">Schrift vergrößern</string> - <string name="pref_use_larger_font_summary">Größere Schrift verwenden</string> - <string name="pref_use_send_button_to_indicate_status">Absende-Knopf zeigt Online-Status an</string> + <string name="additional_information">Zusätzliche Informationen</string> + <string name="skip">Überspringen</string> + <string name="disable_notifications">Benachrichtigungen deaktivieren</string> + <string name="disable_notifications_for_this_conversation">Benachrichtigungen für diese Unterhaltung deaktivieren</string> + <string name="notifications_disabled">Benachrichtigungen sind deaktiviert</string> + <string name="enable">Aktivieren</string> + <string name="conference_requires_password">Konferenz ist passwortgeschützt</string> + <string name="enter_password">Passwort eingeben</string> + <string name="missing_presence_updates">Fehlender Online-Status vom Kontakt</string> + <string name="request_presence_updates">Bitte erst Online-Status vom Kontakt anfragen.\n\n<small>Dies wird verwendet, um festzustellen, welche Client(s) der Kontakt benutzt.</small></string> + <string name="request_now">Jetzt anfordern</string> + <string name="delete_fingerprint">Fingerabdruck löschen</string> + <string name="sure_delete_fingerprint">Soll dieser Fingerabdruck gelöscht werden?</string> + <string name="ignore">Ignorieren</string> + <string name="without_mutual_presence_updates"><b>Achtung:</b> Ohne gegenseitig den Online-Status zu kennen, kann es zu unerwarteten Problemen kommen.\n\n<small>Bitte die Einstellungen in den Kontakt-Details prüfen.</small></string> + <string name="pref_encryption_settings">Verschlüsselungs-Einstellungen</string> + <string name="pref_force_encryption">Ende-zu-Ende-Verschlüsselung erzwingen</string> + <string name="pref_force_encryption_summary">Nachrichten immer verschlüsseln (außer für Konferenzen)</string> + <string name="pref_dont_save_encrypted">Verschlüsselte Nachrichten nicht speichern</string> + <string name="pref_dont_save_encrypted_summary">Achtung: kann zu Nachrichtenverlust führen</string> + <string name="pref_expert_options">Einstellungen für Experten</string> + <string name="pref_expert_options_summary">Hier bitte vorsichtig sein</string> + <string name="title_activity_about">Über Conversations</string> + <string name="pref_about_conversations_summary">Versions- und Lizenzinformationen</string> + <string name="title_pref_quiet_hours">Ruhige Stunden</string> + <string name="title_pref_quiet_hours_start_time">Beginn</string> + <string name="title_pref_quiet_hours_end_time">Ende</string> + <string name="title_pref_enable_quiet_hours">Aktiviere ruhige Stunden</string> + <string name="pref_quiet_hours_summary">Benachrichtigungen sind während der ruhigen Stunden stumm.</string> + <string name="pref_use_larger_font">Schrift vergrößern</string> + <string name="pref_use_larger_font_summary">Größere Schrift verwenden</string> + <string name="pref_use_send_button_to_indicate_status">Absende-Knopf zeigt Online-Status an</string> <string name="pref_use_indicate_received">Anfrage für Nachrichtenempfang</string> <string name="pref_use_indicate_received_summary">Empfangene Nachrichten werden mit einem grünen Häkchen markiert. Bitte beachte, dass dies nicht in allen Fällen funktioniert.</string> <string name="pref_use_send_button_to_indicate_status_summary">Absende-Knopf einfärben, um den Online-Status des Kontakts zu signalisieren</string> @@ -299,23 +299,22 @@ <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="using_account">Verwende Konto %s</string> - <string name="checking_image">Prüfe Bild auf HTTP-Host</string> - <string name="image_file_deleted">Bild wurde gelöscht</string> - <string name="not_connected_try_again">Nicht verbunden, bitte später versuchen</string> - <string name="check_image_filesize">Bildgröße 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> - <string name="send_again">Erneut senden</string> - <string name="image_url">Bild-URL</string> - <string name="message_text">Nachrichtentext</string> - <string name="url_copied_to_clipboard">URL in Zwischenablage kopiert</string> - <string name="message_copied_to_clipboard">Nachricht in Zwischenablage kopiert</string> - <string name="image_transmission_failed">Bild-Übertragung fehlgeschlagen</string> - <string name="scan_qr_code">Scanne QR-Code</string> - <string name="show_qr_code">Zeige QR-Code</string> - <string name="show_block_list">Zeige Sperrliste</string> + <string name="using_account">Verwende Konto %s</string> + <string name="checking_x">%s wird auf HTTP-Host geprü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="message_options">Nachrichtenoptionen</string> + <string name="copy_text">Text kopieren</string> + <string name="copy_original_url">Original-URL kopieren</string> + <string name="send_again">Erneut senden</string> + <string name="file_url">Datei-URL</string> + <string name="message_text">Nachrichtentext</string> + <string name="url_copied_to_clipboard">URL in Zwischenablage kopiert</string> + <string name="message_copied_to_clipboard">Nachricht in Zwischenablage kopiert</string> + <string name="image_transmission_failed">Bild-Übertragung fehlgeschlagen</string> + <string name="scan_qr_code">Scanne QR-Code</string> + <string name="show_qr_code">Zeige QR-Code</string> + <string name="show_block_list">Zeige Sperrliste</string> <string name="account_details">Konto-Details</string> <string name="verify_otr">Prüfe OTR</string> <string name="remote_fingerprint">Fingerabdruck der Gegenseite</string> @@ -439,10 +438,32 @@ <string name="no_application_found_to_display_location">Keine App für die Standort-Anzeige gefunden</string> <string name="location">Standort</string> <string name="received_location">Standort empfangen</string> + <string name="title_undo_swipe_out_conversation">Unterhaltung beendet</string> + <string name="title_undo_swipe_out_muc">Konferenz verlassen</string> + <string name="pref_certificate_options">Zertifikats-Optionen</string> + <string name="pref_dont_trust_system_cas_title">Misstraue Zertifizierungsstellen</string> + <string name="pref_dont_trust_system_cas_summary">Alle Zertifikate müssen manuell bestätigt werden</string> + <string name="pref_remove_trusted_certificates_title">Zertifikate löschen</string> + <string name="pref_remove_trusted_certificates_summary">Als vertrauenswürdig bestätigte Zertifikate löschen</string> + <string name="toast_no_trusted_certs">Keine manuell bestätigten Zertifikate</string> + <string name="dialog_manage_certs_title">Zertifikate löschen</string> + <string name="dialog_manage_certs_positivebutton">Auswahl löschen</string> + <string name="dialog_manage_certs_negativebutton">Abbrechen</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">%d Zertifikat gelöscht</item> + <item quantity="other">%d Zertifikate gelöscht</item> + </plurals> <plurals name="select_contact"> <item quantity="one">%d Kontakt ausgewählt</item> <item quantity="other">%d Kontakte ausgewählt</item> </plurals> + <string name="pref_quick_action_summary">Ersetze Absende-Knopf durch Schnell-Tasten</string> + <string name="pref_quick_action">Schnell-Tasten</string> + <string name="none">keine</string> + <string name="recently_used">zuletzt verwendet</string> + <string name="choose_quick_action">wähle Schnell-Taste</string> + <string name="file_not_found_on_remote_host">Datei auf Server nicht gefunden</string> + <string name="search_for_contacts_or_groups">Nach Kontakten oder Konferenzen suchen</string> <string name="pref_led_notification_color">LED-Benachrichtigung Farbe</string> <string name="pref_led_notification_color_summary">Setze die Farbe der LED-Benachrichtigung</string> </resources> diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index eadf1afc..0e2dc010 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -80,7 +80,6 @@ <string name="send_otr_message">Αποστολή κρυπτογραφημένου μηνύματος OTR</string> <string name="send_pgp_message">Αποστολή κρυπτογραφημένου μηνύματος OpenPGP</string> <string name="your_nick_has_been_changed">Το ψευδώνυμό σας έχει αλλάξει</string> - <string name="download_image">Μεταφόρτωση εικόνας</string> <string name="send_unencrypted">Αποστολή χωρίς κρυπτογράφηση</string> <string name="decryption_failed">Η αποκρυπτογράφηση απέτυχε. Ίσως δεν κατέχετε το σωστό ιδιωτικό κλειδί.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -289,15 +288,11 @@ <string name="conference_members_only">Αυτή η συνδιάσκεψη είναι μόνο για μέλη</string> <string name="conference_kicked">Έχετε διωχθει από αυτή την συνδιάσκεψη</string> <string name="using_account">χρήση λογαριασμού %s</string> - <string name="checking_image">Έλεγχος εικόνας στον διακομιστή HTTP</string> - <string name="image_file_deleted">Το αρχείο εικόνας έχει διαγραφεί</string> <string name="not_connected_try_again">Δεν είστε συνδεμένοι. Δοκιμάστε ξανά αργότερα</string> - <string name="check_image_filesize">Ελέγξτε το μέγεθος του αρχείου εικόνας</string> <string name="message_options">Επιλογές μηνυμάτων</string> <string name="copy_text">Αντιγραφή κειμένου</string> <string name="copy_original_url">Αντιγραφή αρχικής διεύθυνσης URL</string> <string name="send_again">Αποστολή ξανά</string> - <string name="image_url">Διεύθυνση URL εικόνας</string> <string name="message_text">Κείμενο μηνύματος</string> <string name="url_copied_to_clipboard">Η διεύθυνση URL αντιγράφηκε στο πρόχειρο</string> <string name="message_copied_to_clipboard">Το μήνυμα αντιγράφηκε στο πρόχειρο</string> @@ -359,7 +354,7 @@ <string name="could_not_change_password">Δεν ήταν δυνατή η αλλαγή του συνθηματικού</string> <string name="otr_session_not_started">Αποστολή μηνύματος για την έναρξη κρυπτογραφημένης συνομιλίας</string> <string name="ask_question">Ερώτηση</string> - <string name="smp_explain_question">Αν εσείς και η επαφή σας έχετε ένα κοινό μυστικό που κανείς άλλος δεν γνωρίζει (κάτι σαν δικό σας αστείο ή απλώς τι φάγατε την τελευταία φορά που συναντηθήκατε) μπορείτε να χρησιμοποιήσετε αυτό το μυστικό για νε επαληθεύσετε τα αποτυπώματά σας.\n\nΠροσφέρετε έναν υπαινιγμό ή μια ερώτηση για την επαφή σας, που θα απαντήσει με μια φράση στην οποία έχουν διαφοροποίηση τα πεζά από τα κεφαλαία.</string> + <string name="smp_explain_question">Αν εσείς και η επαφή σας έχετε ένα κοινό μυστικό που κανείς άλλος δεν γνωρίζει (κάτι σαν δικό σας αστείο ή απλώς τι φάγατε την τελευταία φορά που συναντηθήκατε) μπορείτε να χρησιμοποιήσετε αυτό το μυστικό για να επαληθεύσετε τα αποτυπώματά σας.\n\nΠροσφέρετε έναν υπαινιγμό ή μια ερώτηση για την επαφή σας, που θα απαντήσει με μια φράση στην οποία έχουν διαφοροποίηση τα πεζά από τα κεφαλαία.</string> <string name="smp_explain_answer">Η επαφή σας θα ήθελε να επαληθεύσει το αποτύπωμά σας χρησιμοποιώντας ένα κοινό μυστικό. Η επαφή σας προμήθευση τον παρακάτω υπαινιγμό ή ερώτηση για το μυστικό αυτό.</string> <string name="shared_secret_hint_should_not_be_empty">Ο υπαινιγμός σας δεν μπορεί να είναι κενός</string> <string name="shared_secret_can_not_be_empty">Το κοινό μυστικό σας δεν μπορεί να είναι κενό</string> @@ -427,4 +422,23 @@ <string name="no_application_found_to_display_location">Δεν βρέθηκε εφαρμογή για την απεικόνιση τοποθεσίας</string> <string name="location">Τοποθεσία</string> <string name="received_location">Ελήφθη τοποθεσία</string> + <string name="title_undo_swipe_out_conversation">Η συζήτηση έκλεισε</string> + <string name="title_undo_swipe_out_muc">Έφυγε από την συνδιάσκεψη</string> + <string name="pref_certificate_options">Επιλογές πιστοποιητικών</string> + <string name="pref_dont_trust_system_cas_title">Μη έμπιστες αρχές πιστοποίησης συστήματος</string> + <string name="pref_dont_trust_system_cas_summary">Όλα τα πιστοποιητικά πρέπει να εγκριθούν χειροκίνητα</string> + <string name="pref_remove_trusted_certificates_title">Αφαίρεση πιστοποιητικών</string> + <string name="pref_remove_trusted_certificates_summary">Διαγραφή με μη αυτόματο τρόπο των αναγνωρισμένων πιστοποιητικών</string> + <string name="toast_no_trusted_certs">Δεν υπάρχουν με μη αυτόματο τρόπο αναγνωρισμένα πιστοποιητικα</string> + <string name="dialog_manage_certs_title">Αφαίρεση πιστοποιητικών</string> + <string name="dialog_manage_certs_positivebutton">Διαγραφή επιλογής</string> + <string name="dialog_manage_certs_negativebutton">Ακύρωση</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">%d πιστοποιητικο διαγραφθηκε</item> + <item quantity="other">%d πιστοποιητικά διαγραφθηκαν</item> + </plurals> + <plurals name="select_contact"> + <item quantity="one">Επιλογή %d επαφής</item> + <item quantity="other">Επιλογή %d επαφών</item> + </plurals> </resources> diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 91da6b80..4d8bef38 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -80,7 +80,6 @@ <string name="send_otr_message">Enviar mensaje cifrado con OTR</string> <string name="send_pgp_message">Enviar mensaje cifrado con OpenPGP</string> <string name="your_nick_has_been_changed">Tu apodo se ha modificado</string> - <string name="download_image">Descargar imagen</string> <string name="send_unencrypted">Enviar sin cifrar</string> <string name="decryption_failed">Falló el descifrado. Tal vez no tengas la clave privada apropiada.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -289,15 +288,14 @@ <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="using_account">Usando cuenta %s</string> - <string name="checking_image">Comprobando imagen en servidor HTTP</string> - <string name="image_file_deleted">El archivo de imagen ha sido eliminado</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_image_filesize">Comprobar el tamaño del archivo de imagen</string> + <string name="check_x_filesize">Comprobar tamaño de %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> <string name="send_again">Volver a enviar</string> - <string name="image_url">URL Imagen</string> + <string name="file_url">URL de archivo</string> <string name="message_text">Mensaje de texto</string> <string name="url_copied_to_clipboard">URL copiada al portapapeles</string> <string name="message_copied_to_clipboard">Mensaje copiado al portapapeles</string> @@ -359,7 +357,7 @@ <string name="could_not_change_password">No se puede cambiar la contraseña</string> <string name="otr_session_not_started">Enviar mensaje para empezar una conversación cifrada</string> <string name="ask_question">Haz una pregunta</string> - <string name="smp_explain_question">Si tu contacto y tú tenéis un secreto en común que nadie más sabe (como un pequeño juego o broma o simplemente lo que tomasteis para comer la última vez os visteis) podéis usar ese secreto para verificar la huella digital de cada uno.\n\nPuedes usar una sugerencia o pregunta para hacer a tu contacto que deberá responder distinguiendo mayúsculas y minúsculas.</string> + <string name="smp_explain_question">Si tu contacto y tú tenéis un secreto en común que nadie más sabe (como una broma o simplemente lo que tomasteis para comer la última vez que os visteis) podéis usar ese secreto para verificar la huella digital de cada uno.\n\nPuedes usar una sugerencia o pregunta para hacer a tu contacto que deberá responder distinguiendo mayúsculas y minúsculas.</string> <string name="smp_explain_answer">Tu contacto quiere verificar tu huella digital a través de un secreto compartido. Te hace la siguiente sugerencia o pregunta para ese secreto.</string> <string name="shared_secret_hint_should_not_be_empty">La pregunta no puede ser vacía</string> <string name="shared_secret_can_not_be_empty">El secreto compartido no puede ser vacío</string> @@ -427,4 +425,30 @@ <string name="no_application_found_to_display_location">No se ha encontrado ninguna aplicación para mostrar la ubicación</string> <string name="location">Ubicación</string> <string name="received_location">Ubicación recibida</string> + <string name="title_undo_swipe_out_conversation">Conversación cerrada</string> + <string name="title_undo_swipe_out_muc">Has salido de la conversación</string> + <string name="pref_certificate_options">Opciones de Certificados</string> + <string name="pref_dont_trust_system_cas_title">No confiar en los CAs del sistema</string> + <string name="pref_dont_trust_system_cas_summary">Todos los certificados deben ser aprobados manualmente</string> + <string name="pref_remove_trusted_certificates_title">Eliminar Certificados</string> + <string name="pref_remove_trusted_certificates_summary">Eliminar manualmente certificados aceptados</string> + <string name="toast_no_trusted_certs">No aceptar certificados manualmente</string> + <string name="dialog_manage_certs_title">Eliminar Certificados</string> + <string name="dialog_manage_certs_positivebutton">Eliminar seleccionados</string> + <string name="dialog_manage_certs_negativebutton">Cancelar</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">%d certificado eliminado</item> + <item quantity="other">%d certificados eliminados</item> + </plurals> + <plurals name="select_contact"> + <item quantity="one">Seleccionado %d contacto</item> + <item quantity="other">Seleccionados %d contactos</item> + </plurals> + <string name="pref_quick_action_summary">Cambiar el botón de enviar por botón de acción rápida</string> + <string name="pref_quick_action">Acción Rápida</string> + <string name="none">Ninguna</string> + <string name="recently_used">Usada más recientemente</string> + <string name="choose_quick_action">Elegir acción rápida</string> + <string name="file_not_found_on_remote_host">Archivo no encontrado en servidor remoto</string> + <string name="search_for_contacts_or_groups">Buscar contactos o grupos</string> </resources> diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index 4b03d8b3..ffc276cc 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -80,7 +80,6 @@ <string name="send_otr_message">OTRz enkriptatutako mezua bidali</string> <string name="send_pgp_message">OpenPGPz enkriptatutako mezua bidali</string> <string name="your_nick_has_been_changed">Zure ezizena aldatu da</string> - <string name="download_image">Irudia deskargatu</string> <string name="send_unencrypted">Enkriptatu gabe bidali</string> <string name="decryption_failed">Desenkriptazioak huts egin du. Agian ez duzu gako pribatu egokia.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -289,15 +288,14 @@ <string name="conference_members_only">Konferentzia hau kideentzat da soilik</string> <string name="conference_kicked">Konferentzia honetatik kanporatua izan zara</string> <string name="using_account">%s kontua erabiltzen</string> - <string name="checking_image">Irudia egiaztatzen HTTP ostalarian</string> - <string name="image_file_deleted">Irudia ezabatu egin da</string> + <string name="checking_x">%s egiaztatzen HTTP ostalarian</string> <string name="not_connected_try_again">Ez zaude konektatuta. Saiatu beranduago berriz</string> - <string name="check_image_filesize">Irudiaren tamaina egiaztatu</string> + <string name="check_x_filesize">Egiaztatu %sren neurria</string> <string name="message_options">Mezuaren aukerak</string> <string name="copy_text">Testua kopiatu</string> <string name="copy_original_url">Jatorrizko URLa kopiatu</string> <string name="send_again">Berriro bidali</string> - <string name="image_url">Irudiaren URLa</string> + <string name="file_url">Fitxategiaren URLa</string> <string name="message_text">Testu mezua</string> <string name="url_copied_to_clipboard">URLa arbelera kopiatu da</string> <string name="message_copied_to_clipboard">Mezua arbelera kopiatu da</string> @@ -427,4 +425,30 @@ <string name="no_application_found_to_display_location">Kokapena erakutsi dezakeen aplikaziorik ez da aurkitu</string> <string name="location">Kokapena</string> <string name="received_location">Kokapena jaso da</string> + <string name="title_undo_swipe_out_conversation">Elkarrizketa itxi egin da</string> + <string name="title_undo_swipe_out_muc">Konferentzia utzi egin da</string> + <string name="pref_certificate_options">Ziurtagirien aukerak</string> + <string name="pref_dont_trust_system_cas_title">Sistemaren CAtaz ez fidatu</string> + <string name="pref_dont_trust_system_cas_summary">Ziurtagiri guztiak eskuz onartu behar dira</string> + <string name="pref_remove_trusted_certificates_title">Ziurtagiriak kendu</string> + <string name="pref_remove_trusted_certificates_summary">Eskuz ezabatu onartutako ziurtagiriak</string> + <string name="toast_no_trusted_certs">Ez dago eskuz onartutako ziurtagiririk</string> + <string name="dialog_manage_certs_title">Ziurtagiriak kendu</string> + <string name="dialog_manage_certs_positivebutton">Aukeratutakoak ezabatu</string> + <string name="dialog_manage_certs_negativebutton">Utzi</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">Ziurtagiri %d ezabatua</item> + <item quantity="other">%d ziurtagiri ezabatuak</item> + </plurals> + <plurals name="select_contact"> + <item quantity="one">Hautatu kontaktu %d</item> + <item quantity="other">Hautatu %d kontaktu</item> + </plurals> + <string name="pref_quick_action_summary">Bidaltze botoia ekintza azkar batekin aldatu</string> + <string name="pref_quick_action">Ekintza azkarra</string> + <string name="none">Bat ere ez</string> + <string name="recently_used">Azkenengo aldiz erabilitakoa</string> + <string name="choose_quick_action">Ekintza azkarra aukeratu</string> + <string name="file_not_found_on_remote_host">Fitxategia ez da aurkitu urruneko zerbitzarian</string> + <string name="search_for_contacts_or_groups">Kontaktuak edo taldeak bilatu</string> </resources> diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index b5df7f98..0a42b56a 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -80,7 +80,6 @@ <string name="send_otr_message">Envoyer un message sécurisé par OTR</string> <string name="send_pgp_message">Envoyer un message sécurisé par OpenPGP</string> <string name="your_nick_has_been_changed">Votre identifiant a été changé</string> - <string name="download_image">Télécharger l\'image</string> <string name="send_unencrypted">Envoyer en clair</string> <string name="decryption_failed">Echec du déchiffrement. Merci de vérifier la clef privée utilisée.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -289,15 +288,11 @@ <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="using_account">utiliser le compte %s</string> - <string name="checking_image">Vérification de l\'image</string> - <string name="image_file_deleted">L\'image a été suprimée</string> <string name="not_connected_try_again">Vous n\'êtes pas connecté. Merci de retenter plus tard.</string> - <string name="check_image_filesize">Vérifier la taille de l\'image</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> <string name="send_again">Envoyer de nouveau</string> - <string name="image_url">URL de l\'image</string> <string name="message_text">Message texte</string> <string name="url_copied_to_clipboard">URL copiée dans le presse-papier</string> <string name="message_copied_to_clipboard">Message copié dans le presse-papier</string> @@ -359,7 +354,6 @@ <string name="could_not_change_password">Impossible de changer le mot de passe</string> <string name="otr_session_not_started">Envoyez un message pour commencer la conversation chiffrée</string> <string name="ask_question">Poser une question</string> - <string name="smp_explain_question">Si vous et votre contact avez un secret en commun connu par vous deux uniquement (comme une histoire personnelle ou même comme ce que vous avez mangé lors de votre dernière rencontre) vous pouvez utiliser ce secret pour vérifier vos empreintes respectives.\n\nVous envoyez un indice ou une question à votre contact qui devra répondre en faisant attention à la casse.</string> <string name="smp_explain_answer">Votre contact voudrait vous identifier de manière sûre grâce à un secret commun. Il vous envoie le message ou la question suivante.</string> <string name="shared_secret_hint_should_not_be_empty">Votre indice ne devrait pas être vide</string> <string name="shared_secret_can_not_be_empty">Votre secret ne peut être vide</string> @@ -427,4 +421,8 @@ <string name="no_application_found_to_display_location">Aucune application trouvée pour afficher la position</string> <string name="location">Position</string> <string name="received_location">Position reçue</string> + <plurals name="select_contact"> + <item quantity="one">%d contact séléctionné</item> + <item quantity="other">%d contacts séléctionnés</item> + </plurals> </resources> diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index c432bd2e..ff6c5eeb 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -48,7 +48,6 @@ <string name="send_otr_message">Enviar mensaxe cifrado con OTR</string> <string name="send_pgp_message">Enviar mensaxe cifrado con OpenPGP</string> <string name="your_nick_has_been_changed">Modificouse o teu apodo</string> - <string name="download_image">Descargar imaxe</string> <string name="send_unencrypted">Enviar sen cifrar</string> <string name="decryption_failed">Fallou o descifrado. Quizábeis non teñas a clave privada apropiada.</string> <string name="openkeychain_required">OpenKeychain</string> diff --git a/src/main/res/values-id/strings.xml b/src/main/res/values-id/strings.xml new file mode 100644 index 00000000..6f2d967f --- /dev/null +++ b/src/main/res/values-id/strings.xml @@ -0,0 +1,447 @@ +<?xml version='1.0' encoding='UTF-8'?> +<resources> + <string name="action_settings">Pengaturan</string> + <string name="action_add">Percakapan Baru</string> + <string name="action_accounts">Pengaturan Akun</string> + <string name="action_end_conversation">Akhiri Percakapan</string> + <string name="action_contact_details">Detil Kontak</string> + <string name="action_muc_details">Detil conference</string> + <string name="action_secure">Amankan Percakapan</string> + <string name="action_add_account">Tambah Akun</string> + <string name="action_edit_contact">Ubah Nama</string> + <string name="action_add_phone_book">Tambah ke buku telepon</string> + <string name="action_delete_contact">Hapus dari roster</string> + <string name="action_block_contact">Blokir kontak</string> + <string name="action_unblock_contact">Batal blokir kontak</string> + <string name="action_block_domain">Blokir domain</string> + <string name="action_unblock_domain">Batal blokir domain</string> + <string name="title_activity_manage_accounts">Pengaturan Akun</string> + <string name="title_activity_settings">Pengaturan</string> + <string name="title_activity_conference_details">Conference Detil</string> + <string name="title_activity_contact_details">Kontak Detil</string> + <string name="title_activity_sharewith">Bagikan dengan Conversation</string> + <string name="title_activity_start_conversation">Mulai Percakapan</string> + <string name="title_activity_choose_contact">Pilih kontak</string> + <string name="title_activity_block_list">Daftar blokir</string> + <string name="just_now">sekarang</string> + <string name="minute_ago">1 min lalu</string> + <string name="minutes_ago">%d min lalu</string> + <string name="unread_conversations">Percakapan belum dibaca</string> + <string name="sending">mengirim...</string> + <string name="encrypted_message">Menerjemahkan pesan. Tunggu sebentar...</string> + <string name="nick_in_use">Nick ini sudah digunakan</string> + <string name="admin">Administrator</string> + <string name="owner">Pemilik</string> + <string name="moderator">Moderator</string> + <string name="participant">Peserta</string> + <string name="visitor">Pengunjung</string> + <string name="remove_contact_text">Apakah Anda ingin menghapus %s dari roster Anda? Percakapan yang terkait dengan kontak ini tidak akan dihapus.</string> + <string name="block_contact_text">Apakah Anda ingin memblokir pesan dari %s?</string> + <string name="unblock_contact_text">Apakah Anda ingin membuka blokir %s dan membolehkannya untuk mengirim pesan?</string> + <string name="block_domain_text">Blokir semua kontak dari %s?</string> + <string name="unblock_domain_text">Batalkan blokir semua kontak dari %s?</string> + <string name="contact_blocked">Kontak terblokir</string> + <string name="remove_bookmark_text">Apakah Anda ingin menghapus %s dari bookmark? Percakapan yang terkait dengan bookmark ini tidak akan dihapus.</string> + <string name="register_account">Daftarkan akun baru di server</string> + <string name="change_password_on_server">Ganti password di server</string> + <string name="share_with">Bagikan dengan...</string> + <string name="start_conversation">Mulai Percakapan</string> + <string name="invite_contact">Undang Kontak</string> + <string name="contacts">Kontak</string> + <string name="cancel">Batal</string> + <string name="set">Atur</string> + <string name="add">Tambah</string> + <string name="edit">Ubah</string> + <string name="delete">Hapus</string> + <string name="block">Blokir</string> + <string name="unblock">Batalkan blokir</string> + <string name="save">Simpan</string> + <string name="ok">YA</string> + <string name="crash_report_title">Percakapan terhenti</string> + <string name="crash_report_message">Dengan mengirimkan laporan kesalahan Anda membantu pengembangan Conversations\n<b>Perhatian:</>Conversations akan menggunakan akun XMPP Anda untuk mengirim laporan kesalahan untuk pengembang.</string> + <string name="send_now">Kirim sekarang</string> + <string name="send_never">Jangan tanya lagi</string> + <string name="problem_connecting_to_account">Tidak terhubung ke akun</string> + <string name="problem_connecting_to_accounts">Tidak bisa terhubung ke banyak akun</string> + <string name="touch_to_fix">Sentuh untuk mengatur akun</string> + <string name="attach_file">Sisipkan berkas</string> + <string name="not_in_roster">Kontak tidak ada dalam roster Anda. Ingin menambahkannya?</string> + <string name="add_contact">Tambah kontak</string> + <string name="send_failed">pengiriman gagal</string> + <string name="send_rejected">ditolak</string> + <string name="preparing_image">Mempersiapkan gambar untuk transmisi</string> + <string name="action_clear_history">Bersihkan riwayat</string> + <string name="clear_conversation_history">Hapus Riwayat Percakapan</string> + <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="also_end_conversation">Akhiri percakapan setelahnya</string> + <string name="choose_presence">Pilih kehadiran untuk kontak</string> + <string name="send_plain_text_message">Kirim pesan teks biasa</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> + <string name="send_unencrypted">Kirim tidak terenkripsi</string> + <string name="decryption_failed">Dekripsi gagal. Mungkin Anda tidak memiliki kunci pribadi yang tepat.</string> + <string name="openkeychain_required">OpenKeychain</string> + <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="offering">menawarkan...</string> + <string name="waiting">menunggu...</string> + <string name="no_pgp_key">Tidak ada kunci OpenPGP ditemukan</string> + <string name="contact_has_no_pgp_key">Conversations tidak dapat mengenkripsi pesan Anda karena kontak tidak mengumumkan kunci publiknya.\n\n<small>Silakan meminta kontak Anda untuk menyetel OpenPGP</small></string> + <string name="no_pgp_keys">Tidak ada kunci OpenPGP ditemukan</string> + <string name="contacts_have_no_pgp_keys">Percakapan tidak dapat mengenkripsi pesan Anda karena kontak tidak mengumumkan kunci publik mereka.\n\n<small>Silakan meminta kontak Anda untuk setup OpenPGP.</small></string> + <string name="encrypted_message_received"><i>Pesan terenkripsi diterima. Sentuh untuk membongkar dan melihatnya.</i></string> + <string name="pref_general">Umum</string> + <string name="pref_xmpp_resource">XMPP resource</string> + <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">Pengaturan Notifikasi</string> + <string name="pref_notifications">Notifikasi</string> + <string name="pref_notifications_summary">Notifikasikan jika pesan baru tiba</string> + <string name="pref_vibrate">Getar</string> + <string name="pref_vibrate_summary">Juga aktifkan getaran bila pesan baru tiba</string> + <string name="pref_sound">Suara</string> + <string name="pref_sound_summary">mainkan suara saat menerima notifikasi</string> + <string name="pref_conference_notifications">Notifikasi Conference</string> + <string name="pref_conference_notifications_summary">Selalu memberitahukan bila pesan conference baru diterima daripada hanya dicetak tebal</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">Opsi 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">Opsi Tampilan</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="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> + <string name="subscriptions">Langganan</string> + <string name="your_account">Akun anda</string> + <string name="keys">Kunci</string> + <string name="send_presence_updates">Kirim pembaruan kehadiran</string> + <string name="receive_presence_updates">Terima pembaruan kehadiran</string> + <string name="ask_for_presence_updates">Tanya untuk pembaruan kehadiran</string> + <string name="attach_choose_picture">Pilih gambar</string> + <string name="attach_take_picture">Ambil gambar</string> + <string name="preemptively_grant">Ijinkan permintaan berlangganan</string> + <string name="error_not_an_image_file">Berkas yang anda pilih bukan gambar</string> + <string name="error_compressing_image">Terjadi kesalahan saat mengubah berkas gambar</string> + <string name="error_file_not_found">Berkas tidak ditemukan</string> + <string name="error_io_exception">Kesalahan Umum I/O. Mungkin Anda kehabisan ruang penyimpanan?</string> + <string name="error_security_exception_during_image_copy">Aplikasi yang digunakan untuk memilih gambar tidak memberikan izin untuk membaca file.\n\n <small>Gunakan file manager yang berbeda untuk memilih gambar</small></string> + <string name="account_status_unknown">Tidak diketahui</string> + <string name="account_status_disabled">Sementara dimatikan</string> + <string name="account_status_online">Online</string> + <string name="account_status_connecting">Menghubungkan\u2026</string> + <string name="account_status_offline">Offline</string> + <string name="account_status_unauthorized">Tidak mendapat izin</string> + <string name="account_status_not_found">Server tidak ditemukan</string> + <string name="account_status_no_internet">Tidak ada koneksi</string> + <string name="account_status_regis_fail">Registrasi gagal</string> + <string name="account_status_regis_conflict">Username telah digunakan</string> + <string name="account_status_regis_success">Registrasi berhasil</string> + <string name="account_status_regis_not_sup">Server tidak mendukung pendaftaran akun.</string> + <string name="account_status_security_error">Kesalahan keamanan</string> + <string name="account_status_incompatible_server">Server tidak cocok</string> + <string name="encryption_choice_none">Teks biasa</string> + <string name="encryption_choice_otr">OTR</string> + <string name="encryption_choice_pgp">OpenPGP</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> + <string name="mgmt_account_publish_avatar">Publikasikan avatar</string> + <string name="mgmt_account_publish_pgp">Publikasikan kunci OpenPGP</string> + <string name="mgmt_account_enable">Aktifkan Akun</string> + <string name="mgmt_account_are_you_sure">Apakah Anda yakin.?</string> + <string name="mgmt_account_delete_confirm_text">Jika Anda menghapus akun, semua data percakapan Anda di Conversation akan hilang.</string> + <string name="attach_record_voice">Rekam suara</string> + <string name="account_settings_jabber_id">Jabber ID</string> + <string name="account_settings_password">Password</string> + <string name="account_settings_example_jabber_id">username@example.com</string> + <string name="account_settings_confirm_password">Ketik ulang password</string> + <string name="password">Password</string> + <string name="confirm_password">Ketik ulang password</string> + <string name="passwords_do_not_match">Password tidak sama</string> + <string name="invalid_jid">Jabber ID tidak valid</string> + <string name="error_out_of_memory">Memori habis. Gambar terlalu besar</string> + <string name="add_phone_book_text">Apakah anda ingin menambahkan %s ke daftar kontak anda?</string> + <string name="contact_status_online">online</string> + <string name="contact_status_free_to_chat">bebas untuk chatting</string> + <string name="contact_status_away">pergi</string> + <string name="contact_status_extended_away">pergi lama</string> + <string name="contact_status_do_not_disturb">jangan ganggu</string> + <string name="contact_status_offline">offline</string> + <string name="muc_details_conference">Conference</string> + <string name="muc_details_other_members">Member lainnya</string> + <string name="server_info_show_more">Info Server</string> + <string name="server_info_mam">XEP-0313: MAM</string> + <string name="server_info_carbon_messages">XEP-0280: Message Carbons</string> + <string name="server_info_csi">XEP-0352: Client State Indication</string> + <string name="server_info_blocking">XEP-0191: Blocking Command</string> + <string name="server_info_roster_version">XEP-0237: Roster Versioning</string> + <string name="server_info_stream_management">XEP-0198: Stream Management</string> + <string name="server_info_pep">XEP-0163: PEP (Avatars)</string> + <string name="server_info_available">tersedia</string> + <string name="server_info_unavailable">tidak tersedia</string> + <string name="missing_public_keys">Pemberitahuan kunci publik tidak ditemukan</string> + <string name="last_seen_now">terakhir terlihat sekarang</string> + <string name="last_seen_min">terakhir terlihat 1 menit lalu</string> + <string name="last_seen_mins">terlihat %d menit lalu</string> + <string name="last_seen_hour">terlihat 1 jam lalu</string> + <string name="last_seen_hours">terlihat %d jam lalu</string> + <string name="last_seen_day">terlihat 1 hari lalu</string> + <string name="last_seen_days">terlihat %d hari lalu</string> + <string name="never_seen">tidak pernah terlihat</string> + <string name="install_openkeychain">Pesan terenskripsi. Pasang OpenKeychain untuk menerjemahkan</string> + <string name="unknown_otr_fingerprint">Sidik OTR tidak diketahui</string> + <string name="openpgp_messages_found">pesan terkunci OpenPGP ditemukan</string> + <string name="reception_failed">Penerimaan gagal</string> + <string name="your_fingerprint">Fingerprint Anda</string> + <string name="otr_fingerprint">OTR fingerprint</string> + <string name="verify">Verifikasi</string> + <string name="decrypt">Deskripsi</string> + <string name="conferences">Conferences</string> + <string name="search">Cari</string> + <string name="create_contact">Buat Kontak</string> + <string name="join_conference">Gabung Conference</string> + <string name="delete_contact">Hapus Kontak</string> + <string name="view_contact_details">Lihat detil kontak</string> + <string name="block_contact">Blokir kontak</string> + <string name="unblock_contact">Lepas blokir kontak</string> + <string name="create">Buat</string> + <string name="contact_already_exists">Kontak ini sudah ada</string> + <string name="join">Gabung</string> + <string name="conference_address">Alamat conference</string> + <string name="conference_address_example">room@conference.example.com</string> + <string name="save_as_bookmark">Simpan sebagai bookmark</string> + <string name="delete_bookmark">Hapus bookmark</string> + <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> + <string name="contact_has_read_up_to_this_point">%s telah membaca hingga disini</string> + <string name="publish">Publikasi</string> + <string name="touch_to_choose_picture">Sentuh Avatar untuk memilih gambar dari gallery</string> + <string name="publish_avatar_explanation">Harap dicatat: Setiap orang yang berlangganan update kehadiran Anda akan diizinkan untuk melihat gambar ini.</string> + <string name="publishing">Mempublikasi...</string> + <string name="error_publish_avatar_server_reject">Server tidak mengijinkan publikasi Anda</string> + <string name="error_publish_avatar_converting">Sesuatu yang salah terjadi ketika mengkonversi gambar anda</string> + <string name="error_saving_avatar">Tidak dapat menyimpan Avatar ke memori</string> + <string name="or_long_press_for_default">(Tekan yang lama untuk mengembalikan semula)</string> + <string name="error_publish_avatar_no_server_support">Server Anda tidak mendukung publikasi Avatar</string> + <string name="private_message">berbisik</string> + <string name="private_message_to">kepada %s</string> + <string name="send_private_message_to">Kirim pesan pribadi ke %s</string> + <string name="connect">Hubungkan</string> + <string name="account_already_exists">Akun ini sudah ada</string> + <string name="next">Selanjutnya</string> + <string name="server_info_session_established">Sesi saat didirikan</string> + <string name="additional_information">Informasi tambahan</string> + <string name="skip">Lewati</string> + <string name="disable_notifications">Nonaktifkan notifikasi</string> + <string name="disable_notifications_for_this_conversation">Nonaktifkan notifikasi untuk percakapan ini</string> + <string name="notifications_disabled">Notifikasi telah dimatikan</string> + <string name="enable">Aktifkan</string> + <string name="conference_requires_password">Conference membutuhkan password</string> + <string name="enter_password">Masukan password</string> + <string name="missing_presence_updates">Pembaruan kehadiran hilang dari kontak</string> + <string name="request_presence_updates">Silakan meminta pembaruan kehadiran dari kontak Anda lebih dulu.\n\n<small>Ini akan digunakan untuk menentukan (klien) mana yang digunakan dikontak Anda.</small></string> + <string name="request_now">Request sekarang</string> + <string name="delete_fingerprint">Hapus sidik jari</string> + <string name="sure_delete_fingerprint">Apakah anda yakin menghapus sidik jari?</string> + <string name="ignore">Abaikan</string> + <string name="without_mutual_presence_updates"><b>Perhatian</b> Mengirim ini tanpa kehadiran sesama pembaruan bisa menyebabkan masalah tak terduga.\n\n<small>Pergi ke kontak untuk memverifikasi langganan kehadiran anda.</small></string> + <string name="pref_encryption_settings">Pengaturan enskripsi</string> + <string name="pref_force_encryption">Paksa enskripsi end-to-end</string> + <string name="pref_force_encryption_summary">Selalu mengirim pesan terenkripsi (kecuali untuk conferences)</string> + <string name="pref_dont_save_encrypted">jangan simpan pesan terenskripsi</string> + <string name="pref_dont_save_encrypted_summary">Peringatan: Hal ini bisa mengakibatkan hilangnya pesan</string> + <string name="pref_expert_options">Pengaturan lanjutan</string> + <string name="pref_expert_options_summary">Harap berhati-hati dengan ini</string> + <string name="title_activity_about">Tentang Conversations</string> + <string name="pref_about_conversations_summary">Build dan informasi lisensi</string> + <string name="title_pref_quiet_hours">Waktu sunyi</string> + <string name="title_pref_quiet_hours_start_time">Waktu mulai</string> + <string name="title_pref_quiet_hours_end_time">Waktu selesai</string> + <string name="title_pref_enable_quiet_hours">Aktifkan waktu sunyi</string> + <string name="pref_quiet_hours_summary">Pemberitahuan akan disunyukan ketika jam sunyi.</string> + <string name="pref_use_larger_font">Perbesar ukuran huruf</string> + <string name="pref_use_larger_font_summary">Gunakan ukuran huruf lebih besar diseluruh aplikasi</string> + <string name="pref_use_send_button_to_indicate_status">Tombol kirim menunjukan statusnya</string> + <string name="pref_use_indicate_received">Permintaan penerimaan pesan</string> + <string name="pref_use_indicate_received_summary">Pesan yang diterima akan ditambahkan centang hijau jika didukung</string> + <string name="pref_use_send_button_to_indicate_status_summary">Warnai tombol kirim untuk mengindikasikan status kontak</string> + <string name="pref_expert_options_other">Lainnya</string> + <string name="pref_conference_name">nama Conference</string> + <string name="pref_conference_name_summary">Gunakan subjek \'room\' daripada JID untuk mengidentifikasi conferences</string> + <string name="toast_message_otr_fingerprint">sidik jari OTR disalin ke clipboard!</string> + <string name="conference_banned">Anda ditendang dari conference ini</string> + <string name="conference_members_only">Conference ini hanya untuk member terdaftar</string> + <string name="conference_kicked">Anda telah ditendang dari conference ini</string> + <string name="using_account">menggunakan akun %s</string> + <string name="not_connected_try_again">Anda tidak terhubung. Coba lagi nanti</string> + <string name="message_options">Opsi pesan</string> + <string name="copy_text">Salin teks</string> + <string name="copy_original_url">Salin URL asli</string> + <string name="send_again">Kirim lagi</string> + <string name="message_text">Pesan teks</string> + <string name="url_copied_to_clipboard">URL disalin ke clipboard</string> + <string name="message_copied_to_clipboard">Pesan disalin ke clipboard</string> + <string name="image_transmission_failed">pengiriman gambar gagal</string> + <string name="scan_qr_code">Pindai kode QR</string> + <string name="show_qr_code">Tampilkan kode QR</string> + <string name="show_block_list">Tampilkan daftar blokir</string> + <string name="account_details">Detil akun</string> + <string name="verify_otr">Verifikasi OTR</string> + <string name="remote_fingerprint">Remote Sidik jari</string> + <string name="scan">pindai</string> + <string name="or_touch_phones">(atau menyentuh telepon)</string> + <string name="smp">Socialist Millionaire Protocol</string> + <string name="shared_secret_hint">Petunjuk atau Pertanyaan</string> + <string name="shared_secret_secret">Rahasia bersama</string> + <string name="confirm">Konfirmasi</string> + <string name="in_progress">Sedang diproses</string> + <string name="respond">Menanggapi</string> + <string name="failed">Gagal</string> + <string name="secrets_do_not_match">Rahasia tidak match</string> + <string name="try_again">Coba lagi</string> + <string name="finish">Selesai</string> + <string name="verified">Terverifikasi!</string> + <string name="smp_requested">Kontak meminta verifikasi SMP</string> + <string name="no_otr_session_found">Tidak ada sesi OTR yang valid ditemukan!</string> + <string name="conversations_foreground_service">Percakapan</string> + <string name="pref_keep_foreground_service">Jaga layanan di latar depan</string> + <string name="pref_keep_foreground_service_summary">Cegah sistem operasi mematikan koneksi</string> + <string name="choose_file">Pilih berkas</string> + <string name="receiving_x_file">Menerima %1$s (%2$d%% terselesaikan)</string> + <string name="download_x_file">Mengunduh %s</string> + <string name="file">berkas</string> + <string name="open_x_file">Buka %s</string> + <string name="sending_file">mengirim (%1$d%% terselesaikan)</string> + <string name="preparing_file">Menyiapkan berkas untuk transmisi</string> + <string name="x_file_offered_for_download">%s ditawarkan untuk mengunduh</string> + <string name="cancel_transmission">batalkan pengiriman</string> + <string name="file_transmission_failed">pengiriman berkas gagal</string> + <string name="file_deleted">Berkas sudah dihapus</string> + <string name="no_application_found_to_open_file">Tidak ditemukan aplikasi untuk membuka berkas</string> + <string name="could_not_verify_fingerprint">Tidak dapat verifikasi fingerprint</string> + <string name="manually_verify">Verifikasi secara manual</string> + <string name="are_you_sure_verify_fingerprint">Yakin untuk memferifikasi OTR fingerprint kontak Anda?</string> + <string name="pref_show_dynamic_tags">Tampilkan tag dinamis</string> + <string name="pref_show_dynamic_tags_summary">Tampilan read-only tag di bawah kontak</string> + <string name="enable_notifications">Aktifkan notifikasi</string> + <string name="conference_with">Buat conference dengan...</string> + <string name="no_conference_server_found">Tidak ada server conference ditemukan</string> + <string name="conference_creation_failed">Pembuatan conference gagal!</string> + <string name="conference_created">Conference dibuat!</string> + <string name="secret_accepted">Rahasia disetujui!</string> + <string name="reset">Ulang</string> + <string name="account_image_description">Avatar akun</string> + <string name="copy_otr_clipboard_description">Salin OTR fingerprint ke clipboard</string> + <string name="fetching_history_from_server">Mengambil data dari server</string> + <string name="no_more_history_on_server">Tidak ada data lagi di server</string> + <string name="updating">Merubah...</string> + <string name="password_changed">Password diganti!</string> + <string name="could_not_change_password">Tidak dapat mengubah password</string> + <string name="otr_session_not_started">Kirim pesan untuk memulai obrolan dienkripsi</string> + <string name="ask_question">Bertanya</string> + <string name="smp_explain_question">Jika Anda dan kontak Anda memiliki rahasia yang sama yang tidak ada orang lain yang tahu (seperti sebuah lelucon atau hanya apa yang Anda punya untuk makan siang terakhir kali Anda bertemu) Anda dapat menggunakan rahasia itu untuk memverifikasi sidik jari masing-masing.\n\nAnda memberikan petunjuk atau pertanyaan untuk kontak yang akan merespon dengan jawaban case-sensitive.</string> + <string name="smp_explain_answer">Kontak Anda ingin memverifikasi sidik jari Anda dengan menantang Anda dengan rahasia bersama. Kontak Anda memberikan petunjuk berikut atau pertanyaan untuk rahasia itu.</string> + <string name="shared_secret_hint_should_not_be_empty">Petunjuk Anda tidak boleh kosong</string> + <string name="shared_secret_can_not_be_empty">Rahasia bersama Anda tidak boleh kosong</string> + <string name="manual_verification_explanation">Hati-hati membandingkan sidik jari yang ditunjukkan di bawah dengan sidik jari kontak Anda.\nAnda dapat mengunakan layanan komunikasi terpercaya seperti e-mail terenskripsi atau panggilan telepon untuk menukarkannya.</string> + <string name="change_password">Ubah password</string> + <string name="current_password">Password sekarang</string> + <string name="new_password">Password baru</string> + <string name="password_should_not_be_empty">Password tidak boleh kosong</string> + <string name="enable_all_accounts">Aktifkan semua akun</string> + <string name="disable_all_accounts">Menonaktifkan semua account</string> + <string name="perform_action_with">Lakukan aksi dengan</string> + <string name="no_affiliation">Tidak ada afiliasi</string> + <string name="no_role">Tidak ada peran</string> + <string name="outcast">Orang buangan</string> + <string name="member">Member</string> + <string name="advanced_mode">Mode lanjut</string> + <string name="grant_membership">Memberikan keanggotaan</string> + <string name="remove_membership">Cabut keanggotaan</string> + <string name="grant_admin_privileges">Memberikan hak istimewa admin</string> + <string name="remove_admin_privileges">Mencabut hak istimewa admin</string> + <string name="remove_from_room">Hapus dari conference</string> + <string name="could_not_change_affiliation">Tidak bisa mengubah afiliasi %s</string> + <string name="ban_from_conference">Tendang dari conference</string> + <string name="removing_from_public_conference">Anda mencoba untuk menghapus %s dari sebuah conference publik. Satu-satunya cara untuk melakukannya adalah untuk menendang pengguna ini selamanya.</string> + <string name="ban_now">Tendang sekarang</string> + <string name="could_not_change_role">Tidak dapat merumah role %s</string> + <string name="public_conference">Conference umum</string> + <string name="private_conference">Rahasia, hanya member conference</string> + <string name="conference_options">Opsi conference</string> + <string name="members_only">Rahasia (Hanya member)</string> + <string name="non_anonymous">Non Anonymous</string> + <string name="modified_conference_options">Opsi conference dimodifikasi!</string> + <string name="could_not_modify_conference_options">Tidak dapat merubah pengaturan conference</string> + <string name="never">Tidak pernah</string> + <string name="thirty_minutes">30 menit</string> + <string name="one_hour">1 jam</string> + <string name="two_hours">2 jam</string> + <string name="eight_hours">8 jam</string> + <string name="until_further_notice">Sampai pemberitahuan selanjutnya</string> + <string name="pref_input_options">Opsi input</string> + <string name="pref_enter_is_send">Enter untuk mengirim</string> + <string name="pref_enter_is_send_summary">Gunakan enter untuk mengrim pesan</string> + <string name="pref_display_enter_key">Tampilkan masukan kunci</string> + <string name="pref_display_enter_key_summary">Mengubah kunci emoji untuk memasukan kunci</string> + <string name="audio">audio</string> + <string name="video">video</string> + <string name="image">Gambar</string> + <string name="pdf_document">Berkas PDF</string> + <string name="apk">Apl Android</string> + <string name="vcard">Kontak</string> + <string name="received_x_file">Diterima %s</string> + <string name="disable_foreground_service">Nonaktifkan layanan latar belakang</string> + <string name="touch_to_open_conversations">Sentuh untuk membuka Conversations</string> + <string name="avatar_has_been_published">Avatar telah diterbitkan!</string> + <string name="sending_x_file">Mengirim %s</string> + <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> + <string name="send_location">Kirim lokasi</string> + <string name="show_location">Tampilkan lokasi</string> + <string name="no_application_found_to_display_location">Tidak ada aplikasi ditemukan untuk menampilkan lokasi</string> + <string name="location">Lokasi</string> + <string name="received_location">Lokasi yang diterima</string> + <string name="title_undo_swipe_out_conversation">Percakapan tertutup</string> + <string name="title_undo_swipe_out_muc">Tinggalkan conference</string> + <string name="pref_certificate_options">Opsi Sertifikat</string> + <string name="pref_dont_trust_system_cas_title">Jangan percaya sistem CA</string> + <string name="pref_dont_trust_system_cas_summary">Semua sertifikat harus disetujui secara manual</string> + <string name="pref_remove_trusted_certificates_title">Hapus sertifikat</string> + <string name="pref_remove_trusted_certificates_summary">Hapus sertifikat yang disahkan secara manual</string> + <string name="toast_no_trusted_certs">Tidak ada sertifikat yang disahkan secara manual</string> + <string name="dialog_manage_certs_title">Hapus sertifikat</string> + <string name="dialog_manage_certs_positivebutton">Hapus seleksi</string> + <string name="dialog_manage_certs_negativebutton">Batal</string> + <plurals name="toast_delete_certificates"> + <item quantity="other">%d sertifikat dihapus</item> + </plurals> + <plurals name="select_contact"> + <item quantity="other">Pilih %d kontak</item> + </plurals> + <string name="pref_quick_action_summary">Timpa tombol kirim dengan aksi cepat</string> + <string name="pref_quick_action">Aksi Cepat</string> + <string name="none">Tak satupun</string> + <string name="recently_used">Maling sering digunakan</string> + <string name="choose_quick_action">Pilih aksi cepat</string> +</resources> diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index d65fdc0d..8397b043 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -80,7 +80,6 @@ <string name="send_otr_message">Messaggio OTR</string> <string name="send_pgp_message">Messaggio OpenPGP</string> <string name="your_nick_has_been_changed">Il tuo nome utente è stato cambiato</string> - <string name="download_image">Scarica Immagine</string> <string name="send_unencrypted">Invia non cifrato</string> <string name="decryption_failed">Decifrazione fallita. Forse non disponi della chiave privata corretta.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -289,21 +288,18 @@ <string name="conference_members_only">Questa conferenza è solo per membri</string> <string name="conference_kicked">Sei stato buttato fuori dalla conferenza</string> <string name="using_account">usando l’utente %s</string> - <string name="checking_image">Controlla immagine su HTTP</string> - <string name="image_file_deleted">Il file dell’immagine è stato cancellato</string> <string name="not_connected_try_again">Non sei connesso. Riprova più tardi</string> - <string name="check_image_filesize">Controlla le dimensioni dell’immagine</string> <string name="message_options">Opzioni del messaggio</string> <string name="copy_text">Copia testo</string> <string name="copy_original_url">Copia URL originale</string> <string name="send_again">Invia di nuovo</string> - <string name="image_url">URL immagine</string> <string name="message_text">Messaggio di testo</string> <string name="url_copied_to_clipboard">URL copiato</string> <string name="message_copied_to_clipboard">Messaggio copiato</string> <string name="image_transmission_failed">Trasmissione dell’immagine fallita</string> <string name="scan_qr_code">Scansiona codice QR</string> <string name="show_qr_code">Mostra codice QR</string> + <string name="show_block_list">Mostra la black list</string> <string name="account_details">Dettagli utente</string> <string name="verify_otr">Verifica OTR</string> <string name="remote_fingerprint">Impronta remota</string> @@ -328,6 +324,7 @@ <string name="choose_file">Scegli file</string> <string name="receiving_x_file">Ricezione di %1$s file (%2$d%% completato)</string> <string name="download_x_file">Scarica %s</string> + <string name="file">file</string> <string name="open_x_file">Apri %s</string> <string name="sending_file">invio (%1$d%% completato)</string> <string name="preparing_file">Preparazione alla trasmissione del file</string> @@ -357,7 +354,7 @@ <string name="could_not_change_password">Impossibile cambiare la password</string> <string name="otr_session_not_started">Invia un messaggio per avviare una conversazione cifrata</string> <string name="ask_question">Fai una domanda</string> - <string name="smp_explain_question">Se tu ed il tuo contatto avete un segreto in comune che nessun’altro conosce (come uno scherzo o semplicemente ciò che avete mangiato a pranzo l’ultima volta che vi siete incontrati), potete usare quel segreto per dimostrare la vostra identità reciprocamente.\n\nFornisci un suggerimento o una domanda al tuo contatto che ti risponderà con una domanda Case Sensitive.</string> + <string name="smp_explain_question">Se condividi con il tuo contatto un segreto in comune non conosciuto da altri (come uno scherzo privato o semplicemente quel che avete mangiato l\'ultima volta insieme) potete usarlo per verificare le vostre fingerprint.\n\nInvia un suggerimento o una domanda al tuo contatto il quale dovrà rispondere con una risposta controllando le maiuscole.</string> <string name="smp_explain_answer">Il tuo contatto vorrebbe verificare la tua identità. Dovrai rispondere correttamente alla sua domanda con una risposta che condividete. Il tuo contatto ti ha fornito il suggerimento o la domanda seguente.</string> <string name="shared_secret_hint_should_not_be_empty">Il suggerimento non dovrebbe essere vuoto</string> <string name="shared_secret_can_not_be_empty">Il segreto condiviso non può essere vuoto</string> @@ -400,6 +397,46 @@ <string name="pref_input_options">Opzioni di ingresso</string> <string name="pref_enter_is_send">Invio invia</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> + <string name="pref_display_enter_key_summary">Cambia il tasto delle faccine nel tastodi invio</string> + <string name="audio">audio</string> + <string name="video">video</string> + <string name="image">immagine</string> <string name="pdf_document">Documento PDF</string> + <string name="apk">Applicazione Android</string> + <string name="vcard">Contatto</string> <string name="received_x_file">Ricevuto %s</string> + <string name="touch_to_open_conversations">Tocca per avviare Conversations</string> + <string name="avatar_has_been_published">Il tuo avatar è stato pubblicato!</string> + <string name="sending_x_file">Invio %s</string> + <string name="offering_x_file">Inviando %s</string> + <string name="hide_offline">Nascondi i contatti offline</string> + <string name="disable_account">Disabilita l\'account</string> + <string name="contact_is_typing">%s sta digitando...</string> + <string name="contact_has_stopped_typing">%s ha smesso di digitare</string> + <string name="pref_chat_states_summary">Permetti al tuo contatto di vedere quando stai digitando</string> + <string name="send_location">Invia la posizione</string> + <string name="show_location">Mostra la posizione</string> + <string name="no_application_found_to_display_location">Non è stata trovata alcuna applicazione per mostrare la posizione</string> + <string name="location">Posizione</string> + <string name="received_location">Posizione ricevuta</string> + <string name="title_undo_swipe_out_conversation">Conversazione interrotta</string> + <string name="title_undo_swipe_out_muc">Conferenza terminata</string> + <string name="pref_certificate_options">Opzioni per i certificati</string> + <string name="pref_dont_trust_system_cas_title">Non ti fidare delle CA di sistema</string> + <string name="pref_dont_trust_system_cas_summary">Tutti i certificati devono essere accettati manualmente</string> + <string name="pref_remove_trusted_certificates_title">Elimina i certificati</string> + <string name="pref_remove_trusted_certificates_summary">Cancella manualmente i certificati già accettati</string> + <string name="toast_no_trusted_certs">Non sono presenti certificati accettati manualmente</string> + <string name="dialog_manage_certs_title">Elimina i certificati</string> + <string name="dialog_manage_certs_positivebutton">Cancella la selezione</string> + <string name="dialog_manage_certs_negativebutton">Annulla</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">Cancellato il %d certificato</item> + <item quantity="other">Cancellati %d certificati</item> + </plurals> + <plurals name="select_contact"> + <item quantity="one">Seleziona il %d contatto</item> + <item quantity="other">Selezionati %d contatti</item> + </plurals> </resources> diff --git a/src/main/res/values-iw/strings.xml b/src/main/res/values-iw/strings.xml index 7ef94abc..cc12c7d0 100644 --- a/src/main/res/values-iw/strings.xml +++ b/src/main/res/values-iw/strings.xml @@ -66,7 +66,6 @@ <string name="send_otr_message">שלח הודעה מוצפנת OTR</string> <string name="send_pgp_message">שלח הודעה מוצפנת OpenPGP</string> <string name="your_nick_has_been_changed">שם כינוי שלך השתנה</string> - <string name="download_image">הורד תצלום</string> <string name="send_unencrypted">שלח לא מוצפנת</string> <string name="decryption_failed">פענוח נכשל. אולי אין לך את המפתח הפרטי המתאים.</string> <string name="openkeychain_required">OpenKeychain</string> diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml new file mode 100644 index 00000000..35347734 --- /dev/null +++ b/src/main/res/values-ja/strings.xml @@ -0,0 +1,452 @@ +<?xml version='1.0' encoding='UTF-8'?> +<resources> + <string name="action_settings">設定</string> + <string name="action_add">新しい会話</string> + <string name="action_accounts">アカウントの管理</string> + <string name="action_end_conversation">この会話を終了</string> + <string name="action_contact_details">連絡先の詳細</string> + <string name="action_muc_details">会議の詳細</string> + <string name="action_secure">安全に会話</string> + <string name="action_add_account">アカウントを追加</string> + <string name="action_edit_contact">名前の編集</string> + <string name="action_add_phone_book">電話帳に追加</string> + <string name="action_delete_contact">名簿から削除</string> + <string name="action_block_contact">連絡先をブロック</string> + <string name="action_unblock_contact">連絡先のブロックを解除</string> + <string name="action_block_domain">ドメインをブロック</string> + <string name="action_unblock_domain">ドメインのブロックを解除</string> + <string name="title_activity_manage_accounts">アカウントの管理</string> + <string name="title_activity_settings">設定</string> + <string name="title_activity_conference_details">会議の詳細</string> + <string name="title_activity_contact_details">連絡先の詳細</string> + <string name="title_activity_sharewith">会話で共有</string> + <string name="title_activity_start_conversation">会話の開始</string> + <string name="title_activity_choose_contact">連絡先の選択</string> + <string name="title_activity_block_list">ブロックリスト</string> + <string name="just_now">ちょうど今</string> + <string name="minute_ago">1 分前</string> + <string name="minutes_ago">%d 分前</string> + <string name="unread_conversations">未読の会話</string> + <string name="sending">送信中…</string> + <string name="encrypted_message">メッセージを復号しています。しばらくお待ちください…</string> + <string name="nick_in_use">ニックネームは既に使用されています</string> + <string name="admin">管理者</string> + <string name="owner">オーナー</string> + <string name="moderator">モデレーター</string> + <string name="participant">参加者</string> + <string name="visitor">ビジター</string> + <string name="remove_contact_text">あなたの名簿から %s を削除しますか? この連絡先と関連した会話は削除されません。</string> + <string name="block_contact_text">%s からあなたに送信されるメッセージをブロックしますか?</string> + <string name="unblock_contact_text">%s のブロックを解除し、あなたにメッセージを送信できるようにしますか?</string> + <string name="block_domain_text">%s からの連絡をすべてブロックしますか?</string> + <string name="unblock_domain_text">%s からの連絡をすべてブロック解除しますか?</string> + <string name="contact_blocked">連絡先をブロックしました</string> + <string name="remove_bookmark_text">ブックマークとして %s を削除しますか? このブックマークに関連した会話は削除されません。</string> + <string name="register_account">サーバーに新しいアカウントを登録</string> + <string name="change_password_on_server">サーバーのパスワードを変更</string> + <string name="share_with">…で共有</string> + <string name="start_conversation">会話を開始</string> + <string name="invite_contact">連絡先を招待</string> + <string name="contacts">連絡先</string> + <string name="cancel">キャンセル</string> + <string name="set">設定</string> + <string name="add">追加</string> + <string name="edit">編集</string> + <string name="delete">削除</string> + <string name="block">ブロック</string> + <string name="unblock">ブロックを解除</string> + <string name="save">保存</string> + <string name="ok">OK</string> + <string name="crash_report_title">Conversations がクラッシュしました</string> + <string name="crash_report_message">Conversations の継続的な開発を支援するために、スタックトレースをお送りください。\n<b>警告:</b> あなたのXMPPアカウントを使用して、開発者にスタックトレースを送信します。</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="touch_to_fix">ここをタッチして、アカウントの管理</string> + <string name="attach_file">添付ファイル</string> + <string name="not_in_roster">連絡先が名簿にありません。追加しますか?</string> + <string name="add_contact">連絡先を追加</string> + <string name="send_failed">配信に失敗しました</string> + <string name="send_rejected">拒否されました</string> + <string name="preparing_image">転送用画像の準備中</string> + <string name="action_clear_history">履歴をクリア</string> + <string name="clear_conversation_history">会話履歴をクリア</string> + <string name="clear_histor_msg">この会話のすべてのメッセージを削除しますか?\n\n<b>警告:</b> これは、他のデバイスやサーバーに保存されているメッセージには影響しません。</string> + <string name="delete_messages">メッセージを削除</string> + <string name="also_end_conversation">その後、この会話を終了</string> + <string name="choose_presence">連絡する参加を選択</string> + <string name="send_plain_text_message">プレーンテキストを送信</string> + <string name="send_otr_message">OTR 暗号化メッセージを送信</string> + <string name="send_pgp_message">OpenPGP 暗号化メッセージを送信</string> + <string name="your_nick_has_been_changed">あなたのニックネームが変更されました</string> + <string name="send_unencrypted">暗号化されていない送信</string> + <string name="decryption_failed">復号化に失敗しました。おそらく秘密鍵が正しくないようです。</string> + <string name="openkeychain_required">OpenKeychain</string> + <string name="openkeychain_required_long">Conversations は <b>OpenKeychain</b> と呼ばれるサードパーティのアプリを利用して、メッセージの暗号化および復号化、そしてあなたの公開鍵を管理します。\n\nOpenKeychain は GPLv3 ライセンスの下で、F-Droid および Google Play から利用可能です。\n\n<small>(後で Conversations を再起動してください。)</small></string> + <string name="restart">再起動</string> + <string name="install">インストール</string> + <string name="offering">依頼中…</string> + <string name="waiting">待機中…</string> + <string name="no_pgp_key">OpenPGP の鍵はありません</string> + <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> + <string name="pref_accept_files">ファイルを受取</string> + <string name="pref_accept_files_summary">自動的に小さいファイルを受取…</string> + <string name="pref_notification_settings">通知設定</string> + <string name="pref_notifications">通知</string> + <string name="pref_notifications_summary">新しいメッセージが到着したときに通知します</string> + <string name="pref_vibrate">振動</string> + <string name="pref_vibrate_summary">新しいメッセージが到着したときに振動もします</string> + <string name="pref_sound">サウンド</string> + <string name="pref_sound_summary">通知で着信音を再生します</string> + <string name="pref_conference_notifications">会議通知</string> + <string name="pref_conference_notifications_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_never_send_crash">クラッシュレポートを送信しない</string> + <string name="pref_never_send_crash_summary">スタックトレースを送信することで、あなたは Conversations の継続的な開発を支援しています</string> + <string name="pref_confirm_messages">メッセージの確認</string> + <string name="pref_confirm_messages_summary">あなたがメッセージを受け取って読んだことを、連絡先に知らせます</string> + <string name="pref_ui_options">UI オプション</string> + <string name="openpgp_error">OpenKeychain がエラーを報告しました</string> + <string name="error_decrypting_file">ファイルの復号化中に I/O エラー</string> + <string name="accept">受付</string> + <string name="error">エラーが発生しました</string> + <string name="pref_grant_presence_updates">参加アップデートの権限を付与</string> + <string name="pref_grant_presence_updates_summary">事前に、作成した連絡先に対して権限を付与して、参加のサブスクリプションを求めます</string> + <string name="subscriptions">サブスクリプション</string> + <string name="your_account">あなたのアカウント</string> + <string name="keys">鍵</string> + <string name="send_presence_updates">参加アップデートを送信</string> + <string name="receive_presence_updates">参加アップデートを受信</string> + <string name="ask_for_presence_updates">参加アップデートを問合せ</string> + <string name="attach_choose_picture">写真の選択</string> + <string name="attach_take_picture">写真の撮影</string> + <string name="preemptively_grant">事前にサブスクリプション要求を許可する</string> + <string name="error_not_an_image_file">選択したファイルは画像ではありません</string> + <string name="error_compressing_image">画像ファイルの変換中にエラー</string> + <string name="error_file_not_found">ファイルが見つかりません</string> + <string name="error_io_exception">一般的な I/O エラー。おそらく空き容量がなくなっていませんか?</string> + <string name="error_security_exception_during_image_copy">選択した画像ファイルは、読み取りに必要なアクセス権がありません。\n\n<small>別のファイルマネージャを使用して、画像を選択してください。</small></string> + <string name="account_status_unknown">不明</string> + <string name="account_status_disabled">一時的に無効</string> + <string name="account_status_online">オンライン</string> + <string name="account_status_connecting">接続中\u2026</string> + <string name="account_status_offline">オフライン</string> + <string name="account_status_unauthorized">許可されていません</string> + <string name="account_status_not_found">サーバーが見つかりません</string> + <string name="account_status_no_internet">接続エラー</string> + <string name="account_status_regis_fail">登録に失敗しました</string> + <string name="account_status_regis_conflict">ユーザー名はすでに使用されています</string> + <string name="account_status_regis_success">登録が完了しました</string> + <string name="account_status_regis_not_sup">サーバーが登録をサポートしていません</string> + <string name="account_status_security_error">セキュリティ エラー</string> + <string name="account_status_incompatible_server">互換性のないサーバー</string> + <string name="encryption_choice_none">プレーンテキスト</string> + <string name="encryption_choice_otr">OTR</string> + <string name="encryption_choice_pgp">OpenPGP</string> + <string name="mgmt_account_edit">アカウントの編集</string> + <string name="mgmt_account_delete">アカウントを削除</string> + <string name="mgmt_account_disable">一時的に無効にする</string> + <string name="mgmt_account_publish_avatar">アバターを公開</string> + <string name="mgmt_account_publish_pgp">OpenPGP 公開鍵を公開</string> + <string name="mgmt_account_enable">アカウントを有効にする</string> + <string name="mgmt_account_are_you_sure">よろしいですか?</string> + <string name="mgmt_account_delete_confirm_text">アカウントを削除するとあなたの会話履歴がすべて失われます</string> + <string name="attach_record_voice">音声を録音</string> + <string name="account_settings_jabber_id">Jabber ID</string> + <string name="account_settings_password">パスワード</string> + <string name="account_settings_example_jabber_id">username@example.com</string> + <string name="account_settings_confirm_password">パスワードを確認</string> + <string name="password">パスワード</string> + <string name="confirm_password">パスワードを確認</string> + <string name="passwords_do_not_match">パスワードが一致しません</string> + <string name="invalid_jid">これは有効な Jabber ID ではありません</string> + <string name="error_out_of_memory">メモリ不足です。画像が大きすぎます</string> + <string name="add_phone_book_text">電話の連絡先リストに %s を追加しますか?</string> + <string name="contact_status_online">オンライン</string> + <string name="contact_status_free_to_chat">自由にチャットできます</string> + <string name="contact_status_away">離席中</string> + <string name="contact_status_extended_away">長時間離席中</string> + <string name="contact_status_do_not_disturb">じゃましないで</string> + <string name="contact_status_offline">オフライン</string> + <string name="muc_details_conference">会議</string> + <string name="muc_details_other_members">他のメンバー</string> + <string name="server_info_show_more">サーバー情報</string> + <string name="server_info_mam">XEP-0313: メッセージ アーカイブ管理</string> + <string name="server_info_carbon_messages">XEP-0280: メッセージ カーボン</string> + <string name="server_info_csi">XEP-0352: クライアント状態表示</string> + <string name="server_info_blocking">XEP-0191: ブロッキング コマンド</string> + <string name="server_info_roster_version">XEP-0237: 名簿バージョニング</string> + <string name="server_info_stream_management">XEP-0198: ストリーム管理</string> + <string name="server_info_pep">XEP-0163: 個人イベントプロトコル (アバター)</string> + <string name="server_info_available">利用可能</string> + <string name="server_info_unavailable">利用不可</string> + <string name="missing_public_keys">公開鍵の通知がありません</string> + <string name="last_seen_now">ちょうど今会いました</string> + <string name="last_seen_min">1 分前に会いました</string> + <string name="last_seen_mins">%d 分前に会いました</string> + <string name="last_seen_hour">1 時間前に会いました</string> + <string name="last_seen_hours">%d 時間前に会いました</string> + <string name="last_seen_day">1 日前に会いました</string> + <string name="last_seen_days">%d 日前に会いました</string> + <string name="never_seen">会ったことはありません</string> + <string name="install_openkeychain">暗号化されたメッセージ。復号化するには OpenKeychain をインストールしてください。</string> + <string name="unknown_otr_fingerprint">不明な OTR フィンガープリント</string> + <string name="openpgp_messages_found">OpenPGP 暗号化されたメッセージが見つかりました</string> + <string name="reception_failed">受信に失敗しました</string> + <string name="your_fingerprint">あなたのフィンガープリント</string> + <string name="otr_fingerprint">OTR フィンガープリント</string> + <string name="verify">検証</string> + <string name="decrypt">復号化</string> + <string name="conferences">会議</string> + <string name="search">検索</string> + <string name="create_contact">連絡先を作成</string> + <string name="join_conference">会議に参加</string> + <string name="delete_contact">連絡先を削除</string> + <string name="view_contact_details">連絡先の詳細を表示</string> + <string name="block_contact">連絡先をブロック</string> + <string name="unblock_contact">連絡先のブロックを解除</string> + <string name="create">作成</string> + <string name="contact_already_exists">連絡先はすでに存在します</string> + <string name="join">参加</string> + <string name="conference_address">会議アドレス</string> + <string name="conference_address_example">room@conference.example.com</string> + <string name="save_as_bookmark">ブックマークとして保存</string> + <string name="delete_bookmark">ブックマークを削除</string> + <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> + <string name="contact_has_read_up_to_this_point">%s はここまで読みました</string> + <string name="publish">公開</string> + <string name="touch_to_choose_picture">アバターをタッチしてギャラリーから画像を選択します</string> + <string name="publish_avatar_explanation">ご注意ください: あなたの参加アップデートを登録している人は、誰でもこの絵を見ることができます。</string> + <string name="publishing">公開中…</string> + <string name="error_publish_avatar_server_reject">サーバーがあなたの公開を拒否しました</string> + <string name="error_publish_avatar_converting">写真の変換中に、何か問題が発生しました</string> + <string name="error_saving_avatar">ディスクにアバターを保存できませんでした</string> + <string name="or_long_press_for_default">(または長押しするとデフォルトに戻します)</string> + <string name="error_publish_avatar_no_server_support">ご利用のサーバーは、アバターの公開をサポートしていません</string> + <string name="private_message">ささやいた</string> + <string name="private_message_to">%s に</string> + <string name="send_private_message_to">プライベートメッセージを %s に送信</string> + <string name="connect">接続</string> + <string name="account_already_exists">このアカウントはすでに存在します</string> + <string name="next">次へ</string> + <string name="server_info_session_established">現在のセッションが確立</string> + <string name="additional_information">追加情報</string> + <string name="skip">スキップ</string> + <string name="disable_notifications">通知を無効にする</string> + <string name="disable_notifications_for_this_conversation">この会話の通知を無効にします</string> + <string name="notifications_disabled">通知を無効にしました</string> + <string name="enable">有効</string> + <string name="conference_requires_password">会議はパスワードが必要です</string> + <string name="enter_password">パスワードを入力してください</string> + <string name="missing_presence_updates">連絡先からの参加アップデートがありません</string> + <string name="request_presence_updates">最初に連絡先から参加アップデートを要求してください。\n\n<small>これは、連絡先が何のクライアントを使用しているかを決めるために使用されます。</small></string> + <string name="request_now">今すぐ要求</string> + <string name="delete_fingerprint">フィンガープリントを削除</string> + <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_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_summary">ご利用は注意してください</string> + <string name="title_activity_about">Conversations について</string> + <string name="pref_about_conversations_summary">ビルドおよびライセンス情報</string> + <string name="title_pref_quiet_hours">消音時間</string> + <string name="title_pref_quiet_hours_start_time">開始時間</string> + <string name="title_pref_quiet_hours_end_time">終了時間</string> + <string name="title_pref_enable_quiet_hours">消音時間を有効にする</string> + <string name="pref_quiet_hours_summary">消音時間の間、通知は無音になります</string> + <string name="pref_use_larger_font">フォントサイズを拡大</string> + <string name="pref_use_larger_font_summary">アプリ全体にわたって大きなフォントサイズを使用します</string> + <string name="pref_use_send_button_to_indicate_status">送信ボタンにステータスを示す</string> + <string name="pref_use_indicate_received">要求メッセージの受信</string> + <string name="pref_use_indicate_received_summary">サポートされている場合、受信したメッセージに緑色のチェックマークが付きます</string> + <string name="pref_use_send_button_to_indicate_status_summary">送信ボタンに連絡先ステータスを示す色が付きます</string> + <string name="pref_expert_options_other">その他</string> + <string name="pref_conference_name">会議名</string> + <string name="pref_conference_name_summary">会議を識別するために JID の代わりにルームのテーマを使用します</string> + <string name="toast_message_otr_fingerprint">OTR フィンガープリントをクリップボードにコピーしました!</string> + <string name="conference_banned">あなたはこの会議から禁止されています</string> + <string name="conference_members_only">この会議はメンバーのみです</string> + <string name="conference_kicked">あなたはこの会議からキックされました</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="message_options">メッセージオプション</string> + <string name="copy_text">テキストをコピー</string> + <string name="copy_original_url">元の URL をコピー</string> + <string name="send_again">再送</string> + <string name="file_url">ファイル URL</string> + <string name="message_text">メッセージテキスト</string> + <string name="url_copied_to_clipboard">URL をクリップボードにコピーしました</string> + <string name="message_copied_to_clipboard">メッセージをクリップボードにコピーしました</string> + <string name="image_transmission_failed">画像の転送に失敗しました</string> + <string name="scan_qr_code">QR コードをスキャン</string> + <string name="show_qr_code">QR コードを表示</string> + <string name="show_block_list">ブロックリストを表示</string> + <string name="account_details">アカウントの詳細</string> + <string name="verify_otr">OTR を検証</string> + <string name="remote_fingerprint">リモート フィンガープリント</string> + <string name="scan">スキャン</string> + <string name="or_touch_phones">(または電話をタッチ)</string> + <string name="smp">ソーシャリスト ミリオネア プロトコル</string> + <string name="shared_secret_hint">ヒントまたは質問</string> + <string name="shared_secret_secret">共有の秘密</string> + <string name="confirm">確認</string> + <string name="in_progress">実行中</string> + <string name="respond">応答</string> + <string name="failed">失敗しました</string> + <string name="secrets_do_not_match">秘密が一致しません</string> + <string name="try_again">再度実行してください</string> + <string name="finish">完了</string> + <string name="verified">検証しました!</string> + <string name="smp_requested">連絡先が SMP 検証を要求しました</string> + <string name="no_otr_session_found">有効な OTR セッションが見つかりません!</string> + <string name="conversations_foreground_service">Conversations</string> + <string name="pref_keep_foreground_service">サービスをフォアグラウンドに保持</string> + <string name="pref_keep_foreground_service_summary">オペレーティングシステムが接続を切断するのを防止します</string> + <string name="choose_file">ファイルの選択</string> + <string name="receiving_x_file">%1$s 受信中 (%2$d%% 完了)</string> + <string name="download_x_file">%s のダウンロード</string> + <string name="file">ファイル</string> + <string name="open_x_file">%s を開く</string> + <string name="sending_file">送信中 (%1$d%% 完了)</string> + <string name="preparing_file">転送用ファイルの準備中</string> + <string name="x_file_offered_for_download">%s ダウンロード依頼中</string> + <string name="cancel_transmission">転送をキャンセル</string> + <string name="file_transmission_failed">ファイル転送に失敗しました</string> + <string name="file_deleted">ファイルを削除しました</string> + <string name="no_application_found_to_open_file">ファイルを開くアプリケーションが見つかりません</string> + <string name="could_not_verify_fingerprint">フィンガープリントを検証できません</string> + <string name="manually_verify">手動で検証</string> + <string name="are_you_sure_verify_fingerprint">連絡先の OTR フィンガープリントを検証してもよろしいですか?</string> + <string name="pref_show_dynamic_tags">ダイナミック タグを表示</string> + <string name="pref_show_dynamic_tags_summary">連絡先の下に、読み取り専用タグを表示します</string> + <string name="enable_notifications">通知を有効にする</string> + <string name="conference_with">…と会議を作成</string> + <string name="no_conference_server_found">会議サーバーが見つかりません</string> + <string name="conference_creation_failed">会議の作成に失敗しました!</string> + <string name="conference_created">会議を作成しました!</string> + <string name="secret_accepted">秘密を受取ました!</string> + <string name="reset">リセット</string> + <string name="account_image_description">アカウント アバター</string> + <string name="copy_otr_clipboard_description">OTR フィンガープリントをクリップボードにコピーしました</string> + <string name="fetching_history_from_server">サーバーから履歴を取得中</string> + <string name="no_more_history_on_server">サーバーにこれ以上履歴はありません</string> + <string name="updating">アップデート中…</string> + <string name="password_changed">パスワードを変更しました!</string> + <string name="could_not_change_password">パスワードを変更できません</string> + <string name="otr_session_not_started">メッセージを送信して、暗号化されたチャットを開始します</string> + <string name="ask_question">質問をたずねる</string> + <string name="smp_explain_question">あなたと連絡先が、(内輪の冗談や、最後に会って食べた昼食などの) 他の誰も知らない共通の秘密を持っている場合、お互いのフィンガープリントを検証するために、その秘密を使用することができます。\n\nあなたはヒントまたは質問を提供して、連絡先は大文字と小文字を区別した答えで応答します。</string> + <string name="smp_explain_answer">連絡先は共有の秘密であなたにチャレンジして、あなたのフィンガープリントを検証したいと思います。連絡先は秘密に対して、次のヒントまたは質問を提供しました。</string> + <string name="shared_secret_hint_should_not_be_empty">あなたのヒントは空にできません。</string> + <string name="shared_secret_can_not_be_empty">あなたの共有の秘密は空にできません。</string> + <string name="manual_verification_explanation">慎重に、連絡先のフィンガープリントと、以下に表示されるフィンガープリントを比較してください。\nこれを交換するためには、暗号化されたメールや、電話のような信頼できる形式の通信を使用することができます。</string> + <string name="change_password">パスワードの変更</string> + <string name="current_password">現在のパスワード</string> + <string name="new_password">新しいパスワード</string> + <string name="password_should_not_be_empty">パスワードは空にできません</string> + <string name="enable_all_accounts">すべてのアカウントを有効にする</string> + <string name="disable_all_accounts">すべてのアカウントを無効にする</string> + <string name="perform_action_with">アクションを実行...</string> + <string name="no_affiliation">所属なし</string> + <string name="no_role">役割なし</string> + <string name="outcast">追放</string> + <string name="member">メンバー</string> + <string name="advanced_mode">詳細モード</string> + <string name="grant_membership">メンバーシップを付与</string> + <string name="remove_membership">メンバーシップを取消</string> + <string name="grant_admin_privileges">管理者権限を付与</string> + <string name="remove_admin_privileges">管理者権限を取消</string> + <string name="remove_from_room">会議から削除</string> + <string name="could_not_change_affiliation">%s の所属を変更できません</string> + <string name="ban_from_conference">会議から禁止</string> + <string name="removing_from_public_conference">公開の会議から %s を削除しようとしています。そのための唯一の方法は、永久にそのユーザーを禁止することです。</string> + <string name="ban_now">今すぐ禁止</string> + <string name="could_not_change_role">%s の役割を変更できません</string> + <string name="public_conference">公開アクセス可能な会議</string> + <string name="private_conference">プライベート、メンバーのみの会議</string> + <string name="conference_options">会議オプション</string> + <string name="members_only">プライベート (メンバーのみ)</string> + <string name="non_anonymous">匿名でない</string> + <string name="modified_conference_options">会議オプションを変更しました!</string> + <string name="could_not_modify_conference_options">会議オプションを変更できません</string> + <string name="never">なし</string> + <string name="thirty_minutes">30 分</string> + <string name="one_hour">1 時間</string> + <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_enter_is_send">Enter は送信</string> + <string name="pref_enter_is_send_summary">Enter キーをメッセージの送信に使用します</string> + <string name="pref_display_enter_key">Enter キーを表示</string> + <string name="pref_display_enter_key_summary">絵文字キーを Enter キーに変更</string> + <string name="audio">オーディオ</string> + <string name="video">ビデオ</string> + <string name="image">画像</string> + <string name="pdf_document">PDF 文書</string> + <string name="apk">Android アプリ</string> + <string name="vcard">連絡先</string> + <string name="received_x_file">%s を受信しました</string> + <string name="disable_foreground_service">フォアグラウンド サービスを無効にする</string> + <string name="touch_to_open_conversations">タッチして Conversations を開く</string> + <string name="avatar_has_been_published">アバターを公開しました!</string> + <string name="sending_x_file">%s の送信中</string> + <string name="offering_x_file">%s の依頼中</string> + <string name="hide_offline">オフラインを非表示にする</string> + <string name="disable_account">アカウントを無効にする</string> + <string name="contact_is_typing">%s は入力中...</string> + <string name="contact_has_stopped_typing">%s は入力を停止しました</string> + <string name="pref_chat_states">入力中通知</string> + <string name="pref_chat_states_summary">あなたが新しいメッセージを書いている時に、連絡先に知らせます</string> + <string name="send_location">位置を送信</string> + <string name="show_location">位置を表示</string> + <string name="no_application_found_to_display_location">位置を表示するアプリケーションが見つかりません</string> + <string name="location">位置</string> + <string name="received_location">位置を受信しました</string> + <string name="title_undo_swipe_out_conversation">会話が閉じられました</string> + <string name="title_undo_swipe_out_muc">退出した会話</string> + <string name="pref_certificate_options">証明書オプション</string> + <string name="pref_dont_trust_system_cas_title">システムの CA を信頼しない</string> + <string name="pref_dont_trust_system_cas_summary">すべての証明書を手動で承認する必要があります</string> + <string name="pref_remove_trusted_certificates_title">証明書を削除</string> + <string name="pref_remove_trusted_certificates_summary">手動で承認された証明書を削除します</string> + <string name="toast_no_trusted_certs">手動で承認された証明書はありません</string> + <string name="dialog_manage_certs_title">証明書を削除</string> + <string name="dialog_manage_certs_positivebutton">選択を削除</string> + <string name="dialog_manage_certs_negativebutton">キャンセル</string> + <plurals name="toast_delete_certificates"> + <item quantity="other">%d 証明書を削除しました</item> + </plurals> + <plurals name="select_contact"> + <item quantity="other">%d 連絡先を選択</item> + </plurals> + <string name="pref_quick_action_summary">送信ボタンをクイックアクションで置き換えます</string> + <string name="pref_quick_action">クイックアクション</string> + <string name="none">なし</string> + <string name="recently_used">最近使用した</string> + <string name="choose_quick_action">クイックアクションの選択</string> + <string name="file_not_found_on_remote_host">リモートサーバーにファイルが見つかりません</string> + <string name="search_for_contacts_or_groups">連絡先またはグループの検索</string> +</resources> diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml new file mode 100644 index 00000000..cdc3737a --- /dev/null +++ b/src/main/res/values-ko/strings.xml @@ -0,0 +1,447 @@ +<?xml version='1.0' encoding='UTF-8'?> +<resources> + <string name="action_settings">설정</string> + <string name="action_add">새 대화</string> + <string name="action_accounts">계정 </string> + <string name="action_end_conversation">이 대화 </string> + <string name="action_contact_details">연락처 정보</string> + <string name="action_muc_details">회의 정보</string> + <string name="action_secure">안전한 대화 </string> + <string name="action_add_account">계정 추가 </string> + <string name="action_edit_contact">이름 편집 </string> + <string name="action_add_phone_book">주소록에 추가 </string> + <string name="action_delete_contact">명단에서 삭제 </string> + <string name="action_block_contact">연락처 </string> + <string name="action_unblock_contact">연락처 차단 해제 </string> + <string name="action_block_domain">도메인 차단 </string> + <string name="action_unblock_domain">도메인 차단 해제 </string> + <string name="title_activity_manage_accounts">계정 관리 </string> + <string name="title_activity_settings">설정 </string> + <string name="title_activity_conference_details">회의 정보 </string> + <string name="title_activity_contact_details">연락처 정보 </string> + <string name="title_activity_sharewith">대화 공유 </string> + <string name="title_activity_start_conversation">대화 시작 </string> + <string name="title_activity_choose_contact">연락처 선택 </string> + <string name="title_activity_block_list">목록 차단 </string> + <string name="just_now">방금 </string> + <string name="minute_ago">1분 전 </string> + <string name="minutes_ago">%d 분 전 </string> + <string name="unread_conversations">읽지 않은 대화 </string> + <string name="sending">보내는중... </string> + <string name="encrypted_message">메세지 복호화중입니다. 기다리세요...</string> + <string name="nick_in_use">사용중인 별명입니다 </string> + <string name="admin">관리자 </string> + <string name="owner">소유자 </string> + <string name="moderator">중재자 </string> + <string name="participant">참가자 </string> + <string name="visitor">방문자 </string> + <string name="remove_contact_text">%s 를 당신의 명단에서 삭제하시겠습니까? 이 연락처와 연관된 대화는 삭제되지 않습니다. </string> + <string name="block_contact_text">%s 이(가) 당신에게 메세지를 보내지 못하도록 차단할까요? </string> + <string name="unblock_contact_text">%s 로부터 메세지를 받을 수 있도록 차단을 해제할까요? </string> + <string name="block_domain_text">%s 의 모든 연락처를 차단할까요? </string> + <string name="unblock_domain_text">%s 의 모든 연락처를 차단 해제할까요? </string> + <string name="contact_blocked">연락처 차단됨 </string> + <string name="remove_bookmark_text">%s 를 즐겨찾기에서 삭제할까요? 이 즐겨찾기와 연관된 대화는 삭제되지 않습니다. </string> + <string name="register_account">서버에서 새 계정을 등록 </string> + <string name="change_password_on_server">서버에서 비밀번호 변경 </string> + <string name="share_with">공유 </string> + <string name="start_conversation">대화 시작 </string> + <string name="invite_contact">연락처 초대 </string> + <string name="contacts">연락처 </string> + <string name="cancel">취소 </string> + <string name="set">설정 </string> + <string name="add">추가 </string> + <string name="edit">편집 </string> + <string name="delete">삭제 </string> + <string name="block">차단 </string> + <string name="unblock">차단 해제 </string> + <string name="save">저장 </string> + <string name="ok">확인 </string> + <string name="crash_report_title">Conversations가 작동 중지되었습니다 </string> + <string name="crash_report_message">Stack trace 정보를 보냄으로서 Conversations의 개발에 기여할 수 있습니다. 경고: 이것은 Stack trace 정보를 개발자에게 보내기 위해 당신의 XMPP 계정을 사용할 것입니다. </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="touch_to_fix">여기를 선택해 당신의 계정을 관리하세요 </string> + <string name="attach_file">파일 첨부 </string> + <string name="not_in_roster">연락처가 당신의 명단에 없습니다. 추가하시겠습니까? </string> + <string name="add_contact">연락처 추가 </string> + <string name="send_failed">전송 실패 </string> + <string name="send_rejected">거부됨 </string> + <string name="preparing_image">이미지 전송 준비중 </string> + <string name="action_clear_history">기록 삭제 </string> + <string name="clear_conversation_history">대화 기록 삭제 </string> + <string name="clear_histor_msg">이 대화의 모든 메세지를 삭제하시겠습니까? 경고: 이것은 다른 기기나 서버에 있는 메세지에는 영향을 미치지 않습니다. </string> + <string name="delete_messages">메세지 삭제 </string> + <string name="also_end_conversation">나중에 이 대화 끝내기 </string> + <string name="choose_presence">연락할 프레즌스 선택 </string> + <string name="send_plain_text_message">평문 메세지 전송 </string> + <string name="send_otr_message">OTR 암호화된 메세지 전송 </string> + <string name="send_pgp_message">OpenPGP 암호화된 메세지 전송 </string> + <string name="your_nick_has_been_changed">닉네임이 변경되었습니다 </string> + <string name="send_unencrypted">암호화하지 않고 전송 </string> + <string name="decryption_failed">복호화 실패. 올바른 개인 키를 가지고 있지 않은 것 같습니다. </string> + <string name="openkeychain_required">OpenKeychain </string> + <string name="openkeychain_required_long">Conversations는 메세지를 암호화 및 복호화하고 공개 키를 관리하기 위해 OpenKeychain이라는 제 3자 앱을 활용합니다. OpenKeychain은 GPLv3 라이센스를 사용하며 F-Droid와 Google Play에서 구하실 수 있습니다. (이후 Conversations를 재시작하세요) </string> + <string name="restart">재시작 </string> + <string name="install">설치 </string> + <string name="offering">제공중... </string> + <string name="waiting">대기중... </string> + <string name="no_pgp_key">OpenPGP 키가 발견되지 않음 </string> + <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">암호화된 메세지 수신됨. 터치해서 복호화 및 열람하세요. </string> + <string name="pref_general">일반 </string> + <string name="pref_xmpp_resource">XMPP 자원 </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_notifications">알림 </string> + <string name="pref_notifications_summary">새 메세지 도착시 알림 </string> + <string name="pref_vibrate">진동 </string> + <string name="pref_vibrate_summary">새 메세지 도착시 진동 </string> + <string name="pref_sound">소리 </string> + <string name="pref_sound_summary">알림과 동시에 벨소리 재생 </string> + <string name="pref_conference_notifications">회의 알림 </string> + <string name="pref_conference_notifications_summary">새 회의 메세지가 도착시, 강조됐을 때 뿐만 아니라 항상 알림 </string> + <string name="pref_notification_grace_period">알림 유예 </string> + <string name="pref_notification_grace_period_summary">Carbon Copy 수신 후에 잠시동안 알림 해제</string> + <string name="pref_advanced_options">추가 설정 </string> + <string name="pref_never_send_crash">충돌 보고서 보내지 않음 </string> + <string name="pref_never_send_crash_summary">Stack trace 정보를 보냄으로서 Conversations의 개발에 기여할 수 있습니다 </string> + <string name="pref_confirm_messages">메세지 확인 </string> + <string name="pref_confirm_messages_summary">메세지를 수신하고 읽었는지를 연락처에게 알려줌 </string> + <string name="pref_ui_options">사용자 환경 설정 </string> + <string name="openpgp_error">OpenKeychain이 오류를 보고합니다 </string> + <string name="error_decrypting_file">파일 복호화 입출력 오류 </string> + <string name="accept">수락 </string> + <string name="error">오류가 발생했습니다 </string> + <string name="pref_grant_presence_updates">프레즌스 업데이트 허가 </string> + <string name="pref_grant_presence_updates_summary">당신이 추가한 연락처의 프레즌스 구독을 선제적으로 허가 및 요청함 </string> + <string name="subscriptions">구독 </string> + <string name="your_account">당신의 계정 </string> + <string name="keys">키 </string> + <string name="send_presence_updates">프레즌스 업데이트 보내기 </string> + <string name="receive_presence_updates">프레즌스 업데이트 받기 </string> + <string name="ask_for_presence_updates">프레즌스 업데이트 요청 </string> + <string name="attach_choose_picture">사진 선택 </string> + <string name="attach_take_picture">사진 찍기 </string> + <string name="preemptively_grant">구독 요청을 선제적으로 허가 </string> + <string name="error_not_an_image_file">선택한 파일은 이미지가 아닙니다 </string> + <string name="error_compressing_image">이미지 파일 변환 중 오류 발생 </string> + <string name="error_file_not_found">파일을 찾을 수 없음 </string> + <string name="error_io_exception">일반 입출력 오류. 저장소 공간이 부족한 것 같습니다.</string> + <string name="error_security_exception_during_image_copy">이 이미지를 선택하기 위해 사용한 앱이 이 파일을 읽는데 필요한 충분한 허가를 제공하지 않았습니다. 다른 파일 탐색기를 이용해 이미지를 선택하세요. </string> + <string name="account_status_unknown">알 수 없음 </string> + <string name="account_status_disabled">임시로 해제 </string> + <string name="account_status_online">접속중 </string> + <string name="account_status_connecting">접속중\u2026</string> + <string name="account_status_offline">오프라인 </string> + <string name="account_status_unauthorized">승인되지 않음 </string> + <string name="account_status_not_found">서버를 찾을 수 없음 </string> + <string name="account_status_no_internet">접속할 수 없음 </string> + <string name="account_status_regis_fail">등록 실패 </string> + <string name="account_status_regis_conflict">사용자 이름이 이미 사용중입니다 </string> + <string name="account_status_regis_success">등록 성공 </string> + <string name="account_status_regis_not_sup">서버가 등록을 지원하지 않습니다</string> + <string name="account_status_security_error">보안 오류 </string> + <string name="account_status_incompatible_server">호환되지 않는 서버 </string> + <string name="encryption_choice_none">평문 </string> + <string name="encryption_choice_otr">OTR</string> + <string name="encryption_choice_pgp">OpenPGP </string> + <string name="mgmt_account_edit">계정 편집 </string> + <string name="mgmt_account_delete">계정 삭제 </string> + <string name="mgmt_account_disable">임시로 해제</string> + <string name="mgmt_account_publish_avatar">아바타 공개 </string> + <string name="mgmt_account_publish_pgp">OpenPGP 공개 키 공개 </string> + <string name="mgmt_account_enable">계정 사용 </string> + <string name="mgmt_account_are_you_sure">확실합니까? </string> + <string name="mgmt_account_delete_confirm_text">계정을 삭제하면 당신의 모든 대화 기록이 사라집니다 </string> + <string name="attach_record_voice">녹음 </string> + <string name="account_settings_jabber_id">Jabber ID</string> + <string name="account_settings_password">비밀번호 </string> + <string name="account_settings_example_jabber_id">username@example.com</string> + <string name="account_settings_confirm_password">암호 확인 </string> + <string name="password">암호 </string> + <string name="confirm_password">암호 확인 </string> + <string name="passwords_do_not_match">암호가 일치하지 않습니다 </string> + <string name="invalid_jid">올바른 Jabber ID가 아닙니다 </string> + <string name="error_out_of_memory">메모리 부족. 이미지 용량이 너무 큽니다 </string> + <string name="add_phone_book_text">%s를 기기의 연락처 목록에 추가하시겠습니까? </string> + <string name="contact_status_online">접속중 </string> + <string name="contact_status_free_to_chat">대화 가능 </string> + <string name="contact_status_away">자리 비움 </string> + <string name="contact_status_extended_away">장기간 비움 </string> + <string name="contact_status_do_not_disturb">방해 금지 </string> + <string name="contact_status_offline">오프라인 </string> + <string name="muc_details_conference">회의 </string> + <string name="muc_details_other_members">다른 멤버 </string> + <string name="server_info_show_more">서버 정보 </string> + <string name="server_info_mam">XEP-0313: MAM </string> + <string name="server_info_carbon_messages">XEP-0280: Message Carbons </string> + <string name="server_info_csi">XEP-0352: Client State Indication </string> + <string name="server_info_blocking">XEP-0191: Blocking Command </string> + <string name="server_info_roster_version">XEP-0237: Roster Versioning </string> + <string name="server_info_stream_management">XEP-0198: Stream Management </string> + <string name="server_info_pep">XEP-0163: PEP (Avatars) </string> + <string name="server_info_available">가능 </string> + <string name="server_info_unavailable">불가 </string> + <string name="missing_public_keys">공개 키 선언 누락 </string> + <string name="last_seen_now">방금 전까지 접속했었음 </string> + <string name="last_seen_min">1분 전까지 접속했었음 </string> + <string name="last_seen_mins">%d 분 전까지 접속했었음 </string> + <string name="last_seen_hour">1시간 전까지 접속했었음 </string> + <string name="last_seen_hours">%d 시간 전까지 접속했었음 </string> + <string name="last_seen_day">1일 전까지 접속했었음 </string> + <string name="last_seen_days">%d 일 전까지 접속했었음 </string> + <string name="never_seen">접속한적 없음 </string> + <string name="install_openkeychain">암호화된 메세지. 복호화하기 위해 OpenKeychain을 설치하세요. </string> + <string name="unknown_otr_fingerprint">알 수 없는 OTR 지문 </string> + <string name="openpgp_messages_found">OpenPGP 암호화 메세지 발견 </string> + <string name="reception_failed">접수 실패 </string> + <string name="your_fingerprint">당신의 지문 </string> + <string name="otr_fingerprint">OTR 지문 </string> + <string name="verify">검증 </string> + <string name="decrypt">복호화 </string> + <string name="conferences">회의 </string> + <string name="search">검색 </string> + <string name="create_contact">연락처 생성 </string> + <string name="join_conference">회의 참석 </string> + <string name="delete_contact">연락처 삭제 </string> + <string name="view_contact_details">연락처 정보 보기 </string> + <string name="block_contact">연락처 차단 </string> + <string name="unblock_contact">연락처 차단 해제 </string> + <string name="create">만들기 </string> + <string name="contact_already_exists">이미 존재하는 연락처입니다 </string> + <string name="join">참석 </string> + <string name="conference_address">회의 주소 </string> + <string name="conference_address_example">room@conference.example.com</string> + <string name="save_as_bookmark">즐겨찾기로 저장 </string> + <string name="delete_bookmark">즐겨찾기 삭제 </string> + <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> + <string name="contact_has_read_up_to_this_point">%s 가 여기까지 읽었습니다 </string> + <string name="publish">공개 </string> + <string name="touch_to_choose_picture">갤러리에서 사진을 선택하기 위해 아바타를 터치하세요 </string> + <string name="publish_avatar_explanation">참고하세요: 당신의 프레즌스 업데이트를 구독한 모든 사람들은 이 사진을 볼 수 있습니다 </string> + <string name="publishing">공개중... </string> + <string name="error_publish_avatar_server_reject">서버가 당신의 발표를 거부했습니다 </string> + <string name="error_publish_avatar_converting">사진을 변환하는 중 오류가 발생했습니다 </string> + <string name="error_saving_avatar">아바타를 저장할 수 없습니다 </string> + <string name="or_long_press_for_default">(혹은 기본값을 되돌리기 위해 길게 누름) </string> + <string name="error_publish_avatar_no_server_support">서버가 아바타 발표를 지원하지 않습니다 </string> + <string name="private_message">속삭임 </string> + <string name="private_message_to">%s 에게 </string> + <string name="send_private_message_to">%s 에게 개인 매세지 보내기 </string> + <string name="connect">접속 </string> + <string name="account_already_exists">계정이 이미 존재합니다 </string> + <string name="next">다음 </string> + <string name="server_info_session_established">현재 세션이 수립되었습니다 </string> + <string name="additional_information">추가 정보 </string> + <string name="skip">건너뛰기 </string> + <string name="disable_notifications">알림 해제 </string> + <string name="disable_notifications_for_this_conversation">이 대화의 알림 해제 </string> + <string name="notifications_disabled">알림이 해제되었습니다 </string> + <string name="enable">사용 </string> + <string name="conference_requires_password">회의에 암호가 필요합니다 </string> + <string name="enter_password">암호 입력 </string> + <string name="missing_presence_updates">연락처로부터 프레즌스 업데이트 찾을 수 없음 </string> + <string name="request_presence_updates">먼저 연락처로부터 프레즌스 업데이트를 요청하세요. 이는 당신의 연락처가 어떤 클라이언트를 사용하는지 결정하는 데 사용됩니다. </string> + <string name="request_now">지금 요청 </string> + <string name="delete_fingerprint">지문 삭제 </string> + <string name="sure_delete_fingerprint">이 지문을 삭제하시겠습니까? </string> + <string name="ignore">무시 </string> + <string name="without_mutual_presence_updates">경고: 상호간의 프레즌스 업데이트 없이 이것을 보내면 예기치 못한 문제를 발생시킬 수 있습니다. 당신의 프레즌스 구독을 검증하기 위해 연락처 상세 정보로 가세요. </string> + <string name="pref_encryption_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_summary">설정시 주의하시기 바랍니다</string> + <string name="title_activity_about">Conversations에 대해서 </string> + <string name="pref_about_conversations_summary">빌드 및 라이센스 정보 </string> + <string name="title_pref_quiet_hours">무음 시간대 </string> + <string name="title_pref_quiet_hours_start_time">시작 시간 </string> + <string name="title_pref_quiet_hours_end_time">마감 시간 </string> + <string name="title_pref_enable_quiet_hours">무음 시간대 사용 </string> + <string name="pref_quiet_hours_summary">무음 시간대에는 알림이 해제됩니다 </string> + <string name="pref_use_larger_font">글자 크기 증가 </string> + <string name="pref_use_larger_font_summary">앱 전반에 큰 글자 크기를 사용합니다 </string> + <string name="pref_use_send_button_to_indicate_status">전송 버튼이 상태를 나타냄</string> + <string name="pref_use_indicate_received">메세지 영수증 요청</string> + <string name="pref_use_indicate_received_summary">만약 지원될 경우, 수신된 메세지는 초록색 기호로 표시됩니다. </string> + <string name="pref_use_send_button_to_indicate_status_summary">연락처 상태를 표시하기 위해 전송 버튼을 색칠함 </string> + <string name="pref_expert_options_other">기타 </string> + <string name="pref_conference_name">회의 이름 </string> + <string name="pref_conference_name_summary">회의를 식별하기 위해 JID 대신 방 제목을 사용 </string> + <string name="toast_message_otr_fingerprint">OTR 지문이 클립보드에 복사되었습니다 </string> + <string name="conference_banned">당신은 이 회의에서 금지되었습니다 </string> + <string name="conference_members_only">이 회의는 멤버 전용입니다 </string> + <string name="conference_kicked">당신은 이 회의에서 추방되었습니다 </string> + <string name="using_account">using account %s</string> + <string name="not_connected_try_again">접속중이 아닙니다. 다시 시도하세요. </string> + <string name="message_options">메세지 설정 </string> + <string name="copy_text">텍스트 복사 </string> + <string name="copy_original_url">원본 URL 복사 </string> + <string name="send_again">다시 보내기 </string> + <string name="message_text">메세지 텍스트 </string> + <string name="url_copied_to_clipboard">URL이 클립보드에 복사되었습니다 </string> + <string name="message_copied_to_clipboard">메세지가 클립보드에 복사되었습니다 </string> + <string name="image_transmission_failed">이미지 전송 실패 </string> + <string name="scan_qr_code">QR코드 스캔 </string> + <string name="show_qr_code">QR코드 보기 </string> + <string name="show_block_list">차단 목록 보기 </string> + <string name="account_details">계정 정보 </string> + <string name="verify_otr">OTR 검증 </string> + <string name="remote_fingerprint">Remote Fingerprint</string> + <string name="scan">스캔 </string> + <string name="or_touch_phones">(혹은 기기 터치) </string> + <string name="smp">Socialist Millionaire Protocol</string> + <string name="shared_secret_hint">힌트 혹은 질문 </string> + <string name="shared_secret_secret">공유된 비밀 </string> + <string name="confirm">확인 </string> + <string name="in_progress">진행중 </string> + <string name="respond">응답 </string> + <string name="failed">실패 </string> + <string name="secrets_do_not_match">비밀이 일치하지 않습니다 </string> + <string name="try_again">다시 시도하세요 </string> + <string name="finish">완료 </string> + <string name="verified">검증 완료 </string> + <string name="smp_requested">연락처가 SMP 검증을 요구했습니다 </string> + <string name="no_otr_session_found">올바른 OTR 세션이 발견되지 않았습니다 </string> + <string name="conversations_foreground_service">Conversations</string> + <string name="pref_keep_foreground_service">포어그라운드에서 서비스 유지 </string> + <string name="pref_keep_foreground_service_summary">운영체제가 접속을 해제하지 못하도록 예방합니다 </string> + <string name="choose_file">파일 선택 </string> + <string name="receiving_x_file">수신중 %1$s (%2$d%% 완료) </string> + <string name="download_x_file">%s 다운로드 </string> + <string name="file">파일 </string> + <string name="open_x_file">%s 열기 </string> + <string name="sending_file">전송중 (%1$d%% 완료) </string> + <string name="preparing_file">파일 전송 준비중 </string> + <string name="x_file_offered_for_download">%s 다운로드 제공됨 </string> + <string name="cancel_transmission">전송 취소 </string> + <string name="file_transmission_failed">파일 전송 실패 </string> + <string name="file_deleted">파일이 삭제되었습니다 </string> + <string name="no_application_found_to_open_file">파일을 열기 위한 앱이 발견되지 않았습니다 </string> + <string name="could_not_verify_fingerprint">지문을 검증할 수 없습니다 </string> + <string name="manually_verify">수동 검증 </string> + <string name="are_you_sure_verify_fingerprint">연락처의 OTR 지문을 검증하시겠습니까? </string> + <string name="pref_show_dynamic_tags">동적 태그 표시 </string> + <string name="pref_show_dynamic_tags_summary">연락처 밑에 읽기 전용 태그 표시 </string> + <string name="enable_notifications">알림 사용 </string> + <string name="conference_with">회의 생성 </string> + <string name="no_conference_server_found">회의 서버가 발견되지 않았습니다 </string> + <string name="conference_creation_failed">회의 생성 실패 </string> + <string name="conference_created">회의 생성됨 </string> + <string name="secret_accepted">비밀 접수됨 </string> + <string name="reset">초기화 </string> + <string name="account_image_description">계정 아바타 </string> + <string name="copy_otr_clipboard_description">OTR 지문을 클립보드에 복사 </string> + <string name="fetching_history_from_server">서버로부터 기록 가져오는중 </string> + <string name="no_more_history_on_server">서버에 더이상 기록이 없습니다 </string> + <string name="updating">업데이트중...</string> + <string name="password_changed">암호 변경됨 </string> + <string name="could_not_change_password">암호를 변경할 수 없습니다 </string> + <string name="otr_session_not_started">암호화된 대화를 시작하기 위해 메세지 보내기 </string> + <string name="ask_question">질문하기 </string> + <string name="smp_explain_question">만약 당신과 당신의 연락처가 다른 사람은 모르는 비밀을 공유하고 있다면, 그 비밀을 서로의 지문을 검증하는 데 사용할 수 있습니다. 대소문자가 구분된 대답을 할 연락처에게 힌트나 질문을 주세요. </string> + <string name="smp_explain_answer">당신의 연락처는 당신의 지문을 검증하고자 공유된 비밀을 확인하려고 합니다. 당신의 연락처는 그 비밀에 관한 다음과 같은 힌트 혹은 질문을 제공했습니다. </string> + <string name="shared_secret_hint_should_not_be_empty">힌트를 반드시 입력해야 합니다 </string> + <string name="shared_secret_can_not_be_empty">공유된 비밀을 반드시 입력해야 합니다 </string> + <string name="manual_verification_explanation">아래에 보이는 지문을 당신의 연락처의 지문과 세심하게 비교하세요. 당신은 암호화된 이메일이나 전화와 같은 믿을만한 통신수단으로 이것을 주고 받을 수 있습니다. </string> + <string name="change_password">암호 변경 </string> + <string name="current_password">현재 암호 </string> + <string name="new_password">새 암호 </string> + <string name="password_should_not_be_empty">암호를 반드시 입력해야 합니다 </string> + <string name="enable_all_accounts">모든 계정 사용 </string> + <string name="disable_all_accounts">모든 계정 해제 </string> + <string name="perform_action_with">다음으로 동작을 수행 </string> + <string name="no_affiliation">관련 없음 </string> + <string name="no_role">역할 없음 </string> + <string name="outcast">추방됨 </string> + <string name="member">멤버 </string> + <string name="advanced_mode">고급 모드 </string> + <string name="grant_membership">멤버십 허가 </string> + <string name="remove_membership">멤버십 철회 </string> + <string name="grant_admin_privileges">관리자 특권 허가 </string> + <string name="remove_admin_privileges">관리자 특권 철회 </string> + <string name="remove_from_room">회의에서 제거 </string> + <string name="could_not_change_affiliation">%s 의 관련 여부를 변경할 수 없습니다 </string> + <string name="ban_from_conference">회의에서 금지 </string> + <string name="removing_from_public_conference">%s 를 공개 회의에서 제거하려고 하고 있습니다. 유일한 방법은 이 사용자를 앞으로 금지시키는 것입니다. </string> + <string name="ban_now">지금 금지 </string> + <string name="could_not_change_role">%s 의 역할을 변경할 수 없습니다 </string> + <string name="public_conference">공개적으로 접근 가능한 회의 </string> + <string name="private_conference">멤버 전용 사설 회의 </string> + <string name="conference_options">회의 설정 </string> + <string name="members_only">사설 (멤버 전용) </string> + <string name="non_anonymous">익명 아님 </string> + <string name="modified_conference_options">회의 설정 변경됨 </string> + <string name="could_not_modify_conference_options">회의 설정을 변경할 수 없습니다 </string> + <string name="never">안함 </string> + <string name="thirty_minutes">30분 </string> + <string name="one_hour">1시간 </string> + <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_enter_is_send">엔터 키로 전송 </string> + <string name="pref_enter_is_send_summary">엔터 키로 메세지를 보냅니다 </string> + <string name="pref_display_enter_key">엔터 키 표시 </string> + <string name="pref_display_enter_key_summary">이모티콘 키를 엔터 키로 바꿉니다 </string> + <string name="audio">오디오 </string> + <string name="video">비디오 </string> + <string name="image">이미지 </string> + <string name="pdf_document">PDF 문서 </string> + <string name="apk">안드로이드 앱 </string> + <string name="vcard">연락처 </string> + <string name="received_x_file">%s 수신 </string> + <string name="disable_foreground_service">포어그라운드 서비스 해제 </string> + <string name="touch_to_open_conversations">터치해서 Conversations 열기 </string> + <string name="avatar_has_been_published">아바타가 공개되었습니다 </string> + <string name="sending_x_file">%s 전송중 </string> + <string name="offering_x_file">%s 제공중 </string> + <string name="hide_offline">오프라인 숨기기 </string> + <string name="disable_account">계정 해제 </string> + <string name="contact_is_typing">%s 이(가) 입력중입니다... </string> + <string name="contact_has_stopped_typing">%s 이(가) 입력을 중단했습니다 </string> + <string name="pref_chat_states">입력 알림 </string> + <string name="pref_chat_states_summary">새 메세지를 작성할 때 이를 연락처에게 알립니다 </string> + <string name="send_location">위치 전송 </string> + <string name="show_location">위치 표시 </string> + <string name="no_application_found_to_display_location">위치를 표시할 수 있는 앱이 발견되지 않았습니다 </string> + <string name="location">위치 </string> + <string name="received_location">위치 수신 </string> + <string name="title_undo_swipe_out_conversation">대화 끝남 </string> + <string name="title_undo_swipe_out_muc">회의에서 나감 </string> + <string name="pref_certificate_options">인증 설정 </string> + <string name="pref_dont_trust_system_cas_title">시스템 CA를 신뢰하지 않음 </string> + <string name="pref_dont_trust_system_cas_summary">모든 인증서는 수동으로 승인되어야 함 </string> + <string name="pref_remove_trusted_certificates_title">인증서 삭제 </string> + <string name="pref_remove_trusted_certificates_summary">수동으로 승인된 인증서 삭제 </string> + <string name="toast_no_trusted_certs">수동으로 승인된 인증서 없음 </string> + <string name="dialog_manage_certs_title">인증서 삭제 </string> + <string name="dialog_manage_certs_positivebutton">선택 삭제 </string> + <string name="dialog_manage_certs_negativebutton">취소 </string> + <plurals name="toast_delete_certificates"> + <item quantity="other">%d 인증서 삭제됨 </item> + </plurals> + <plurals name="select_contact"> + <item quantity="other">%d 연락처 선택 </item> + </plurals> + <string name="pref_quick_action_summary">전송 버튼을 빠른 동작 버튼으로 교체</string> + <string name="pref_quick_action">빠른 동작</string> + <string name="none">없음</string> + <string name="recently_used">최근 사용된 항목</string> + <string name="choose_quick_action">빠른 동작 선택</string> +</resources> diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index d8d13f81..2873a793 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -80,7 +80,6 @@ <string name="send_otr_message">Verstuur OTR versleuteld bericht</string> <string name="send_pgp_message">Verstuur OpenPGP versleuteld bericht</string> <string name="your_nick_has_been_changed">Je naam is veranderd</string> - <string name="download_image">Download afbeelding</string> <string name="send_unencrypted">Verstuur onversleuteld</string> <string name="decryption_failed">Ontsleutelen mislukt. Misschien heb je niet de juiste private sleutel.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -289,15 +288,13 @@ <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="using_account">account %s gebruiken</string> - <string name="checking_image">Afbeelding op HTTP host nakijken</string> - <string name="image_file_deleted">De afbeelding is verwijderd</string> <string name="not_connected_try_again">Je bent niet verbonden. Probeer later opnieuw</string> - <string name="check_image_filesize">Bekijk bestandsgrootte van afbeelding</string> + <string name="check_x_filesize">Bekijk bestandsgrootte van %s</string> <string name="message_options">Berichtopties</string> <string name="copy_text">Kopieer tekst</string> <string name="copy_original_url">Kopieer oorspronkelijke URL</string> <string name="send_again">Verstuur opnieuw</string> - <string name="image_url">AfbeeldingsURL</string> + <string name="file_url">Bestands-URL</string> <string name="message_text">Berichttekst</string> <string name="url_copied_to_clipboard">URL gekopieerd naar klembord</string> <string name="message_copied_to_clipboard">Bericht gekopieerd naar klembord</string> @@ -427,8 +424,29 @@ <string name="no_application_found_to_display_location">Geen applicatie gevonden om locatie weer te geven</string> <string name="location">Locatie</string> <string name="received_location">Locatie ontvangen</string> + <string name="title_undo_swipe_out_conversation">Gesprek gesloten</string> + <string name="title_undo_swipe_out_muc">Groepsgesprek verlaten</string> + <string name="pref_certificate_options">Certificaatopties</string> + <string name="pref_dont_trust_system_cas_title">Vertrouw geen systeem-CA\'s.</string> + <string name="pref_dont_trust_system_cas_summary">Alle certificaten moeten handmatig goedgekeurd worden</string> + <string name="pref_remove_trusted_certificates_title">Verwijder certificaten</string> + <string name="pref_remove_trusted_certificates_summary">Verwijder handmatig goedgekeurde certificaten</string> + <string name="toast_no_trusted_certs">Geen handmatig goedgekeurde certificaten</string> + <string name="dialog_manage_certs_title">Verwijder certificaten</string> + <string name="dialog_manage_certs_positivebutton">Verwijder selectie</string> + <string name="dialog_manage_certs_negativebutton">Annuleer</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">%d certificaat verwijderd</item> + <item quantity="other">%d certificaten verwijderd</item> + </plurals> <plurals name="select_contact"> <item quantity="one">Selecteer %d contact</item> <item quantity="other">Selecteer %d contacten</item> </plurals> + <string name="pref_quick_action_summary">Vervang verzendknop door snelle actie</string> + <string name="pref_quick_action">Snelle actie</string> + <string name="none">Geen</string> + <string name="recently_used">Recent gebruikt</string> + <string name="choose_quick_action">Kies snelle actie</string> + <string name="file_not_found_on_remote_host">Bestand niet gevonden op externe server</string> </resources> diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 58dea13b..ea2ecc52 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -5,8 +5,11 @@ <string name="action_accounts">Zarządzaj kontami</string> <string name="action_end_conversation">Zakończ konwersację</string> <string name="action_contact_details">Szczegóły kontaktu</string> - <string name="action_muc_details">Szczególy konferencji</string> + <string name="action_muc_details">Szczegóły konferencji</string> + <string name="action_secure">Konwersacja szyfrowana</string> <string name="action_add_account">Dodaj konto</string> + <string name="action_edit_contact">Edytuj nazwę</string> + <string name="action_add_phone_book">Dodaj do książki telefonicznej</string> <string name="action_delete_contact">Usuń z rostera</string> <string name="action_block_contact">Zablokuj kontakt</string> <string name="action_unblock_contact">Odblokuj kontakt</string> @@ -16,6 +19,7 @@ <string name="title_activity_settings">Ustawienia</string> <string name="title_activity_conference_details">Szczegóły konferencji</string> <string name="title_activity_contact_details">Szczegóły kontaktu</string> + <string name="title_activity_sharewith">Udostępnij w konwersacji</string> <string name="title_activity_start_conversation">Rozpocznij konwersację</string> <string name="title_activity_choose_contact">Wybierz kontakt</string> <string name="title_activity_block_list">Czarna lista</string> @@ -25,6 +29,7 @@ <string name="unread_conversations">nieprzeczytanych konwersacji</string> <string name="sending">wysyłanie...</string> <string name="encrypted_message">Deszyfrowanie wiadomości. Proszę czekać...</string> + <string name="nick_in_use">Nazwa jest już w użyciu</string> <string name="admin">Admin</string> <string name="owner">Właściciel</string> <string name="moderator">Moderator</string> @@ -36,12 +41,15 @@ <string name="block_domain_text">Zablokować wszystkie kontakty z %s?</string> <string name="unblock_domain_text">Odblokować wszystkie kontakty z %s?</string> <string name="contact_blocked">Kontakt zablokowany</string> + <string name="remove_bookmark_text">Czy usunąć zakładkę %s? Konwersacja powiązana z zakładką nie zostanie usunięta.</string> <string name="register_account">Zarejestruj nowe konto na serwerze</string> <string name="change_password_on_server">Zmień hasło na serwerze</string> + <string name="share_with">Udostępnij...</string> <string name="start_conversation">Rozpocznij konwersację</string> <string name="invite_contact">Zaproś kontakt</string> <string name="contacts">Kontakty</string> <string name="cancel">Anuluj</string> + <string name="set">Ustaw</string> <string name="add">Dodaj</string> <string name="edit">Edytuj</string> <string name="delete">Usuń</string> @@ -50,6 +58,7 @@ <string name="save">Zapisz</string> <string name="ok">Ok</string> <string name="crash_report_title">Conversations uległo awarii</string> + <string name="crash_report_message">Wysyłając ślady stosu pomagasz rozwijać Conversations\n<b>Uwaga:</b> Ślad stosu zostanie przesłany deweloperowi przy użyciu twojego konta XMPP.</string> <string name="send_now">Wyślij teraz</string> <string name="send_never">Nie pytaj ponownie</string> <string name="problem_connecting_to_account">Nie można połączyć się z kontem</string> @@ -60,30 +69,383 @@ <string name="add_contact">Dodaj kontakt</string> <string name="send_failed">wysyłanie nie powiodło się</string> <string name="send_rejected">odrzucono</string> + <string name="preparing_image">Przygotowywanie obrazu do wysłania</string> <string name="action_clear_history">Wyczyść historię</string> <string name="clear_conversation_history">Wyczyść historię konwersacji</string> + <string name="clear_histor_msg">Czy na pewno usunąć wszystkie wiadomości powiązane z konwersacją?\n\n<b>Uwaga:</b> Działanie nie wpływa na wiadomości przechowywane na innych urządzeniach lub serwerach.</string> <string name="delete_messages">Usuń wiadomości</string> + <string name="also_end_conversation">Zakończ konwersację po usunięciu historii</string> + <string name="choose_presence">Wybierz widoczność dla kontaktu</string> + <string name="send_plain_text_message">Wyślij wiadomość jawną</string> + <string name="send_otr_message">Wyślij zaszyfrowaną wiadomość (OTR)</string> + <string name="send_pgp_message">Wyślij zaszyfrowaną wiadomość (OpenPGP)</string> + <string name="your_nick_has_been_changed">Twoja nazwa została zmieniona</string> + <string name="send_unencrypted">Wyślij bez szyfrowania</string> + <string name="decryption_failed">Nie można odszyfrować. Sprawdź poprawność klucza prywatnego.</string> + <string name="openkeychain_required">OpenKeychain</string> + <string name="openkeychain_required_long">Conversations używa zewnętrznej aplikacji <b>OpenKeychain</b> do szyfrowania wiadomości i zarządzania kluczami publicznymi.\n\nOpenKeychain rozpowszechniany jest na licencji GPLv3 przez F-Droid lub Google Play.\n\n<small>(Zrestartuj Conversations po instalacji).</small></string> + <string name="restart">Zrestartuj</string> + <string name="install">Zainstaluj</string> + <string name="offering">oferowanie...</string> + <string name="waiting">oczekiwanie...</string> + <string name="no_pgp_key">Nie znaleziono klucza OpenPGP</string> + <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>Otrzymano zaszyfrowaną wiadomość. Dotknij, aby odszyfrować i wyświetlić.</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_notifications">Powiadomienia</string> + <string name="pref_notifications_summary">Powiadamiaj, gdy nadejdzie wiadomość</string> + <string name="pref_vibrate">Wibracje</string> + <string name="pref_vibrate_summary">Wibruj, gdy nadejdzie wiadomość</string> + <string name="pref_sound">Dźwięk</string> + <string name="pref_sound_summary">Odtwórz dźwięk z powiadomieniem</string> + <string name="pref_conference_notifications">Powiadomienia konferencji</string> + <string name="pref_conference_notifications_summary">Zawsze powiadamiaj o nowej wiadomości w konferencji</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_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="openpgp_error">Wystąpił błąd OpenKeychain</string> + <string name="error_decrypting_file">Błąd podczas deszyfrowania pliku</string> + <string name="accept">Akceptuj</string> + <string name="error">Wystąpił błąd</string> + <string name="pref_grant_presence_updates">Zezwól na powiadomienia obecności</string> + <string name="pref_grant_presence_updates_summary">Automatycznie zezwalaj i pytaj o powiadomienia obecności, kiedy utworzysz kontakt</string> + <string name="subscriptions">Subskrypcje</string> + <string name="your_account">Twoje konto</string> + <string name="keys">Klucze</string> + <string name="send_presence_updates">Wysyłaj powiadomienia obecności</string> + <string name="receive_presence_updates">Otrzymuj powiadomienia obecności</string> + <string name="ask_for_presence_updates">Poproś o powiadomienia obecności</string> + <string name="attach_choose_picture">Wybierz obraz</string> + <string name="attach_take_picture">Zrób zdjęcie</string> + <string name="preemptively_grant">Automatyczne powiadomienia obecności</string> + <string name="error_not_an_image_file">Wybrany plik nie jest obrazem</string> + <string name="error_compressing_image">Błąd kompresji obrazu</string> + <string name="error_file_not_found">Nie odnaleziono pliku</string> + <string name="error_io_exception">Ogólny błąd wejścia/wyjścia</string> + <string name="error_security_exception_during_image_copy">Aplikacja użyta do wyboru obrazu nie zezwoliła na odczyt pliku.\n\n<small>Wybierz obraz przy użyciu innego menedżera plików</small></string> + <string name="account_status_unknown">nieznany</string> + <string name="account_status_disabled">Tymczasowo wyłączono</string> + <string name="account_status_online">Połączono</string> + <string name="account_status_connecting">Łączenie...</string> + <string name="account_status_offline">Rozłączono</string> + <string name="account_status_unauthorized">Błąd uwierzytelnienia</string> + <string name="account_status_not_found">Nie odnaleziono serwera</string> + <string name="account_status_no_internet">Brak połączenia</string> + <string name="account_status_regis_fail">Błąd rejestracji</string> + <string name="account_status_regis_conflict">Nazwa jest już w użyciu</string> + <string name="account_status_regis_success">Zarejestrowano pomyślnie</string> + <string name="account_status_regis_not_sup">Serwer nie umożliwia rejestracji</string> + <string name="account_status_security_error">Błąd zabezpieczeń</string> + <string name="account_status_incompatible_server">Serwer niekompatybilny</string> + <string name="encryption_choice_none">Tekst jawny</string> + <string name="encryption_choice_otr">OTR</string> + <string name="encryption_choice_pgp">OpenPGP</string> + <string name="mgmt_account_edit">Edytuj konto</string> + <string name="mgmt_account_delete">Usuń konto</string> + <string name="mgmt_account_disable">Wyłącz tymczasowo</string> + <string name="mgmt_account_publish_avatar">Publikuj awatar</string> + <string name="mgmt_account_publish_pgp">Udostępnij klucz publiczny OpenPGP</string> + <string name="mgmt_account_enable">Włącz konto</string> + <string name="mgmt_account_are_you_sure">Czy na pewno?</string> + <string name="mgmt_account_delete_confirm_text">Wraz z kontem zostanie nieodwracalnie usunięta powiązana historia konwersacji.</string> + <string name="attach_record_voice">Nagraj głos</string> + <string name="account_settings_jabber_id">Jabber ID</string> + <string name="account_settings_password">Hasło</string> + <string name="account_settings_example_jabber_id">username@example.com</string> + <string name="account_settings_confirm_password">Potwierdź hasło</string> + <string name="password">Hasło</string> + <string name="confirm_password">Potwierdź hasło</string> + <string name="passwords_do_not_match">Hasła są niezgodne</string> + <string name="invalid_jid">Wprowadzono niepoprawny Jabber ID</string> + <string name="error_out_of_memory">Brak pamięci, obraz jest za duży</string> + <string name="add_phone_book_text">Czy chcesz dodać kontakt %s do książki telefonicznej?</string> + <string name="contact_status_online">dostępny</string> + <string name="contact_status_free_to_chat">chętny do rozmowy</string> + <string name="contact_status_away">zaraz wracam</string> + <string name="contact_status_extended_away">będę później</string> + <string name="contact_status_do_not_disturb">nie przeszkadzać</string> + <string name="contact_status_offline">niedostępny</string> + <string name="muc_details_conference">Konferencja</string> + <string name="muc_details_other_members">Pozostali uczestnicy</string> + <string name="server_info_show_more">Informacje o serwerze</string> + <string name="server_info_mam">XEP-0313: MAM</string> + <string name="server_info_carbon_messages">XEP-0280: Kopie wiadomości</string> + <string name="server_info_csi">XEP-0352: Client State Indication</string> + <string name="server_info_blocking">XEP-0191: Blocking Command</string> + <string name="server_info_roster_version">XEP-0237: Roster Versioning</string> + <string name="server_info_stream_management">XEP-0198: Stream Management</string> + <string name="server_info_pep">XEP-0163: PEP (Avatars)</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> + <string name="last_seen_now">widziany chwilę temu</string> + <string name="last_seen_min">widziany minutę temu</string> + <string name="last_seen_mins">widziany %d minut(y) temu</string> + <string name="last_seen_hour">widziany godzinę temu</string> + <string name="last_seen_hours">widziany %d godzin(y) temu</string> + <string name="last_seen_day">widziany wczoraj</string> + <string name="last_seen_days">widziany %d dni temu</string> + <string name="never_seen">nigdy nie widziany</string> + <string name="install_openkeychain">Wiadomość zaszyfrowana. Zainstaluj OpenKeychain, żeby odszyfrować.</string> + <string name="unknown_otr_fingerprint">Nieznany odcisk klucza OTR</string> + <string name="openpgp_messages_found">Znaleziono wiadomości zaszyfrowane przez OpenPGP</string> + <string name="reception_failed">Odbiór nieudany</string> + <string name="your_fingerprint">Twój odcisk klucza</string> + <string name="otr_fingerprint">Odcisk klucza OTR</string> + <string name="verify">Weryfikuj</string> + <string name="decrypt">Odszyfruj</string> + <string name="conferences">Konferencje</string> + <string name="search">Szukaj</string> + <string name="create_contact">Utwórz kontakt</string> + <string name="join_conference">Dołącz do konferencji</string> + <string name="delete_contact">Usuń kontakt</string> + <string name="view_contact_details">Szczegóły kontaktu</string> + <string name="block_contact">Zablokuj kontakt</string> + <string name="unblock_contact">Odblokuj kontakt</string> + <string name="create">Utwórz</string> + <string name="contact_already_exists">Kontakt już istnieje</string> + <string name="join">Dołącz</string> + <string name="conference_address">Adres konferencji</string> + <string name="conference_address_example">room@conference.example.com</string> + <string name="save_as_bookmark">Dodaj jako zakładkę</string> + <string name="delete_bookmark">Usuń zakładkę</string> + <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="leave">Opuść pokój</string> + <string name="contact_added_you">Kontakt został dodany do listy</string> + <string name="add_back">Dodaj ponownie</string> + <string name="contact_has_read_up_to_this_point">%s przeczytał do tego miejsca</string> + <string name="publish">Publikuj</string> + <string name="touch_to_choose_picture">Naciśnij awatar, żeby wybrać obraz z galerii</string> + <string name="publish_avatar_explanation">Uwaga: Obraz będzie widoczny dla wszystkich kontaktów, którym udostępniasz powiadomienia o obecności.</string> + <string name="publishing">Publikowanie...</string> + <string name="error_publish_avatar_server_reject">Serwer odrzucił żądanie publikacji</string> + <string name="error_publish_avatar_converting">Wystąpił błąd podczas konwersji obrazu</string> + <string name="error_saving_avatar">Nie udało się zapisać obrazu w pamięci urządzenia</string> + <string name="or_long_press_for_default">(lub przytrzymaj długo, aby ustawić domyślny)</string> + <string name="error_publish_avatar_no_server_support">Serwer nie udostępnia możliwości publikacji awatarów</string> + <string name="private_message">szepcze</string> + <string name="private_message_to">do %s</string> + <string name="send_private_message_to">Wyślij prywatną wiadomość do %s</string> + <string name="connect">Połącz</string> + <string name="account_already_exists">Konto już istnieje</string> + <string name="next">Dalej</string> + <string name="server_info_session_established">Połączono z serwerem</string> + <string name="additional_information">Dodatkowe informacje</string> + <string name="skip">Pomiń</string> + <string name="disable_notifications">Wyłącz powiadomienia</string> + <string name="disable_notifications_for_this_conversation">Wyłącz powiadomienia tej konwersacji</string> + <string name="notifications_disabled">Powiadomienia są wyłączone</string> + <string name="enable">Włącz</string> + <string name="conference_requires_password">Konferencja jest zabezpieczona hasłem</string> + <string name="enter_password">Wprowadź hasło</string> + <string name="missing_presence_updates">Kontakt nie udostępnia powiadomień o obecności</string> + <string name="request_presence_updates">Poproś kontakt o udostępnienie powiadomień o obecności.\n\n<small>Pozwoli to na ustalenie klienta, z którego korzysta rozmówca.</small></string> + <string name="request_now">Zażądaj teraz</string> + <string name="delete_fingerprint">Usuń odcisk klucza</string> + <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_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> + <string name="pref_dont_save_encrypted_summary">Uwaga: Może powodować utratę wiadomości</string> + <string name="pref_expert_options">Ustawienia zaawansowane</string> + <string name="pref_expert_options_summary">Modyfikuj ustawienia ostrożnie</string> + <string name="title_activity_about">O Conversations</string> + <string name="pref_about_conversations_summary">Informacje o kompilacji i licencji</string> + <string name="title_pref_quiet_hours">Godziny ciszy</string> + <string name="title_pref_quiet_hours_start_time">Początek</string> + <string name="title_pref_quiet_hours_end_time">Koniec</string> + <string name="title_pref_enable_quiet_hours">Włącz godziny ciszy</string> + <string name="pref_quiet_hours_summary">Powiadomienia będą wyciszone w wybranym przedziale czasu</string> + <string name="pref_use_larger_font">Większy rozmiar czcionki</string> + <string name="pref_use_larger_font_summary">Używaj większego rozmiaru czcionki w aplikacji</string> + <string name="pref_use_send_button_to_indicate_status">Przycisk wysyłania wskazuje status</string> + <string name="pref_use_indicate_received">Raporty dostarczenia</string> + <string name="pref_use_indicate_received_summary">Jeżeli to możliwe, oznaczaj dostarczone wiadomości zielonym haczykiem</string> + <string name="pref_use_send_button_to_indicate_status_summary">Koloruj przycisk wysyłania w zależności od statusu kontaktu</string> + <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="toast_message_otr_fingerprint">Odcisk klucza OTR 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="using_account">używając konta %s</string> + <string name="not_connected_try_again">Brak połączenia. Spróbuj ponownie później</string> + <string name="message_options">Opcje wiadomości</string> + <string name="copy_text">Skopiuj tekst</string> + <string name="copy_original_url">Skopiuj oryginalny URL</string> + <string name="send_again">Wyślij ponownie</string> + <string name="message_text">Treść wiadomości</string> + <string name="url_copied_to_clipboard">URL obrazu został skopiowany do schowka</string> + <string name="message_copied_to_clipboard">Wiadomość została skopiowana do schowka</string> + <string name="image_transmission_failed">Błąd podczas przesyłania obrazu</string> + <string name="scan_qr_code">Zeskanuj kod QR</string> + <string name="show_qr_code">Pokaż kod QR</string> + <string name="show_block_list">Wyświetl listę banów</string> + <string name="account_details">Szczegóły konta</string> + <string name="verify_otr">Weryfikuj OTR</string> + <string name="remote_fingerprint">Zdalny odcisk klucza</string> + <string name="scan">skanuj</string> + <string name="or_touch_phones">(lub zetknij telefony)</string> + <string name="smp">Protokół socialist millionaire</string> + <string name="shared_secret_hint">Podpowiedź lub pytanie</string> + <string name="shared_secret_secret">Wspólny sekret</string> + <string name="confirm">Potwierdź</string> + <string name="in_progress">W toku</string> + <string name="respond">Odpowiedz</string> + <string name="failed">Operacja nieudana</string> + <string name="secrets_do_not_match">Sekrety są niezgodne</string> + <string name="try_again">Spróbuj ponownie</string> + <string name="finish">Zakończ</string> + <string name="verified">Weryfikacja udana!</string> + <string name="smp_requested">Kontakt zażądał weryfikacji SMP</string> + <string name="no_otr_session_found">Brak ważnej sesji OTR!</string> + <string name="conversations_foreground_service">Conversations</string> + <string name="pref_keep_foreground_service">Usługa na pierwszym planie</string> + <string name="pref_keep_foreground_service_summary">Uniemożliwia systemowi przerwanie połączenia</string> + <string name="choose_file">Wybierz plik</string> + <string name="receiving_x_file">Odbieranie %1$s (ukończono %2$d%%)</string> + <string name="download_x_file">Pobierz %s</string> + <string name="file">plik</string> + <string name="open_x_file">Otwórz %s</string> + <string name="sending_file">Wysyłanie (ukończono %1$d%%)</string> + <string name="preparing_file">Przygotowywanie pliku do wysłania</string> + <string name="x_file_offered_for_download">Zaproponowano pobranie pliku %s</string> + <string name="cancel_transmission">Anuluj przesyłanie</string> + <string name="file_transmission_failed">Przesyłanie pliku nie powiodło się</string> + <string name="file_deleted">Plik został usunięty</string> + <string name="no_application_found_to_open_file">Nie odnaleziono aplikacji skojarzonej z typem pliku</string> + <string name="could_not_verify_fingerprint">Weryfikacja odcisku klucza nieudana</string> + <string name="manually_verify">Weryfikuj ręcznie</string> + <string name="are_you_sure_verify_fingerprint">Czy na pewno chcesz zweryfikować odcisk klucza OTR kontaktu?</string> + <string name="pref_show_dynamic_tags">Etykiety kontaktów</string> + <string name="pref_show_dynamic_tags_summary">Wyświetlaj etykiety pod kontaktami</string> + <string name="enable_notifications">Włącz powiadomienia</string> + <string name="conference_with">Utwórz konferencję...</string> + <string name="no_conference_server_found">Nie odnaleziono serwera konferencji</string> + <string name="conference_creation_failed">Nie udało się utworzyć konferencji!</string> + <string name="conference_created">Konferencja została utworzona!</string> + <string name="secret_accepted">Sekret został zaakceptowany!</string> + <string name="reset">Resetuj</string> + <string name="account_image_description">Awatar konta</string> + <string name="copy_otr_clipboard_description">Skopiuj odcisk klucza OTR do schowka</string> + <string name="fetching_history_from_server">Pobieranie historii z serwera</string> + <string name="no_more_history_on_server">Koniec historii na serwerze</string> + <string name="updating">Aktualizowanie...</string> + <string name="password_changed">Hasło zostało zmienione!</string> + <string name="could_not_change_password">Nie udało się zmienić hasła</string> + <string name="otr_session_not_started">Wyślij wiadomość, żeby rozpocząć szyfrowaną rozmowę</string> + <string name="ask_question">Zadaj pytanie</string> + <string name="smp_explain_question">Jeśli ty i twój kontakt macie jakiś wspólny sekret, którego nie zna nikt inny (żart znany tylko wam lub po prostu co jedliście ostatnim razem, gdy się widzieliście) możecie użyć tego sekretu by zweryfikować wzajemnie odciski swoich kluczy.\n\nPodasz podpowiedź lub pytanie dla twojego kontaktu i otrzymasz odpowiedź (wielkość liter ma znaczenie).</string> + <string name="smp_explain_answer">Twój kontakt chciałby zweryfikować odcisk Twojego klucza poprzez sprawdzenie znajomości wspólnego sekretu. Twój znajomy przedstawił następującą podpowiedź lub pytanie dotyczącego tego sekretu.</string> + <string name="shared_secret_hint_should_not_be_empty">Wskazówka nie powinna być pusta</string> + <string name="shared_secret_can_not_be_empty">Sekret nie może być pusty</string> + <string name="manual_verification_explanation">Ostrożnie porównaj odcisk klucza pokazany poniżej z odciskiem klucza twojego kontaktu.\nMożesz użyć bezpiecznego kanału komunikacji takiego, jak szyfrowany e-mail lub rozmowa telefoniczna, by wymienić odciski klucza.</string> <string name="change_password">Zmień hasło</string> <string name="current_password">Obecne hasło</string> <string name="new_password">Nowe hasło</string> <string name="password_should_not_be_empty">Hasło nie może być puste</string> <string name="enable_all_accounts">Aktywuj wszystkie konta</string> <string name="disable_all_accounts">Wyłącz wszystkie konta</string> + <string name="perform_action_with">Użyj</string> + <string name="no_affiliation">Brak stanowiska</string> + <string name="no_role">Brak funkcji</string> + <string name="outcast">Wykluczony</string> + <string name="member">Członek</string> + <string name="advanced_mode">Tryb zaawansowany</string> + <string name="grant_membership">Przyznaj członkostwo</string> + <string name="remove_membership">Cofnij członkostwo</string> + <string name="grant_admin_privileges">Przyznaj uprawnienia administratora</string> + <string name="remove_admin_privileges">Odbierz uprawnienia administratora</string> + <string name="remove_from_room">Usuń z konferencji</string> + <string name="could_not_change_affiliation">Nie udało się zmienić stanowiska dla %s</string> + <string name="ban_from_conference">Zbanuj uczestnika konferencji</string> + <string name="removing_from_public_conference">Próbujesz usunąć %s z publicznego pokoju. Jedyny sposób, by to zrobić, to wykluczyć tego użytkownika na zawsze.</string> + <string name="ban_now">Zbanuj teraz</string> + <string name="could_not_change_role">Nie udało się zmienić funkcji %s</string> + <string name="public_conference">Konferencja publiczna</string> + <string name="private_conference">Konferencja prywatna, dla zaakceptowanych uczestników</string> + <string name="conference_options">Opcje konferencji</string> + <string name="members_only">Prywatna (tylko zaakceptowani)</string> + <string name="non_anonymous">Nieanonimowa</string> + <string name="modified_conference_options">Opcje konferencji zostały zmienione!</string> + <string name="could_not_modify_conference_options">Nie udało się zmienić opcji konferencji</string> <string name="never">Nigdy</string> <string name="thirty_minutes">30 minut</string> <string name="one_hour">1 godzina</string> <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> <string name="pref_display_enter_key_summary">Zamień klawisz emotikon na klawisz Enter</string> <string name="audio">plik audio</string> <string name="video">plik wideo</string> <string name="image">obraz</string> + <string name="pdf_document">Dokument PDF</string> + <string name="apk">Aplikacja Androida</string> <string name="vcard">Kontakt</string> <string name="received_x_file">Odebrano %s</string> + <string name="disable_foreground_service">Wyłącz podtrzymanie pierszego planu usługi</string> + <string name="touch_to_open_conversations">Dotknij, aby otworzyć Conversations</string> <string name="avatar_has_been_published">Avatar został pomyślnie opublikowany!</string> <string name="sending_x_file">Wysyłanie %s</string> <string name="offering_x_file">Oferowanie %s</string> <string name="hide_offline">Ukryj niedostępnych</string> + <string name="disable_account">Wyłącz konto</string> + <string name="contact_is_typing">%s pisze...</string> + <string name="contact_has_stopped_typing">%s przestał(a) pisać</string> + <string name="pref_chat_states">Powiadomienia pisania</string> + <string name="pref_chat_states_summary">Powiadamiaj rozmówcę, kiedy rozpoczynasz nową wiadomość</string> + <string name="send_location">Wyślij lokalizację</string> + <string name="show_location">Pokaż lokalizację</string> + <string name="no_application_found_to_display_location">Nie odnaleziono aplikacji do wyświetlenia lokalizacji</string> + <string name="location">Lokalizacja</string> + <string name="received_location">Otrzymano lokalizację</string> + <string name="title_undo_swipe_out_conversation">Zamknięto konwersację</string> + <string name="title_undo_swipe_out_muc">Opuszczono konferencję</string> + <string name="pref_certificate_options">Ustawienia certyfikatów</string> + <string name="pref_dont_trust_system_cas_title">Nie ufaj certyfikatom systemowym</string> + <string name="pref_dont_trust_system_cas_summary">Wymagaj ręcznego potwierdzania certyfikatów</string> + <string name="pref_remove_trusted_certificates_title">Usuń certyfikat</string> + <string name="pref_remove_trusted_certificates_summary">Wybierz zaufane certyfikaty do usunięcia</string> + <string name="toast_no_trusted_certs">Brak ręcznie zaufanych certyfikatów</string> + <string name="dialog_manage_certs_title">Usuń certyfikaty</string> + <string name="dialog_manage_certs_positivebutton">Usuń zaznaczone</string> + <string name="dialog_manage_certs_negativebutton">Anuluj</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">Usunięto %d certyfikat</item> + <item quantity="few">Usunięto %d certyfikaty</item> + <item quantity="other">Usunięto %d certyfikatów</item> + </plurals> + <plurals name="select_contact"> + <item quantity="one">%d kontakt wybrany</item> + <item quantity="few">%d kontakty wybrane</item> + <item quantity="other">%d kontaktów wybranych</item> + </plurals> + <string name="pref_quick_action_summary">Zastąp przycisk wysyłania szybką akcją</string> + <string name="pref_quick_action">Szybka akcja</string> + <string name="none">Brak</string> + <string name="recently_used">Ostatnio używana</string> + <string name="choose_quick_action">Wybierz szybką akcję</string> </resources> diff --git a/src/main/res/values-pt/strings.xml b/src/main/res/values-pt/strings.xml new file mode 100644 index 00000000..559ae6cf --- /dev/null +++ b/src/main/res/values-pt/strings.xml @@ -0,0 +1,297 @@ +<?xml version='1.0' encoding='UTF-8'?> +<resources> + <string name="action_settings">Configurações</string> + <string name="action_add">Nova conversa</string> + <string name="action_accounts">Gerenciar contas</string> + <string name="action_end_conversation">Finalizar essa conversa</string> + <string name="action_contact_details">Detalhes do contato</string> + <string name="action_muc_details">Detalhes da conferência</string> + <string name="action_secure">Conversa segura</string> + <string name="action_add_account">Adicionar conta</string> + <string name="action_edit_contact">Editar nome</string> + <string name="action_add_phone_book">Adicionar aos contatos</string> + <string name="action_block_contact">Bloquear contato</string> + <string name="action_unblock_contact">Desbloquear contato</string> + <string name="action_block_domain">Bloquear domínio</string> + <string name="action_unblock_domain">Desbloquear domínio</string> + <string name="title_activity_manage_accounts">Gerenciar contas</string> + <string name="title_activity_settings">Configurações</string> + <string name="title_activity_conference_details">Detalhes da conferência</string> + <string name="title_activity_contact_details">Detalhes do contato</string> + <string name="title_activity_sharewith">Compartilhar conversa</string> + <string name="title_activity_start_conversation">Iniciar conversa</string> + <string name="title_activity_choose_contact">Escolher contato</string> + <string name="title_activity_block_list">Bloquear lista</string> + <string name="just_now">agora há pouco</string> + <string name="minute_ago">1 minuto atrás</string> + <string name="minutes_ago">%d minutos atrás</string> + <string name="unread_conversations">Conversas não lidas</string> + <string name="sending">enviando...</string> + <string name="encrypted_message">Descriptografando mensagem. Por favor aguarde...</string> + <string name="nick_in_use">O apelido já está em uso</string> + <string name="admin">Administrador</string> + <string name="owner">Dono</string> + <string name="moderator">Moderador</string> + <string name="participant">Participante</string> + <string name="visitor">Visitante</string> + <string name="block_contact_text">Deseja bloquear o o recebimento de mensagens de %s?</string> + <string name="unblock_contact_text">Deseja desbloquear o recebimento de mensagens de %s?</string> + <string name="block_domain_text">Bloquear todos os contatos de %s?</string> + <string name="unblock_domain_text">Desbloquear todos os contatos de %s?</string> + <string name="contact_blocked">Contato bloqueado</string> + <string name="remove_bookmark_text">Você deseja remover %s dos favoritos? A conversa associada a esse favorito não será removida.</string> + <string name="register_account">Registre uma nova conta no servidor</string> + <string name="change_password_on_server">Altere a senha no servidor</string> + <string name="share_with">Compartilhar com...</string> + <string name="start_conversation">Iniciar conversa</string> + <string name="invite_contact">Convidar contato</string> + <string name="contacts">Contatos</string> + <string name="cancel">Cancelar</string> + <string name="set">Definir</string> + <string name="add">Adicionar</string> + <string name="edit">Editar</string> + <string name="delete">Remover</string> + <string name="block">Bloquear</string> + <string name="unblock">Desbloquear</string> + <string name="save">Salvar</string> + <string name="ok">OK</string> + <string name="crash_report_title">A conversa foi interrompida</string> + <string name="crash_report_message">Ao enviar os stack traces você ajudará o desenvolvimento do aplicativo\n<b>Atenção:</b> Isso usará a sua conta XMPP para enviar o stack trace para o desenvolvedor.</string> + <string name="send_now">Enviar agora</string> + <string name="send_never">Não pergunte novamente</string> + <string name="problem_connecting_to_account">Não foi possível se conectar à conta</string> + <string name="problem_connecting_to_accounts">Não foi possível conectar a múltiplas contas</string> + <string name="touch_to_fix">Toque aqui para gerenciar suas contas</string> + <string name="attach_file">Anexar arquivo</string> + <string name="not_in_roster">O contato não está no seu rol</string> + <string name="add_contact">Adicionar contato</string> + <string name="send_failed">a entrega falhou</string> + <string name="send_rejected">rejeitado</string> + <string name="preparing_image">Preparando a imagem para transmissão</string> + <string name="action_clear_history">Limpar histórico</string> + <string name="clear_conversation_history">Limpar o histórico de conversas</string> + <string name="clear_histor_msg">Você deseja remover todas as mensagens nessa conversa?\n\n<b>Atenção:<b> Isso não irá influenciar mensagens salvas em outros dispositivos ou servidores.</string> + <string name="delete_messages">Remover mensagens</string> + <string name="also_end_conversation">Finalizar essa conversa ao final</string> + <string name="choose_presence">Escolha a presença do contato</string> + <string name="send_plain_text_message">Enviar mensagem de texto puro</string> + <string name="send_otr_message">Enviar mensagem criptografada com OTR</string> + <string name="send_pgp_message">Enviar mensagem criptografada com OpenPGP</string> + <string name="your_nick_has_been_changed">Seu apelido foi alterado</string> + <string name="send_unencrypted">Enviar sem criptografia</string> + <string name="decryption_failed">A desencriptação falhou. Talvez você não tenha a chave privada correta.</string> + <string name="openkeychain_required">OpenKeychain</string> + <string name="openkeychain_required_long">Conversas utiliza um aplicativo de terceiro chamado <b>OpenKeychain</b> para criptografar e descriptografar mensagens e gerenciar suas chaves públicas.\n\nOpenKeychain é licenciado sob a licença GPLv3 e está disponível no F-Droid e Google Play.\n\n<small>(Por favor reinicie Coversas em seguida)</small></string> + <string name="restart">Reiniciar</string> + <string name="install">Instalar</string> + <string name="offering">oferecendo...</string> + <string name="waiting">aguardando...</string> + <string name="no_pgp_key">Nenhuma chave OpenPGP encontrado</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> + <string name="pref_accept_files">Aceitar arquivos</string> + <string name="pref_accept_files_summary">Automaticamente aceita arquivos menores que...</string> + <string name="pref_notification_settings">Configurações de notificação</string> + <string name="pref_notifications">Notificações</string> + <string name="pref_notifications_summary">Notificar quando uma nova mensagem for recebida</string> + <string name="pref_vibrate">Vibrar</string> + <string name="pref_vibrate_summary">Vibrar também quando uma nova mensagem for recebida</string> + <string name="pref_sound">Som</string> + <string name="pref_sound_summary">Tocar um som com a notificação</string> + <string name="pref_conference_notifications">Notificações de conferência</string> + <string name="pref_conference_notifications_summary">Sempre notificar quando uma nova mensagem de conferencia for recebida ao invés de apenas quando a mesma for ressaltada</string> + <string name="pref_notification_grace_period">Período de carência da notificação</string> + <string name="pref_notification_grace_period_summary">Desativar notificações por um curto período após a copia oculta ser recebida</string> + <string name="pref_advanced_options">Opções avançadas</string> + <string name="pref_never_send_crash">Nunca enviar relatórios de quebra</string> + <string name="pref_never_send_crash_summary">Ao enviar os stack traces você ajuda o desenvolvimento do aplicativo</string> + <string name="pref_confirm_messages">Confirmar mensanges</string> + <string name="pref_confirm_messages_summary">Permitir que um contato saiba quando você recebeu e leu uma mensagem</string> + <string name="pref_ui_options">Opções de UI</string> + <string name="openpgp_error">O OpenKeychain informou um erro</string> + <string name="error_decrypting_file">Erro de I/O de critpografia</string> + <string name="accept">Aceitar</string> + <string name="error">Ocorreu um erro</string> + <string name="pref_grant_presence_updates">Permitir atualizações de presença</string> + <string name="subscriptions">Inscrições</string> + <string name="your_account">Sua conta</string> + <string name="keys">Chaves</string> + <string name="send_presence_updates">Enviar atualizações de presença</string> + <string name="receive_presence_updates">Receber atualizações de presença</string> + <string name="ask_for_presence_updates">Pedir atualizações de presença</string> + <string name="attach_choose_picture">Escolher imagem</string> + <string name="attach_take_picture">Tirar foto</string> + <string name="error_not_an_image_file">O arquivo selecionado não é uma imagem</string> + <string name="error_compressing_image">Erro ao converter o arquivo de imagem</string> + <string name="error_file_not_found">Arquivo não encontrado</string> + <string name="account_status_unknown">Desconhecido</string> + <string name="account_status_disabled">Temporariamente desabilitado</string> + <string name="account_status_online">Online</string> + <string name="account_status_connecting">Conectando\u2026</string> + <string name="account_status_offline">Offline</string> + <string name="account_status_unauthorized">Não autorizado</string> + <string name="account_status_not_found">Servidor não encontrado</string> + <string name="account_status_no_internet">Sem conectividade</string> + <string name="account_status_regis_fail">O registro falhou</string> + <string name="account_status_regis_conflict">O nome de usuário já está em uso</string> + <string name="account_status_regis_success">Registro efetuado com sucesso</string> + <string name="account_status_regis_not_sup">O servidor não aceita o registro</string> + <string name="account_status_security_error">Erro de segurança</string> + <string name="account_status_incompatible_server">Servidor incompatível</string> + <string name="encryption_choice_none">Texto puro</string> + <string name="encryption_choice_otr">OTR</string> + <string name="encryption_choice_pgp">OpenPGP</string> + <string name="mgmt_account_edit">Editar conta</string> + <string name="mgmt_account_delete">Remover conta</string> + <string name="mgmt_account_disable">Desabilitar temporariamente</string> + <string name="mgmt_account_publish_avatar">Publicar o avatar</string> + <string name="mgmt_account_publish_pgp">Publicar chave pública OpenPGP</string> + <string name="mgmt_account_enable">Ativar conta</string> + <string name="mgmt_account_are_you_sure">Tem certeza?</string> + <string name="attach_record_voice">Gravar voz</string> + <string name="account_settings_jabber_id">ID Jabber</string> + <string name="account_settings_password">Senha</string> + <string name="account_settings_example_jabber_id">nomedeusuario@exemplo.com</string> + <string name="account_settings_confirm_password">Confirmar senha</string> + <string name="password">Senha</string> + <string name="confirm_password">Confirmar senha</string> + <string name="passwords_do_not_match">As senhas não combina</string> + <string name="invalid_jid">Esse não é um ID Jabber válido</string> + <string name="error_out_of_memory">Memória insuficiente. A imagem é muito grande</string> + <string name="add_phone_book_text">Você tem certeza que deseja adicionar %s à sua lista de contato do telefone?</string> + <string name="contact_status_online">online</string> + <string name="contact_status_free_to_chat">disponível para conversa</string> + <string name="contact_status_away">fora</string> + <string name="contact_status_extended_away">fora extendido</string> + <string name="contact_status_do_not_disturb">não pertube</string> + <string name="contact_status_offline">offline</string> + <string name="muc_details_conference">Conferência</string> + <string name="muc_details_other_members">Outros Membros</string> + <string name="server_info_show_more">Informações do servior</string> + <string name="server_info_available">disponível</string> + <string name="server_info_unavailable">indisponível</string> + <string name="missing_public_keys">Anúncios de ausência de chave pública</string> + <string name="last_seen_now">visto agora há pouco</string> + <string name="last_seen_min">visto há 1 minuto atrás</string> + <string name="last_seen_mins">visto %d minutos atrás</string> + <string name="last_seen_hour">visto há 1 hora atrás</string> + <string name="last_seen_hours">visto %d horas atrás</string> + <string name="last_seen_day">visto há 1 dia atrás</string> + <string name="last_seen_days">visto %d dias atrás</string> + <string name="never_seen">nunca visto</string> + <string name="install_openkeychain">Mensagem criptografada. Por favor instale o OpenKeychain para desencriptar</string> + <string name="unknown_otr_fingerprint">Impressão OTR inválida</string> + <string name="openpgp_messages_found">Mensagens encriptadas com OpenPGP não encontrada</string> + <string name="reception_failed">A recepção falhou</string> + <string name="your_fingerprint">Sua impressão</string> + <string name="otr_fingerprint">Impressão OTR</string> + <string name="verify">Verificar</string> + <string name="decrypt">Desencriptar</string> + <string name="conferences">Conferências</string> + <string name="search">Buscar</string> + <string name="create_contact">Criar contato</string> + <string name="join_conference">Se juntar à conferência</string> + <string name="delete_contact">Remover contato</string> + <string name="view_contact_details">Ver os detalhes dos contatos</string> + <string name="block_contact">Bloquear contato</string> + <string name="unblock_contact">Desbloquear contato</string> + <string name="create">Criar</string> + <string name="contact_already_exists">O contato já existe</string> + <string name="join">Juntar</string> + <string name="conference_address">Endereço da conferência</string> + <string name="conference_address_example">sala@conferencia.example.com</string> + <string name="save_as_bookmark">Salvar como favorito</string> + <string name="delete_bookmark">Salvar favorito</string> + <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> + <string name="contact_has_read_up_to_this_point">%s leu até esse ponto</string> + <string name="publish">Publicar</string> + <string name="touch_to_choose_picture">Toque o avatar para escolher uma imagem da sua galeria</string> + <string name="publish_avatar_explanation">Por favor observe: Todos inscritos na sua atualização de presença poderá ver essa imagem</string> + <string name="publishing">Publicando...</string> + <string name="error_publish_avatar_server_reject">O servidor rejeitou sua publicação</string> + <string name="error_publish_avatar_converting">Algo deu errado ao converter sua imagem</string> + <string name="error_saving_avatar">Não foi possível salvar o avatar no disco</string> + <string name="or_long_press_for_default">(Ou mantenha pressionado por um tempo para voltar para o padrão)</string> + <string name="error_publish_avatar_no_server_support">O seu servidor não suporta a publicação de avatares</string> + <string name="private_message">sussurado</string> + <string name="private_message_to">para %s</string> + <string name="send_private_message_to">Enviar mensagem privada para %s</string> + <string name="connect">Conectar</string> + <string name="account_already_exists">Essa conta já existe</string> + <string name="next">Próximo</string> + <string name="server_info_session_established">Sessão atual estebelecida</string> + <string name="additional_information">Informação adicional</string> + <string name="skip">Pular</string> + <string name="disable_notifications">Desativar notificações</string> + <string name="disable_notifications_for_this_conversation">Desativar notificações para essa conversa</string> + <string name="notifications_disabled">As notificações foram desativadas</string> + <string name="enable">Ativar</string> + <string name="conference_requires_password">Essa conferencia requer uma senha</string> + <string name="enter_password">Informar a senha</string> + <string name="missing_presence_updates">Atualizações de presença inexistente para o contato</string> + <string name="request_now">Solicitar agora</string> + <string name="delete_fingerprint">Remover impressão</string> + <string name="sure_delete_fingerprint">Tem certeza que deseja remover essa assinatura?</string> + <string name="pref_encryption_settings">Configurações de criptografia</string> + <string name="pref_force_encryption">Forçar criptografia ponto-a-ponto</string> + <string name="pref_force_encryption_summary">Sempre envie mensagem criptografada (exceto para conferências)</string> + <string name="pref_dont_save_encrypted">Não salve mensagens criptografadas</string> + <string name="pref_dont_save_encrypted_summary">Atenção: Isso pode levar a perda de mensagens</string> + <string name="pref_expert_options">Opções de expert</string> + <string name="pref_expert_options_summary">Por favor tenha cuidado com essas</string> + <string name="title_activity_about">Sobre Conversas</string> + <string name="pref_about_conversations_summary">Informação de licença e construção</string> + <string name="title_pref_quiet_hours">Horas de tranquilidade</string> + <string name="title_pref_quiet_hours_start_time">Hora de início</string> + <string name="title_pref_quiet_hours_end_time">Hora de fim</string> + <string name="title_pref_enable_quiet_hours">Habilitar hora de tranquilidade</string> + <string name="pref_quiet_hours_summary">Notificações serão silenciadas nas horas de tranquilidade</string> + <string name="pref_use_larger_font">Aumentar o tamanho da fonte</string> + <string name="pref_use_larger_font_summary">Usar fontes maiores por todo aplicativo</string> + <string name="pref_use_send_button_to_indicate_status">O botão de enviar indica o estado</string> + <string name="pref_use_indicate_received">Solicitar recibo de mensagem</string> + <string name="pref_use_indicate_received_summary">Mensagens recebidas serão marcadas com um check verde se suportado</string> + <string name="pref_use_send_button_to_indicate_status_summary">Colorir o botão de enviar para indicar o estado do contato</string> + <string name="pref_expert_options_other">Outros</string> + <string name="pref_conference_name">Nome da conferência</string> + <string name="pref_conference_name_summary">Use o assunto da sala ao invés do JID para identificar as conferências</string> + <string name="toast_message_otr_fingerprint">Impressão OTR copiada para a área de transferência!</string> + <string name="try_again">Tentar novamente</string> + <string name="finish">Finalizar</string> + <string name="perform_action_with">Realizar a ação com</string> + <string name="no_affiliation">Sem afiliação</string> + <string name="no_role">Sem papel</string> + <string name="member">Membro</string> + <string name="advanced_mode">Modo avançado</string> + <string name="one_hour">1 hora</string> + <string name="two_hours">2 horas</string> + <string name="eight_hours">8 horas</string> + <string name="until_further_notice">Até segunda ordem</string> + <string name="pref_input_options">Opções de entrada</string> + <string name="pref_enter_is_send">O enter envia</string> + <string name="pref_enter_is_send_summary">Use o enter para enviar a mensagem</string> + <string name="pref_display_enter_key">Exibir tecla enter</string> + <string name="audio">áudio</string> + <string name="video">vídeo</string> + <string name="image">imagem</string> + <string name="pdf_document">Documento PDF</string> + <string name="apk">Aplicativo Android</string> + <string name="vcard">Contato</string> + <string name="sending_x_file">Enviando %s</string> + <string name="offering_x_file">Oferecendo %s</string> + <string name="contact_is_typing">%s está digitando...</string> + <string name="contact_has_stopped_typing">%s parou de digitar</string> + <string name="pref_chat_states">Notificações de digitação</string> + <string name="send_location">Enviar localização</string> + <string name="show_location">Exibir localização</string> + <string name="dialog_manage_certs_negativebutton">Cancelar</string> + <string name="pref_quick_action">Ação rápida</string> + <string name="none">Nada</string> +</resources> diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml index 9572b3db..9cbfcdbf 100644 --- a/src/main/res/values-ro-rRO/strings.xml +++ b/src/main/res/values-ro-rRO/strings.xml @@ -79,7 +79,6 @@ <string name="send_otr_message">Trimite mesaj criptat cu OTR</string> <string name="send_pgp_message">Trimite mesaj criptat cu OpenPGP</string> <string name="your_nick_has_been_changed">Numele tau a fost schimbat</string> - <string name="download_image">Copiaza imagine</string> <string name="send_unencrypted">Trimite necriptat</string> <string name="decryption_failed">Decriptia a esuat. Poate nu ai cheia privata corecta.</string> <string name="openkeychain_required">OpenKeychain</string> diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index 8c4d0950..3a2515b7 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -11,6 +11,10 @@ <string name="action_edit_contact">Редактировать контакт</string> <string name="action_add_phone_book">Добавить в телефонную книгу</string> <string name="action_delete_contact">Удалить из списка</string> + <string name="action_block_contact">Заблокировать контакт</string> + <string name="action_unblock_contact">Разблокировать контакт</string> + <string name="action_block_domain">Заблокировать домен</string> + <string name="action_unblock_domain">Разблокировать домен</string> <string name="title_activity_manage_accounts">Управление Аккаунтами</string> <string name="title_activity_settings">Настройки</string> <string name="title_activity_conference_details">Сведения о Конференции</string> @@ -18,6 +22,7 @@ <string name="title_activity_sharewith">Поделиться</string> <string name="title_activity_start_conversation">Начать беседу</string> <string name="title_activity_choose_contact">Выберите собеседника</string> + <string name="title_activity_block_list">Черный список</string> <string name="just_now">только что</string> <string name="minute_ago">1 минуту назад</string> <string name="minutes_ago">%d мин. назад</string> @@ -31,16 +36,25 @@ <string name="participant">Участник</string> <string name="visitor">Посетитель</string> <string name="remove_contact_text">Вы хотите удалить %s из своего списка? Беседы, связанные с этим аккаунтом будут сохранены.</string> + <string name="block_contact_text">Вы хотите заблокировать дальнейшие сообщения от %s?</string> + <string name="unblock_contact_text">Вы хотите разблокировать пользователя %s?</string> + <string name="block_domain_text">Заблокировать всех пользователей домена %s?</string> + <string name="unblock_domain_text">Разблокировать всех пользователей домена %s?</string> + <string name="contact_blocked">Контакт заблокирован</string> <string name="remove_bookmark_text">Вы хотите удалить %s из избранного? Беседы, связанные с данной закладкой будут сохранены</string> <string name="register_account">Создать новый аккаунт на сервере</string> + <string name="change_password_on_server">Изменить пароль на сервере</string> <string name="share_with">Поделиться с</string> <string name="start_conversation">Начать беседу</string> <string name="invite_contact">Пригласить собеседника</string> <string name="contacts">Контакты</string> <string name="cancel">Отмена</string> + <string name="set">Установить</string> <string name="add">Добавить</string> <string name="edit">Редактировать</string> <string name="delete">Удалить</string> + <string name="block">Заблокировать</string> + <string name="unblock">Разблокировать</string> <string name="save">Сохранить</string> <string name="ok">ОК</string> <string name="crash_report_title">Conversations был неожиданно остановлен</string> @@ -66,7 +80,6 @@ <string name="send_otr_message">Отправить OTR защифрованное сообщение</string> <string name="send_pgp_message">Отправить OpenPGP защифрованное сообщение</string> <string name="your_nick_has_been_changed">Ваш псевдоним был изменен</string> - <string name="download_image">Загрузить изображение</string> <string name="send_unencrypted">Отправить в незашифрованном виде</string> <string name="decryption_failed">Расшифровка не удалась. Вероятно, что у вас нет надлежащего ключа.</string> <string name="openkeychain_required">Установите OpenKeychain</string> @@ -134,6 +147,8 @@ <string name="account_status_regis_conflict">Имя пользователя уже используется</string> <string name="account_status_regis_success">Регистрация завершена</string> <string name="account_status_regis_not_sup">Сервер не поддерживает регистрацию</string> + <string name="account_status_security_error">Ошибка безопасности</string> + <string name="account_status_incompatible_server">Несовместимый сервер</string> <string name="encryption_choice_none">Без шифрования</string> <string name="encryption_choice_otr">OTR</string> <string name="encryption_choice_pgp">OpenPGP</string> @@ -164,8 +179,13 @@ <string name="contact_status_offline">не в сети</string> <string name="muc_details_conference">Конференция</string> <string name="muc_details_other_members">Другие участники</string> + <string name="server_info_show_more">Информация о сервере</string> + <string name="server_info_mam">XEP-0313: Сохранение сообщений в архив</string> <string name="server_info_carbon_messages">Дублирование сообщений</string> - <string name="server_info_stream_management">Управление потоками</string> + <string name="server_info_csi">XEP-0352: Индикатор Состояния Клиента</string> + <string name="server_info_blocking">XEP-0191: Команда Блокирования</string> + <string name="server_info_roster_version">XEP-0237: Управление версиями списков</string> + <string name="server_info_stream_management">XEP-0198: Управление потоками</string> <string name="server_info_pep">XEP-0163: PEP (Аватары)</string> <string name="server_info_available">доступен</string> <string name="server_info_unavailable">недоступен</string> @@ -192,6 +212,8 @@ <string name="join_conference">Присоединиться к конференции</string> <string name="delete_contact">Удалить Контакт</string> <string name="view_contact_details">Посмотреть данные контакта</string> + <string name="block_contact">Заблокировать контакт</string> + <string name="unblock_contact">Разблокировать контакт</string> <string name="create">Создать</string> <string name="contact_already_exists">Контакт уже существует</string> <string name="join">Присоединиться</string> @@ -245,8 +267,187 @@ <string name="pref_dont_save_encrypted_summary">Внимание: Это может привести к потере сообщений</string> <string name="pref_expert_options">Расширенные настройки</string> <string name="pref_expert_options_summary">Пожалуйста, будьте осторожны с данными настройками</string> + <string name="title_activity_about">О Conversations</string> + <string name="pref_about_conversations_summary">Информация о билде и лицензировании</string> + <string name="title_pref_quiet_hours">Тихие Часы</string> + <string name="title_pref_quiet_hours_start_time">Начало</string> + <string name="title_pref_quiet_hours_end_time">Окончание</string> + <string name="title_pref_enable_quiet_hours">Включить режим «тихих часов»</string> + <string name="pref_quiet_hours_summary">Уведомления будут отключены во время «тихих часов»</string> <string name="pref_use_larger_font">Увеличить размер шрифта</string> <string name="pref_use_larger_font_summary">Установите больший размер шрифта по всей программе</string> <string name="pref_use_send_button_to_indicate_status">Использовать кнопку-индикатор</string> + <string name="pref_use_indicate_received">Запрос в получении сообщения</string> + <string name="pref_use_indicate_received_summary">Если поддерживается, поступившие сообщения будут отмечены зеленой галочкой</string> <string name="pref_use_send_button_to_indicate_status_summary">Раскрасить кнопку отправить, указывая текущий статус собеседника</string> + <string name="pref_expert_options_other">Другие</string> + <string name="pref_conference_name">Название конференции</string> + <string name="pref_conference_name_summary">Использовать тему беседы заместо JID для отображения конференций</string> + <string name="toast_message_otr_fingerprint">OTR-отпечаток скопирован в буфер обмена!</string> + <string name="conference_banned">Вы заблокированы в этой конференции</string> + <string name="conference_members_only">Эта конференция требует членства</string> + <string name="conference_kicked">Вы были удалены из конференции</string> + <string name="using_account">использовать учётную запись %s</string> + <string name="not_connected_try_again">Вы неподключены. Попробуйте позже</string> + <string name="message_options">Опции сообщения</string> + <string name="copy_text">Копировать текст</string> + <string name="copy_original_url">Копировать адрес ссылки</string> + <string name="send_again">Отправить ещё раз</string> + <string name="message_text">Текст сообщения</string> + <string name="url_copied_to_clipboard">Ссылка скопирована в буфер обмена</string> + <string name="message_copied_to_clipboard">Сообщение скопировано в буфер обмена</string> + <string name="image_transmission_failed">Передача изображения не удалась</string> + <string name="scan_qr_code">Поиск QR-кода</string> + <string name="show_qr_code">Показать QR-код</string> + <string name="show_block_list">Показать чёрный список</string> + <string name="account_details">Сведения об учётной записи</string> + <string name="verify_otr">Подтвердить OTR</string> + <string name="remote_fingerprint">Удалённый отпечаток</string> + <string name="scan">поиск</string> + <string name="or_touch_phones">(или сенсорные телефоны)</string> + <string name="smp">Socialist Millionaire Protocol</string> + <string name="shared_secret_hint">Подсказка или вопрос</string> + <string name="shared_secret_secret">Общий секретный ключ</string> + <string name="confirm">Подтвердить</string> + <string name="in_progress">В ходе выполнения</string> + <string name="respond">Ответ</string> + <string name="failed">Не удалось</string> + <string name="secrets_do_not_match">Ключи не совпадают</string> + <string name="try_again">Попробуйте ещё раз</string> + <string name="finish">Завершить</string> + <string name="verified">Подтверждён!</string> + <string name="smp_requested">Контакт запросил подтверждение SMP</string> + <string name="no_otr_session_found">Действительная OTR-сессия не найдена!</string> + <string name="conversations_foreground_service">Диалоги</string> + <string name="pref_keep_foreground_service">Оставить службу на переднем плане</string> + <string name="pref_keep_foreground_service_summary">Не позволяет операционной системе закрыть ваше соединение</string> + <string name="choose_file">Выберите файл</string> + <string name="receiving_x_file">Получение %1$s (%2$d%% выполнено)</string> + <string name="download_x_file">Загружено %s</string> + <string name="file">файл</string> + <string name="open_x_file">Открыть %s</string> + <string name="sending_file">отправка (%1$d%% выполнено)</string> + <string name="preparing_file">Файл готовится для передачи</string> + <string name="x_file_offered_for_download">%s предлагается скачать</string> + <string name="cancel_transmission">Отменить передачу</string> + <string name="file_transmission_failed">передача файла не удалась</string> + <string name="file_deleted">Файл был удалён</string> + <string name="no_application_found_to_open_file">Не найдено приложения для открытия файла</string> + <string name="could_not_verify_fingerprint">Не удалось подтвердить отпечаток</string> + <string name="manually_verify">Ручная проверка</string> + <string name="are_you_sure_verify_fingerprint">Вы точно хотите подтвердить OTR-отпечатки ваших контактов?</string> + <string name="pref_show_dynamic_tags">Показывать динамические тэги</string> + <string name="pref_show_dynamic_tags_summary">Отображать теги только для чтения под контактами</string> + <string name="enable_notifications">Включить уведомления</string> + <string name="conference_with">Создать конференцию с...</string> + <string name="no_conference_server_found">Сервер конференции не был найден</string> + <string name="conference_creation_failed">Не удалось создать конференцию!</string> + <string name="conference_created">Конференция создана!</string> + <string name="secret_accepted">Секретный ключ принят!</string> + <string name="reset">Сброс</string> + <string name="account_image_description">Изображение учётной записи</string> + <string name="copy_otr_clipboard_description">Скопировать OTR-отпечаток в буфер обмена</string> + <string name="fetching_history_from_server">Получение истории с сервера</string> + <string name="no_more_history_on_server">На сервере больше нет истории</string> + <string name="updating">Обновление...</string> + <string name="password_changed">Пароль изменён!</string> + <string name="could_not_change_password">Не удалось изменить пароль</string> + <string name="otr_session_not_started">Отправить сообщение чтобы начать зашифрованный чат</string> + <string name="ask_question">Задать вопрос</string> + <string name="smp_explain_question">Если у вас и вашего собеседника есть общая тайна, о которой никто кроме вас не знает (например, шутка, понятная только \"своим\" или что вы ели на обед при последней вашей встрече) вы можете использовать эту тайну, чтобы подтвердить отпечаток друг друга.\n\nВы даёте подсказку или задаёте вопрос вашему контакту, а она даст регистрозависимый ответ.</string> + <string name="smp_explain_answer">Ваш собеседник желает проверить ваш отпечаток с помощью общего секрета. Ваш собеседник дал следующую подсказку или вопрос в качестве этого секрета.</string> + <string name="shared_secret_hint_should_not_be_empty">Ваша подсказка не может быть пустой</string> + <string name="shared_secret_can_not_be_empty">Ваш общий секрет не может быть пустым</string> + <string name="manual_verification_explanation">Внимательно сверьте отпечаток, показанный ниже с отпечатком вашего собеседника.\nДля обмена ими вы можете использовать любое доверенное средство связи, например, зашифрованное электронное письмо или телефонный звонок.</string> + <string name="change_password">Изменить пароль</string> + <string name="current_password">Текущий пароль</string> + <string name="new_password">Новый пароль</string> + <string name="password_should_not_be_empty">Пароль не может быть пустым</string> + <string name="enable_all_accounts">Включить все учётные записи</string> + <string name="disable_all_accounts">Отключить все учётные записи</string> + <string name="perform_action_with">Взаимодействовать с</string> + <string name="no_affiliation">Не является участником</string> + <string name="no_role">Нет роли</string> + <string name="outcast">Заблокированный пользователь</string> + <string name="member">Участник</string> + <string name="advanced_mode">Расширенный режим</string> + <string name="grant_membership">Предоставить участие</string> + <string name="remove_membership">Отменить участие</string> + <string name="grant_admin_privileges">Назначить административные права</string> + <string name="remove_admin_privileges">Снять административные права</string> + <string name="remove_from_room">Исключить из конференции</string> + <string name="could_not_change_affiliation">Не удалось изменить принадлежность %s</string> + <string name="ban_from_conference">Запретить доступ к конференции</string> + <string name="removing_from_public_conference">Вы пытаетесь исключить %s из публичной конференции. Единственный способ — это навсегда запретить доступ пользователю. </string> + <string name="ban_now">Заблокировать</string> + <string name="could_not_change_role">Не удалось сменить роль %s</string> + <string name="public_conference">Публичная конференция</string> + <string name="private_conference">Приватная конференция только для членов</string> + <string name="conference_options">Настройки конференции</string> + <string name="members_only">Закрытый доступ (только для участников)</string> + <string name="non_anonymous">Не анонимно </string> + <string name="modified_conference_options">Настройки конференции изменены!</string> + <string name="could_not_modify_conference_options">Не удалось изменить настройки конференции</string> + <string name="never">Никогда</string> + <string name="thirty_minutes">30 минут</string> + <string name="one_hour">1 час</string> + <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_enter_is_send">Отправить на \"Enter\"</string> + <string name="pref_enter_is_send_summary">Клавиша \"Enter\" отправляет сообщение</string> + <string name="pref_display_enter_key">Показывать клавишу ввода</string> + <string name="pref_display_enter_key_summary">Поменять кнопку смайликов на кнопку ввода</string> + <string name="audio">аудио</string> + <string name="video">звук</string> + <string name="image">изображение</string> + <string name="pdf_document">PDF-документ</string> + <string name="apk">Приложение Android</string> + <string name="vcard">Контакт</string> + <string name="received_x_file">Получено %s</string> + <string name="disable_foreground_service">Отключить фоновую службу</string> + <string name="touch_to_open_conversations">Коснитесь, чтобы открыть Conversations</string> + <string name="avatar_has_been_published">Аватар загружен!</string> + <string name="sending_x_file">Отправляется %s</string> + <string name="offering_x_file">Предложен %s</string> + <string name="hide_offline">Скрыть пользователей вне сети</string> + <string name="disable_account">Отключить учётную запись</string> + <string name="contact_is_typing">%s набирает сообщение...</string> + <string name="contact_has_stopped_typing">%s прекратил набор</string> + <string name="pref_chat_states">Оповещения о наборе</string> + <string name="pref_chat_states_summary">Позволяет вашим контактам видеть когда вы пишете новое сообщение</string> + <string name="send_location">Отправить местоположение</string> + <string name="show_location">Показать местоположение</string> + <string name="no_application_found_to_display_location">Не найдено приложений для отображения местоположения</string> + <string name="location">Местоположение</string> + <string name="received_location">Получено местоположение</string> + <string name="title_undo_swipe_out_conversation">Беседа окончена</string> + <string name="title_undo_swipe_out_muc">Покинул беседу</string> + <string name="pref_certificate_options">Опции сертификата</string> + <string name="pref_dont_trust_system_cas_title">Не доверять системным УЦ</string> + <string name="pref_dont_trust_system_cas_summary">Все сертификаты должны быть подтверждены вручную</string> + <string name="pref_remove_trusted_certificates_title">Удалить сертификат</string> + <string name="pref_remove_trusted_certificates_summary">Удалить сертификаты, подтверждённые вручную</string> + <string name="toast_no_trusted_certs">Не найдено сертификатов, подтверждённых вручную</string> + <string name="dialog_manage_certs_title">Удалить сертификаты</string> + <string name="dialog_manage_certs_positivebutton">Удалить отмеченное</string> + <string name="dialog_manage_certs_negativebutton">Отмена</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">Удалён %d сертификат</item> + <item quantity="few">Удалено %d сертификатов</item> + <item quantity="many"></item> + <item quantity="other">Удалено %d сертификатов</item> + </plurals> + <plurals name="select_contact"> + <item quantity="one">Выбран %d контакт</item> + <item quantity="few">Выбрано %d контактов</item> + <item quantity="many"></item> + <item quantity="other">Выбрано %d контактов</item> + </plurals> + <string name="pref_quick_action_summary">Заменить кнопку отправки кнопкой быстрого действия</string> + <string name="pref_quick_action">Быстрое действие</string> + <string name="none">Нет</string> + <string name="recently_used">Последнее выбранное</string> + <string name="choose_quick_action">Выбрать быстрое действие</string> </resources> diff --git a/src/main/res/values-sk/strings.xml b/src/main/res/values-sk/strings.xml index 911f9cd5..01211ad2 100644 --- a/src/main/res/values-sk/strings.xml +++ b/src/main/res/values-sk/strings.xml @@ -80,7 +80,6 @@ <string name="send_otr_message">Poslať OTR šifrovanú správu</string> <string name="send_pgp_message">Poslať OpenPGP šifrovanú správu</string> <string name="your_nick_has_been_changed">Prezývka sa zmenila</string> - <string name="download_image">Stiahnuť obrázok</string> <string name="send_unencrypted">Poslať nešifrované</string> <string name="decryption_failed">Zašifrovanie zlyhalo. Možno nemáte správny privátny kľúč.</string> <string name="openkeychain_required">OpenKeychain</string> @@ -110,7 +109,7 @@ <string name="pref_conference_notifications_summary">Vždy upozorniť pri novej konferenčnej správe, nie len ak je zvýraznená</string> <string name="pref_notification_grace_period">Doba na prečítanie upozornenia</string> <string name="pref_notification_grace_period_summary">Neupozorňovať krátko po obdržaní kópie správy</string> - <string name="pref_advanced_options">Pokročilé nastavenia</string> + <string name="pref_advanced_options">Rozšírené možnosti</string> <string name="pref_never_send_crash">Neodosielať detaily o zlyhaní aplikácie</string> <string name="pref_never_send_crash_summary">Zaslaním detailov o dôvode zlyhania pomáhate ďalšiemu vývoju aplikácie Conversations</string> <string name="pref_confirm_messages">Potvrdzovať správy</string> @@ -266,7 +265,7 @@ <string name="pref_force_encryption_summary">Vždy zasielať šifrované správy (okrem skupinových konverzácií)</string> <string name="pref_dont_save_encrypted">Neukladať šifrované správy</string> <string name="pref_dont_save_encrypted_summary">Varovanie: Toto môže viesť k strate správ</string> - <string name="pref_expert_options">Expertné nastavenia</string> + <string name="pref_expert_options">Expertné možnosti</string> <string name="pref_expert_options_summary">S týmto narábajte veľmi opatrne, prosím</string> <string name="title_activity_about">O Conversations</string> <string name="pref_about_conversations_summary">Informácie o tvorbe a licencii</string> @@ -289,15 +288,13 @@ <string name="conference_members_only">Táto konverzácia je iba pre členov</string> <string name="conference_kicked">Vyčlenili vás z tejto konverzácie</string> <string name="using_account">Používa sa účet %s</string> - <string name="checking_image">Overuje sa obrázok na serveri HTTP</string> - <string name="image_file_deleted">Súbor s obrázkom bol vymazaný</string> <string name="not_connected_try_again">Nie ste pripojený. Skúste to neskôr</string> - <string name="check_image_filesize">Overiť veľkosť obrázku</string> + <string name="check_x_filesize">Overiť %s veľkosť</string> <string name="message_options">Možnosti správy</string> <string name="copy_text">Skopírovať text</string> <string name="copy_original_url">Skopírovať originálny URL</string> <string name="send_again">Poslať znova</string> - <string name="image_url">Obrázok URL</string> + <string name="file_url">URL súbor</string> <string name="message_text">Textová správa</string> <string name="url_copied_to_clipboard">URL skopírovaný do schránky</string> <string name="message_copied_to_clipboard">Správa skopírovaná do schránky</string> @@ -359,7 +356,7 @@ <string name="could_not_change_password">Nepodarilo sa zmeniť heslo</string> <string name="otr_session_not_started">Poslať správu pre spustenie šifrovaného chatu</string> <string name="ask_question">Položiť otázku</string> - <string name="smp_explain_question">Ak máte vy a váš kontakt spoločné tajomstvo, ktoré nikto iný nepozná (napríklad spoločný vtip alebo čo ste mali na obed na vašom poslednom stretnutí), môžete ho použiť na overenie vzájomných identifikátorov.\n\nZadáte pomôcku alebo otázku a kontakt na ňu správne odpovie. </string> + <string name="smp_explain_question">Ak máte vy a váš kontakt spoločné tajomstvo, ktoré nikto iný nepozná (napríklad nejaký spoločný vtip alebo len čo ste mali na obed, keď ste sa naposledy stretli), môžte použiť toto tajomstvo na overenie vzájomných identifikátorov. \n\nZadáte pomôcku alebo otázku pre váš kontakt, ktorý na ňu správne odpovie. </string> <string name="smp_explain_answer">Váš kontakt by chcel overiť váš identifikátor pomocou spoločného tajomstva. Váš kontakt zadal nasledujúcu pomôcku alebo otázku týkajúcu sa tajomstva. </string> <string name="shared_secret_hint_should_not_be_empty">Vaša pomôcka by nemala byť prázdna</string> <string name="shared_secret_can_not_be_empty">Vaše spoločné tajomstvo nemôže byť prázdne</string> @@ -375,7 +372,7 @@ <string name="no_role">Bez úlohy</string> <string name="outcast">Vylúčený</string> <string name="member">Člen</string> - <string name="advanced_mode">Pokročilý režim</string> + <string name="advanced_mode">Rozšírený režim</string> <string name="grant_membership">Povoliť členstvo</string> <string name="remove_membership">Odobrať členstvo</string> <string name="grant_admin_privileges">Povoliť administrátorské výsady</string> @@ -422,4 +419,36 @@ <string name="contact_has_stopped_typing">%s prestal písať</string> <string name="pref_chat_states">Upozornenia pri písaní</string> <string name="pref_chat_states_summary">Upozorniť kontakt, keď píšete novú správu</string> + <string name="send_location">Poslať polohu</string> + <string name="show_location">Zobraziť polohu</string> + <string name="no_application_found_to_display_location">Nenašla sa aplikácia na zobrazenie polohy</string> + <string name="location">Poloha</string> + <string name="received_location">Prijatá poloha</string> + <string name="title_undo_swipe_out_conversation">Konverzácia zatvorená</string> + <string name="title_undo_swipe_out_muc">Opustil skupinovú konverzáciu</string> + <string name="pref_certificate_options">Možnosti certifikátu</string> + <string name="pref_dont_trust_system_cas_title">Nedôverovať systému CAs</string> + <string name="pref_dont_trust_system_cas_summary">Všetky certifikáty musia byť ručne schválené</string> + <string name="pref_remove_trusted_certificates_title">Odstrániť certifikáty</string> + <string name="pref_remove_trusted_certificates_summary">Vymazať ručne schválené certifikáty</string> + <string name="toast_no_trusted_certs">Žiadne ručne schválené certifikáty</string> + <string name="dialog_manage_certs_title">Odstrániť certifikáty</string> + <string name="dialog_manage_certs_positivebutton">Vymazať výber</string> + <string name="dialog_manage_certs_negativebutton">Zrušiť</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">%d certifikátu vymazaných</item> + <item quantity="few">%d certifikátu vymazaných</item> + <item quantity="other">%d certifikátov vymazaných</item> + </plurals> + <plurals name="select_contact"> + <item quantity="one">Vybrať %d kontaktu</item> + <item quantity="few">Vybrať %d kontaktu</item> + <item quantity="other">Vybrať %d kontaktov</item> + </plurals> + <string name="pref_quick_action_summary">Nahradiť tlačidlo poslať rýchlou akciou</string> + <string name="pref_quick_action">Rýchla akcia</string> + <string name="none">Žiadny</string> + <string name="recently_used">Naposledy použitý</string> + <string name="choose_quick_action">Vybrať rýchlu voľbu</string> + <string name="file_not_found_on_remote_host">Súbor sa na vzdialenom serveri nenašiel</string> </resources> diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index 6517a309..082cfa46 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -80,7 +80,6 @@ <string name="send_otr_message">Пошаљи ОТР шифровану поруку</string> <string name="send_pgp_message">Пошаљи ОпенПГП шифровану поруку</string> <string name="your_nick_has_been_changed">Ваш надимак је промењен</string> - <string name="download_image">Преузми слику</string> <string name="send_unencrypted">Пошаљи нешифровано</string> <string name="decryption_failed">Шифровање није успело. Можда немате одговарајући лични кључ.</string> <string name="openkeychain_required">Отворени кључарник</string> @@ -97,8 +96,8 @@ <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_size_summary">Аутоматски прихваћај фајлове мање од…</string> + <string name="pref_accept_files">Прихватај фајлове</string> + <string name="pref_accept_files_summary">Аутоматски прихватај фајлове мање од…</string> <string name="pref_notification_settings">Поставке обавештења</string> <string name="pref_notifications">Обавештења</string> <string name="pref_notifications_summary">Обавести кад стигне нова порука</string> @@ -289,21 +288,20 @@ <string name="conference_members_only">Ова конференција је само за чланове</string> <string name="conference_kicked">Шутнути сте из ове конференције</string> <string name="using_account">преко налога %s</string> - <string name="checking_image">Проверавам слику на ХТТП домаћину</string> - <string name="image_file_deleted">Ова слика је обрисана</string> + <string name="checking_x">Проверавам %s на ХТТП домаћину</string> <string name="not_connected_try_again">Нисте повезани. Покушајте поново касније</string> - <string name="check_image_filesize">Провери величину слике</string> + <string name="check_x_filesize">Провери величину %s</string> <string name="message_options">Опције поруке</string> <string name="copy_text">Копирај текст</string> <string name="copy_original_url">Копирај изворни УРЛ</string> <string name="send_again">Пошаљи поново</string> - <string name="image_url">УРЛ слике</string> + <string name="file_url">УРЛ фајла</string> <string name="message_text">Текст поруке</string> <string name="url_copied_to_clipboard">УРЛ је копиран на клипборд</string> <string name="message_copied_to_clipboard">Порука је копирана на клипборд</string> <string name="image_transmission_failed">Пренос слике није успео</string> - <string name="scan_qr_code">Очитај бар-код</string> - <string name="show_qr_code">Прикажи бар-код</string> + <string name="scan_qr_code">Очитај бар-кôд</string> + <string name="show_qr_code">Прикажи бар-кôд</string> <string name="show_block_list">Прикажи списак блокираних</string> <string name="account_details">Детаљи налога</string> <string name="verify_otr">Овери ОТР</string> @@ -427,4 +425,32 @@ <string name="no_application_found_to_display_location">Нема апликације за приказ локације</string> <string name="location">Локација</string> <string name="received_location">Примљена локација</string> + <string name="title_undo_swipe_out_conversation">Преписка затворена</string> + <string name="title_undo_swipe_out_muc">Напусти конференцију</string> + <string name="pref_certificate_options">Опције сертификата</string> + <string name="pref_dont_trust_system_cas_title">Не веруј системским сертификационим телима</string> + <string name="pref_dont_trust_system_cas_summary">Сви сертификати морају ручно да се одобре</string> + <string name="pref_remove_trusted_certificates_title">Уклони сертификате</string> + <string name="pref_remove_trusted_certificates_summary">Обриши ручно одобрене сертификате</string> + <string name="toast_no_trusted_certs">Нема ручно одобрених сертификата</string> + <string name="dialog_manage_certs_title">Уклањање сертификата</string> + <string name="dialog_manage_certs_positivebutton">Обриши изабрано</string> + <string name="dialog_manage_certs_negativebutton">Одустани</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">%d сертификат обрисан</item> + <item quantity="few">%d сертификата обрисана</item> + <item quantity="other">%d сертификата обрисано</item> + </plurals> + <plurals name="select_contact"> + <item quantity="one">Изабери %d контакт</item> + <item quantity="few">Изабери %d контакта</item> + <item quantity="other">Изабери %d контаката</item> + </plurals> + <string name="pref_quick_action_summary">Замени дугме за слање брзом радњом</string> + <string name="pref_quick_action">Брза радња</string> + <string name="none">Ниједна</string> + <string name="recently_used">Недавно коришћена</string> + <string name="choose_quick_action">Изаберите брзу радњу</string> + <string name="file_not_found_on_remote_host">Фајл није нађен на удаљеном серверу</string> + <string name="search_for_contacts_or_groups">Тражите контакте или групе</string> </resources> diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 6f2b9858..d635831c 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -36,10 +36,10 @@ <string name="participant">Deltagare</string> <string name="visitor">Besökare</string> <string name="remove_contact_text">Vill du ta bort %s från din kontaktlista? Konversationer associerade med denna kontakt kommer inte tas bort.</string> - <string name="block_contact_text">Vill du blockera %s från att skicka dig meddelanden</string> + <string name="block_contact_text">Vill du blockera %s från att skicka dig meddelanden?</string> <string name="unblock_contact_text">Vill du avblockera %s och tillåta denne att skicka dig meddelanden?</string> - <string name="block_domain_text">Blockera alla kontakter från %s</string> - <string name="unblock_domain_text">Avblockera alla kontakter från %s</string> + <string name="block_domain_text">Blockera alla kontakter från %s?</string> + <string name="unblock_domain_text">Avblockera alla kontakter från %s?</string> <string name="contact_blocked">Kontakt blockerad</string> <string name="remove_bookmark_text">Vill du ta bort %s som bokmärke? Konversationer associerade med detta bokmärke kommer inte tas bort.</string> <string name="register_account">Registrera nytt konto på servern</string> @@ -74,17 +74,16 @@ <string name="clear_conversation_history">Rensa konversationshistorik</string> <string name="clear_histor_msg">Vill du ta bort alla meddelanden i denna konversation?\n\n<b>Varning:</b> Detta kommer inte påverka meddelanden lagrade på andra enheter eller servrar.</string> <string name="delete_messages">Ta bort meddelanden</string> - <string name="also_end_conversation">Avsluta denna konversation efter</string> + <string name="also_end_conversation">Avsluta sedan denna konversation</string> <string name="choose_presence">Välj tillgänglighet till kontakt</string> <string name="send_plain_text_message">Skicka meddelande i klartext</string> <string name="send_otr_message">Skicka OTR-krypterat meddelande</string> <string name="send_pgp_message">Skicka OpenPGP-krypterat meddelande</string> <string name="your_nick_has_been_changed">Ditt nick har ändrats</string> - <string name="download_image">Ladda ner bild</string> <string name="send_unencrypted">Skicka okrypterat</string> - <string name="decryption_failed">Avkryptering gick fel. Du kanske inte har rätt privat nyckel.</string> + <string name="decryption_failed">Avkryptering misslyckades. Du har kanske kanske inte rätt privat nyckel.</string> <string name="openkeychain_required">OpenKeychain</string> - <string name="openkeychain_required_long">Conversations använder en tredjeparts applikation som heter <b>OpenKeychain</b> för att kryptera och avkryptera meddelanden och hantera dina publika nycklar.\n\nOpenKeychain är licensierat under GPLv3 och tillgängligt på F-Droid och Google Play.\n\n<small>(Starta om Conversations efter.)</small></string> + <string name="openkeychain_required_long">Conversations använder en tredjeparts-applikation som heter <b>OpenKeychain</b> för att kryptera och avkryptera meddelanden och hantera dina publika nycklar.\n\nOpenKeychain är licensierad under GPLv3 och tillgänglig på F-Droid och Google Play.\n\n<small>(Starta om Conversations efter installation.)</small></string> <string name="restart">Starta om</string> <string name="install">Installera</string> <string name="offering">erbjuder…</string> @@ -229,7 +228,7 @@ <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> - <string name="contact_has_read_up_to_this_point">%s har läst fram hit</string> + <string name="contact_has_read_up_to_this_point">%s har läst hit</string> <string name="publish">Publicera</string> <string name="touch_to_choose_picture">Tryck på avatarbild för att välja en bild från bildgalleriet</string> <string name="publish_avatar_explanation">Notera: Alla som kan se dina tillgänglighetsuppdateringar kommer se denna bild.</string> @@ -289,15 +288,14 @@ <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="using_account">använder konto %s</string> - <string name="checking_image">Kontrollerar bild på HTTP host</string> - <string name="image_file_deleted">Bildfilen har blivit borttagen</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_image_filesize">Kontrollera bildens filstorlek</string> + <string name="check_x_filesize">Kontrollera storleken på %s</string> <string name="message_options">Meddelandealternativ</string> <string name="copy_text">Kopiera text</string> <string name="copy_original_url">Kopiera orginal-URL</string> <string name="send_again">Skicka igen</string> - <string name="image_url">Bild-URL</string> + <string name="file_url">Fil URL</string> <string name="message_text">Meddelandetext</string> <string name="url_copied_to_clipboard">URL kopierad till urklipp</string> <string name="message_copied_to_clipboard">Meddelande kopierat till urklipp</string> @@ -427,8 +425,30 @@ <string name="no_application_found_to_display_location">Kunde inte hitta applikation för att visa position</string> <string name="location">Position</string> <string name="received_location">Mottog position</string> + <string name="title_undo_swipe_out_conversation">Konversation stängd</string> + <string name="title_undo_swipe_out_muc">Lämnade konferens</string> + <string name="pref_certificate_options">Certifikatalternativ</string> + <string name="pref_dont_trust_system_cas_title">Lita inte på systemets CAs</string> + <string name="pref_dont_trust_system_cas_summary">Alla certifikat måste manuellt godkännas</string> + <string name="pref_remove_trusted_certificates_title">Ta bort certifikat</string> + <string name="pref_remove_trusted_certificates_summary">Ta bort manuellt accepterade certifikat</string> + <string name="toast_no_trusted_certs">Inga manuellt accepterade certifikat</string> + <string name="dialog_manage_certs_title">Ta bort certifikat</string> + <string name="dialog_manage_certs_positivebutton">Ta bort val</string> + <string name="dialog_manage_certs_negativebutton">Avbryt</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">%d certifikat borttaget</item> + <item quantity="other">%d certifikat borttagna</item> + </plurals> <plurals name="select_contact"> <item quantity="one">Välj %d kontakt</item> <item quantity="other">Välj %d kontakter</item> </plurals> + <string name="pref_quick_action_summary">Byt sänd-knappen mot snabbfunktion</string> + <string name="pref_quick_action">Snabbfunktion</string> + <string name="none">Ingen</string> + <string name="recently_used">Senast använd</string> + <string name="choose_quick_action">Välj snabbfunktion</string> + <string name="file_not_found_on_remote_host">Filen hittas ej på servern</string> + <string name="search_for_contacts_or_groups">Sök efter kontakter eller grupper</string> </resources> diff --git a/src/main/res/values-v21/dimens.xml b/src/main/res/values-v21/dimens.xml new file mode 100644 index 00000000..b689d100 --- /dev/null +++ b/src/main/res/values-v21/dimens.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <dimen name="elv_undo_bottom_offset">63dp</dimen> <!-- 48dp + 15dp --> + <dimen name="image_button_padding">12dp</dimen> +</resources>
\ No newline at end of file diff --git a/src/main/res/values-v21/themes.xml b/src/main/res/values-v21/themes.xml index 75539533..d1679f92 100644 --- a/src/main/res/values-v21/themes.xml +++ b/src/main/res/values-v21/themes.xml @@ -2,33 +2,31 @@ <resources> <style name="ConversationsTheme" parent="@android:style/Theme.Material.Light.DarkActionBar"> - <item name="android:colorPrimary">@color/primary</item> - <item name="android:colorPrimaryDark">@color/primarydark</item> + <item name="android:colorPrimary">@color/green500</item> + <item name="android:colorPrimaryDark">@color/green700</item> <item name="android:colorAccent">@color/accent</item> <item name="TextSizeInfo">12sp</item> <item name="TextSizeBody">14sp</item> <item name="TextSizeHeadline">20sp</item> - <item name="attr/icon_add_group">@drawable/ic_group_add_white_48dp</item> - <item name="attr/icon_add_person">@drawable/ic_person_add_white_48dp</item> + <item name="attr/icon_add_group">@drawable/ic_group_add_white_24dp</item> + <item name="attr/icon_add_person">@drawable/ic_person_add_white_24dp</item> <item name="attr/icon_cancel">@drawable/ic_cancel_white_24dp</item> - <item name="attr/icon_chat">@drawable/ic_chat_white_48dp</item> - <item name="attr/icon_copy">@drawable/ic_content_copy_grey600_36dp</item> - <item name="attr/icon_discard">@drawable/ic_delete_white_48dp</item> + <item name="attr/icon_copy">@drawable/ic_content_copy_grey600_24dp</item> + <item name="attr/icon_discard">@drawable/ic_delete_white_24dp</item> <item name="attr/icon_download">@drawable/ic_file_download_white_24dp</item> - <item name="attr/icon_edit">@drawable/ic_edit_white_48dp</item> - <item name="attr/icon_edit_dark">@drawable/ic_edit_grey600_36dp</item> - <item name="attr/icon_group">@drawable/ic_group_white_48dp</item> - <item name="attr/icon_new">@drawable/ic_add_white_48dp</item> - <item name="attr/icon_new_attachment">@drawable/ic_attach_file_white_48dp</item> - <item name="attr/icon_not_secure">@drawable/ic_lock_open_white_48dp</item> - <item name="attr/icon_refresh">@drawable/ic_refresh_white_48dp</item> - <item name="attr/icon_remove">@drawable/ic_remove_grey600_36dp</item> - <item name="attr/icon_search">@drawable/ic_search_white_48dp</item> - <item name="attr/icon_secure">@drawable/ic_lock_outline_white_48dp</item> - <item name="attr/icon_settings">@drawable/ic_settings_grey600_36dp</item> - <item name="attr/icon_import_export">@drawable/ic_import_export_white_48dp</item> + <item name="attr/icon_edit">@drawable/ic_edit_white_24dp</item> + <item name="attr/icon_edit_dark">@drawable/ic_edit_grey600_24dp</item> + <item name="attr/icon_group">@drawable/ic_group_white_24dp</item> + <item name="attr/icon_new">@drawable/ic_add_white_24dp</item> + <item name="attr/icon_new_attachment">@drawable/ic_attach_file_white_24dp</item> + <item name="attr/icon_not_secure">@drawable/ic_lock_open_white_24dp</item> + <item name="attr/icon_remove">@drawable/ic_delete_grey600_24dp</item> + <item name="attr/icon_search">@drawable/ic_search_white_24dp</item> + <item name="attr/icon_secure">@drawable/ic_lock_open_white_24dp</item> + <item name="attr/icon_settings">@drawable/ic_settings_grey600_24dp</item> + <item name="attr/icon_import_export">@drawable/ic_import_export_white_24dp</item> </style> diff --git a/src/main/res/values-w600dp/dimens.xml b/src/main/res/values-w585dp/dimens.xml index df6525e6..df6525e6 100644 --- a/src/main/res/values-w600dp/dimens.xml +++ b/src/main/res/values-w585dp/dimens.xml diff --git a/src/main/res/values-w960dp/dimens.xml b/src/main/res/values-w945dp/dimens.xml index 146c0e15..146c0e15 100644 --- a/src/main/res/values-w960dp/dimens.xml +++ b/src/main/res/values-w945dp/dimens.xml diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index b78176c4..32bfa38a 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -11,6 +11,10 @@ <string name="action_edit_contact">编辑姓名</string> <string name="action_add_phone_book">添加到手机通讯录</string> <string name="action_delete_contact">从列表中删除</string> + <string name="action_block_contact">屏蔽联系人</string> + <string name="action_unblock_contact">解除联系人屏蔽</string> + <string name="action_block_domain">屏蔽域名</string> + <string name="action_unblock_domain">解除域名屏蔽</string> <string name="title_activity_manage_accounts">管理账户</string> <string name="title_activity_settings">设置</string> <string name="title_activity_conference_details">讨论组详情</string> @@ -18,6 +22,7 @@ <string name="title_activity_sharewith">分享会话</string> <string name="title_activity_start_conversation">开始会话</string> <string name="title_activity_choose_contact">选择联系人</string> + <string name="title_activity_block_list">屏蔽列表</string> <string name="just_now">刚刚</string> <string name="minute_ago">1 分钟前</string> <string name="minutes_ago">%d分钟前</string> @@ -30,56 +35,64 @@ <string name="moderator">版主</string> <string name="participant">参与者</string> <string name="visitor">访客</string> - <string name="remove_contact_text">将 %s从列表中移除? 与该联系人的会话消息不会清除.</string> - <string name="remove_bookmark_text">从书签中移除 %s?相关会话消息不会被清除 .</string> + <string name="remove_contact_text">将 %s 从列表中移除? 与该联系人的会话消息不会清除.</string> + <string name="block_contact_text">你想屏蔽 %s 将不能发送信息给你?</string> + <string name="unblock_contact_text">你想解除对 %s 的屏蔽吗,他们将可以发送信息给你?</string> + <string name="block_domain_text">屏蔽 %s 中的所有联系人?</string> + <string name="unblock_domain_text">解除对 %s 中所有联系人的屏蔽?</string> + <string name="contact_blocked">联系人已屏蔽</string> + <string name="remove_bookmark_text">从书签中移除 %s ?相关会话消息不会被清除 .</string> <string name="register_account">在服务器上注册新账户</string> + <string name="change_password_on_server">在服务器上改变密码</string> <string name="share_with">分享</string> <string name="start_conversation">开始会话</string> <string name="invite_contact">邀请联系人</string> <string name="contacts">联系人</string> <string name="cancel">取消</string> + <string name="set">设置</string> <string name="add">添加</string> <string name="edit">编辑</string> <string name="delete">删除</string> + <string name="block">屏蔽</string> + <string name="unblock">解除屏蔽</string> <string name="save">保存</string> <string name="ok">完成</string> - <string name="crash_report_title">Conversations停止运行</string> - <string name="crash_report_message">发送堆栈跟踪到正在开发Conversations的人员\n<b>警告:</b> 该操作将用您的 XMPP账户发送堆栈跟踪到开发人员.</string> + <string name="crash_report_title">Conversations 崩溃</string> + <string name="crash_report_message">发送堆栈跟踪信息到 Conversations 的开发人员\n<b>警告:</b> 该操作将用您的 XMPP 账户发送堆栈跟踪给开发人员。</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="touch_to_fix">点击此处管理账户</string> - <string name="attach_file">附件</string> - <string name="not_in_roster">该联系人不在您的列表.需要加为联系人吗 ?</string> + <string name="attach_file">附加文件</string> + <string name="not_in_roster">该联系人不在您的列表。需要加为联系人吗 ?</string> <string name="add_contact">添加联系人</string> <string name="send_failed">传递失败</string> <string name="send_rejected">拒绝</string> <string name="preparing_image">准备传输图像</string> <string name="action_clear_history">清除历史记录</string> <string name="clear_conversation_history">清除会话记录</string> - <string name="clear_histor_msg">删除该会话中所有信息?\n\n<b>注:</b> 该操作不会影响其他设备或服务器保存的信息.</string> + <string name="clear_histor_msg">删除该会话中所有信息?\n\n<b>注:</b> 该操作不会影响其他设备或服务器保存的信息。</string> <string name="delete_messages">删除消息</string> <string name="also_end_conversation">之后结束该会话</string> <string name="choose_presence">添加在线用户至联系人</string> <string name="send_plain_text_message">发送纯文本信息</string> <string name="send_otr_message">发送 OTR 加密信息</string> <string name="send_pgp_message">发送 OpenPGP 加密信息</string> - <string name="your_nick_has_been_changed">用户名修改成功</string> - <string name="download_image">下载图片</string> + <string name="your_nick_has_been_changed">昵称修改成功</string> <string name="send_unencrypted">不加密发送</string> - <string name="decryption_failed">解密失败,可能是私钥不正确.</string> + <string name="decryption_failed">解密失败,可能是私钥不正确。</string> <string name="openkeychain_required">OpenKeychain</string> - <string name="openkeychain_required_long">会话运用了第三方app,名为 <b>OpenKeychain</b> 用来加密、解码信息以及管理您的公钥.\n\nOpenKeychain 遵循 GPLv3 并且在 F-Droid和Google Play上可操作.\n\n<small>(之后请重启conversations.)</small></string> + <string name="openkeychain_required_long">会话运用了第三方app,名为 <b>OpenKeychain</b> 用来加密、解密信息以及管理您的密钥。\n\nOpenKeychain 遵循 GPLv3 并且可以在 F-Droid 和 Google Play 上获取。\n\n<small>(之后请重启 conversations)</small></string> <string name="restart">重启</string> <string name="install">安装</string> <string name="offering">输入…</string> <string name="waiting">等待…</string> - <string name="no_pgp_key">未发现OpenPGP 密码</string> - <string name="contact_has_no_pgp_key">会话加密信息失败,因为联系人未告知他/她的公钥.\n\n<small>请通知联系人设置 OpenPGP.</small></string> - <string name="no_pgp_keys">未找到 OpenPGP 密码</string> + <string name="no_pgp_key">未发现 OpenPGP 密钥</string> + <string name="contact_has_no_pgp_key">会话加密信息失败,因为联系人未提供他/她的公钥。\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="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> @@ -91,41 +104,41 @@ <string name="pref_vibrate">震动</string> <string name="pref_vibrate_summary">收到新消息时震动</string> <string name="pref_sound">声音</string> - <string name="pref_sound_summary">收到新消息时播放铃声</string> + <string name="pref_sound_summary">收到新消息时的铃声</string> <string name="pref_conference_notifications">讨论组通知</string> <string name="pref_conference_notifications_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_never_send_crash">总不发送故障报告</string> - <string name="pref_never_send_crash_summary">发送堆栈跟踪帮助Conversations开发人员</string> + <string name="pref_never_send_crash">总不发送崩溃报告</string> + <string name="pref_never_send_crash_summary">发送堆栈跟踪帮助 Conversations 开发人员</string> <string name="pref_confirm_messages">确认消息</string> <string name="pref_confirm_messages_summary">当你已收到消息并且已阅时通知好友</string> - <string name="pref_ui_options">UI选项</string> + <string name="pref_ui_options">UI 选项</string> <string name="openpgp_error">OpenKeychain 报告了一个错误</string> - <string name="error_decrypting_file">解码文件时出现I/O错误</string> + <string name="error_decrypting_file">解密文件时出现 I/O 错误</string> <string name="accept">接受</string> <string name="error">产生了一个错误</string> <string name="pref_grant_presence_updates">同意更新在线联系人</string> - <string name="pref_grant_presence_updates_summary">预先同意并请求您的联系人进行更新</string> + <string name="pref_grant_presence_updates_summary">预先同意并请求更新您的联系人</string> <string name="subscriptions">关注</string> <string name="your_account">你的账号</string> - <string name="keys">Keys</string> - <string name="send_presence_updates">发送在线联系人更新列表</string> - <string name="receive_presence_updates">接收在线联系人更新列表</string> - <string name="ask_for_presence_updates">请求在线联系人更新列表</string> + <string name="keys">密钥</string> + <string name="send_presence_updates">发送在线联系人列表更新</string> + <string name="receive_presence_updates">接收在线联系人列表更新</string> + <string name="ask_for_presence_updates">请求在线联系人列表更新</string> <string name="attach_choose_picture">选择图片</string> <string name="attach_take_picture">照相</string> <string name="preemptively_grant">预先同意订阅请求</string> <string name="error_not_an_image_file">您选择的文件不是图像文件</string> <string name="error_compressing_image">转换图像出错</string> <string name="error_file_not_found">未找到文件</string> - <string name="error_io_exception">常规的I/O错误.可能是存储空间不足的原因?</string> - <string name="error_security_exception_during_image_copy">您用来选择图片的app没有给予足够权限支持我们读取文件.\n\n<small>请使用另一文件管理器选择图片</small></string> + <string name="error_io_exception">常规的 I/O 错误。可能是存储空间不足?</string> + <string name="error_security_exception_during_image_copy">您用来选择图片的 app 没有给予足够权限支持我们读取文件。\n\n<small>请使用另一文件管理器选择图片</small></string> <string name="account_status_unknown">未知</string> <string name="account_status_disabled">暂时不可用</string> <string name="account_status_online">在线</string> - <string name="account_status_connecting">Connecting\u2026</string> + <string name="account_status_connecting">连接中\u2026</string> <string name="account_status_offline">离线</string> <string name="account_status_unauthorized">未授权</string> <string name="account_status_not_found">未找到服务器</string> @@ -134,6 +147,8 @@ <string name="account_status_regis_conflict"> 用户名已存在</string> <string name="account_status_regis_success">注册完成</string> <string name="account_status_regis_not_sup">服务器不支持注册</string> + <string name="account_status_security_error">安全错误</string> + <string name="account_status_incompatible_server">服务器不兼容</string> <string name="encryption_choice_none">纯文本内容</string> <string name="encryption_choice_otr">OTR</string> <string name="encryption_choice_pgp">OpenPGP</string> @@ -141,11 +156,11 @@ <string name="mgmt_account_delete">删除账号</string> <string name="mgmt_account_disable">暂时不可用</string> <string name="mgmt_account_publish_avatar">发布头像</string> - <string name="mgmt_account_publish_pgp">发布 OpenPGP 公共秘钥</string> + <string name="mgmt_account_publish_pgp">发布 OpenPGP 公钥</string> <string name="mgmt_account_enable">启用账户</string> <string name="mgmt_account_are_you_sure">确定?</string> <string name="mgmt_account_delete_confirm_text">如果删除用户,所有会话信息将会丢失</string> - <string name="attach_record_voice">Record voice 录音</string> + <string name="attach_record_voice">录音</string> <string name="account_settings_jabber_id">Jabber ID</string> <string name="account_settings_password">密码</string> <string name="account_settings_example_jabber_id">username@example.com</string> @@ -153,23 +168,28 @@ <string name="password">密码</string> <string name="confirm_password">确认密码</string> <string name="passwords_do_not_match">密码不一致</string> - <string name="invalid_jid">该Jabber ID 无效</string> - <string name="error_out_of_memory">空间不足,图片过大</string> + <string name="invalid_jid">该 Jabber ID 无效</string> + <string name="error_out_of_memory">空间不足。图片过大</string> <string name="add_phone_book_text">您将添加 %s 至手机联系人列表?</string> <string name="contact_status_online">在线</string> - <string name="contact_status_free_to_chat">免费对话</string> + <string name="contact_status_free_to_chat">自由畅聊</string> <string name="contact_status_away">离开</string> <string name="contact_status_extended_away">长时间离开</string> <string name="contact_status_do_not_disturb">请勿打扰</string> <string name="contact_status_offline">离线</string> <string name="muc_details_conference">讨论组</string> <string name="muc_details_other_members">其他成员</string> + <string name="server_info_show_more">服务器信息</string> + <string name="server_info_mam">XEP-0313: MAM</string> <string name="server_info_carbon_messages">XEP-0280: 消息碳</string> + <string name="server_info_csi">XEP-0352: 客户端状态指示</string> + <string name="server_info_blocking">XEP-0191: 屏蔽指令</string> + <string name="server_info_roster_version">XEP-0237: 花名册版本</string> <string name="server_info_stream_management">XEP-0198: 流管理</string> <string name="server_info_pep">XEP-0163: PEP (头像)</string> <string name="server_info_available">有效</string> <string name="server_info_unavailable">无效</string> - <string name="missing_public_keys">缺少公共秘钥公告</string> + <string name="missing_public_keys">缺少公钥通知</string> <string name="last_seen_now">最近一次查看为刚刚</string> <string name="last_seen_min"> 最近一次查看为一分钟前</string> <string name="last_seen_mins">最近一次查看为 %d 分钟前</string> @@ -178,9 +198,9 @@ <string name="last_seen_day">最近一次查看为一天前</string> <string name="last_seen_days">最近一次查看为 %d天前</string> <string name="never_seen">未曾查看</string> - <string name="install_openkeychain">加密信息. 请安装OpenKeychain进行解码.</string> - <string name="unknown_otr_fingerprint">未知 OTR指纹</string> - <string name="openpgp_messages_found">OpenPGP 发现加密信息</string> + <string name="install_openkeychain">加密信息. 请安装 OpenKeychain 以解密。</string> + <string name="unknown_otr_fingerprint">未知 OTR 指纹</string> + <string name="openpgp_messages_found">发现 OpenPGP 加密信息</string> <string name="reception_failed">接收失败</string> <string name="your_fingerprint">你的指纹</string> <string name="otr_fingerprint">OTR 指纹</string> @@ -192,6 +212,8 @@ <string name="join_conference">加入讨论组</string> <string name="delete_contact">删除联系人</string> <string name="view_contact_details">查看联系人详细信息</string> + <string name="block_contact">屏蔽联系人</string> + <string name="unblock_contact">解除联系人屏蔽</string> <string name="create">创建</string> <string name="contact_already_exists">联系人已存在</string> <string name="join">加入</string> @@ -200,26 +222,26 @@ <string name="save_as_bookmark">保存为书签</string> <string name="delete_bookmark">删除书签</string> <string name="bookmark_already_exists">该书签已存在</string> - <string name="you">你的</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> - <string name="contact_has_read_up_to_this_point">目前读到%s 处</string> + <string name="contact_has_read_up_to_this_point">目前读到 %s 处</string> <string name="publish">发布</string> - <string name="touch_to_choose_picture">点击头像可选择头像 </string> - <string name="publish_avatar_explanation">请注意: 所有关注您最新动态的人将看到该图像.</string> - <string name="publishing">发布…</string> + <string name="touch_to_choose_picture">点击头像可从相册中选择头像 </string> + <string name="publish_avatar_explanation">请注意: 所有关注您最新动态的人将看到该图像。</string> + <string name="publishing">正在发布…</string> <string name="error_publish_avatar_server_reject">服务器拒绝了您的发布请求</string> - <string name="error_publish_avatar_converting">转换头像出错</string> - <string name="error_saving_avatar">不能将头像保存至disk</string> + <string name="error_publish_avatar_converting">转换头像图片出错</string> + <string name="error_saving_avatar">不能将头像保存至磁盘</string> <string name="or_long_press_for_default">(或长按按钮将返回默认头像)</string> <string name="error_publish_avatar_no_server_support">您的服务器不支持发布头像</string> <string name="private_message">密谈</string> <string name="private_message_to">至 %s</string> - <string name="send_private_message_to">发送私密消息到%s</string> - <string name="connect">Connect</string> + <string name="send_private_message_to">发送私密消息到 %s</string> + <string name="connect">连接</string> <string name="account_already_exists">该账号已存在</string> <string name="next">下一步</string> <string name="server_info_session_established">当前会话已建立</string> @@ -232,21 +254,194 @@ <string name="conference_requires_password">讨论组设有密码</string> <string name="enter_password">输入密码</string> <string name="missing_presence_updates">缺少在线联系人更新</string> - <string name="request_presence_updates">请先发送更新在线联系人请求.\n\n<small>这将用来判断您的联系人所用的客户端类型人.</small></string> + <string name="request_presence_updates">请先发送更新在线联系人的请求。\n\n<small>以判断您的联系人所用的客户端类型。</small></string> <string name="request_now">现在发送请求</string> <string name="delete_fingerprint">删除指纹</string> <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="without_mutual_presence_updates"><b>警告:</b>在没有相互更新在线联系人的情况下发送将会出现未知问题。\n\n<small>前往联系人详情以验证您订阅的在线联系人。</small></string> <string name="pref_encryption_settings">加密设置</string> - <string name="pref_force_encryption">强制要求 end-to-end 加密</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">Expert 选项</string> + <string name="pref_expert_options">导出选项</string> <string name="pref_expert_options_summary">请谨慎使用</string> + <string name="title_activity_about">关于 Conversations</string> + <string name="pref_about_conversations_summary">构建及许可证信息</string> + <string name="title_pref_quiet_hours">静默时间段</string> + <string name="title_pref_quiet_hours_start_time">开始时间</string> + <string name="title_pref_quiet_hours_end_time">结束时间</string> + <string name="title_pref_enable_quiet_hours">启用静默时间段</string> + <string name="pref_quiet_hours_summary">在静默时间段内通知将保持静音</string> <string name="pref_use_larger_font"> 放大字体</string> - <string name="pref_use_larger_font_summary">整个app界面使用更大号的字体</string> + <string name="pref_use_larger_font_summary">整个 app 界面使用较大的字体</string> <string name="pref_use_send_button_to_indicate_status">发送按钮显示状态</string> + <string name="pref_use_indicate_received">请求消息回复</string> + <string name="pref_use_indicate_received_summary">如果支持消息将会以绿色对勾标识</string> <string name="pref_use_send_button_to_indicate_status_summary">发送按钮采用其他颜色以示发送状态的区别</string> + <string name="pref_expert_options_other">其他</string> + <string name="pref_conference_name">讨论组名称</string> + <string name="pref_conference_name_summary">用讨论组的主题来标示讨论组而不是 JID</string> + <string name="toast_message_otr_fingerprint">OTR 指纹已拷贝到剪贴板!</string> + <string name="conference_banned">你被此讨论组屏蔽</string> + <string name="conference_members_only">此讨论组只允许成员加入</string> + <string name="conference_kicked">你被从此讨论组踢出</string> + <string name="using_account">用账户 %s</string> + <string name="not_connected_try_again">你没有连接。请稍后重试</string> + <string name="message_options">消息选项</string> + <string name="copy_text">拷贝文本</string> + <string name="copy_original_url">拷贝原始URL</string> + <string name="send_again">再次发送</string> + <string name="message_text">消息文本</string> + <string name="url_copied_to_clipboard">已经拷贝 URL 到剪贴板</string> + <string name="message_copied_to_clipboard">消息已经拷贝到剪贴板</string> + <string name="image_transmission_failed">图片传送失败</string> + <string name="scan_qr_code">扫描二维码</string> + <string name="show_qr_code">显示二维码</string> + <string name="show_block_list">显示屏蔽列表</string> + <string name="account_details">账户详情</string> + <string name="verify_otr">验证 OTR</string> + <string name="remote_fingerprint">远程指纹</string> + <string name="scan">扫描</string> + <string name="or_touch_phones">(或轻触手机)</string> + <string name="smp">Socialist Millionaire Protocol</string> + <string name="shared_secret_hint">提示或问题</string> + <string name="shared_secret_secret">共知的秘密</string> + <string name="confirm">确认</string> + <string name="in_progress">处理中</string> + <string name="respond">回应</string> + <string name="failed">失败</string> + <string name="secrets_do_not_match">秘密不符</string> + <string name="try_again">再试一遍</string> + <string name="finish">完成</string> + <string name="verified">验证通过!</string> + <string name="smp_requested">联系人请求 SMP 验证</string> + <string name="no_otr_session_found">没有找到 OTR 会话</string> + <string name="conversations_foreground_service">Conversations</string> + <string name="pref_keep_foreground_service">保持前台服务</string> + <string name="pref_keep_foreground_service_summary">防止操作系统中断你的连接</string> + <string name="choose_file">关闭文件</string> + <string name="receiving_x_file">接收中 %1$s (已完成 %2$d%%)</string> + <string name="download_x_file">下载 %s</string> + <string name="file">文件</string> + <string name="open_x_file"> 打开 %s</string> + <string name="sending_file">发送中 (已完成 %1$d%%)</string> + <string name="preparing_file">准备传送文件</string> + <string name="x_file_offered_for_download">可以下载 %s</string> + <string name="cancel_transmission">取消传送</string> + <string name="file_transmission_failed">文件传送失败</string> + <string name="file_deleted">文件已经删除</string> + <string name="no_application_found_to_open_file">没有可以打开此文件的应用</string> + <string name="could_not_verify_fingerprint">不能验证指纹</string> + <string name="manually_verify">手工验证</string> + <string name="are_you_sure_verify_fingerprint">你确认验证你的联系人的 OTR 指纹?</string> + <string name="pref_show_dynamic_tags">现实动态标签</string> + <string name="pref_show_dynamic_tags_summary">在联系人下方显示只读标签</string> + <string name="enable_notifications">启用通知</string> + <string name="conference_with">与…创建讨论组</string> + <string name="no_conference_server_found">无法找到讨论组服务器</string> + <string name="conference_creation_failed">讨论组创建失败!</string> + <string name="conference_created">讨论组已创建!</string> + <string name="secret_accepted">秘密被接受!</string> + <string name="reset">重置</string> + <string name="account_image_description">账户头像</string> + <string name="copy_otr_clipboard_description">拷贝 OTR 指纹到剪贴板</string> + <string name="fetching_history_from_server">从服务器获取历史记录</string> + <string name="no_more_history_on_server">服务器上没有更多历史记录</string> + <string name="updating">更新中…</string> + <string name="password_changed">密码已修改!</string> + <string name="could_not_change_password">不能修改密码</string> + <string name="otr_session_not_started">要启动加密聊天先发送一条消息</string> + <string name="ask_question">提出问题</string> + <string name="smp_explain_question">如果你和你的联系人有一个共知的秘密(比如一个内部笑话或者仅仅只是上次见面时吃的午餐) 你可以使用这个秘密来验证彼此的指纹。\n\n你的联系人将以大小写敏感的方式给出答案,你可以给出提示或问题。</string> + <string name="smp_explain_answer">你的联系人可以通过一个你们共知的秘密来验证指纹。你的联系人给出了如下的提示或问题。</string> + <string name="shared_secret_hint_should_not_be_empty">你的提示不能为空</string> + <string name="shared_secret_can_not_be_empty">你共知的秘密不能为空</string> + <string name="manual_verification_explanation">请仔细核对下面显示出来的你的联系人的指纹。\n你可以使用任何可信赖的联系方式,比如加密邮件或电话,来交换这些指纹信息。</string> + <string name="change_password">修改密码</string> + <string name="current_password">当前密码</string> + <string name="new_password">新密码</string> + <string name="password_should_not_be_empty">密码不能为空</string> + <string name="enable_all_accounts">启用所有账户</string> + <string name="disable_all_accounts">禁用所有账户</string> + <string name="perform_action_with">做一个动作和</string> + <string name="no_affiliation">没有从属关系</string> + <string name="no_role">没有角色</string> + <string name="outcast">抛弃</string> + <string name="member">成员</string> + <string name="advanced_mode">高级模式</string> + <string name="grant_membership">已授予的成员</string> + <string name="remove_membership">吊销的成员</string> + <string name="grant_admin_privileges">授予管理员权限</string> + <string name="remove_admin_privileges">吊销管理员权限</string> + <string name="remove_from_room">从讨论组移出</string> + <string name="could_not_change_affiliation">不能修改 %s 的从属关系</string> + <string name="ban_from_conference">屏蔽出讨论组</string> + <string name="removing_from_public_conference">你正尝试将 %s 移出一个公共的讨论组。唯一的方式是永远屏蔽这个用户</string> + <string name="ban_now">现在屏蔽</string> + <string name="could_not_change_role">不能修改 %s 的角色</string> + <string name="public_conference">公开访问的讨论组</string> + <string name="private_conference">私密,只有成员可以加入的讨论组</string> + <string name="conference_options">讨论组选项</string> + <string name="members_only">私密(只对成员开放)</string> + <string name="non_anonymous">非匿名</string> + <string name="modified_conference_options">讨论组选项已修改!</string> + <string name="could_not_modify_conference_options">不能修改讨论组选项</string> + <string name="never">从不</string> + <string name="thirty_minutes">30 分钟</string> + <string name="one_hour">1 个小时</string> + <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_enter_is_send">回车是发送</string> + <string name="pref_enter_is_send_summary">用回车键来发送消息</string> + <string name="pref_display_enter_key">显示回车键</string> + <string name="pref_display_enter_key_summary">改变表情键为回车键</string> + <string name="audio">音频</string> + <string name="video">视频</string> + <string name="image">图像</string> + <string name="pdf_document">PDF 文档</string> + <string name="apk">Android App</string> + <string name="vcard">联系人</string> + <string name="received_x_file">已经收到 %s</string> + <string name="disable_foreground_service">禁用前端服务</string> + <string name="touch_to_open_conversations">轻触打开 Conversations</string> + <string name="avatar_has_been_published">头像已经发布!</string> + <string name="sending_x_file">发送中 %s</string> + <string name="offering_x_file">提供中 %s</string> + <string name="hide_offline">隐藏离线联系人</string> + <string name="disable_account">禁用账户</string> + <string name="contact_is_typing">%s 正在输入…</string> + <string name="contact_has_stopped_typing">%s 已停止输入</string> + <string name="pref_chat_states">键盘输入通知</string> + <string name="pref_chat_states_summary">让对方知道你正在输入新消息</string> + <string name="send_location">发送位置</string> + <string name="show_location">显示位置</string> + <string name="no_application_found_to_display_location">无法找到显示位置的应用</string> + <string name="location">位置</string> + <string name="received_location">位置已收到</string> + <string name="title_undo_swipe_out_conversation">Conversation 已关闭</string> + <string name="title_undo_swipe_out_muc">离开讨论组</string> + <string name="pref_certificate_options">证书选项</string> + <string name="pref_dont_trust_system_cas_title">不相信系统 CA</string> + <string name="pref_dont_trust_system_cas_summary">所有证书必须人工通过</string> + <string name="pref_remove_trusted_certificates_title">移除证书</string> + <string name="pref_remove_trusted_certificates_summary">删除人工通过的证书</string> + <string name="toast_no_trusted_certs">没有人工通过的证书</string> + <string name="dialog_manage_certs_title">移除证书</string> + <string name="dialog_manage_certs_positivebutton">删除选项</string> + <string name="dialog_manage_certs_negativebutton">取消</string> + <plurals name="toast_delete_certificates"> + <item quantity="other">%d 个证书已被删除</item> + </plurals> + <plurals name="select_contact"> + <item quantity="other">选择 %d 个联系人</item> + </plurals> + <string name="pref_quick_action_summary">以快速动作替代发送按钮</string> + <string name="pref_quick_action">快速动作</string> + <string name="none">无</string> + <string name="recently_used">最近使用过的</string> + <string name="choose_quick_action">选择快速动作</string> </resources> diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index f3e67665..95d9e4e6 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -64,7 +64,6 @@ <string name="send_otr_message">發送 OTR 加密訊息</string> <string name="send_pgp_message">發送 OpenPGP 加密訊息</string> <string name="your_nick_has_been_changed">用戶名稱修改成功</string> - <string name="download_image">下載圖片</string> <string name="send_unencrypted">不加密發送</string> <string name="decryption_failed">解密失敗,可能是私鑰不正確。</string> <string name="openkeychain_required">OpenKeychain</string> diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml index 942117d8..c555d4ba 100644 --- a/src/main/res/values/arrays.xml +++ b/src/main/res/values/arrays.xml @@ -40,6 +40,24 @@ <item>-1</item> </integer-array> + <string-array name="quick_actions"> + <item>@string/none</item> + <item>@string/recently_used</item> + <item>@string/attach_take_picture</item> + <item>@string/attach_choose_picture</item> + <item>@string/attach_record_voice</item> + <item>@string/send_location</item> + </string-array> + + <string-array name="quick_action_values"> + <item>none</item> + <item>recent</item> + <item>photo</item> + <item>picture</item> + <item>voice</item> + <item>location</item> + </string-array> + <string-array name="confirm_strings"> <item>@string/pref_confirm_messages_none</item> <item>@string/pref_confirm_messages_received</item> diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index c21650a5..3a778a21 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -1,18 +1,16 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - - <color name="primary" type="color">#ff259b24</color> - <color name="primarydark" type="color">#ff0a7e07</color> - <color name="accent">#ff0091ea</color> - <color name="primarytext" type="color">#de000000</color> - <color name="secondarytext" type="color">#8a000000</color> - <color name="ondarktext" type="color">#fffafafa</color> - <color name="primarybackground" type="color">#fffafafa</color> - <color name="secondarybackground" type="color">#ffeeeeee</color> - <color name="darkbackground" type="color">#ff323232</color> - <color name="divider">#1f000000</color> - <color name="red">#ffe51c23</color> - <color name="orange">#ffff9800</color> - <color name="green">#ff259b24</color> - + <color name="green500">#ff259b24</color> + <color name="green700">#ff0a7e07</color> + <color name="accent">#ff0091ea</color> + <color name="black87">#de000000</color> + <color name="black54">#8a000000</color> + <color name="black12">#1f000000</color> + <color name="white">#ffffffff</color> + <color name="white70">#b2ffffff</color> + <color name="grey50">#fffafafa</color> + <color name="grey200">#ffeeeeee</color> + <color name="grey800">#ff424242</color> + <color name="red500">#fff44336</color> + <color name="orange500">#ffff9800</color> </resources>
\ No newline at end of file diff --git a/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml index e8e1194c..d5d4aad4 100644 --- a/src/main/res/values/dimens.xml +++ b/src/main/res/values/dimens.xml @@ -1,9 +1,10 @@ <resources> - <!-- Default screen margins, per the Android Design guidelines. --> - <dimen name="activity_horizontal_margin">8dp</dimen> - <dimen name="activity_vertical_margin">8dp</dimen> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">8dp</dimen> + <dimen name="activity_vertical_margin">8dp</dimen> <dimen name="infocard_padding">16dp</dimen> <dimen name="conversations_overview_width">288dp</dimen> + <dimen name="image_button_padding">8dp</dimen> <dimen name="ambilwarna_hsvHeight">240dp</dimen> <dimen name="ambilwarna_hsvWidth">240dp</dimen> <dimen name="ambilwarna_hueWidth">30dp</dimen> diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index d454cc1f..94d75164 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -285,123 +285,124 @@ <string name="pref_about_conversations_summary">Build and licensing information</string> <string name="pref_about_message" translatable="false"> Conversations • the very last word in instant messaging. - \n\nCopyright © 2014 Daniel Gultsch - \n\nThis program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - \n\nThis program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - \n\nYou should have received a copy of the GNU General Public License - along with this program. If not, see https://www.gnu.org/licenses - \n\nDownload the full source code at https://github.com/siacs/Conversations - \n\n\nLibraries - \n\nhttps://www.bouncycastle.org\n(The MIT License (MIT)) - \n\nhttps://www.gnu.org/software/libidn\n(Apache License, Version 2.0) - \n\nhttps://github.com/ge0rg/MemorizingTrustManager\n(The MIT License (MIT)) - \n\nhttps://github.com/rtreffer/minidns\n(WTFPL) - \n\nhttps://github.com/open-keychain/openkeychain-api-lib\n(Apache License, Version 2.0) - \n\nhttps://github.com/jitsi/otr4j\n(LGPL-3.0) - \n\nhttps://developer.android.com/tools/support-library\n(Apache License, Version 2.0) - \n\nhttps://github.com/zxing/zxing\n(Apache License, Version 2.0) - \n\nhttps://github.com/google/material-design-icons\n(CC BY 4.0) - </string> - <string name="title_pref_quiet_hours">Quiet Hours</string> - <string name="title_pref_quiet_hours_start_time">Start time</string> - <string name="title_pref_quiet_hours_end_time">End time</string> - <string name="title_pref_enable_quiet_hours">Enable quiet hours</string> - <string name="pref_quiet_hours_summary">Notifications will be silenced during quiet hours</string> - <string name="pref_use_larger_font">Increase font size</string> - <string name="pref_use_larger_font_summary">Use larger font sizes across the entire app</string> - <string name="pref_use_send_button_to_indicate_status">Send button indicates status</string> - <string name="pref_use_indicate_received">Request message receipts</string> - <string name="pref_use_indicate_received_summary">Received messages will be marked with a green tick if supported</string> - <string name="pref_use_send_button_to_indicate_status_summary">Colorize send button to indicate contact status</string> - <string name="pref_expert_options_other">Other</string> - <string name="pref_conference_name">Conference name</string> - <string name="pref_conference_name_summary">Use room’s subject instead of JID to identify conferences</string> - <string name="toast_message_otr_fingerprint">OTR fingerprint copied to clipboard!</string> - <string name="conference_banned">You are banned from this conference</string> - <string name="conference_members_only">This conference is members only</string> - <string name="conference_kicked">You have been kicked from this conference</string> - <string name="using_account">using account %s</string> - <string name="checking_image">Checking image on HTTP host</string> + \n\nCopyright © 2014-2015 Daniel Gultsch + \n\nThis program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + \n\nThis program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + \n\nYou should have received a copy of the GNU General Public License + along with this program. If not, see https://www.gnu.org/licenses + \n\nDownload the full source code at https://github.com/siacs/Conversations + \n\n\nLibraries + \n\nhttps://www.bouncycastle.org\n(The MIT License (MIT)) + \n\nhttps://www.gnu.org/software/libidn\n(Apache License, Version 2.0) + \n\nhttps://github.com/ge0rg/MemorizingTrustManager\n(The MIT License (MIT)) + \n\nhttps://github.com/rtreffer/minidns\n(WTFPL) + \n\nhttps://github.com/open-keychain/openkeychain-api-lib\n(Apache License, Version 2.0) + \n\nhttps://github.com/jitsi/otr4j\n(LGPL-3.0) + \n\nhttps://developer.android.com/tools/support-library\n(Apache License, Version 2.0) + \n\nhttps://github.com/zxing/zxing\n(Apache License, Version 2.0) + \n\nhttps://github.com/google/material-design-icons\n(CC BY 4.0) + \n\nhttps://github.com/timroes/EnhancedListView\n(Apache License, Version 2.0) + </string> + <string name="title_pref_quiet_hours">Quiet Hours</string> + <string name="title_pref_quiet_hours_start_time">Start time</string> + <string name="title_pref_quiet_hours_end_time">End time</string> + <string name="title_pref_enable_quiet_hours">Enable quiet hours</string> + <string name="pref_quiet_hours_summary">Notifications will be silenced during quiet hours</string> + <string name="pref_use_larger_font">Increase font size</string> + <string name="pref_use_larger_font_summary">Use larger font sizes across the entire app</string> + <string name="pref_use_send_button_to_indicate_status">Send button indicates status</string> + <string name="pref_use_indicate_received">Request message receipts</string> + <string name="pref_use_indicate_received_summary">Received messages will be marked with a green tick if supported</string> + <string name="pref_use_send_button_to_indicate_status_summary">Colorize send button to indicate contact status</string> + <string name="pref_expert_options_other">Other</string> + <string name="pref_conference_name">Conference name</string> + <string name="pref_conference_name_summary">Use room’s subject instead of JID to identify conferences</string> + <string name="toast_message_otr_fingerprint">OTR fingerprint copied to clipboard!</string> + <string name="conference_banned">You are banned from this conference</string> + <string name="conference_members_only">This conference is members only</string> + <string name="conference_kicked">You have been kicked from this conference</string> + <string name="using_account">using account %s</string> + <string name="checking_x">Checking %s on HTTP host</string> <string name="image_file_deleted">The image file has been deleted</string> - <string name="not_connected_try_again">You are not connected. Try again later</string> - <string name="check_image_filesize">Check image file size</string> - <string name="message_options">Message options</string> - <string name="copy_text">Copy text</string> - <string name="copy_original_url">Copy original URL</string> - <string name="send_again">Send again</string> - <string name="image_url">Image URL</string> - <string name="message_text">Message text</string> - <string name="url_copied_to_clipboard">URL copied to clipboard</string> - <string name="message_copied_to_clipboard">Message copied to clipboard</string> - <string name="image_transmission_failed">Image transmission failed</string> - <string name="scan_qr_code">Scan QR code</string> - <string name="show_qr_code">Show QR code</string> - <string name="show_block_list">Show block list</string> - <string name="account_details">Account details</string> - <string name="verify_otr">Verify OTR</string> - <string name="remote_fingerprint">Remote Fingerprint</string> - <string name="scan">scan</string> - <string name="or_touch_phones">(or touch phones)</string> - <string name="smp">Socialist Millionaire Protocol</string> - <string name="shared_secret_hint">Hint or Question</string> - <string name="shared_secret_secret">Shared Secret</string> - <string name="confirm">Confirm</string> - <string name="in_progress">In progress</string> - <string name="respond">Respond</string> - <string name="failed">Failed</string> - <string name="secrets_do_not_match">Secrets do not match</string> - <string name="try_again">Try again</string> - <string name="finish">Finish</string> - <string name="verified">Verified!</string> - <string name="smp_requested">Contact requested SMP verification</string> - <string name="no_otr_session_found">No valid OTR session has been found!</string> - <string name="conversations_foreground_service">Conversations</string> - <string name="pref_keep_foreground_service">Keep service in foreground</string> - <string name="pref_keep_foreground_service_summary">Prevents the operating system from killing your connection</string> - <string name="choose_file">Choose file</string> - <string name="receiving_x_file">Receiving %1$s (%2$d%% completed)</string> - <string name="download_x_file">Download %s</string> - <string name="file">file</string> - <string name="open_x_file">Open %s</string> - <string name="sending_file">sending (%1$d%% completed)</string> - <string name="preparing_file">Preparing file for transmission</string> - <string name="x_file_offered_for_download">%s offered for download</string> - <string name="cancel_transmission">Cancel transmission</string> - <string name="file_transmission_failed">file transmission failed</string> - <string name="file_deleted">The file has been deleted</string> - <string name="no_application_found_to_open_file">No application found to open file</string> - <string name="could_not_verify_fingerprint">Could not verify fingerprint</string> - <string name="manually_verify">Manually verify</string> - <string name="are_you_sure_verify_fingerprint">Are you sure that you want to verify your contacts OTR fingerprint?</string> - <string name="pref_show_dynamic_tags">Show dynamic tags</string> - <string name="pref_show_dynamic_tags_summary">Display read-only tags underneath contacts</string> - <string name="enable_notifications">Enable notifications</string> - <string name="conference_with">Create conference with…</string> - <string name="no_conference_server_found">No conference server found</string> - <string name="conference_creation_failed">Conference creation failed!</string> - <string name="conference_created">Conference created!</string> - <string name="secret_accepted">Secret accepted!</string> - <string name="reset">Reset</string> - <string name="account_image_description">Account avatar</string> - <string name="copy_otr_clipboard_description">Copy OTR fingerprint to clipboard</string> - <string name="fetching_history_from_server">Fetching history from server</string> - <string name="no_more_history_on_server">No more history on server</string> - <string name="updating">Updating…</string> - <string name="password_changed">Password changed!</string> - <string name="could_not_change_password">Could not change password</string> - <string name="otr_session_not_started">Send a message to start an encrypted chat</string> - <string name="ask_question">Ask question</string> - <string name="smp_explain_question">If you and your contact have a secret in common that no one else knows (like an inside joke or simply what you had for lunch the last time you met) you can use that secret to verify each other\'s fingerprints.\n\nYou provide a hint or a question for your contact who will respond with a case-sensitive answer.</string> - <string name="smp_explain_answer">Your contact would like to verify your fingerprint by challenging you with a shared secret. Your contact provided the following hint or question for that secret.</string> - <string name="shared_secret_hint_should_not_be_empty">Your hint should not be empty</string> - <string name="shared_secret_can_not_be_empty">Your shared secret can not be empty</string> - <string name="manual_verification_explanation">Carefully compare the fingerprint shown below with the fingerprint of your contact.\nYou can use any trusted form of communication like an encrypted e-mail or a telephone call to exchange those.</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="message_options">Message options</string> + <string name="copy_text">Copy text</string> + <string name="copy_original_url">Copy original URL</string> + <string name="send_again">Send again</string> + <string name="file_url">File URL</string> + <string name="message_text">Message text</string> + <string name="url_copied_to_clipboard">URL copied to clipboard</string> + <string name="message_copied_to_clipboard">Message copied to clipboard</string> + <string name="image_transmission_failed">Image transmission failed</string> + <string name="scan_qr_code">Scan QR code</string> + <string name="show_qr_code">Show QR code</string> + <string name="show_block_list">Show block list</string> + <string name="account_details">Account details</string> + <string name="verify_otr">Verify OTR</string> + <string name="remote_fingerprint">Remote Fingerprint</string> + <string name="scan">scan</string> + <string name="or_touch_phones">(or touch phones)</string> + <string name="smp">Socialist Millionaire Protocol</string> + <string name="shared_secret_hint">Hint or Question</string> + <string name="shared_secret_secret">Shared Secret</string> + <string name="confirm">Confirm</string> + <string name="in_progress">In progress</string> + <string name="respond">Respond</string> + <string name="failed">Failed</string> + <string name="secrets_do_not_match">Secrets do not match</string> + <string name="try_again">Try again</string> + <string name="finish">Finish</string> + <string name="verified">Verified!</string> + <string name="smp_requested">Contact requested SMP verification</string> + <string name="no_otr_session_found">No valid OTR session has been found!</string> + <string name="conversations_foreground_service">Conversations</string> + <string name="pref_keep_foreground_service">Keep service in foreground</string> + <string name="pref_keep_foreground_service_summary">Prevents the operating system from killing your connection</string> + <string name="choose_file">Choose file</string> + <string name="receiving_x_file">Receiving %1$s (%2$d%% completed)</string> + <string name="download_x_file">Download %s</string> + <string name="file">file</string> + <string name="open_x_file">Open %s</string> + <string name="sending_file">sending (%1$d%% completed)</string> + <string name="preparing_file">Preparing file for transmission</string> + <string name="x_file_offered_for_download">%s offered for download</string> + <string name="cancel_transmission">Cancel transmission</string> + <string name="file_transmission_failed">file transmission failed</string> + <string name="file_deleted">The file has been deleted</string> + <string name="no_application_found_to_open_file">No application found to open file</string> + <string name="could_not_verify_fingerprint">Could not verify fingerprint</string> + <string name="manually_verify">Manually verify</string> + <string name="are_you_sure_verify_fingerprint">Are you sure that you want to verify your contacts OTR fingerprint?</string> + <string name="pref_show_dynamic_tags">Show dynamic tags</string> + <string name="pref_show_dynamic_tags_summary">Display read-only tags underneath contacts</string> + <string name="enable_notifications">Enable notifications</string> + <string name="conference_with">Create conference with…</string> + <string name="no_conference_server_found">No conference server found</string> + <string name="conference_creation_failed">Conference creation failed!</string> + <string name="conference_created">Conference created!</string> + <string name="secret_accepted">Secret accepted!</string> + <string name="reset">Reset</string> + <string name="account_image_description">Account avatar</string> + <string name="copy_otr_clipboard_description">Copy OTR fingerprint to clipboard</string> + <string name="fetching_history_from_server">Fetching history from server</string> + <string name="no_more_history_on_server">No more history on server</string> + <string name="updating">Updating…</string> + <string name="password_changed">Password changed!</string> + <string name="could_not_change_password">Could not change password</string> + <string name="otr_session_not_started">Send a message to start an encrypted chat</string> + <string name="ask_question">Ask question</string> + <string name="smp_explain_question">If you and your contact have a secret in common that no one else knows (like an inside joke or simply what you had for lunch the last time you met) you can use that secret to verify each other’s fingerprints.\n\nYou provide a hint or a question for your contact who will respond with a case-sensitive answer.</string> + <string name="smp_explain_answer">Your contact would like to verify your fingerprint by challenging you with a shared secret. Your contact provided the following hint or question for that secret.</string> + <string name="shared_secret_hint_should_not_be_empty">Your hint should not be empty</string> + <string name="shared_secret_can_not_be_empty">Your shared secret can not be empty</string> + <string name="manual_verification_explanation">Carefully compare the fingerprint shown below with the fingerprint of your contact.\nYou can use any trusted form of communication like an encrypted e-mail or a telephone call to exchange those.</string> <string name="change_password">Change password</string> <string name="current_password">Current password</string> <string name="new_password">New password</string> @@ -453,23 +454,45 @@ <string name="disable_foreground_service">Disable foreground service</string> <string name="touch_to_open_conversations">Touch to open Conversations</string> <string name="avatar_has_been_published">Avatar has been published!</string> - <string name="sending_x_file">Sending %s</string> - <string name="offering_x_file">Offering %s</string> - <string name="hide_offline">Hide offline</string> - <string name="disable_account">Disable Account</string> - <string name="contact_is_typing">%s is typing...</string> - <string name="contact_has_stopped_typing">%s has stopped typing</string> - <string name="pref_chat_states">Typing notifications</string> - <string name="pref_chat_states_summary">Let your contact know when you are writing a new message</string> - <string name="send_location">Send location</string> - <string name="show_location">Show location</string> - <string name="no_application_found_to_display_location">No application found to display location</string> - <string name="location">Location</string> - <string name="received_location">Received location</string> + <string name="sending_x_file">Sending %s</string> + <string name="offering_x_file">Offering %s</string> + <string name="hide_offline">Hide offline</string> + <string name="disable_account">Disable Account</string> + <string name="contact_is_typing">%s is typing...</string> + <string name="contact_has_stopped_typing">%s has stopped typing</string> + <string name="pref_chat_states">Typing notifications</string> + <string name="pref_chat_states_summary">Let your contact know when you are writing a new message</string> + <string name="send_location">Send location</string> + <string name="show_location">Show location</string> + <string name="no_application_found_to_display_location">No application found to display location</string> + <string name="location">Location</string> + <string name="received_location">Received location</string> + <string name="title_undo_swipe_out_conversation">Conversation closed</string> + <string name="title_undo_swipe_out_muc">Left conference</string> + <string name="pref_certificate_options">Certificate options</string> + <string name="pref_dont_trust_system_cas_title">Don’t trust system CAs</string> + <string name="pref_dont_trust_system_cas_summary">All certificates must be manually approved</string> + <string name="pref_remove_trusted_certificates_title">Remove certificates</string> + <string name="pref_remove_trusted_certificates_summary">Delete manually approved certificates</string> + <string name="toast_no_trusted_certs">No manually approved certificates</string> + <string name="dialog_manage_certs_title">Remove certificates</string> + <string name="dialog_manage_certs_positivebutton">Delete selection</string> + <string name="dialog_manage_certs_negativebutton">Cancel</string> + <plurals name="toast_delete_certificates"> + <item quantity="one">%d certificate deleted</item> + <item quantity="other">%d certificates deleted</item> + </plurals> <plurals name="select_contact"> <item quantity="one">Select %d contact</item> <item quantity="other">Select %d contacts</item> </plurals> + <string name="pref_quick_action_summary">Replace send button with quick action</string> + <string name="pref_quick_action">Quick Action</string> + <string name="none">None</string> + <string name="recently_used">Most recently used</string> + <string name="choose_quick_action">Choose quick action</string> + <string name="file_not_found_on_remote_host">File not found on remote server</string> + <string name="search_for_contacts_or_groups">Search for contacts or groups</string> <string name="pref_led_notification_color">LED notification color</string> <string name="pref_led_notification_color_summary">Change the color of the LED notification</string> </resources> diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index b329eee4..b98a37fc 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -2,7 +2,7 @@ <style name="Divider"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">1.5dp</item> - <item name="android:background">@color/divider</item> + <item name="android:background">@color/black12</item> </style> <style name="Tag"> diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml index 80f2bfe6..5c67203b 100644 --- a/src/main/res/values/themes.xml +++ b/src/main/res/values/themes.xml @@ -38,8 +38,8 @@ </style> <style name="ConversationsActionBar" parent="@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse"> - <item name="android:background">@color/primary</item> - <item name="android:backgroundStacked">@color/primarydark</item> + <item name="android:background">@color/green500</item> + <item name="android:backgroundStacked">@color/green700</item> <item name="android:displayOptions">showHome|homeAsUp|showTitle</item> <item name="android:icon">@android:color/transparent</item> </style> diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index 9cf2ed93..9def87c4 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -139,16 +139,27 @@ android:key="send_button_status" android:summary="@string/pref_use_send_button_to_indicate_status_summary" android:title="@string/pref_use_send_button_to_indicate_status" /> + <ListPreference + android:key="quick_action" + android:defaultValue="recent" + android:entries="@array/quick_actions" + android:entryValues="@array/quick_action_values" + android:summary="@string/pref_quick_action_summary" + android:title="@string/pref_quick_action" + android:dialogTitle="@string/choose_quick_action"/> <CheckBoxPreference android:defaultValue="false" android:key="show_dynamic_tags" android:summary="@string/pref_show_dynamic_tags_summary" android:title="@string/pref_show_dynamic_tags" /> </PreferenceCategory> - <PreferenceCategory android:title="@string/pref_advanced_options" > + <PreferenceCategory + android:title="@string/pref_advanced_options" + android:key="advanced"> <PreferenceScreen android:summary="@string/pref_expert_options_summary" - android:title="@string/pref_expert_options" > + android:title="@string/pref_expert_options" + android:key="expert"> <PreferenceCategory android:title="@string/pref_encryption_settings" > <CheckBoxPreference android:defaultValue="false" @@ -173,6 +184,17 @@ android:title="@string/pref_display_enter_key" android:summary="@string/pref_display_enter_key_summary" /> </PreferenceCategory> + <PreferenceCategory android:title="@string/pref_certificate_options"> + <CheckBoxPreference + android:defaultValue="false" + android:key="dont_trust_system_cas" + android:title="@string/pref_dont_trust_system_cas_title" + android:summary="@string/pref_dont_trust_system_cas_summary" /> + <Preference + android:key="remove_trusted_certificates" + android:title="@string/pref_remove_trusted_certificates_title" + android:summary="@string/pref_remove_trusted_certificates_summary" /> + </PreferenceCategory> <PreferenceCategory android:title="@string/pref_expert_options_other" > <CheckBoxPreference android:defaultValue="false" |