diff options
Diffstat (limited to 'src/main')
170 files changed, 1656 insertions, 858 deletions
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 5cca6c0b..779cbbe8 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; @@ -28,7 +29,8 @@ 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 = false; // log stanza counts + 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 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/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index cef03ebe..9dbca59a 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -15,6 +15,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"; @@ -40,11 +41,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, @@ -61,7 +62,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) { @@ -135,10 +140,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()) { @@ -187,7 +192,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()); @@ -411,17 +416,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) { @@ -478,6 +486,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..95a8c957 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 diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 7ea3d60b..38152edb 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -9,6 +9,7 @@ import java.util.Arrays; import eu.siacs.conversations.Config; import eu.siacs.conversations.utils.GeoHelper; +import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.Jid; @@ -16,6 +17,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; @@ -95,9 +98,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 +182,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 +214,7 @@ public class Message extends AbstractEntity { return null; } else { return this.conversation.getAccount().getRoster() - .getContactFromRoster(this.trueCounterpart); + .getContactFromRoster(this.trueCounterpart); } } } @@ -359,41 +362,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.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) && + !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().trim() + '\n' + next.getMergedBody(); + return getBody().trim() + MERGE_SEPARATOR + next.getMergedBody(); } return getBody().trim(); } @@ -435,7 +440,7 @@ public class Message extends AbstractEntity { * "http://example.com/image.jpg text that will not be shown /abc.png" * or more than one image link in one message. */ - if (body.contains(" ")) { + if (body.trim().contains(" ")) { return false; } try { @@ -443,7 +448,7 @@ public class Message extends AbstractEntity { if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) { return false; - } + } String sUrlPath = url.getPath(); if (sUrlPath == null || sUrlPath.isEmpty()) { @@ -457,14 +462,14 @@ public class Message extends AbstractEntity { String[] extensionParts = sLastUrlPath.split("\\."); if (extensionParts.length == 2 && Arrays.asList(Downloadable.VALID_IMAGE_EXTENSIONS).contains( - extensionParts[extensionParts.length - 1])) { + 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])) { + extensionParts[extensionParts.length - 2])) { return true; } else { return false; @@ -474,6 +479,10 @@ public class Message extends AbstractEntity { } } + public boolean bodyIsHeart() { + return body != null && UIHelper.HEARTS.contains(body.trim()); + } + public ImageParams getImageParams() { ImageParams params = getLegacyImageParams(); if (params != null) { 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/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index 6bc629b5..d7366daa 100644 --- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java @@ -91,7 +91,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 +99,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) { diff --git a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java index 526005f3..c40c6d05 100644 --- a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java @@ -49,7 +49,7 @@ 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; @@ -61,4 +61,4 @@ public class PresenceGenerator extends AbstractGenerator { packet.setAttribute("type","unavailable"); return packet; } -}
\ No newline at end of file +} diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 76d01468..7870fdbf 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -494,7 +494,7 @@ public class MessageParser extends AbstractParser implements } else { Contact contact = account.getRoster().getContact( from); - contact.setAvatar(avatar.getFilename()); + contact.setAvatar(avatar); mXmppConnectionService.getAvatarService().clear( contact); mXmppConnectionService.updateConversationUi(); diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java index 7505b091..f7872210 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 @@ -101,6 +102,20 @@ public class PresenceParser extends AbstractParser implements if (nick != null) { contact.setPresenceName(nick.getContent()); } + Element x = packet.findChild("x","vcard-temp:x:update"); + Avatar avatar = Avatar.parsePresence(x); + 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); + } + } 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..ed88e434 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) { diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index fc40ce75..e111da95 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -85,7 +85,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); } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index ec0b3f92..63d9ba7a 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -41,6 +41,7 @@ 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; @@ -210,6 +211,7 @@ 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; @@ -328,7 +330,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); } @@ -347,7 +349,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); @@ -390,7 +391,6 @@ 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() { @Override @@ -422,6 +422,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(), @@ -440,14 +445,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 { @@ -529,6 +527,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); @@ -801,7 +811,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa 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) { @@ -811,7 +820,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } catch (InvalidJidException e) { return; } - markMessage(message, Message.STATUS_OFFERED); mJingleConnectionManager.createNewConnection(message); } } @@ -1867,6 +1875,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); @@ -1893,13 +1902,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) { @@ -1916,7 +1951,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(); @@ -1925,8 +1960,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 { @@ -1949,8 +1983,38 @@ 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; + Element binval = photo != null ? photo.findChild("BINVAL") : null; + String image = binval != null ? binval.getContent() : 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() { @@ -1972,7 +2036,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa getAvatarService().clear(account); callback.success(avatar); } else { - fetchAvatar(account, avatar, callback); + fetchAvatarPep(account, avatar, callback); } return; } @@ -2008,6 +2072,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)); } @@ -2031,6 +2105,7 @@ 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); } diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java index 8c4f6eaf..07b8819d 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java @@ -385,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/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java index aec755fc..1b5e5178 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java @@ -60,11 +60,11 @@ 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"; @@ -398,61 +398,88 @@ 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 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); - 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); - 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 (attachmentChoice == ATTACHMENT_CHOICE_LOCATION && encryption != Message.ENCRYPTION_OTR) { + getSelectedConversation().setNextCounterpart(null); + callback.onPresenceSelected(); + } else { + selectPresence(getSelectedConversation(),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; + } final Conversation conversation = getSelectedConversation(); final int encryption = conversation.getNextEncryption(forceEncryption()); if (encryption == Message.ENCRYPTION_PGP) { @@ -875,6 +902,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; @@ -901,9 +934,7 @@ 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(); mPendingImageUris.clear(); mPendingFileUris.clear(); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 5b1e9b4d..20fc1750 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -59,6 +59,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; @@ -117,16 +118,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; } @@ -134,29 +156,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(); } } }); @@ -174,7 +185,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(); } }); @@ -208,7 +219,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<>(); @@ -219,7 +230,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; @@ -232,15 +243,39 @@ 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 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); @@ -253,16 +288,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (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())); + .forceEncryption())); if (conversation.getMode() == Conversation.MODE_MULTI) { if (conversation.getNextCounterpart() != null) { message.setCounterpart(conversation.getNextCounterpart()); @@ -282,13 +310,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)); @@ -304,7 +332,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); @@ -313,8 +341,8 @@ 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(); @@ -365,21 +393,21 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } }); messageListAdapter - .setOnContactPictureLongClicked(new OnContactPictureLongClicked() { + .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()); + @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); @@ -389,7 +417,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; @@ -416,7 +444,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if ((m.getType() == Message.TYPE_TEXT || m.getType() == Message.TYPE_PRIVATE || m.getDownloadable() != null) - && (!GeoHelper.isGeoUri(m.getBody()))) { + && (!GeoHelper.isGeoUri(m.getBody()))) { shareWith.setVisible(false); } if (m.getStatus() != Message.STATUS_SEND_FAILED) { @@ -425,17 +453,17 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa 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)))) { + || (m.isFileOrImage() && (m.getStatus() == Message.STATUS_WAITING + || m.getStatus() == Message.STATUS_OFFERED)))) { cancelTransmission.setVisible(false); - } + } } } @@ -483,12 +511,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } shareIntent.setType(mime); } - activity.startActivity(Intent.createChooser(shareIntent,getText(R.string.share_with))); + activity.startActivity(Intent.createChooser(shareIntent, getText(R.string.share_with))); } 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(); } @@ -498,7 +526,7 @@ 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(); + Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show(); message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED)); return; } @@ -519,20 +547,20 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (activity.copyTextToClipboard(url, resId)) { Toast.makeText(activity, R.string.url_copied_to_clipboard, Toast.LENGTH_SHORT).show(); - } + } } private void downloadImage(Message message) { activity.xmppConnectionService.getHttpConnectionManager() - .createNewConnection(message); + .createNewConnection(message); } private void cancelTransmission(Message message) { Downloadable downloadable = message.getDownloadable(); - if (downloadable!=null) { + if (downloadable != null) { downloadable.cancel(); } else { - activity.xmppConnectionService.markMessage(message,Message.STATUS_SEND_FAILED); + activity.xmppConnectionService.markMessage(message, Message.STATUS_SEND_FAILED); } } @@ -540,6 +568,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa this.mEditMessage.setText(""); this.conversation.setNextCounterpart(counterpart); updateChatMsgHint(); + updateSendButton(); } protected void highlightInConference(String nick) { @@ -548,9 +577,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 + " "); } @@ -563,7 +592,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); } } @@ -586,7 +615,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(); } @@ -632,7 +661,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); @@ -655,7 +684,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); } }; @@ -665,11 +694,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: @@ -693,18 +722,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(); } @@ -722,12 +751,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa for (final Message message : this.messageList) { if (message.getEncryption() == Message.ENCRYPTION_PGP && (message.getStatus() == Message.STATUS_RECEIVED || message - .getStatus() >= Message.STATUS_SEND) + .getStatus() >= Message.STATUS_SEND) && message.getDownloadable() == null) { if (!mEncryptedMessages.contains(message)) { mEncryptedMessages.add(message); } - } + } } decryptNext(); updateStatusMessages(); @@ -790,53 +819,128 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa updateChatMsgHint(); } - public void updateSendButton() { - Conversation c = this.conversation; - if (activity.useSendButtonToIndicateStatus() && 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} + + 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: + case Presences.DND: + return R.drawable.ic_send_text_dnd; + default: + 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: - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_away); - break; case Presences.DND: - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_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; + } + } + 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; + if (c.getMode() == Conversation.MODE_MULTI) { + if (empty && c.getNextCounterpart() != null) { + action = SendButtonAction.CANCEL; + } else { + action = SendButtonAction.TEXT; + } + } else { + if (empty) { + 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; default: - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_offline); + action = SendButtonAction.TEXT; break; } - } else if (c.getMode() == Conversation.MODE_MULTI) { - if (c.getMucOptions().online()) { - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_online); - } else { - this.mSendButton - .setImageResource(R.drawable.ic_action_send_now_offline); - } } 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() { @@ -865,7 +969,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } 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); @@ -897,7 +1001,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); @@ -921,11 +1025,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(); @@ -936,9 +1040,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(); } @@ -950,12 +1054,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(); } @@ -968,7 +1072,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) { @@ -1026,6 +1130,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (status == Account.State.ONLINE && conversation.setOutgoingChatState(ChatState.COMPOSING)) { activity.xmppConnectionService.sendChatState(conversation); } + updateSendButton(); } @Override @@ -1042,6 +1147,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (status == Account.State.ONLINE && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) { activity.xmppConnectionService.sendChatState(conversation); } + updateSendButton(); } } diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index 7aa7b1c2..931a1a2f 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -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); diff --git a/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java b/src/main/java/eu/siacs/conversations/ui/PublishProfilePictureActivity.java index e8ab8dae..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(); diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index a556b8b7..7863ff94 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -65,6 +65,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; @@ -757,14 +758,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 392e57a7..934c696f 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -113,6 +113,8 @@ public abstract class XmppActivity extends Activity { } }; + protected ConferenceInvite mPendingConferenceInvite = null; + protected void refreshUi() { final long diff = SystemClock.elapsedRealtime() - mLastUiRefresh; @@ -367,7 +369,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) { @@ -435,7 +437,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); @@ -446,13 +448,13 @@ public abstract class XmppActivity extends Activity { @Override public void success(Account account) { xmppConnectionService.databaseBackend - .updateAccount(account); + .updateAccount(account); xmppConnectionService.sendPresence(account); if (conversation != null) { conversation - .setNextEncryption(Message.ENCRYPTION_PGP); + .setNextEncryption(Message.ENCRYPTION_PGP); xmppConnectionService.databaseBackend - .updateConversation(conversation); + .updateConversation(conversation); } } @@ -665,32 +667,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; } } } @@ -855,6 +836,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/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index da92fb18..29dfced2 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -7,10 +7,11 @@ import android.graphics.Typeface; import android.net.Uri; 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; @@ -24,7 +25,6 @@ import android.widget.Toast; import java.util.List; -import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; @@ -42,7 +42,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; @@ -77,14 +76,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; @@ -207,22 +204,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()) { final Spannable span = new SpannableString(formattedBody); @@ -230,7 +247,7 @@ public class MessageAdapter extends ArrayAdapter<Message> { Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); viewHolder.messageBody.setText(span); } else { - viewHolder.messageBody.setText(message.getMergedBody()); + viewHolder.messageBody.setText(formattedBody); } } else { String privateMarker; @@ -289,7 +306,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 @@ -334,7 +351,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() { @@ -359,10 +376,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); @@ -429,25 +442,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) { @@ -528,7 +522,11 @@ public class MessageAdapter extends ArrayAdapter<Message> { if (GeoHelper.isGeoUri(message.getBody())) { displayLocationMessage(viewHolder,message); } else { - displayTextMessage(viewHolder, message); + if (message.bodyIsHeart()) { + displayHeartMessage(viewHolder, message.getBody().trim()); + } else { + displayTextMessage(viewHolder, message); + } } } diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java index c3195d86..2f96a83a 100644 --- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java @@ -1,8 +1,11 @@ package eu.siacs.conversations.utils; import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.Date; +import java.util.Locale; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Contact; @@ -17,6 +20,33 @@ import android.text.format.DateUtils; 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 @@ -225,4 +255,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/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index a2b58a14..9bda3c09 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -167,7 +167,7 @@ public class XmppConnection implements Runnable { try { String srvRecordServer; try { - srvRecordServer=IDN.toASCII(namePort.getString("name")); + srvRecordServer = IDN.toASCII(namePort.getString("name")); } catch (final IllegalArgumentException e) { // TODO: Handle me?` srvRecordServer = ""; @@ -187,7 +187,7 @@ public class XmppConnection implements Runnable { + srvRecordServer + ":" + srvRecordPort); } socket = new Socket(); - socket.connect(addr, 20000); + socket.connect(addr, Config.SOCKET_TIMEOUT * 1000); socketError = false; } catch (final UnknownHostException e) { Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); @@ -224,6 +224,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 +237,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 +292,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"); @@ -437,6 +449,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; @@ -538,8 +554,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()); @@ -550,8 +565,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(); } } @@ -590,8 +604,7 @@ public class XmppConnection implements Runnable { " 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); + throw new SecurityException(); } } catch (final JSONException e) { Log.d(Config.LOGTAG, "Parse error while checking pinned auth mechanism"); @@ -603,8 +616,7 @@ public class XmppConnection implements Runnable { } tagWriter.writeElement(auth); } else { - disconnect(true); - changeStatus(Account.State.INCOMPATIBLE_SERVER); + throw new IncompatibleServerException(); } } else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" + smVersion) @@ -643,10 +655,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() { @@ -659,7 +669,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); @@ -673,7 +683,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() : "")); } } }); @@ -688,7 +698,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) { @@ -901,6 +911,11 @@ 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; @@ -1091,6 +1106,18 @@ public class XmppConnection implements Runnable { 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; 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 e448f947..c9bb9c93 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -99,7 +99,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)); @@ -213,7 +213,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); @@ -271,6 +271,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"); @@ -381,6 +384,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,7 +399,9 @@ 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; } 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..c19dd04c 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -9,6 +9,7 @@ import android.annotation.SuppressLint; import android.util.Log; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Downloadable; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.AbstractConnectionManager; import eu.siacs.conversations.services.XmppConnectionService; @@ -58,7 +59,12 @@ public class JingleConnectionManager extends AbstractConnectionManager { } public JingleConnection createNewConnection(Message message) { + Downloadable old = message.getDownloadable(); + 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; 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 9866af03..29efcf8f 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java @@ -8,7 +8,9 @@ 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; @@ -95,11 +97,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(); } } @@ -110,12 +114,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; } @@ -124,6 +133,7 @@ public class JingleInbandTransport extends JingleTransport { } } catch (NoSuchAlgorithmException e) { callback.onFileTransferAborted(); + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage()); } } @@ -150,29 +160,33 @@ public class JingleInbandTransport extends JingleTransport { byte[] buffer = new byte[this.bufferSize]; try { int count = fileInputStream.read(buffer); - if (count == -1) { + this.remainingSize -= count; + if (count != buffer.length && count != -1) { + int rem = fileInputStream.read(buffer,count,buffer.length-count); + if (rem > 0) { + count += rem; + } + } + 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())); - 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++; - connection.updateProgress((int) ((((double) (this.fileSize - this.remainingSize)) / this.fileSize) * 100)); + fileInputStream.close(); } } catch (IOException e) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": "+e.getMessage()); FileBackend.close(fileInputStream); this.onFileTransmissionStatusChanged.onFileTransferAborted(); } @@ -182,14 +196,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())); @@ -200,6 +210,7 @@ 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(); } 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 72015a05..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,15 +1,20 @@ 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; @@ -53,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 }; @@ -102,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; } @@ -121,10 +128,13 @@ 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 { FileBackend.close(fileInputStream); @@ -150,6 +160,7 @@ public class JingleSocks5Transport extends JingleTransport { 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(); @@ -160,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); @@ -173,10 +185,13 @@ 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); 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..04d55bbe 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,44 @@ 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) { + Element photo = x != null ? x.findChild("photo") : null; + String hash = photo != null ? photo.getContent() : null; + 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/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_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_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_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_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_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_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_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_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_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_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_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-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-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-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-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_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_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-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-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-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-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_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_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-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-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-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_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_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/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml index f9aae10a..5aa7dffa 100644 --- a/src/main/res/layout/fragment_conversation.xml +++ b/src/main/res/layout/fragment_conversation.xml @@ -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" /> </RelativeLayout> <RelativeLayout 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/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index a0861b16..9583932b 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> @@ -359,6 +359,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 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> @@ -416,4 +417,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-pl/strings.xml b/src/main/res/values-pl/strings.xml index e1849357..70c3a49d 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -202,6 +202,7 @@ <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> @@ -236,7 +237,9 @@ <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> @@ -252,10 +255,12 @@ <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> @@ -283,6 +288,7 @@ <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="checking_image">Sprawdzanie obrazka na hoście HTTP</string> <string name="image_file_deleted">Obraz został usunięty</string> <string name="not_connected_try_again">Brak połączenia. Spróbuj ponownie później</string> @@ -302,6 +308,7 @@ <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> @@ -309,6 +316,7 @@ <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> @@ -382,6 +390,7 @@ <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> @@ -420,7 +429,23 @@ <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> </resources> diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index 2fc257f7..7b104820 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -441,11 +441,13 @@ <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> </resources> diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index bcf420ba..ea5cbdb8 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -97,8 +97,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_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> diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 49ca9b97..2f80e064 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,65 @@ <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="your_nick_has_been_changed">昵称修改成功</string> <string name="download_image">下载图片</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 +105,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 +148,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 +157,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 +169,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 +199,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 +213,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 +223,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 +255,193 @@ <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="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> + <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> </resources> diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml index c413a3c4..fec077cc 100644 --- a/src/main/res/values/arrays.xml +++ b/src/main/res/values/arrays.xml @@ -40,4 +40,19 @@ <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_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>voice</item> + <item>location</item> + </string-array> </resources> diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index c21650a5..753a56df 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -11,7 +11,7 @@ <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="red">#fff44336</color> <color name="orange">#ffff9800</color> <color name="green">#ff259b24</color> diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index cc7727d2..4631bd60 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -1,397 +1,397 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <string name="app_name" translatable="false">Conversations</string> - <string name="action_settings">Settings</string> - <string name="action_add">New conversation</string> - <string name="action_accounts">Manage accounts</string> - <string name="action_end_conversation">End this conversation</string> - <string name="action_contact_details">Contact details</string> - <string name="action_muc_details">Conference details</string> - <string name="action_secure">Secure conversation</string> - <string name="action_add_account">Add account</string> - <string name="action_edit_contact">Edit name</string> - <string name="action_add_phone_book">Add to phone book</string> - <string name="action_delete_contact">Delete from roster</string> - <string name="action_block_contact">Block contact</string> - <string name="action_unblock_contact">Unblock contact</string> - <string name="action_block_domain">Block domain</string> - <string name="action_unblock_domain">Unblock domain</string> - <string name="title_activity_manage_accounts">Manage Accounts</string> - <string name="title_activity_settings">Settings</string> - <string name="title_activity_conference_details">Conference Details</string> - <string name="title_activity_contact_details">Contact Details</string> - <string name="title_activity_sharewith">Share with Conversation</string> - <string name="title_activity_start_conversation">Start Conversation</string> - <string name="title_activity_choose_contact">Choose contact</string> - <string name="title_activity_block_list">Block list</string> - <string name="just_now">just now</string> - <string name="minute_ago">1 min ago</string> - <string name="minutes_ago">%d mins ago</string> - <string name="unread_conversations">unread Conversations</string> - <string name="sending">sending…</string> - <string name="encrypted_message">Decrypting message. Please wait…</string> - <string name="nick_in_use">Nickname is already in use</string> - <string name="admin">Admin</string> - <string name="owner">Owner</string> - <string name="moderator">Moderator</string> - <string name="participant">Participant</string> - <string name="visitor">Visitor</string> - <string name="remove_contact_text">Would you like to remove %s from your roster? The conversation associated with this contact will not be removed.</string> - <string name="block_contact_text">Would you like to block %s from sending you messages?</string> - <string name="unblock_contact_text">Would you like to unblock %s and allow them to send you messages?</string> - <string name="block_domain_text">Block all contacts from %s?</string> - <string name="unblock_domain_text">Unblock all contacts from %s?</string> - <string name="contact_blocked">Contact blocked</string> - <string name="remove_bookmark_text">Would you like to remove %s as a bookmark? The conversation associated with this bookmark will not be removed.</string> - <string name="register_account">Register new account on server</string> - <string name="change_password_on_server">Change password on server</string> - <string name="share_with">Share with…</string> - <string name="start_conversation">Start Conversation</string> - <string name="invite_contact">Invite Contact</string> - <string name="contacts">Contacts</string> - <string name="cancel">Cancel</string> - <string name="set">Set</string> - <string name="add">Add</string> - <string name="edit">Edit</string> - <string name="delete">Delete</string> - <string name="block">Block</string> - <string name="unblock">Unblock</string> - <string name="save">Save</string> - <string name="ok">OK</string> - <string name="crash_report_title">Conversations has crashed</string> - <string name="crash_report_message">By sending in stack traces you are helping the ongoing development of Conversations\n<b>Warning:</b> This will use your XMPP account to send the stack trace to the developer.</string> - <string name="send_now">Send now</string> - <string name="send_never">Never ask again</string> - <string name="problem_connecting_to_account">Unable to connect to account</string> - <string name="problem_connecting_to_accounts">Unable to connect to multiple accounts</string> - <string name="touch_to_fix">Touch here to manage your accounts</string> - <string name="attach_file">Attach file</string> - <string name="not_in_roster">The contact is not in your roster. Would you like to add it?</string> - <string name="add_contact">Add contact</string> - <string name="send_failed">delivery failed</string> - <string name="send_rejected">rejected</string> - <string name="preparing_image">Preparing image for transmission</string> - <string name="action_clear_history">Clear history</string> - <string name="clear_conversation_history">Clear Conversation History</string> - <string name="clear_histor_msg">Do you want to delete all messages within this Conversation?\n\n<b>Warning:</b> This will not influence messages stored on other devices or servers.</string> - <string name="delete_messages">Delete messages</string> - <string name="also_end_conversation">End this conversations afterwards</string> - <string name="choose_presence">Choose presence to contact</string> - <string name="send_plain_text_message">Send plain text message</string> - <string name="send_otr_message">Send OTR encrypted message</string> - <string name="send_pgp_message">Send OpenPGP encrypted message</string> - <string name="your_nick_has_been_changed">Your nickname has been changed</string> - <string name="download_image">Download Image</string> - <string name="send_unencrypted">Send unencrypted</string> - <string name="decryption_failed">Decryption failed. Maybe you don’t have the proper private key.</string> - <string name="openkeychain_required">OpenKeychain</string> - <string name="openkeychain_required_long">Conversations utilizes a third party app called <b>OpenKeychain</b> to encrypt and decrypt messages and to manage your public keys.\n\nOpenKeychain is licensed under GPLv3 and available on F-Droid and Google Play.\n\n<small>(Please restart Conversations afterwards.)</small></string> - <string name="restart">Restart</string> - <string name="install">Install</string> - <string name="offering">offering…</string> - <string name="waiting">waiting…</string> - <string name="no_pgp_key">No OpenPGP Key found</string> - <string name="contact_has_no_pgp_key">Conversations is unable to encrypt your messages because your contact is not announcing his or hers public key.\n\n<small>Please ask your contact to setup OpenPGP.</small></string> - <string name="no_pgp_keys">No OpenPGP Keys found</string> - <string name="contacts_have_no_pgp_keys">Conversations is unable to encrypt your messages because your contacts are not announcing their public key.\n\n<small>Please ask your contacts to setup OpenPGP.</small></string> - <string name="encrypted_message_received"><i>Encrypted message received. Touch to view and decrypt.</i></string> - <string name="pref_general">General</string> - <string name="pref_xmpp_resource">XMPP resource</string> - <string name="pref_xmpp_resource_summary">The name this client identifies itself with</string> - <string name="pref_accept_files">Accept files</string> - <string name="pref_accept_files_summary">Automatically accept files smaller than…</string> - <string name="pref_notification_settings">Notification Settings</string> - <string name="pref_notifications">Notifications</string> - <string name="pref_notifications_summary">Notify when a new message arrives</string> - <string name="pref_vibrate">Vibrate</string> - <string name="pref_vibrate_summary">Also vibrate when a new message arrives</string> - <string name="pref_sound">Sound</string> - <string name="pref_sound_summary">Play ringtone with notification</string> - <string name="pref_conference_notifications">Conference notifications</string> - <string name="pref_conference_notifications_summary">Always notify when a new conference message arrives instead of only when highlighted</string> - <string name="pref_notification_grace_period">Notification grace period</string> - <string name="pref_notification_grace_period_summary">Disable notifications for a short time after a carbon copy was received</string> - <string name="pref_advanced_options">Advanced Options</string> - <string name="pref_never_send_crash">Never send crash reports</string> - <string name="pref_never_send_crash_summary">By sending in stack traces you are helping the ongoing development of Conversations</string> - <string name="pref_confirm_messages">Confirm Messages</string> - <string name="pref_confirm_messages_summary">Let your contact know when you have received and read a message</string> - <string name="pref_ui_options">UI Options</string> - <string name="openpgp_error">OpenKeychain reported an error</string> - <string name="error_decrypting_file">I/O Error decrypting file</string> - <string name="accept">Accept</string> - <string name="error">An error has occurred</string> - <string name="pref_grant_presence_updates">Grant presence updates</string> - <string name="pref_grant_presence_updates_summary">Preemptively grant and ask for presence subscription for contacts you created</string> - <string name="subscriptions">Subscriptions</string> - <string name="your_account">Your account</string> - <string name="keys">Keys</string> - <string name="send_presence_updates">Send presence updates</string> - <string name="receive_presence_updates">Receive presence updates</string> - <string name="ask_for_presence_updates">Ask for presence updates</string> - <string name="attach_choose_picture">Choose picture</string> - <string name="attach_take_picture">Take picture</string> - <string name="preemptively_grant">Preemptively grant subscription request</string> - <string name="error_not_an_image_file">The file you selected is not an image</string> - <string name="error_compressing_image">Error while converting the image file</string> - <string name="error_file_not_found">File not found</string> - <string name="error_io_exception">General I/O error. Maybe you ran out of storage space?</string> - <string name="error_security_exception_during_image_copy">The app you used to select this image did not provide us with enough permissions to read the file.\n\n<small>Use a different file manager to choose an image</small></string> - <string name="account_status_unknown">Unknown</string> - <string name="account_status_disabled">Temporarily disabled</string> - <string name="account_status_online">Online</string> - <string name="account_status_connecting">Connecting\u2026</string> - <string name="account_status_offline">Offline</string> - <string name="account_status_unauthorized">Unauthorized</string> - <string name="account_status_not_found">Server not found</string> - <string name="account_status_no_internet">No connectivity</string> - <string name="account_status_regis_fail">Registration failed</string> - <string name="account_status_regis_conflict">Username already in use</string> - <string name="account_status_regis_success">Registration completed</string> - <string name="account_status_regis_not_sup">Server does not support registration</string> - <string name="account_status_security_error">Security error</string> - <string name="account_status_incompatible_server">Incompatible server</string> - <string name="encryption_choice_none">Plain text</string> - <string name="encryption_choice_otr">OTR</string> - <string name="encryption_choice_pgp">OpenPGP</string> - <string name="mgmt_account_edit">Edit account</string> - <string name="mgmt_account_delete">Delete account</string> - <string name="mgmt_account_disable">Temporarily disable</string> - <string name="mgmt_account_publish_avatar">Publish avatar</string> - <string name="mgmt_account_publish_pgp">Publish OpenPGP public key</string> - <string name="mgmt_account_enable">Enable account</string> - <string name="mgmt_account_are_you_sure">Are you sure?</string> - <string name="mgmt_account_delete_confirm_text">If you delete your account your entire conversation history will be lost</string> - <string name="attach_record_voice">Record voice</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">Confirm password</string> - <string name="password">Password</string> - <string name="confirm_password">Confirm password</string> - <string name="passwords_do_not_match">Passwords do not match</string> - <string name="invalid_jid">This is not a valid Jabber ID</string> - <string name="error_out_of_memory">Out of memory. Image is too large</string> - <string name="add_phone_book_text">Do you want to add %s to your phones contact list?</string> - <string name="contact_status_online">online</string> - <string name="contact_status_free_to_chat">free to chat</string> - <string name="contact_status_away">away</string> - <string name="contact_status_extended_away">extended away</string> - <string name="contact_status_do_not_disturb">do not disturb</string> - <string name="contact_status_offline">offline</string> - <string name="muc_details_conference">Conference</string> - <string name="muc_details_other_members">Other Members</string> - <string name="server_info_show_more">Server info</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">available</string> - <string name="server_info_unavailable">unavailable</string> - <string name="missing_public_keys">Missing public key announcements</string> - <string name="last_seen_now">last seen just now</string> - <string name="last_seen_min">last seen 1 minute ago</string> - <string name="last_seen_mins">last seen %d minutes ago</string> - <string name="last_seen_hour">last seen 1 hour ago</string> - <string name="last_seen_hours">last seen %d hours ago</string> - <string name="last_seen_day">last seen 1 day ago</string> - <string name="last_seen_days">last seen %d days ago</string> - <string name="never_seen">never seen</string> - <string name="install_openkeychain">Encrypted message. Please install OpenKeychain to decrypt.</string> - <string name="unknown_otr_fingerprint">Unknown OTR fingerprint</string> - <string name="openpgp_messages_found">OpenPGP encrypted messages found</string> - <string name="reception_failed">Reception failed</string> - <string name="your_fingerprint">Your fingerprint</string> - <string name="otr_fingerprint">OTR fingerprint</string> - <string name="verify">Verify</string> - <string name="decrypt">Decrypt</string> - <string name="conferences">Conferences</string> - <string name="search">Search</string> - <string name="create_contact">Create Contact</string> - <string name="join_conference">Join Conference</string> - <string name="delete_contact">Delete Contact</string> - <string name="view_contact_details">View contact details</string> - <string name="block_contact">Block contact</string> - <string name="unblock_contact">Unblock contact</string> - <string name="create">Create</string> - <string name="contact_already_exists">The contact already exists</string> - <string name="join">Join</string> - <string name="conference_address">Conference address</string> - <string name="conference_address_example">room@conference.example.com</string> - <string name="save_as_bookmark">Save as bookmark</string> - <string name="delete_bookmark">Delete bookmark</string> - <string name="bookmark_already_exists">This bookmark already exists</string> - <string name="you">You</string> - <string name="action_edit_subject">Edit conference subject</string> - <string name="conference_not_found">Conference not found</string> - <string name="leave">Leave</string> - <string name="contact_added_you">Contact added you to contact list</string> - <string name="add_back">Add back</string> - <string name="contact_has_read_up_to_this_point">%s has read up to this point</string> - <string name="publish">Publish</string> - <string name="touch_to_choose_picture">Touch avatar to select picture from gallery</string> - <string name="publish_avatar_explanation">Please note: Everyone subscribed to your presence updates will be allowed to see this picture.</string> - <string name="publishing">Publishing…</string> - <string name="error_publish_avatar_server_reject">The server rejected your publication</string> - <string name="error_publish_avatar_converting">Something went wrong while converting your picture</string> - <string name="error_saving_avatar">Could not save avatar to disk</string> - <string name="or_long_press_for_default">(Or long press to bring back default)</string> - <string name="error_publish_avatar_no_server_support">Your server does not support the publication of avatars</string> - <string name="private_message">whispered</string> - <string name="private_message_to">to %s</string> - <string name="send_private_message_to">Send private message to %s</string> - <string name="connect">Connect</string> - <string name="account_already_exists">This account already exists</string> - <string name="next">Next</string> - <string name="server_info_session_established">Current session established</string> - <string name="additional_information">Additional Information</string> - <string name="skip">Skip</string> - <string name="disable_notifications">Disable notifications</string> - <string name="disable_notifications_for_this_conversation">Disable notifications for this conversation</string> - <string name="notifications_disabled">Notifications are disabled</string> - <string name="enable">Enable</string> - <string name="conference_requires_password">Conference requires password</string> - <string name="enter_password">Enter password</string> - <string name="missing_presence_updates">Missing presence updates from contact</string> - <string name="request_presence_updates">Please request presence updates from your contact first.\n\n<small>This will be used to determine what client(s) your contact is using.</small></string> - <string name="request_now">Request now</string> - <string name="delete_fingerprint">Delete Fingerprint</string> - <string name="sure_delete_fingerprint">Are you sure you would like to delete this fingerprint?</string> - <string name="ignore">Ignore</string> - <string name="without_mutual_presence_updates"><b>Warning:</b> Sending this without mutual presence updates could cause unexpected problems.\n\n<small>Go to contact details to verify your presence subscriptions.</small></string> - <string name="pref_encryption_settings">Encryption settings</string> - <string name="pref_force_encryption">Force end-to-end encryption</string> - <string name="pref_force_encryption_summary">Always send messages encrypted (except for conferences)</string> - <string name="pref_dont_save_encrypted">Don’t save encrypted messages</string> - <string name="pref_dont_save_encrypted_summary">Warning: This could lead to message loss</string> - <string name="pref_expert_options">Expert options</string> - <string name="pref_expert_options_summary">Please be careful with these</string> - <string name="title_activity_about">About Conversations</string> - <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) - \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_image">Checking image 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="app_name" translatable="false">Conversations</string> + <string name="action_settings">Settings</string> + <string name="action_add">New conversation</string> + <string name="action_accounts">Manage accounts</string> + <string name="action_end_conversation">End this conversation</string> + <string name="action_contact_details">Contact details</string> + <string name="action_muc_details">Conference details</string> + <string name="action_secure">Secure conversation</string> + <string name="action_add_account">Add account</string> + <string name="action_edit_contact">Edit name</string> + <string name="action_add_phone_book">Add to phone book</string> + <string name="action_delete_contact">Delete from roster</string> + <string name="action_block_contact">Block contact</string> + <string name="action_unblock_contact">Unblock contact</string> + <string name="action_block_domain">Block domain</string> + <string name="action_unblock_domain">Unblock domain</string> + <string name="title_activity_manage_accounts">Manage Accounts</string> + <string name="title_activity_settings">Settings</string> + <string name="title_activity_conference_details">Conference Details</string> + <string name="title_activity_contact_details">Contact Details</string> + <string name="title_activity_sharewith">Share with Conversation</string> + <string name="title_activity_start_conversation">Start Conversation</string> + <string name="title_activity_choose_contact">Choose contact</string> + <string name="title_activity_block_list">Block list</string> + <string name="just_now">just now</string> + <string name="minute_ago">1 min ago</string> + <string name="minutes_ago">%d mins ago</string> + <string name="unread_conversations">unread Conversations</string> + <string name="sending">sending…</string> + <string name="encrypted_message">Decrypting message. Please wait…</string> + <string name="nick_in_use">Nickname is already in use</string> + <string name="admin">Admin</string> + <string name="owner">Owner</string> + <string name="moderator">Moderator</string> + <string name="participant">Participant</string> + <string name="visitor">Visitor</string> + <string name="remove_contact_text">Would you like to remove %s from your roster? The conversation associated with this contact will not be removed.</string> + <string name="block_contact_text">Would you like to block %s from sending you messages?</string> + <string name="unblock_contact_text">Would you like to unblock %s and allow them to send you messages?</string> + <string name="block_domain_text">Block all contacts from %s?</string> + <string name="unblock_domain_text">Unblock all contacts from %s?</string> + <string name="contact_blocked">Contact blocked</string> + <string name="remove_bookmark_text">Would you like to remove %s as a bookmark? The conversation associated with this bookmark will not be removed.</string> + <string name="register_account">Register new account on server</string> + <string name="change_password_on_server">Change password on server</string> + <string name="share_with">Share with…</string> + <string name="start_conversation">Start Conversation</string> + <string name="invite_contact">Invite Contact</string> + <string name="contacts">Contacts</string> + <string name="cancel">Cancel</string> + <string name="set">Set</string> + <string name="add">Add</string> + <string name="edit">Edit</string> + <string name="delete">Delete</string> + <string name="block">Block</string> + <string name="unblock">Unblock</string> + <string name="save">Save</string> + <string name="ok">OK</string> + <string name="crash_report_title">Conversations has crashed</string> + <string name="crash_report_message">By sending in stack traces you are helping the ongoing development of Conversations\n<b>Warning:</b> This will use your XMPP account to send the stack trace to the developer.</string> + <string name="send_now">Send now</string> + <string name="send_never">Never ask again</string> + <string name="problem_connecting_to_account">Unable to connect to account</string> + <string name="problem_connecting_to_accounts">Unable to connect to multiple accounts</string> + <string name="touch_to_fix">Touch here to manage your accounts</string> + <string name="attach_file">Attach file</string> + <string name="not_in_roster">The contact is not in your roster. Would you like to add it?</string> + <string name="add_contact">Add contact</string> + <string name="send_failed">delivery failed</string> + <string name="send_rejected">rejected</string> + <string name="preparing_image">Preparing image for transmission</string> + <string name="action_clear_history">Clear history</string> + <string name="clear_conversation_history">Clear Conversation History</string> + <string name="clear_histor_msg">Do you want to delete all messages within this Conversation?\n\n<b>Warning:</b> This will not influence messages stored on other devices or servers.</string> + <string name="delete_messages">Delete messages</string> + <string name="also_end_conversation">End this conversations afterwards</string> + <string name="choose_presence">Choose presence to contact</string> + <string name="send_plain_text_message">Send plain text message</string> + <string name="send_otr_message">Send OTR encrypted message</string> + <string name="send_pgp_message">Send OpenPGP encrypted message</string> + <string name="your_nick_has_been_changed">Your nickname has been changed</string> + <string name="download_image">Download Image</string> + <string name="send_unencrypted">Send unencrypted</string> + <string name="decryption_failed">Decryption failed. Maybe you don’t have the proper private key.</string> + <string name="openkeychain_required">OpenKeychain</string> + <string name="openkeychain_required_long">Conversations utilizes a third party app called <b>OpenKeychain</b> to encrypt and decrypt messages and to manage your public keys.\n\nOpenKeychain is licensed under GPLv3 and available on F-Droid and Google Play.\n\n<small>(Please restart Conversations afterwards.)</small></string> + <string name="restart">Restart</string> + <string name="install">Install</string> + <string name="offering">offering…</string> + <string name="waiting">waiting…</string> + <string name="no_pgp_key">No OpenPGP Key found</string> + <string name="contact_has_no_pgp_key">Conversations is unable to encrypt your messages because your contact is not announcing his or hers public key.\n\n<small>Please ask your contact to setup OpenPGP.</small></string> + <string name="no_pgp_keys">No OpenPGP Keys found</string> + <string name="contacts_have_no_pgp_keys">Conversations is unable to encrypt your messages because your contacts are not announcing their public key.\n\n<small>Please ask your contacts to setup OpenPGP.</small></string> + <string name="encrypted_message_received"><i>Encrypted message received. Touch to view and decrypt.</i></string> + <string name="pref_general">General</string> + <string name="pref_xmpp_resource">XMPP resource</string> + <string name="pref_xmpp_resource_summary">The name this client identifies itself with</string> + <string name="pref_accept_files">Accept files</string> + <string name="pref_accept_files_summary">Automatically accept files smaller than…</string> + <string name="pref_notification_settings">Notification Settings</string> + <string name="pref_notifications">Notifications</string> + <string name="pref_notifications_summary">Notify when a new message arrives</string> + <string name="pref_vibrate">Vibrate</string> + <string name="pref_vibrate_summary">Also vibrate when a new message arrives</string> + <string name="pref_sound">Sound</string> + <string name="pref_sound_summary">Play ringtone with notification</string> + <string name="pref_conference_notifications">Conference notifications</string> + <string name="pref_conference_notifications_summary">Always notify when a new conference message arrives instead of only when highlighted</string> + <string name="pref_notification_grace_period">Notification grace period</string> + <string name="pref_notification_grace_period_summary">Disable notifications for a short time after a carbon copy was received</string> + <string name="pref_advanced_options">Advanced Options</string> + <string name="pref_never_send_crash">Never send crash reports</string> + <string name="pref_never_send_crash_summary">By sending in stack traces you are helping the ongoing development of Conversations</string> + <string name="pref_confirm_messages">Confirm Messages</string> + <string name="pref_confirm_messages_summary">Let your contact know when you have received and read a message</string> + <string name="pref_ui_options">UI Options</string> + <string name="openpgp_error">OpenKeychain reported an error</string> + <string name="error_decrypting_file">I/O Error decrypting file</string> + <string name="accept">Accept</string> + <string name="error">An error has occurred</string> + <string name="pref_grant_presence_updates">Grant presence updates</string> + <string name="pref_grant_presence_updates_summary">Preemptively grant and ask for presence subscription for contacts you created</string> + <string name="subscriptions">Subscriptions</string> + <string name="your_account">Your account</string> + <string name="keys">Keys</string> + <string name="send_presence_updates">Send presence updates</string> + <string name="receive_presence_updates">Receive presence updates</string> + <string name="ask_for_presence_updates">Ask for presence updates</string> + <string name="attach_choose_picture">Choose picture</string> + <string name="attach_take_picture">Take picture</string> + <string name="preemptively_grant">Preemptively grant subscription request</string> + <string name="error_not_an_image_file">The file you selected is not an image</string> + <string name="error_compressing_image">Error while converting the image file</string> + <string name="error_file_not_found">File not found</string> + <string name="error_io_exception">General I/O error. Maybe you ran out of storage space?</string> + <string name="error_security_exception_during_image_copy">The app you used to select this image did not provide us with enough permissions to read the file.\n\n<small>Use a different file manager to choose an image</small></string> + <string name="account_status_unknown">Unknown</string> + <string name="account_status_disabled">Temporarily disabled</string> + <string name="account_status_online">Online</string> + <string name="account_status_connecting">Connecting\u2026</string> + <string name="account_status_offline">Offline</string> + <string name="account_status_unauthorized">Unauthorized</string> + <string name="account_status_not_found">Server not found</string> + <string name="account_status_no_internet">No connectivity</string> + <string name="account_status_regis_fail">Registration failed</string> + <string name="account_status_regis_conflict">Username already in use</string> + <string name="account_status_regis_success">Registration completed</string> + <string name="account_status_regis_not_sup">Server does not support registration</string> + <string name="account_status_security_error">Security error</string> + <string name="account_status_incompatible_server">Incompatible server</string> + <string name="encryption_choice_none">Plain text</string> + <string name="encryption_choice_otr">OTR</string> + <string name="encryption_choice_pgp">OpenPGP</string> + <string name="mgmt_account_edit">Edit account</string> + <string name="mgmt_account_delete">Delete account</string> + <string name="mgmt_account_disable">Temporarily disable</string> + <string name="mgmt_account_publish_avatar">Publish avatar</string> + <string name="mgmt_account_publish_pgp">Publish OpenPGP public key</string> + <string name="mgmt_account_enable">Enable account</string> + <string name="mgmt_account_are_you_sure">Are you sure?</string> + <string name="mgmt_account_delete_confirm_text">If you delete your account your entire conversation history will be lost</string> + <string name="attach_record_voice">Record voice</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">Confirm password</string> + <string name="password">Password</string> + <string name="confirm_password">Confirm password</string> + <string name="passwords_do_not_match">Passwords do not match</string> + <string name="invalid_jid">This is not a valid Jabber ID</string> + <string name="error_out_of_memory">Out of memory. Image is too large</string> + <string name="add_phone_book_text">Do you want to add %s to your phones contact list?</string> + <string name="contact_status_online">online</string> + <string name="contact_status_free_to_chat">free to chat</string> + <string name="contact_status_away">away</string> + <string name="contact_status_extended_away">extended away</string> + <string name="contact_status_do_not_disturb">do not disturb</string> + <string name="contact_status_offline">offline</string> + <string name="muc_details_conference">Conference</string> + <string name="muc_details_other_members">Other Members</string> + <string name="server_info_show_more">Server info</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">available</string> + <string name="server_info_unavailable">unavailable</string> + <string name="missing_public_keys">Missing public key announcements</string> + <string name="last_seen_now">last seen just now</string> + <string name="last_seen_min">last seen 1 minute ago</string> + <string name="last_seen_mins">last seen %d minutes ago</string> + <string name="last_seen_hour">last seen 1 hour ago</string> + <string name="last_seen_hours">last seen %d hours ago</string> + <string name="last_seen_day">last seen 1 day ago</string> + <string name="last_seen_days">last seen %d days ago</string> + <string name="never_seen">never seen</string> + <string name="install_openkeychain">Encrypted message. Please install OpenKeychain to decrypt.</string> + <string name="unknown_otr_fingerprint">Unknown OTR fingerprint</string> + <string name="openpgp_messages_found">OpenPGP encrypted messages found</string> + <string name="reception_failed">Reception failed</string> + <string name="your_fingerprint">Your fingerprint</string> + <string name="otr_fingerprint">OTR fingerprint</string> + <string name="verify">Verify</string> + <string name="decrypt">Decrypt</string> + <string name="conferences">Conferences</string> + <string name="search">Search</string> + <string name="create_contact">Create Contact</string> + <string name="join_conference">Join Conference</string> + <string name="delete_contact">Delete Contact</string> + <string name="view_contact_details">View contact details</string> + <string name="block_contact">Block contact</string> + <string name="unblock_contact">Unblock contact</string> + <string name="create">Create</string> + <string name="contact_already_exists">The contact already exists</string> + <string name="join">Join</string> + <string name="conference_address">Conference address</string> + <string name="conference_address_example">room@conference.example.com</string> + <string name="save_as_bookmark">Save as bookmark</string> + <string name="delete_bookmark">Delete bookmark</string> + <string name="bookmark_already_exists">This bookmark already exists</string> + <string name="you">You</string> + <string name="action_edit_subject">Edit conference subject</string> + <string name="conference_not_found">Conference not found</string> + <string name="leave">Leave</string> + <string name="contact_added_you">Contact added you to contact list</string> + <string name="add_back">Add back</string> + <string name="contact_has_read_up_to_this_point">%s has read up to this point</string> + <string name="publish">Publish</string> + <string name="touch_to_choose_picture">Touch avatar to select picture from gallery</string> + <string name="publish_avatar_explanation">Please note: Everyone subscribed to your presence updates will be allowed to see this picture.</string> + <string name="publishing">Publishing…</string> + <string name="error_publish_avatar_server_reject">The server rejected your publication</string> + <string name="error_publish_avatar_converting">Something went wrong while converting your picture</string> + <string name="error_saving_avatar">Could not save avatar to disk</string> + <string name="or_long_press_for_default">(Or long press to bring back default)</string> + <string name="error_publish_avatar_no_server_support">Your server does not support the publication of avatars</string> + <string name="private_message">whispered</string> + <string name="private_message_to">to %s</string> + <string name="send_private_message_to">Send private message to %s</string> + <string name="connect">Connect</string> + <string name="account_already_exists">This account already exists</string> + <string name="next">Next</string> + <string name="server_info_session_established">Current session established</string> + <string name="additional_information">Additional Information</string> + <string name="skip">Skip</string> + <string name="disable_notifications">Disable notifications</string> + <string name="disable_notifications_for_this_conversation">Disable notifications for this conversation</string> + <string name="notifications_disabled">Notifications are disabled</string> + <string name="enable">Enable</string> + <string name="conference_requires_password">Conference requires password</string> + <string name="enter_password">Enter password</string> + <string name="missing_presence_updates">Missing presence updates from contact</string> + <string name="request_presence_updates">Please request presence updates from your contact first.\n\n<small>This will be used to determine what client(s) your contact is using.</small></string> + <string name="request_now">Request now</string> + <string name="delete_fingerprint">Delete Fingerprint</string> + <string name="sure_delete_fingerprint">Are you sure you would like to delete this fingerprint?</string> + <string name="ignore">Ignore</string> + <string name="without_mutual_presence_updates"><b>Warning:</b> Sending this without mutual presence updates could cause unexpected problems.\n\n<small>Go to contact details to verify your presence subscriptions.</small></string> + <string name="pref_encryption_settings">Encryption settings</string> + <string name="pref_force_encryption">Force end-to-end encryption</string> + <string name="pref_force_encryption_summary">Always send messages encrypted (except for conferences)</string> + <string name="pref_dont_save_encrypted">Don’t save encrypted messages</string> + <string name="pref_dont_save_encrypted_summary">Warning: This could lead to message loss</string> + <string name="pref_expert_options">Expert options</string> + <string name="pref_expert_options_summary">Please be careful with these</string> + <string name="title_activity_about">About Conversations</string> + <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) + \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_image">Checking image 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="change_password">Change password</string> <string name="current_password">Current password</string> <string name="new_password">New password</string> @@ -442,19 +442,19 @@ <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> @@ -474,4 +474,9 @@ <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> </resources> diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index 417e60a4..5b7d6904 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -105,6 +105,14 @@ 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" |