diff --git a/.travis.yml b/.travis.yml
index 4f147a61da249ef79e160d568b1276cda0580a64..a2959e0a2eef0d48bce254ce67fd052ac44b6901 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,7 +3,8 @@ android:
components:
- platform-tools
- tools
+ - build-tools-22.0.1
- build-tools-21.1.2
- build-tools-19.1.0
- - android-21
+ - android-22
- extra-android-m2repository
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae267086e891474ddf1fea09ef0d7a2c79663cc9..61980b6184f4394f09072c89d9564a43e6a94f6a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
###Changelog
+####Version 1.4.0
+* send button turns into quick action button to offer faster access to take photo, send location or record audio
+* visually seperate merged messages
+* faster reconnects of failed accounts after network switches
+* r/o vcard avatars for contacts
+* various bug fixes
+
####Version 1.3.0
* swipe conversations to end them
* quickly enable / disable account via slider
diff --git a/README.md b/README.md
index 5519e47f6ef4370ea90f8cd7f83af521702477dd..e7221eab54b495c64afb0120cce829293d7c6aec 100644
--- a/README.md
+++ b/README.md
@@ -67,12 +67,13 @@ run your own XMPP server for you and your friends. These XEP's are:
(In order of appearance)
-* [Rene Treffer](https://github.com/rtreffer)
-* [Andreas Straub](https://github.com/strb)
-* [Alethea Butler](https://github.com/alethea)
-* [M. Dietrich](https://github.com/emdete)
-* [betheg](https://github.com/betheg)
-* [Sam Whited](https://github.com/SamWhited)
+* [Rene Treffer](https://github.com/rtreffer) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Artreffer+is%3Amerged))
+* [Andreas Straub](https://github.com/strb) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Astrb+is%3Amerged))
+* [Alethea Butler](https://github.com/alethea) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Aalethea+is%3Amerged))
+* [M. Dietrich](https://github.com/emdete) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Aemdete+is%3Amerged))
+* [betheg](https://github.com/betheg) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3Abetheg+is%3Amerged))
+* [Sam Whited](https://github.com/SamWhited) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3ASamWhited+is%3Amerged))
+* [BrianBlade](https://github.com/BrianBlade) ([PRs](https://github.com/siacs/Conversations/pulls?utf8=%E2%9C%93&q=is%3Apr+author%3ABrianBlade+is%3Amerged))
#### Logo
* [Ilia Rostovtsev](https://github.com/qooob) (Progress)
diff --git a/art/ic_received_indicator.svg b/art/ic_received_indicator.svg
index d9378c60dde503e54a4de1ce0b4b8185f47cff3d..43689c2674d0e20ad43a61acdefa1f80159a237e 100644
--- a/art/ic_received_indicator.svg
+++ b/art/ic_received_indicator.svg
@@ -13,7 +13,7 @@
width="95"
height="95"
id="Yes_check"
- inkscape:version="0.48.5 r10040"
+ inkscape:version="0.91 r13725"
sodipodi:docname="ic_received_indicator.svg">
@@ -23,7 +23,7 @@
image/svg+xml
-
+
@@ -36,17 +36,17 @@
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
- inkscape:window-width="1233"
- inkscape:window-height="828"
+ inkscape:window-width="956"
+ inkscape:window-height="1156"
id="namedview8"
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="5.04"
- inkscape:cx="26.829268"
+ inkscape:cx="-4.3215257"
inkscape:cy="37.489149"
- inkscape:window-x="0"
- inkscape:window-y="0"
+ inkscape:window-x="2880"
+ inkscape:window-y="20"
inkscape:window-maximized="0"
inkscape:current-layer="Yes_check"
fit-margin-top="0"
@@ -69,7 +69,7 @@
diff --git a/art/ic_send_cancel_away.svg b/art/ic_send_cancel_away.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1ee9c40f61b37a16ca5abd1971df24beacea7a27
--- /dev/null
+++ b/art/ic_send_cancel_away.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/art/ic_send_cancel_dnd.svg b/art/ic_send_cancel_dnd.svg
new file mode 100644
index 0000000000000000000000000000000000000000..67a562b2f40fa8ce83303d2a43f95ce69cd8e293
--- /dev/null
+++ b/art/ic_send_cancel_dnd.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/art/ic_send_cancel_offline.svg b/art/ic_send_cancel_offline.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b88ade09db0c0e32d5db585f4679becb6b5f0b0e
--- /dev/null
+++ b/art/ic_send_cancel_offline.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/art/ic_send_cancel_online.svg b/art/ic_send_cancel_online.svg
new file mode 100644
index 0000000000000000000000000000000000000000..40946af6758f0eb14340e5dbbc3f53ea22b92bd3
--- /dev/null
+++ b/art/ic_send_cancel_online.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/art/ic_send_location_away.svg b/art/ic_send_location_away.svg
new file mode 100644
index 0000000000000000000000000000000000000000..fcd50b5211fc56a6f41b9580711060c856c65786
--- /dev/null
+++ b/art/ic_send_location_away.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/art/ic_send_location_dnd.svg b/art/ic_send_location_dnd.svg
new file mode 100644
index 0000000000000000000000000000000000000000..705cdb6f4e62520f7878d8d8b3332a9a4a57adb2
--- /dev/null
+++ b/art/ic_send_location_dnd.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/art/ic_send_location_offline.svg b/art/ic_send_location_offline.svg
new file mode 100644
index 0000000000000000000000000000000000000000..56529b723026e6086d8df89692ab0afa49c596b0
--- /dev/null
+++ b/art/ic_send_location_offline.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/art/ic_send_location_online.svg b/art/ic_send_location_online.svg
new file mode 100644
index 0000000000000000000000000000000000000000..275a7a789fe22bbc25259efc6a1728e5df25dfb5
--- /dev/null
+++ b/art/ic_send_location_online.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/art/ic_send_photo_away.svg b/art/ic_send_photo_away.svg
new file mode 100644
index 0000000000000000000000000000000000000000..31a20e09f81667a5c52203c57d3a5e1459667c93
--- /dev/null
+++ b/art/ic_send_photo_away.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/art/ic_send_photo_dnd.svg b/art/ic_send_photo_dnd.svg
new file mode 100644
index 0000000000000000000000000000000000000000..9ef8b7821b09808e8df898f43e5ec11f9d51c8f7
--- /dev/null
+++ b/art/ic_send_photo_dnd.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/art/ic_send_photo_offline.svg b/art/ic_send_photo_offline.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b2ca20a6f5e1be0a743ef18176042a3d37c17d63
--- /dev/null
+++ b/art/ic_send_photo_offline.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/art/ic_send_photo_online.svg b/art/ic_send_photo_online.svg
new file mode 100644
index 0000000000000000000000000000000000000000..79f7134754603423d6e0a60b6b95701ba50282b3
--- /dev/null
+++ b/art/ic_send_photo_online.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/art/ic_send_text_away.svg b/art/ic_send_text_away.svg
new file mode 100644
index 0000000000000000000000000000000000000000..ea83086aea9c18a8f806a2b7b0c673609ff4f587
--- /dev/null
+++ b/art/ic_send_text_away.svg
@@ -0,0 +1,69 @@
+
+
+
+
diff --git a/art/ic_send_text_dnd.svg b/art/ic_send_text_dnd.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1b7ad51f431d81ccae5a5500b6801f690a613b58
--- /dev/null
+++ b/art/ic_send_text_dnd.svg
@@ -0,0 +1,69 @@
+
+
+
+
diff --git a/art/ic_send_text_offline.svg b/art/ic_send_text_offline.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c87bfaac66985118de28b1adc35ec93eb36f60e7
--- /dev/null
+++ b/art/ic_send_text_offline.svg
@@ -0,0 +1,69 @@
+
+
+
+
diff --git a/art/ic_action_send_now.svg b/art/ic_send_text_online.svg
similarity index 80%
rename from art/ic_action_send_now.svg
rename to art/ic_send_text_online.svg
index 6bde9158fad2bea9936de7738701af2ca5d6b696..39e3d1e8a3a0085bb34111ac0a82246f48e60961 100644
--- a/art/ic_action_send_now.svg
+++ b/art/ic_send_text_online.svg
@@ -11,7 +11,7 @@
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg3621"
version="1.1"
- inkscape:version="0.48.4 r9939"
+ inkscape:version="0.91 r13725"
width="96"
height="96"
sodipodi:docname="ic_action_send_now.svg"
@@ -26,7 +26,7 @@
image/svg+xml
-
+
@@ -41,16 +41,16 @@
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
- inkscape:window-width="1916"
- inkscape:window-height="1161"
+ inkscape:window-width="1920"
+ inkscape:window-height="1200"
id="namedview3623"
showgrid="true"
showguides="true"
- inkscape:zoom="1"
- inkscape:cx="47.28873"
- inkscape:cy="43.262706"
- inkscape:window-x="0"
- inkscape:window-y="18"
+ inkscape:zoom="8"
+ inkscape:cx="69.783303"
+ inkscape:cy="56.761328"
+ inkscape:window-x="1920"
+ inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg3621">
+
diff --git a/art/ic_send_voice_dnd.svg b/art/ic_send_voice_dnd.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b1b7a7a9785e663ed6871f26cdc89d32b57402c1
--- /dev/null
+++ b/art/ic_send_voice_dnd.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/art/ic_send_voice_offline.svg b/art/ic_send_voice_offline.svg
new file mode 100644
index 0000000000000000000000000000000000000000..64ea447317a3985405a03c614978ba8d7ee011a6
--- /dev/null
+++ b/art/ic_send_voice_offline.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/art/ic_send_voice_online.svg b/art/ic_send_voice_online.svg
new file mode 100644
index 0000000000000000000000000000000000000000..97284b021da63a23efb3ed86110e426267d3b1d0
--- /dev/null
+++ b/art/ic_send_voice_online.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/art/render.rb b/art/render.rb
index 2ab3e94fc6181755e004a62fd3490ad4048ed038..ad53b1f4fa6c52c980bbcfded8175c5a91ce853f 100755
--- a/art/render.rb
+++ b/art/render.rb
@@ -10,6 +10,26 @@ images = {
'conversations_baloon.svg' => ['ic_launcher', 48],
'conversations_mono.svg' => ['ic_notification', 24],
'ic_received_indicator.svg' => ['ic_received_indicator', 12],
+ 'ic_send_text_offline.svg' => ['ic_send_text_offline', 36],
+ 'ic_send_text_online.svg' => ['ic_send_text_online', 36],
+ 'ic_send_text_away.svg' => ['ic_send_text_away', 36],
+ 'ic_send_text_dnd.svg' => ['ic_send_text_dnd', 36],
+ 'ic_send_photo_online.svg' => ['ic_send_photo_online', 36],
+ 'ic_send_photo_offline.svg' => ['ic_send_photo_offline', 36],
+ 'ic_send_photo_away.svg' => ['ic_send_photo_away', 36],
+ 'ic_send_photo_dnd.svg' => ['ic_send_photo_dnd', 36],
+ 'ic_send_location_online.svg' => ['ic_send_location_online', 36],
+ 'ic_send_location_offline.svg' => ['ic_send_location_offline', 36],
+ 'ic_send_location_away.svg' => ['ic_send_location_away', 36],
+ 'ic_send_location_dnd.svg' => ['ic_send_location_dnd', 36],
+ 'ic_send_voice_online.svg' => ['ic_send_voice_online', 36],
+ 'ic_send_voice_offline.svg' => ['ic_send_voice_offline', 36],
+ 'ic_send_voice_away.svg' => ['ic_send_voice_away', 36],
+ 'ic_send_voice_dnd.svg' => ['ic_send_voice_dnd', 36],
+ 'ic_send_cancel_online.svg' => ['ic_send_cancel_online', 36],
+ 'ic_send_cancel_offline.svg' => ['ic_send_cancel_offline', 36],
+ 'ic_send_cancel_away.svg' => ['ic_send_cancel_away', 36],
+ 'ic_send_cancel_dnd.svg' => ['ic_send_cancel_dnd', 36],
}
images.each do |source, result|
resolutions.each do |name, factor|
diff --git a/build.gradle b/build.gradle
index afc7bb4bc1c78ad595a288188bdcbf16ca93a856..98b341d52af80145ee2942e9f019d9b5afefeba1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -38,14 +38,14 @@ dependencies {
}
android {
- compileSdkVersion 21
- buildToolsVersion "21.1.2"
+ compileSdkVersion 22
+ buildToolsVersion "22.0.1"
defaultConfig {
minSdkVersion 14
targetSdkVersion 21
- versionCode 61
- versionName "1.3.1"
+ versionCode 66
+ versionName "1.4.0"
}
compileOptions {
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index 5cca6c0b7061405a0b759250a06f9dc4a1cd9204..779cbbe866eb6a4bc6028b328a5d0eb87a3865c4 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 cef03ebe47c53f487040f3a31736807ff5a9ae56..9dbca59a184dddc3cd2e480924a7c2bad5e1e355 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 bfee500753794adcb4ec414fa8ec1e02e8a4b81c..95a8c957ddb2657d5077542e0178c589d0582bc4 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 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 7ea3d60b0b62c7d36eb5db6f446fb8ff8a7c3405..38152edbafbe651ef31c0a362bc08ab7d9f5c50f 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 addee8db26b0298407b41b512677f1f98ae196bc..d867a370800e8bf843cd4bbbb6472d1dd16484fe 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 6bc629b58a7ba81b30756d4cf51dc9c8e828ce94..d7366daa8dbaf4bd8102c5fb71ffcc7e30bfe02d 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 526005f3352b26dcc8b18dbd28fa2b2bedf6bf21..c40c6d0514c858241034ea75416729df3913fdcd 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 76d014688d85af1e2b42ba3054e33877c890d18f..7870fdbff9549b167b39521d75e1209d320a3599 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 7505b091b1ea1decb89494a2bdbea0c47a3770ba..f7872210df5e9e4f7f48852155df1e7c2366ec04 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 28e1c47e7445f0fceddb86099e71146e002878e6..ed88e434e3a66d34ebb1de41e4bbff8a06f55a90 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 fc40ce7562e06792b38733788b94ff68e2884cff..e111da95508413c749abbea4896203eee1dbe4c1 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 ec0b3f928d7a04471a7e0c392170be2380f8720b..63d9ba7a8b3ce13663e093d467f3d532a9807705 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 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 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 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 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 callback) {
+ private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback 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 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 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 8c4f6eafe5e1f2e9f662cf173dbd7c7cdc2bcd88..07b8819d963caec4c69af13aaa6e685896f43558 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 aec755fcea0a3f83971c2a5bcf640ef7519113e3..1b5e51781222b3634aa0fbd6bd1d25ea438bbdcc 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 5b1e9b4d68170b359c4512e8ec64bf432c952f81..20fc175068a4b4f18d01da39165c9ce5e63fae0b 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 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 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 7aa7b1c2e29cd5cf88202e3ba1a0337803003fc7..931a1a2fa5158f2aaf24c1e7486e1d5acf18455d 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 e8ab8dae71aaa6c826c1543dacede60ec4684d12..4333dbdb0d05e48f022397e85c28d01969ea4357 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 a556b8b7f6c4b2ff082ea61784f59479477c508e..7863ff9459836fad85002ac69ed1679ac984ae94 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 392e57a7069225ae6eec6999179ba954228259e6..934c696f092994a3581640d48eed580ba64f838c 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 jids = new ArrayList();
- 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 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 da92fb183dec32d34c5016122359fc664a293c9b..29dfced2e3c797186186a793796051aa48009bd2 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 {
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 {
@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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 c3195d868ee7877e3988a291a419b61ba84812a2..2f96a83a93efdb677e4a1b94ac2059a51adf0bf2 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 HEARTS = new ArrayList<>(Arrays.asList(BLACK_HEART_SUIT,HEAVY_BLACK_HEART_SUIT,WHITE_HEART_SUIT));
+
+ private static final ArrayList 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 a2b58a14f9c5a2e1fd11b6bfe4aeaac6c064e4cd..9bda3c096d3b60cf18bb00497569215792c17545 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> 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 295e067a6bcda521fb37254c8ed56a8cf9948360..f989c0c25e367beb1b6d68d588db5df4a24ea5eb 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 e448f94777d646c4186e8560fe6378ce33d2757b..c9bb9c93bce45c72df74788c55b1fee234f1424d 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 getCandidatesAsElements() {
List 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 5dfa3ff4091bbfc2961263667cf231419a0c3d41..c19dd04c75396382e97b2068626a221582ca85d0 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 9866af03dbf0e65dcb312ee188a1a4eeeb427687..29efcf8f122c32f7791ca144bcfa34d8910dda31 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 72015a058b350559eb36f6ce9d9c29d8c8b11daf..8d74f44e332c1fcad9dde199acd66fc1ca852ed2 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 9f5ac988b96b120ac4b218e27f23a721339de3eb..04d55bbe500cb3e540113e97de97a248778647a4 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
deleted file mode 100644
index 505cbe63abc1a4af6e3dd2eef57a82e53edd0e60..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-hdpi/ic_action_send_now_away.png and /dev/null differ
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
deleted file mode 100644
index a376524d76e2bef02443ca4603222906203063d6..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-hdpi/ic_action_send_now_dnd.png and /dev/null differ
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
deleted file mode 100644
index d4d2d5103000557b5c46fce4a6a2602436b747ab..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-hdpi/ic_action_send_now_offline.png and /dev/null differ
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
deleted file mode 100644
index 48676f7bdfd6584d541b65a8d9768703f483e953..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-hdpi/ic_action_send_now_online.png and /dev/null differ
diff --git a/src/main/res/drawable-hdpi/ic_launcher.png b/src/main/res/drawable-hdpi/ic_launcher.png
index bffc1c65293422066b2d664f5abed906cee2c1f8..25fc8591f694bd7162cddecdb4b51380ac1bcc58 100644
Binary files a/src/main/res/drawable-hdpi/ic_launcher.png and b/src/main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/src/main/res/drawable-hdpi/ic_notification.png b/src/main/res/drawable-hdpi/ic_notification.png
index c466a7b1c299f31542e73cec0788c15820eade72..31c0ee1ae4c9b47c1ec950a7a51710c8cec62e9e 100644
Binary files a/src/main/res/drawable-hdpi/ic_notification.png and b/src/main/res/drawable-hdpi/ic_notification.png differ
diff --git a/src/main/res/drawable-hdpi/ic_received_indicator.png b/src/main/res/drawable-hdpi/ic_received_indicator.png
index b1e3f27486a20085b651510660b0a07a5cf751ea..4d2eab562978bb8e8955679d2c5d8b914054fcf4 100644
Binary files a/src/main/res/drawable-hdpi/ic_received_indicator.png and b/src/main/res/drawable-hdpi/ic_received_indicator.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_cancel_away.png b/src/main/res/drawable-hdpi/ic_send_cancel_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..9525c040f959b525aaed82a771c98ad944b5f8bd
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_cancel_away.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_cancel_dnd.png b/src/main/res/drawable-hdpi/ic_send_cancel_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..83a7b94b2e8db05b7a84926d9e623f0c75159d61
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_cancel_dnd.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_cancel_offline.png b/src/main/res/drawable-hdpi/ic_send_cancel_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..2cb4f4f3802bddb22dfea04aff477d957677e8d6
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_cancel_offline.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_cancel_online.png b/src/main/res/drawable-hdpi/ic_send_cancel_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb3bc3b561d2e4e8a86311dbe1bdef5f01962a79
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_cancel_online.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_location_away.png b/src/main/res/drawable-hdpi/ic_send_location_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..d139818b474bc97feac0eac8f61143355916ef68
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_location_away.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_location_dnd.png b/src/main/res/drawable-hdpi/ic_send_location_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..a9c51317e58f7ad921b2741e88a2770440d6f56d
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_location_dnd.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_location_offline.png b/src/main/res/drawable-hdpi/ic_send_location_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..4aec18af7fee6b12afa5174aceeb477e289386c5
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_location_offline.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_location_online.png b/src/main/res/drawable-hdpi/ic_send_location_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..19ddc51e3cdde0f7ddf10ce5582daa855e2b5988
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_location_online.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_photo_away.png b/src/main/res/drawable-hdpi/ic_send_photo_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..f6beb23cb1219d9f0de22257b229209d6d59cc88
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_photo_away.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_photo_dnd.png b/src/main/res/drawable-hdpi/ic_send_photo_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..15b1eb7a0af4acd4035f6b6f8d864676d4ef921c
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_photo_dnd.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_photo_offline.png b/src/main/res/drawable-hdpi/ic_send_photo_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..046f989b857e7f8730e24ce78eae5a789a7c3af6
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_photo_offline.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_photo_online.png b/src/main/res/drawable-hdpi/ic_send_photo_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..3fd20d10bb8c12cda30a78c6cde12a68d5fdc278
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_photo_online.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_text_away.png b/src/main/res/drawable-hdpi/ic_send_text_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..d9ef99c5652ea0558cd8de04e42f8005e327907c
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_text_away.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_text_dnd.png b/src/main/res/drawable-hdpi/ic_send_text_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..b43428decb113910085882d65326a54c16af5cba
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_text_dnd.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_text_offline.png b/src/main/res/drawable-hdpi/ic_send_text_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..0f23fdbb0404c16f87712f0213d36ebe299cdb42
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_text_offline.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_text_online.png b/src/main/res/drawable-hdpi/ic_send_text_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..305f1ec2fa895a019f1f345f65794754cab56d30
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_text_online.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_voice_away.png b/src/main/res/drawable-hdpi/ic_send_voice_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..e87d97510337824245798ac1376762c18ba2086d
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_voice_away.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_voice_dnd.png b/src/main/res/drawable-hdpi/ic_send_voice_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..26a89e8e3d80832f131d681f48f3c05e4b609fa0
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_voice_dnd.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_voice_offline.png b/src/main/res/drawable-hdpi/ic_send_voice_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..68ce48b8a352c981177fca45bed2c067dec288c6
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_voice_offline.png differ
diff --git a/src/main/res/drawable-hdpi/ic_send_voice_online.png b/src/main/res/drawable-hdpi/ic_send_voice_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..bfd7dfa7b038cb6735beb46ca40c368172af7320
Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_send_voice_online.png differ
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
deleted file mode 100644
index 0fdca901a5d8443e450e352629fc618bab00be46..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-mdpi/ic_action_send_now_away.png and /dev/null differ
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
deleted file mode 100644
index c0aef36ccb0a258d97aed7a967b18af587f358b1..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-mdpi/ic_action_send_now_dnd.png and /dev/null differ
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
deleted file mode 100644
index 7723f4aa94b7a5eec5fc5baf0ce02de7afebd030..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-mdpi/ic_action_send_now_offline.png and /dev/null differ
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
deleted file mode 100644
index 39d00ee482965004debc7e134f1ba96469487ff2..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-mdpi/ic_action_send_now_online.png and /dev/null differ
diff --git a/src/main/res/drawable-mdpi/ic_launcher.png b/src/main/res/drawable-mdpi/ic_launcher.png
index 063ee15d014fec1d418f592424d59db4c1698a73..733e9615812f295c236c2ec4f972e8b2916fad30 100644
Binary files a/src/main/res/drawable-mdpi/ic_launcher.png and b/src/main/res/drawable-mdpi/ic_launcher.png differ
diff --git a/src/main/res/drawable-mdpi/ic_notification.png b/src/main/res/drawable-mdpi/ic_notification.png
index fa35b7c107d363878d17f566b3279894fbef58bc..aafc54f5492da4bcbbbc13b71bf9f05e46091bf3 100644
Binary files a/src/main/res/drawable-mdpi/ic_notification.png and b/src/main/res/drawable-mdpi/ic_notification.png differ
diff --git a/src/main/res/drawable-mdpi/ic_received_indicator.png b/src/main/res/drawable-mdpi/ic_received_indicator.png
index 88ff1efb90664e193981d988003767bfd62b9772..2ba92b698ff19d1333a55afa5ab9842cdce9ff00 100644
Binary files a/src/main/res/drawable-mdpi/ic_received_indicator.png and b/src/main/res/drawable-mdpi/ic_received_indicator.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_cancel_away.png b/src/main/res/drawable-mdpi/ic_send_cancel_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..bbc86b45a26b20a3873988bbb1d1b9d09785c880
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_cancel_away.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_cancel_dnd.png b/src/main/res/drawable-mdpi/ic_send_cancel_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..75006446906ddb163dae01069789a243d58f8b03
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_cancel_dnd.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_cancel_offline.png b/src/main/res/drawable-mdpi/ic_send_cancel_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..604dfefaf9b6675cf6afe89ba9346de2d79c2dff
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_cancel_offline.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_cancel_online.png b/src/main/res/drawable-mdpi/ic_send_cancel_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..1b0d1811fd32474771bdc1bc9b8372073dd72cc4
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_cancel_online.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_location_away.png b/src/main/res/drawable-mdpi/ic_send_location_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..821e80d257871e13401b97d3424b71c69deb10f5
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_location_away.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_location_dnd.png b/src/main/res/drawable-mdpi/ic_send_location_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..3f4d6aa45cd861113e01dbe633a945d8d09ecc33
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_location_dnd.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_location_offline.png b/src/main/res/drawable-mdpi/ic_send_location_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff11a080fcb8a1793724bf94deb6cfd25822ea2e
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_location_offline.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_location_online.png b/src/main/res/drawable-mdpi/ic_send_location_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..a0eb40189562761cb86a60ddf83204f34ad2c226
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_location_online.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_photo_away.png b/src/main/res/drawable-mdpi/ic_send_photo_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..d9c1f26618c9d928639f15785a414e077c67838e
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_photo_away.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_photo_dnd.png b/src/main/res/drawable-mdpi/ic_send_photo_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..08033f6331e02ee64168625f570f2391a55cbf8b
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_photo_dnd.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_photo_offline.png b/src/main/res/drawable-mdpi/ic_send_photo_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..f3e6e1fa462e8d2a9eb6f3774ebc71961b59b327
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_photo_offline.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_photo_online.png b/src/main/res/drawable-mdpi/ic_send_photo_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..0aaab38d8699c3fb1ebc81063b3e15668b83f839
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_photo_online.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_text_away.png b/src/main/res/drawable-mdpi/ic_send_text_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..ddd983b5614a46b852fb48aeabee736494c2094b
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_text_away.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_text_dnd.png b/src/main/res/drawable-mdpi/ic_send_text_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..c3d9e79a84a82cf5200aec560b8ecb8ef1b2fef7
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_text_dnd.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_text_offline.png b/src/main/res/drawable-mdpi/ic_send_text_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..72b9da27023c882d9851ded99b179ec275e86fd0
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_text_offline.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_text_online.png b/src/main/res/drawable-mdpi/ic_send_text_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..86d1e33063899bb1013bbe636c846bde378a1170
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_text_online.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_voice_away.png b/src/main/res/drawable-mdpi/ic_send_voice_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..943f690feea562470b970af27c04f70922d09fe5
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_voice_away.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_voice_dnd.png b/src/main/res/drawable-mdpi/ic_send_voice_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..aaf8ff32c4e6267483f95ae1a45d677eb6609472
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_voice_dnd.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_voice_offline.png b/src/main/res/drawable-mdpi/ic_send_voice_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..e6b2355fbfcd7f98992f17245e88ea33cd9eed7c
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_voice_offline.png differ
diff --git a/src/main/res/drawable-mdpi/ic_send_voice_online.png b/src/main/res/drawable-mdpi/ic_send_voice_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..bd0e1f8702dc20e0c5f400bfb7f16d4d49ece480
Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_send_voice_online.png differ
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
deleted file mode 100644
index bb999d85d4e128d988763034bd6735356693c388..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-xhdpi/ic_action_send_now_away.png and /dev/null differ
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
deleted file mode 100644
index a0bf5561c76fd8479e3268eadd05a19962391656..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-xhdpi/ic_action_send_now_dnd.png and /dev/null differ
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
deleted file mode 100644
index 6da9ff7bd40abf3ffa2437cc91fa4ae1724dc695..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-xhdpi/ic_action_send_now_offline.png and /dev/null differ
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
deleted file mode 100644
index 348ba657d5efc149aa5f60c89e1a0241b226f77b..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-xhdpi/ic_action_send_now_online.png and /dev/null differ
diff --git a/src/main/res/drawable-xhdpi/ic_launcher.png b/src/main/res/drawable-xhdpi/ic_launcher.png
index fd9937f1ad1c63276dd8b5cdaaf831895865488a..c9e48859abe6cfaaa501ea4b9070da173b16c399 100644
Binary files a/src/main/res/drawable-xhdpi/ic_launcher.png and b/src/main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_notification.png b/src/main/res/drawable-xhdpi/ic_notification.png
index 43daff659daa776aee96b42f72b51714579219c9..042d2cdaa5428d18e409c7b42b64b82b51ea77c5 100644
Binary files a/src/main/res/drawable-xhdpi/ic_notification.png and b/src/main/res/drawable-xhdpi/ic_notification.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_received_indicator.png b/src/main/res/drawable-xhdpi/ic_received_indicator.png
index 2c8719337380416ddd15afb0f46924e90da01ea8..cf7c2bb8506349e960c06aa6468f9a4fb8a6919a 100644
Binary files a/src/main/res/drawable-xhdpi/ic_received_indicator.png and b/src/main/res/drawable-xhdpi/ic_received_indicator.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_cancel_away.png b/src/main/res/drawable-xhdpi/ic_send_cancel_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..44b5f0963e0f59e2ee40afaf38c9516e1b745d61
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_cancel_away.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_cancel_dnd.png b/src/main/res/drawable-xhdpi/ic_send_cancel_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..dde1b7076d606ce0a44319cbaf55a68934f9b283
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_cancel_dnd.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_cancel_offline.png b/src/main/res/drawable-xhdpi/ic_send_cancel_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..53a9ac90962352d9af19fa98a8c86eae50080fbc
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_cancel_offline.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_cancel_online.png b/src/main/res/drawable-xhdpi/ic_send_cancel_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..23a5f2632b0fe2a8b95547a64eb9e8f799c507fc
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_cancel_online.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_location_away.png b/src/main/res/drawable-xhdpi/ic_send_location_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..0a5f3d54e3a713bc2e591ff29092641d208bde57
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_location_away.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_location_dnd.png b/src/main/res/drawable-xhdpi/ic_send_location_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..99c8ce36f2da0664278836c893374c64723c7ef0
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_location_dnd.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_location_offline.png b/src/main/res/drawable-xhdpi/ic_send_location_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..114ce01be585454bda68ffec9001ceb059dc798a
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_location_offline.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_location_online.png b/src/main/res/drawable-xhdpi/ic_send_location_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..17204eeab0d054c4421f966b1f0dc38fbc78811d
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_location_online.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_photo_away.png b/src/main/res/drawable-xhdpi/ic_send_photo_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..7ac674eacd40034f0212448e036e4f3090e06401
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_photo_away.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_photo_dnd.png b/src/main/res/drawable-xhdpi/ic_send_photo_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..fc69cb416fc817de7308661a4e65651d033b1e9e
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_photo_dnd.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_photo_offline.png b/src/main/res/drawable-xhdpi/ic_send_photo_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..6ef1e16a95c1d685102c17b13b42e71dba39000e
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_photo_offline.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_photo_online.png b/src/main/res/drawable-xhdpi/ic_send_photo_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..f585ef98c35d71d24dc5a8f5506d17392acbd46a
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_photo_online.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_text_away.png b/src/main/res/drawable-xhdpi/ic_send_text_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..41f223f6c263497173495a23c082ad67ef178190
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_text_away.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_text_dnd.png b/src/main/res/drawable-xhdpi/ic_send_text_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..8b93ec10916509f90cc7045a92e75c2de3aa9fc6
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_text_dnd.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_text_offline.png b/src/main/res/drawable-xhdpi/ic_send_text_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..d0a98e5d02dd4e4daf0e77b5f2feef08ff2b91eb
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_text_offline.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_text_online.png b/src/main/res/drawable-xhdpi/ic_send_text_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..91d240d246a33cb3c9ca777f48555d33b3a93f20
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_text_online.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_voice_away.png b/src/main/res/drawable-xhdpi/ic_send_voice_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..34f8ea86faa66cf5eae3f3ea0ba3f55e729d22ee
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_voice_away.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_voice_dnd.png b/src/main/res/drawable-xhdpi/ic_send_voice_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..66b0c67715d0fcb8f270c3b72a6f28f42722881f
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_voice_dnd.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_voice_offline.png b/src/main/res/drawable-xhdpi/ic_send_voice_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..fc4cff1f14eb886e5eb40102b5442d20978b3daf
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_voice_offline.png differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_voice_online.png b/src/main/res/drawable-xhdpi/ic_send_voice_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..d2f03dd5103d66f55efdabe7bf054de268d006fa
Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_send_voice_online.png differ
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
deleted file mode 100644
index 12ec4d33f19d18ef0bde48e635cdc671609bcee5..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-xxhdpi/ic_action_send_now_away.png and /dev/null differ
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
deleted file mode 100644
index 7719f81a9a42aeae09dcd7d226ec6c241b6d6162..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-xxhdpi/ic_action_send_now_dnd.png and /dev/null differ
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
deleted file mode 100644
index 1889581322980550b9d312e62980aed751d33286..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-xxhdpi/ic_action_send_now_offline.png and /dev/null differ
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
deleted file mode 100644
index 29bde36e3f72c35ed863e3db4c780dab0ab0de0f..0000000000000000000000000000000000000000
Binary files a/src/main/res/drawable-xxhdpi/ic_action_send_now_online.png and /dev/null differ
diff --git a/src/main/res/drawable-xxhdpi/ic_launcher.png b/src/main/res/drawable-xxhdpi/ic_launcher.png
index 0e06656ff4b07e84785733b84222d98e4011be7c..e69b9c8d5864c9f35b6c846d422f7946c470403b 100644
Binary files a/src/main/res/drawable-xxhdpi/ic_launcher.png and b/src/main/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_notification.png b/src/main/res/drawable-xxhdpi/ic_notification.png
index c2ee5dec815b892bbc9043422019d8a8d2f76d0e..42c62d3278f00a252a3748dbcd79eceb18216eaa 100644
Binary files a/src/main/res/drawable-xxhdpi/ic_notification.png and b/src/main/res/drawable-xxhdpi/ic_notification.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_received_indicator.png b/src/main/res/drawable-xxhdpi/ic_received_indicator.png
index 039a9ef9ba6d588442aec93dd251eef7345538c2..5d1c9b876913b39ad8be2f87949410fb19ff8c58 100644
Binary files a/src/main/res/drawable-xxhdpi/ic_received_indicator.png and b/src/main/res/drawable-xxhdpi/ic_received_indicator.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_cancel_away.png b/src/main/res/drawable-xxhdpi/ic_send_cancel_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..07113f020de82cc16b5cf5e4440deab660b3106d
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_cancel_away.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_cancel_dnd.png b/src/main/res/drawable-xxhdpi/ic_send_cancel_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..e68a912d75c1fab78a4ac903730c3fca5dc85406
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_cancel_dnd.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_cancel_offline.png b/src/main/res/drawable-xxhdpi/ic_send_cancel_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..10412faba21133da5b4064ca088d10345ae19f90
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_cancel_offline.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_cancel_online.png b/src/main/res/drawable-xxhdpi/ic_send_cancel_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..6fa18239acaecfdca95fd159b24d46dd36ae2247
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_cancel_online.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_location_away.png b/src/main/res/drawable-xxhdpi/ic_send_location_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..4fb370ff98d78b0fb1d1f78ac8bc302d9776325e
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_location_away.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_location_dnd.png b/src/main/res/drawable-xxhdpi/ic_send_location_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..1773e62d22d3b429cdba9c02c72f81ee95821470
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_location_dnd.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_location_offline.png b/src/main/res/drawable-xxhdpi/ic_send_location_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..b4317aae49a174b2d97765f5ea1cfc3d5a75a616
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_location_offline.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_location_online.png b/src/main/res/drawable-xxhdpi/ic_send_location_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..10dfed811cceb901c99bf92d7f4e069e60a152f6
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_location_online.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_photo_away.png b/src/main/res/drawable-xxhdpi/ic_send_photo_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..78eea39e363f9a4cc031dbcd107097aad53982bb
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_photo_away.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_photo_dnd.png b/src/main/res/drawable-xxhdpi/ic_send_photo_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..fe33a1d0d4b56fb966e40faeb3ce0e7cfe685586
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_photo_dnd.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_photo_offline.png b/src/main/res/drawable-xxhdpi/ic_send_photo_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..6b41c3bd9d246cf172354c53cd414ebc8eec16bd
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_photo_offline.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_photo_online.png b/src/main/res/drawable-xxhdpi/ic_send_photo_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..1c8992c525fdb4a8afe99b3e210ba7dfeac52651
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_photo_online.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_text_away.png b/src/main/res/drawable-xxhdpi/ic_send_text_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b2b07935830f475db6fdd1bbf0d70b7026b7e72
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_text_away.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_text_dnd.png b/src/main/res/drawable-xxhdpi/ic_send_text_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..32f5e29c73b0d82538b938180e8cf3b028ebcc23
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_text_dnd.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_text_offline.png b/src/main/res/drawable-xxhdpi/ic_send_text_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..6bd9c414faf34d64e2bf4c997936603750c32732
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_text_offline.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_text_online.png b/src/main/res/drawable-xxhdpi/ic_send_text_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb6a2dba8f1b361d01d609a93bd75268f6574673
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_text_online.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_voice_away.png b/src/main/res/drawable-xxhdpi/ic_send_voice_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..b8b9e807995dc4a332e7703329b98498cf7b05c3
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_voice_away.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_voice_dnd.png b/src/main/res/drawable-xxhdpi/ic_send_voice_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..4a5b41040e42f41801189767953884ee495912e0
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_voice_dnd.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_voice_offline.png b/src/main/res/drawable-xxhdpi/ic_send_voice_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..3d58f699441c3432afa948f9824a21254f392921
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_voice_offline.png differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_voice_online.png b/src/main/res/drawable-xxhdpi/ic_send_voice_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..600371eb2b615953d2cd9f6d8cf60aa5da3880d6
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_send_voice_online.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_launcher.png b/src/main/res/drawable-xxxhdpi/ic_launcher.png
index b6dcb0b9f99c7e309e2a1ea4682ab213c97c1c87..668504dfbde72f01c2f6d4dbcf1bcc9b566f4e07 100644
Binary files a/src/main/res/drawable-xxxhdpi/ic_launcher.png and b/src/main/res/drawable-xxxhdpi/ic_launcher.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_notification.png b/src/main/res/drawable-xxxhdpi/ic_notification.png
index ee2f3a43235d34f72ecfd36e242aa7ee93d46d48..c3439f1a3e9f9c1a57386b9f86685f0e5a7360ab 100644
Binary files a/src/main/res/drawable-xxxhdpi/ic_notification.png and b/src/main/res/drawable-xxxhdpi/ic_notification.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_received_indicator.png b/src/main/res/drawable-xxxhdpi/ic_received_indicator.png
index 86db9890c23ca93096441af04a38abea5f690db1..f35c8b4423bff4c7e0e308e7885788aabe6303ea 100644
Binary files a/src/main/res/drawable-xxxhdpi/ic_received_indicator.png and b/src/main/res/drawable-xxxhdpi/ic_received_indicator.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_cancel_away.png b/src/main/res/drawable-xxxhdpi/ic_send_cancel_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..a9340a88674e296125a2e26f10bab203ad8fd484
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_cancel_away.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_cancel_dnd.png b/src/main/res/drawable-xxxhdpi/ic_send_cancel_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..307ca8a0fe5261265fce5c537d842b6f3c2921d3
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_cancel_dnd.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_cancel_offline.png b/src/main/res/drawable-xxxhdpi/ic_send_cancel_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..abd3af2c2149d3e88a11c30d430069d8c4ee99dc
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_cancel_offline.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_cancel_online.png b/src/main/res/drawable-xxxhdpi/ic_send_cancel_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..80be8edfc926294a7f1692db5302d4c52cddc105
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_cancel_online.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_location_away.png b/src/main/res/drawable-xxxhdpi/ic_send_location_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..0fab2554ab1544be28e265d035981806d1c5e845
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_location_away.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_location_dnd.png b/src/main/res/drawable-xxxhdpi/ic_send_location_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..e7f6fde74b8264ef77012cdc28a2e173771fe43c
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_location_dnd.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_location_offline.png b/src/main/res/drawable-xxxhdpi/ic_send_location_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..2af75cde35fccf80532baad733bff7d40657479c
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_location_offline.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_location_online.png b/src/main/res/drawable-xxxhdpi/ic_send_location_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..2e54ef892dc65f6a322bf5dd5192a1d16ec0a072
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_location_online.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_photo_away.png b/src/main/res/drawable-xxxhdpi/ic_send_photo_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..ba171ce13f699e5ee6b1b0ef4b99dd4bb80395c1
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_photo_away.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_photo_dnd.png b/src/main/res/drawable-xxxhdpi/ic_send_photo_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..8a9b07005395e40f26d54390894e1403900ae80e
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_photo_dnd.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_photo_offline.png b/src/main/res/drawable-xxxhdpi/ic_send_photo_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..e94e930dd89d6996734557f7214ae7dcab532619
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_photo_offline.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_photo_online.png b/src/main/res/drawable-xxxhdpi/ic_send_photo_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..1bf680ebbd77609a6255f5487aecb21e1c937f3f
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_photo_online.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_text_away.png b/src/main/res/drawable-xxxhdpi/ic_send_text_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..afcfa89d9cca79194ccf095061fabeaea677cf3f
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_text_away.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_text_dnd.png b/src/main/res/drawable-xxxhdpi/ic_send_text_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..b11cd6b62080446ee18d434ef7b8f1e52ef1f84f
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_text_dnd.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_text_offline.png b/src/main/res/drawable-xxxhdpi/ic_send_text_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..b9122e454bc53c37cc742bb9270059ead63a1ef9
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_text_offline.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_text_online.png b/src/main/res/drawable-xxxhdpi/ic_send_text_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..abec2e0b740ff1d673e33bd79de4e0bdad79b8f4
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_text_online.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_voice_away.png b/src/main/res/drawable-xxxhdpi/ic_send_voice_away.png
new file mode 100644
index 0000000000000000000000000000000000000000..de1375e2fde5eb5deaec10307ecddb30124a50e5
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_voice_away.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_voice_dnd.png b/src/main/res/drawable-xxxhdpi/ic_send_voice_dnd.png
new file mode 100644
index 0000000000000000000000000000000000000000..4ad9d389e3b5c73bf134653a925691e2ff1b9aab
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_voice_dnd.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_voice_offline.png b/src/main/res/drawable-xxxhdpi/ic_send_voice_offline.png
new file mode 100644
index 0000000000000000000000000000000000000000..eec3d8f2b744298ea9a93e0e8772ad54ad6f3ec1
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_voice_offline.png differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_voice_online.png b/src/main/res/drawable-xxxhdpi/ic_send_voice_online.png
new file mode 100644
index 0000000000000000000000000000000000000000..fcdfcb4378c0444ce389c7edc66083dd6f2f5760
Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_send_voice_online.png differ
diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml
index f9aae10a09d5833153f7073ef8748c20e3d43587..5aa7dffabe298bb51db72d3c6daf2a4311b906fe 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" />
-
-
-
\ 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 a0861b167e4fdc16a78ac80e913977031b3f0f1e..9583932bb7794daf18c9ea4bca1d686fca1da48e 100644
--- a/src/main/res/values-ca/strings.xml
+++ b/src/main/res/values-ca/strings.xml
@@ -24,8 +24,8 @@
Escollir un contacte
LLista bloqueix
Ara
- 1 min avans
- %de minuts avans
+ 1 min abans
+ %de minuts abans
Converses sense llegir o no llegides
enviant…
Desxifrant missatge. Espera si us plau…
@@ -359,6 +359,7 @@
No s\'ha pogut canviar la contrasenya
Començar a enviar un missatge de conversació xifrat
Fer una pregunta
+ 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.
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.
El seu suggeriment no pot estar buit
El teu secret compartit no pot estar buit
@@ -416,4 +417,33 @@
Enviant %s
Oferint %s
Amaga el fora de línia
+ Deshabilita el compte
+ %s està escrivint...
+ %s ha deixat d\'escriure
+ Notificacions d\'escriptura
+ Permet el teu contacte saber quan estàs escrivint un missatge nou
+ Enviar localització
+ Mostrar localització
+ No s\'ha trobat cap aplicació per mostrar la localització
+ Localització
+ Localització rebuda
+ Conversa tancada
+ S\'ha sortit de la conferència
+ Opcions de certificats
+ No confiar en les CAs del sistema
+ Tots els certificats han de ser aprovats manualment
+ Eliminar certificats
+ Esborrar certificats aprovats manualment
+ No hi ha certificats aprovats manualment
+ Esborrar certificats
+ Esborrar selecció
+ Cancel·lar
+
+ - %d certificat esborrat
+ - %d certificats esborrats
+
+
+ - Seleccionar %d contacte
+ - Seleccionar %d contactes
+
diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml
index e184935732682be3aea55958b0c892d3dd0bd264..70c3a49df1f3c06d0919952e342b4121e4de0fe5 100644
--- a/src/main/res/values-pl/strings.xml
+++ b/src/main/res/values-pl/strings.xml
@@ -202,6 +202,7 @@
Wiadomość zaszyfrowana. Zainstaluj OpenKeychain, żeby odszyfrować.
Nieznany odcisk klucza OTR
Znaleziono wiadomości zaszyfrowane przez OpenPGP
+ Odbiór nieudany
Twój odcisk klucza
Odcisk klucza OTR
Weryfikuj
@@ -236,7 +237,9 @@
Serwer odrzucił żądanie publikacji
Wystąpił błąd podczas konwersji obrazu
Nie udało się zapisać obrazu w pamięci urządzenia
+ (lub przytrzymaj długo, aby ustawić domyślny)
Serwer nie udostępnia możliwości publikacji awatarów
+ szepcze
do %s
Wyślij prywatną wiadomość do %s
Połącz
@@ -252,10 +255,12 @@
Konferencja jest zabezpieczona hasłem
Wprowadź hasło
Kontakt nie udostępnia powiadomień o obecności
+ Poproś kontakt o udostępnienie powiadomień o obecności.\n\nPozwoli to na ustalenie klienta, z którego korzysta rozmówca.
Zażądaj teraz
Usuń odcisk klucza
Czy na pewno chcesz usunąć odcisk klucza?
Ignoruj
+ Uwaga: Wysyłanie bez obustronnych powiadomień o obecności może powodować nieoczekiwane problemy.\n\nSprawdź subskrypcję powiadomień w szczegółach kontaktu.
Ustawienia szyfrowania
Wymuszaj szyfrowanie typu end-to-end
Szyfruj wszystkie wiadomości (poza konferencjami)
@@ -283,6 +288,7 @@
Zbanowano cię w konferencji
To jest zamknięty pokój
Wyrzucono cię z konferencji
+ używając konta %s
Sprawdzanie obrazka na hoście HTTP
Obraz został usunięty
Brak połączenia. Spróbuj ponownie później
@@ -302,6 +308,7 @@
Szczegóły konta
Weryfikuj OTR
Zdalny odcisk klucza
+ skanuj
(lub zetknij telefony)
Protokół socialist millionaire
Podpowiedź lub pytanie
@@ -309,6 +316,7 @@
Potwierdź
W toku
Odpowiedz
+ Operacja nieudana
Sekrety są niezgodne
Spróbuj ponownie
Zakończ
@@ -382,6 +390,7 @@
Konferencja prywatna, dla zaakceptowanych uczestników
Opcje konferencji
Prywatna (tylko zaakceptowani)
+ Nieanonimowa
Opcje konferencji zostały zmienione!
Nie udało się zmienić opcji konferencji
Nigdy
@@ -420,7 +429,23 @@
Otrzymano lokalizację
Zamknięto konwersację
Opuszczono konferencję
+ Ustawienia certyfikatów
Nie ufaj certyfikatom systemowym
Wymagaj ręcznego potwierdzania certyfikatów
+ Usuń certyfikat
+ Wybierz zaufane certyfikaty do usunięcia
+ Brak ręcznie zaufanych certyfikatów
+ Usuń certyfikaty
+ Usuń zaznaczone
Anuluj
+
+ - Usunięto %d certyfikat
+ - Usunięto %d certyfikaty
+ - Usunięto %d certyfikatów
+
+
+ - %d kontakt wybrany
+ - %d kontakty wybrane
+ - %d kontaktów wybranych
+
diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml
index 2fc257f7cad14d3b11ed11c83fd18de2f570ca1a..7b1048209bd348dbcbb1804150f0a903d4f87832 100644
--- a/src/main/res/values-ru/strings.xml
+++ b/src/main/res/values-ru/strings.xml
@@ -441,11 +441,13 @@
- Удалён %d сертификат
- Удалено %d сертификатов
+
- Удалено %d сертификатов
- Выбран %d контакт
- Выбрано %d контактов
+
- Выбрано %d контактов
diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml
index bcf420ba4d8f07fac313cbecafb320b8685b98b3..ea5cbdb8579ba0d2d84135f4839d0cc25c47faf2 100644
--- a/src/main/res/values-sr/strings.xml
+++ b/src/main/res/values-sr/strings.xml
@@ -97,8 +97,8 @@
Опште
ИксМПП ресурс
Име са којим се овај клијент идентификује
- Прихваћај фајлове
- Аутоматски прихваћај фајлове мање од…
+ Прихватај фајлове
+ Аутоматски прихватај фајлове мање од…
Поставке обавештења
Обавештења
Обавести кад стигне нова порука
diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml
index 49ca9b9742c8277bc75a8093efe01d103e780beb..2f80e064a8879cbde3e22375ebd3957e578cf854 100644
--- a/src/main/res/values-zh-rCN/strings.xml
+++ b/src/main/res/values-zh-rCN/strings.xml
@@ -11,6 +11,10 @@
编辑姓名
添加到手机通讯录
从列表中删除
+ 屏蔽联系人
+ 解除联系人屏蔽
+ 屏蔽域名
+ 解除域名屏蔽
管理账户
设置
讨论组详情
@@ -18,6 +22,7 @@
分享会话
开始会话
选择联系人
+ 屏蔽列表
刚刚
1 分钟前
%d分钟前
@@ -30,56 +35,65 @@
版主
参与者
访客
- 将 %s从列表中移除? 与该联系人的会话消息不会清除.
- 从书签中移除 %s?相关会话消息不会被清除 .
+ 将 %s 从列表中移除? 与该联系人的会话消息不会清除.
+ 你想屏蔽 %s 将不能发送信息给你?
+ 你想解除对 %s 的屏蔽吗,他们将可以发送信息给你?
+ 屏蔽 %s 中的所有联系人?
+ 解除对 %s 中所有联系人的屏蔽?
+ 联系人已屏蔽
+ 从书签中移除 %s ?相关会话消息不会被清除 .
在服务器上注册新账户
+ 在服务器上改变密码
分享
开始会话
邀请联系人
联系人
取消
+ 设置
添加
编辑
删除
+ 屏蔽
+ 解除屏蔽
保存
完成
- Conversations停止运行
- 发送堆栈跟踪到正在开发Conversations的人员\n警告: 该操作将用您的 XMPP账户发送堆栈跟踪到开发人员.
+ Conversations 崩溃
+ 发送堆栈跟踪信息到 Conversations 的开发人员\n警告: 该操作将用您的 XMPP 账户发送堆栈跟踪给开发人员。
现在发送
不再询问
无法连接至账户
无法连接至多个账户
点击此处管理账户
- 附件
- 该联系人不在您的列表.需要加为联系人吗 ?
+ 附加文件
+ 该联系人不在您的列表。需要加为联系人吗 ?
添加联系人
传递失败
拒绝
准备传输图像
清除历史记录
清除会话记录
- 删除该会话中所有信息?\n\n注: 该操作不会影响其他设备或服务器保存的信息.
+ 删除该会话中所有信息?\n\n注: 该操作不会影响其他设备或服务器保存的信息。
删除消息
之后结束该会话
添加在线用户至联系人
发送纯文本信息
发送 OTR 加密信息
发送 OpenPGP 加密信息
- 用户名修改成功
+ 昵称修改成功
下载图片
不加密发送
- 解密失败,可能是私钥不正确.
+ 解密失败,可能是私钥不正确。
OpenKeychain
- 会话运用了第三方app,名为 OpenKeychain 用来加密、解码信息以及管理您的公钥.\n\nOpenKeychain 遵循 GPLv3 并且在 F-Droid和Google Play上可操作.\n\n(之后请重启conversations.)
+ 会话运用了第三方app,名为 OpenKeychain 用来加密、解密信息以及管理您的密钥。\n\nOpenKeychain 遵循 GPLv3 并且可以在 F-Droid 和 Google Play 上获取。\n\n(之后请重启 conversations)
重启
安装
输入…
等待…
- 未发现OpenPGP 密码
- 会话加密信息失败,因为联系人未告知他/她的公钥.\n\n请通知联系人设置 OpenPGP.
- 未找到 OpenPGP 密码
+ 未发现 OpenPGP 密钥
+ 会话加密信息失败,因为联系人未提供他/她的公钥。\n\n请通知联系人设置 OpenPGP。
+ 未找到 OpenPGP 密钥
因您的联系人未公布公钥,Conversations未能成功加密您的信息.\n\n请通知联系人设置OpenPGP.
- 加密信息已接收.点击进行解密和查看.
+ 加密信息已接收。点击解密并查看。
常规
XMPP 资源
客户端标识名称
@@ -91,41 +105,41 @@
震动
收到新消息时震动
声音
- 收到新消息时播放铃声
+ 收到新消息时的铃声
讨论组通知
当有新的消息时总是通知而不是亮屏时才通知
通知限期
接收副本短时间内关闭通知
高级选项
- 总不发送故障报告
- 发送堆栈跟踪帮助Conversations开发人员
+ 总不发送崩溃报告
+ 发送堆栈跟踪帮助 Conversations 开发人员
确认消息
当你已收到消息并且已阅时通知好友
- UI选项
+ UI 选项
OpenKeychain 报告了一个错误
- 解码文件时出现I/O错误
+ 解密文件时出现 I/O 错误
接受
产生了一个错误
同意更新在线联系人
- 预先同意并请求您的联系人进行更新
+ 预先同意并请求更新您的联系人
关注
你的账号
- Keys
- 发送在线联系人更新列表
- 接收在线联系人更新列表
- 请求在线联系人更新列表
+ 密钥
+ 发送在线联系人列表更新
+ 接收在线联系人列表更新
+ 请求在线联系人列表更新
选择图片
照相
预先同意订阅请求
您选择的文件不是图像文件
转换图像出错
未找到文件
- 常规的I/O错误.可能是存储空间不足的原因?
- 您用来选择图片的app没有给予足够权限支持我们读取文件.\n\n请使用另一文件管理器选择图片
+ 常规的 I/O 错误。可能是存储空间不足?
+ 您用来选择图片的 app 没有给予足够权限支持我们读取文件。\n\n请使用另一文件管理器选择图片
未知
暂时不可用
在线
- Connecting\u2026
+ 连接中\u2026
离线
未授权
未找到服务器
@@ -134,6 +148,8 @@
用户名已存在
注册完成
服务器不支持注册
+ 安全错误
+ 服务器不兼容
纯文本内容
OTR
OpenPGP
@@ -141,11 +157,11 @@
删除账号
暂时不可用
发布头像
- 发布 OpenPGP 公共秘钥
+ 发布 OpenPGP 公钥
启用账户
确定?
如果删除用户,所有会话信息将会丢失
- Record voice 录音
+ 录音
Jabber ID
密码
username@example.com
@@ -153,23 +169,28 @@
密码
确认密码
密码不一致
- 该Jabber ID 无效
- 空间不足,图片过大
+ 该 Jabber ID 无效
+ 空间不足。图片过大
您将添加 %s 至手机联系人列表?
在线
- 免费对话
+ 自由畅聊
离开
长时间离开
请勿打扰
离线
讨论组
其他成员
+ 服务器信息
+ XEP-0313: MAM
XEP-0280: 消息碳
+ XEP-0352: 客户端状态指示
+ XEP-0191: 屏蔽指令
+ XEP-0237: 花名册版本
XEP-0198: 流管理
XEP-0163: PEP (头像)
有效
无效
- 缺少公共秘钥公告
+ 缺少公钥通知
最近一次查看为刚刚
最近一次查看为一分钟前
最近一次查看为 %d 分钟前
@@ -178,9 +199,9 @@
最近一次查看为一天前
最近一次查看为 %d天前
未曾查看
- 加密信息. 请安装OpenKeychain进行解码.
- 未知 OTR指纹
- OpenPGP 发现加密信息
+ 加密信息. 请安装 OpenKeychain 以解密。
+ 未知 OTR 指纹
+ 发现 OpenPGP 加密信息
接收失败
你的指纹
OTR 指纹
@@ -192,6 +213,8 @@
加入讨论组
删除联系人
查看联系人详细信息
+ 屏蔽联系人
+ 解除联系人屏蔽
创建
联系人已存在
加入
@@ -200,26 +223,26 @@
保存为书签
删除书签
该书签已存在
- 你的
+ 你
编辑讨论组主题
讨论组未找到
离开
联系人已添加你到联系人列表
反向添加
- 目前读到%s 处
+ 目前读到 %s 处
发布
- 点击头像可选择头像
- 请注意: 所有关注您最新动态的人将看到该图像.
- 发布…
+ 点击头像可从相册中选择头像
+ 请注意: 所有关注您最新动态的人将看到该图像。
+ 正在发布…
服务器拒绝了您的发布请求
- 转换头像出错
- 不能将头像保存至disk
+ 转换头像图片出错
+ 不能将头像保存至磁盘
(或长按按钮将返回默认头像)
您的服务器不支持发布头像
密谈
至 %s
- 发送私密消息到%s
- Connect
+ 发送私密消息到 %s
+ 连接
该账号已存在
下一步
当前会话已建立
@@ -232,21 +255,193 @@
讨论组设有密码
输入密码
缺少在线联系人更新
- 请先发送更新在线联系人请求.\n\n这将用来判断您的联系人所用的客户端类型人.
+ 请先发送更新在线联系人的请求。\n\n以判断您的联系人所用的客户端类型。
现在发送请求
删除指纹
是否确定删除该指纹?
忽略
- 警告:在没有相互更新在线联系人的情况下发送将会出现未知问题.\n\n到联系人详情确认您订阅的在线联系人.
+ 警告:在没有相互更新在线联系人的情况下发送将会出现未知问题。\n\n前往联系人详情以验证您订阅的在线联系人。
加密设置
- 强制要求 end-to-end 加密
+ 强制要求端对端加密
总是发送加密信息(讨论组信息除外)
不保存加密信息
警告:此操作将会导致信息丢失
- Expert 选项
+ 导出选项
请谨慎使用
+ 关于 Conversations
+ 构建及许可证信息
+ 静默时间段
+ 开始时间
+ 结束时间
+ 启用静默时间段
+ 在静默时间段内通知将保持静音
放大字体
- 整个app界面使用更大号的字体
+ 整个 app 界面使用较大的字体
发送按钮显示状态
+ 请求消息回复
+ 如果支持消息将会以绿色对勾标识
发送按钮采用其他颜色以示发送状态的区别
+ 其他
+ 讨论组名称
+ 用讨论组的主题来标示讨论组而不是 JID
+ OTR 指纹已拷贝到剪贴板!
+ 你被此讨论组屏蔽
+ 此讨论组只允许成员加入
+ 你被从此讨论组踢出
+ 用账户 %s
+ 正在 HTTP 托管中检查图片
+ 此图片已经被删除
+ 你没有连接。请稍后重试
+ 检查图片文件尺寸
+ 消息选项
+ 拷贝文本
+ 拷贝原始URL
+ 再次发送
+ 图片 URL
+ 消息文本
+ 已经拷贝 URL 到剪贴板
+ 消息已经拷贝到剪贴板
+ 图片传送失败
+ 扫描二维码
+ 显示二维码
+ 显示屏蔽列表
+ 账户详情
+ 验证 OTR
+ 远程指纹
+ 扫描
+ (或轻触手机)
+ Socialist Millionaire Protocol
+ 提示或问题
+ 共知的秘密
+ 确认
+ 处理中
+ 回应
+ 失败
+ 秘密不符
+ 再试一遍
+ 完成
+ 验证通过!
+ 联系人请求 SMP 验证
+ 没有找到 OTR 会话
+ Conversations
+ 保持前台服务
+ 防止操作系统中断你的连接
+ 关闭文件
+ 接收中 %1$s (已完成 %2$d%%)
+ 下载 %s
+ 文件
+ 打开 %s
+ 发送中 (已完成 %1$d%%)
+ 准备传送文件
+ 可以下载 %s
+ 取消传送
+ 文件传送失败
+ 文件已经删除
+ 没有可以打开此文件的应用
+ 不能验证指纹
+ 手工验证
+ 你确认验证你的联系人的 OTR 指纹?
+ 现实动态标签
+ 在联系人下方显示只读标签
+ 启用通知
+ 与…创建讨论组
+ 无法找到讨论组服务器
+ 讨论组创建失败!
+ 讨论组已创建!
+ 秘密被接受!
+ 重置
+ 账户头像
+ 拷贝 OTR 指纹到剪贴板
+ 从服务器获取历史记录
+ 服务器上没有更多历史记录
+ 更新中…
+ 密码已修改!
+ 不能修改密码
+ 要启动加密聊天先发送一条消息
+ 提出问题
+ 如果你和你的联系人有一个共知的秘密(比如一个内部笑话或者仅仅只是上次见面时吃的午餐) 你可以使用这个秘密来验证彼此的指纹。\n\n你的联系人将以大小写敏感的方式给出答案,你可以给出提示或问题。
+ 你的联系人可以通过一个你们共知的秘密来验证指纹。你的联系人给出了如下的提示或问题。
+ 你的提示不能为空
+ 你共知的秘密不能为空
+ 请仔细核对下面显示出来的你的联系人的指纹。\n你可以使用任何可信赖的联系方式,比如加密邮件或电话,来交换这些指纹信息。
+ 修改密码
+ 当前密码
+ 新密码
+ 密码不能为空
+ 启用所有账户
+ 禁用所有账户
+ 做一个动作和
+ 没有从属关系
+ 没有角色
+ 抛弃
+ 成员
+ 高级模式
+ 已授予的成员
+ 吊销的成员
+ 授予管理员权限
+ 吊销管理员权限
+ 从讨论组移出
+ 不能修改 %s 的从属关系
+ 屏蔽出讨论组
+ 你正尝试将 %s 移出一个公共的讨论组。唯一的方式是永远屏蔽这个用户
+ 现在屏蔽
+ 不能修改 %s 的角色
+ 公开访问的讨论组
+ 私密,只有成员可以加入的讨论组
+ 讨论组选项
+ 私密(只对成员开放)
+ 非匿名
+ 讨论组选项已修改!
+ 不能修改讨论组选项
+ 从不
+ 30 分钟
+ 1 个小时
+ 2 个小时
+ 8 个小时
+ 直到新的通知
+ 输入选项
+ 回车是发送
+ 用回车键来发送消息
+ 显示回车键
+ 改变表情键为回车键
+ 音频
+ 视频
+ 图像
+ PDF 文档
+ Android App
+ 联系人
+ 已经收到 %s
+ 禁用前端服务
+ 轻触打开 Conversations
+ 头像已经发布!
+ 发送中 %s
+ 提供中 %s
+ 隐藏离线联系人
+ 禁用账户
+ %s 正在输入…
+ %s 已停止输入
+ 键盘输入通知
+ 让对方知道你正在输入新消息
+ 发送位置
+ 显示位置
+ 无法找到显示位置的应用
+ 位置
+ 位置已收到
+ Conversation 已关闭
+ 离开讨论组
+ 证书选项
+ 不相信系统 CA
+ 所有证书必须人工通过
+ 移除证书
+ 删除人工通过的证书
+ 没有人工通过的证书
+ 移除证书
+ 删除选项
+ 取消
+
+ - %d 个证书已被删除
+
+
+ - 选择 %d 个联系人
+
diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml
index c413a3c48441c0f5ab6c9e9893f1e8be3ee8d4e7..fec077ccc21a67b4b2a825f28898b3af76613781 100644
--- a/src/main/res/values/arrays.xml
+++ b/src/main/res/values/arrays.xml
@@ -40,4 +40,19 @@
- -1
+
+ - @string/none
+ - @string/recently_used
+ - @string/attach_take_picture
+ - @string/attach_record_voice
+ - @string/send_location
+
+
+
+ - none
+ - recent
+ - photo
+ - voice
+ - location
+
diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml
index c21650a5d025209688586b60763bca5b2c8a6779..753a56df97306b20d39aa17d46c9465dc56074ea 100644
--- a/src/main/res/values/colors.xml
+++ b/src/main/res/values/colors.xml
@@ -11,7 +11,7 @@
#ffeeeeee
#ff323232
#1f000000
- #ffe51c23
+ #fff44336
#ffff9800
#ff259b24
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index cc7727d22c8e6a6cc84cd803f001535b2f4988f0..4631bd60c84d746e0825a0c79208b517ce87d94d 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -1,397 +1,397 @@
- Conversations
- Settings
- New conversation
- Manage accounts
- End this conversation
- Contact details
- Conference details
- Secure conversation
- Add account
- Edit name
- Add to phone book
- Delete from roster
- Block contact
- Unblock contact
- Block domain
- Unblock domain
- Manage Accounts
- Settings
- Conference Details
- Contact Details
- Share with Conversation
- Start Conversation
- Choose contact
- Block list
- just now
- 1 min ago
- %d mins ago
- unread Conversations
- sending…
- Decrypting message. Please wait…
- Nickname is already in use
- Admin
- Owner
- Moderator
- Participant
- Visitor
- Would you like to remove %s from your roster? The conversation associated with this contact will not be removed.
- Would you like to block %s from sending you messages?
- Would you like to unblock %s and allow them to send you messages?
- Block all contacts from %s?
- Unblock all contacts from %s?
- Contact blocked
- Would you like to remove %s as a bookmark? The conversation associated with this bookmark will not be removed.
- Register new account on server
- Change password on server
- Share with…
- Start Conversation
- Invite Contact
- Contacts
- Cancel
- Set
- Add
- Edit
- Delete
- Block
- Unblock
- Save
- OK
- Conversations has crashed
- By sending in stack traces you are helping the ongoing development of Conversations\nWarning: This will use your XMPP account to send the stack trace to the developer.
- Send now
- Never ask again
- Unable to connect to account
- Unable to connect to multiple accounts
- Touch here to manage your accounts
- Attach file
- The contact is not in your roster. Would you like to add it?
- Add contact
- delivery failed
- rejected
- Preparing image for transmission
- Clear history
- Clear Conversation History
- Do you want to delete all messages within this Conversation?\n\nWarning: This will not influence messages stored on other devices or servers.
- Delete messages
- End this conversations afterwards
- Choose presence to contact
- Send plain text message
- Send OTR encrypted message
- Send OpenPGP encrypted message
- Your nickname has been changed
- Download Image
- Send unencrypted
- Decryption failed. Maybe you don’t have the proper private key.
- OpenKeychain
- Conversations utilizes a third party app called OpenKeychain 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(Please restart Conversations afterwards.)
- Restart
- Install
- offering…
- waiting…
- No OpenPGP Key found
- Conversations is unable to encrypt your messages because your contact is not announcing his or hers public key.\n\nPlease ask your contact to setup OpenPGP.
- No OpenPGP Keys found
- Conversations is unable to encrypt your messages because your contacts are not announcing their public key.\n\nPlease ask your contacts to setup OpenPGP.
- Encrypted message received. Touch to view and decrypt.
- General
- XMPP resource
- The name this client identifies itself with
- Accept files
- Automatically accept files smaller than…
- Notification Settings
- Notifications
- Notify when a new message arrives
- Vibrate
- Also vibrate when a new message arrives
- Sound
- Play ringtone with notification
- Conference notifications
- Always notify when a new conference message arrives instead of only when highlighted
- Notification grace period
- Disable notifications for a short time after a carbon copy was received
- Advanced Options
- Never send crash reports
- By sending in stack traces you are helping the ongoing development of Conversations
- Confirm Messages
- Let your contact know when you have received and read a message
- UI Options
- OpenKeychain reported an error
- I/O Error decrypting file
- Accept
- An error has occurred
- Grant presence updates
- Preemptively grant and ask for presence subscription for contacts you created
- Subscriptions
- Your account
- Keys
- Send presence updates
- Receive presence updates
- Ask for presence updates
- Choose picture
- Take picture
- Preemptively grant subscription request
- The file you selected is not an image
- Error while converting the image file
- File not found
- General I/O error. Maybe you ran out of storage space?
- The app you used to select this image did not provide us with enough permissions to read the file.\n\nUse a different file manager to choose an image
- Unknown
- Temporarily disabled
- Online
- Connecting\u2026
- Offline
- Unauthorized
- Server not found
- No connectivity
- Registration failed
- Username already in use
- Registration completed
- Server does not support registration
- Security error
- Incompatible server
- Plain text
- OTR
- OpenPGP
- Edit account
- Delete account
- Temporarily disable
- Publish avatar
- Publish OpenPGP public key
- Enable account
- Are you sure?
- If you delete your account your entire conversation history will be lost
- Record voice
- Jabber ID
- Password
- username@example.com
- Confirm password
- Password
- Confirm password
- Passwords do not match
- This is not a valid Jabber ID
- Out of memory. Image is too large
- Do you want to add %s to your phones contact list?
- online
- free to chat
- away
- extended away
- do not disturb
- offline
- Conference
- Other Members
- Server info
- XEP-0313: MAM
- XEP-0280: Message Carbons
- XEP-0352: Client State Indication
- XEP-0191: Blocking Command
- XEP-0237: Roster Versioning
- XEP-0198: Stream Management
- XEP-0163: PEP (Avatars)
- available
- unavailable
- Missing public key announcements
- last seen just now
- last seen 1 minute ago
- last seen %d minutes ago
- last seen 1 hour ago
- last seen %d hours ago
- last seen 1 day ago
- last seen %d days ago
- never seen
- Encrypted message. Please install OpenKeychain to decrypt.
- Unknown OTR fingerprint
- OpenPGP encrypted messages found
- Reception failed
- Your fingerprint
- OTR fingerprint
- Verify
- Decrypt
- Conferences
- Search
- Create Contact
- Join Conference
- Delete Contact
- View contact details
- Block contact
- Unblock contact
- Create
- The contact already exists
- Join
- Conference address
- room@conference.example.com
- Save as bookmark
- Delete bookmark
- This bookmark already exists
- You
- Edit conference subject
- Conference not found
- Leave
- Contact added you to contact list
- Add back
- %s has read up to this point
- Publish
- Touch avatar to select picture from gallery
- Please note: Everyone subscribed to your presence updates will be allowed to see this picture.
- Publishing…
- The server rejected your publication
- Something went wrong while converting your picture
- Could not save avatar to disk
- (Or long press to bring back default)
- Your server does not support the publication of avatars
- whispered
- to %s
- Send private message to %s
- Connect
- This account already exists
- Next
- Current session established
- Additional Information
- Skip
- Disable notifications
- Disable notifications for this conversation
- Notifications are disabled
- Enable
- Conference requires password
- Enter password
- Missing presence updates from contact
- Please request presence updates from your contact first.\n\nThis will be used to determine what client(s) your contact is using.
- Request now
- Delete Fingerprint
- Are you sure you would like to delete this fingerprint?
- Ignore
- Warning: Sending this without mutual presence updates could cause unexpected problems.\n\nGo to contact details to verify your presence subscriptions.
- Encryption settings
- Force end-to-end encryption
- Always send messages encrypted (except for conferences)
- Don’t save encrypted messages
- Warning: This could lead to message loss
- Expert options
- Please be careful with these
- About Conversations
- Build and licensing information
-
- 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)
-
- Quiet Hours
- Start time
- End time
- Enable quiet hours
- Notifications will be silenced during quiet hours
- Increase font size
- Use larger font sizes across the entire app
- Send button indicates status
- Request message receipts
- Received messages will be marked with a green tick if supported
- Colorize send button to indicate contact status
- Other
- Conference name
- Use room’s subject instead of JID to identify conferences
- OTR fingerprint copied to clipboard!
- You are banned from this conference
- This conference is members only
- You have been kicked from this conference
- using account %s
- Checking image on HTTP host
- The image file has been deleted
- You are not connected. Try again later
- Check image file size
- Message options
- Copy text
- Copy original URL
- Send again
- Image URL
- Message text
- URL copied to clipboard
- Message copied to clipboard
- Image transmission failed
- Scan QR code
- Show QR code
- Show block list
- Account details
- Verify OTR
- Remote Fingerprint
- scan
- (or touch phones)
- Socialist Millionaire Protocol
- Hint or Question
- Shared Secret
- Confirm
- In progress
- Respond
- Failed
- Secrets do not match
- Try again
- Finish
- Verified!
- Contact requested SMP verification
- No valid OTR session has been found!
- Conversations
- Keep service in foreground
- Prevents the operating system from killing your connection
- Choose file
- Receiving %1$s (%2$d%% completed)
- Download %s
- file
- Open %s
- sending (%1$d%% completed)
- Preparing file for transmission
- %s offered for download
- Cancel transmission
- file transmission failed
- The file has been deleted
- No application found to open file
- Could not verify fingerprint
- Manually verify
- Are you sure that you want to verify your contacts OTR fingerprint?
- Show dynamic tags
- Display read-only tags underneath contacts
- Enable notifications
- Create conference with…
- No conference server found
- Conference creation failed!
- Conference created!
- Secret accepted!
- Reset
- Account avatar
- Copy OTR fingerprint to clipboard
- Fetching history from server
- No more history on server
- Updating…
- Password changed!
- Could not change password
- Send a message to start an encrypted chat
- Ask 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.
- 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.
- Your hint should not be empty
- Your shared secret can not be empty
- 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.
+ Conversations
+ Settings
+ New conversation
+ Manage accounts
+ End this conversation
+ Contact details
+ Conference details
+ Secure conversation
+ Add account
+ Edit name
+ Add to phone book
+ Delete from roster
+ Block contact
+ Unblock contact
+ Block domain
+ Unblock domain
+ Manage Accounts
+ Settings
+ Conference Details
+ Contact Details
+ Share with Conversation
+ Start Conversation
+ Choose contact
+ Block list
+ just now
+ 1 min ago
+ %d mins ago
+ unread Conversations
+ sending…
+ Decrypting message. Please wait…
+ Nickname is already in use
+ Admin
+ Owner
+ Moderator
+ Participant
+ Visitor
+ Would you like to remove %s from your roster? The conversation associated with this contact will not be removed.
+ Would you like to block %s from sending you messages?
+ Would you like to unblock %s and allow them to send you messages?
+ Block all contacts from %s?
+ Unblock all contacts from %s?
+ Contact blocked
+ Would you like to remove %s as a bookmark? The conversation associated with this bookmark will not be removed.
+ Register new account on server
+ Change password on server
+ Share with…
+ Start Conversation
+ Invite Contact
+ Contacts
+ Cancel
+ Set
+ Add
+ Edit
+ Delete
+ Block
+ Unblock
+ Save
+ OK
+ Conversations has crashed
+ By sending in stack traces you are helping the ongoing development of Conversations\nWarning: This will use your XMPP account to send the stack trace to the developer.
+ Send now
+ Never ask again
+ Unable to connect to account
+ Unable to connect to multiple accounts
+ Touch here to manage your accounts
+ Attach file
+ The contact is not in your roster. Would you like to add it?
+ Add contact
+ delivery failed
+ rejected
+ Preparing image for transmission
+ Clear history
+ Clear Conversation History
+ Do you want to delete all messages within this Conversation?\n\nWarning: This will not influence messages stored on other devices or servers.
+ Delete messages
+ End this conversations afterwards
+ Choose presence to contact
+ Send plain text message
+ Send OTR encrypted message
+ Send OpenPGP encrypted message
+ Your nickname has been changed
+ Download Image
+ Send unencrypted
+ Decryption failed. Maybe you don’t have the proper private key.
+ OpenKeychain
+ Conversations utilizes a third party app called OpenKeychain 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(Please restart Conversations afterwards.)
+ Restart
+ Install
+ offering…
+ waiting…
+ No OpenPGP Key found
+ Conversations is unable to encrypt your messages because your contact is not announcing his or hers public key.\n\nPlease ask your contact to setup OpenPGP.
+ No OpenPGP Keys found
+ Conversations is unable to encrypt your messages because your contacts are not announcing their public key.\n\nPlease ask your contacts to setup OpenPGP.
+ Encrypted message received. Touch to view and decrypt.
+ General
+ XMPP resource
+ The name this client identifies itself with
+ Accept files
+ Automatically accept files smaller than…
+ Notification Settings
+ Notifications
+ Notify when a new message arrives
+ Vibrate
+ Also vibrate when a new message arrives
+ Sound
+ Play ringtone with notification
+ Conference notifications
+ Always notify when a new conference message arrives instead of only when highlighted
+ Notification grace period
+ Disable notifications for a short time after a carbon copy was received
+ Advanced Options
+ Never send crash reports
+ By sending in stack traces you are helping the ongoing development of Conversations
+ Confirm Messages
+ Let your contact know when you have received and read a message
+ UI Options
+ OpenKeychain reported an error
+ I/O Error decrypting file
+ Accept
+ An error has occurred
+ Grant presence updates
+ Preemptively grant and ask for presence subscription for contacts you created
+ Subscriptions
+ Your account
+ Keys
+ Send presence updates
+ Receive presence updates
+ Ask for presence updates
+ Choose picture
+ Take picture
+ Preemptively grant subscription request
+ The file you selected is not an image
+ Error while converting the image file
+ File not found
+ General I/O error. Maybe you ran out of storage space?
+ The app you used to select this image did not provide us with enough permissions to read the file.\n\nUse a different file manager to choose an image
+ Unknown
+ Temporarily disabled
+ Online
+ Connecting\u2026
+ Offline
+ Unauthorized
+ Server not found
+ No connectivity
+ Registration failed
+ Username already in use
+ Registration completed
+ Server does not support registration
+ Security error
+ Incompatible server
+ Plain text
+ OTR
+ OpenPGP
+ Edit account
+ Delete account
+ Temporarily disable
+ Publish avatar
+ Publish OpenPGP public key
+ Enable account
+ Are you sure?
+ If you delete your account your entire conversation history will be lost
+ Record voice
+ Jabber ID
+ Password
+ username@example.com
+ Confirm password
+ Password
+ Confirm password
+ Passwords do not match
+ This is not a valid Jabber ID
+ Out of memory. Image is too large
+ Do you want to add %s to your phones contact list?
+ online
+ free to chat
+ away
+ extended away
+ do not disturb
+ offline
+ Conference
+ Other Members
+ Server info
+ XEP-0313: MAM
+ XEP-0280: Message Carbons
+ XEP-0352: Client State Indication
+ XEP-0191: Blocking Command
+ XEP-0237: Roster Versioning
+ XEP-0198: Stream Management
+ XEP-0163: PEP (Avatars)
+ available
+ unavailable
+ Missing public key announcements
+ last seen just now
+ last seen 1 minute ago
+ last seen %d minutes ago
+ last seen 1 hour ago
+ last seen %d hours ago
+ last seen 1 day ago
+ last seen %d days ago
+ never seen
+ Encrypted message. Please install OpenKeychain to decrypt.
+ Unknown OTR fingerprint
+ OpenPGP encrypted messages found
+ Reception failed
+ Your fingerprint
+ OTR fingerprint
+ Verify
+ Decrypt
+ Conferences
+ Search
+ Create Contact
+ Join Conference
+ Delete Contact
+ View contact details
+ Block contact
+ Unblock contact
+ Create
+ The contact already exists
+ Join
+ Conference address
+ room@conference.example.com
+ Save as bookmark
+ Delete bookmark
+ This bookmark already exists
+ You
+ Edit conference subject
+ Conference not found
+ Leave
+ Contact added you to contact list
+ Add back
+ %s has read up to this point
+ Publish
+ Touch avatar to select picture from gallery
+ Please note: Everyone subscribed to your presence updates will be allowed to see this picture.
+ Publishing…
+ The server rejected your publication
+ Something went wrong while converting your picture
+ Could not save avatar to disk
+ (Or long press to bring back default)
+ Your server does not support the publication of avatars
+ whispered
+ to %s
+ Send private message to %s
+ Connect
+ This account already exists
+ Next
+ Current session established
+ Additional Information
+ Skip
+ Disable notifications
+ Disable notifications for this conversation
+ Notifications are disabled
+ Enable
+ Conference requires password
+ Enter password
+ Missing presence updates from contact
+ Please request presence updates from your contact first.\n\nThis will be used to determine what client(s) your contact is using.
+ Request now
+ Delete Fingerprint
+ Are you sure you would like to delete this fingerprint?
+ Ignore
+ Warning: Sending this without mutual presence updates could cause unexpected problems.\n\nGo to contact details to verify your presence subscriptions.
+ Encryption settings
+ Force end-to-end encryption
+ Always send messages encrypted (except for conferences)
+ Don’t save encrypted messages
+ Warning: This could lead to message loss
+ Expert options
+ Please be careful with these
+ About Conversations
+ Build and licensing information
+
+ 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)
+
+ Quiet Hours
+ Start time
+ End time
+ Enable quiet hours
+ Notifications will be silenced during quiet hours
+ Increase font size
+ Use larger font sizes across the entire app
+ Send button indicates status
+ Request message receipts
+ Received messages will be marked with a green tick if supported
+ Colorize send button to indicate contact status
+ Other
+ Conference name
+ Use room’s subject instead of JID to identify conferences
+ OTR fingerprint copied to clipboard!
+ You are banned from this conference
+ This conference is members only
+ You have been kicked from this conference
+ using account %s
+ Checking image on HTTP host
+ The image file has been deleted
+ You are not connected. Try again later
+ Check image file size
+ Message options
+ Copy text
+ Copy original URL
+ Send again
+ Image URL
+ Message text
+ URL copied to clipboard
+ Message copied to clipboard
+ Image transmission failed
+ Scan QR code
+ Show QR code
+ Show block list
+ Account details
+ Verify OTR
+ Remote Fingerprint
+ scan
+ (or touch phones)
+ Socialist Millionaire Protocol
+ Hint or Question
+ Shared Secret
+ Confirm
+ In progress
+ Respond
+ Failed
+ Secrets do not match
+ Try again
+ Finish
+ Verified!
+ Contact requested SMP verification
+ No valid OTR session has been found!
+ Conversations
+ Keep service in foreground
+ Prevents the operating system from killing your connection
+ Choose file
+ Receiving %1$s (%2$d%% completed)
+ Download %s
+ file
+ Open %s
+ sending (%1$d%% completed)
+ Preparing file for transmission
+ %s offered for download
+ Cancel transmission
+ file transmission failed
+ The file has been deleted
+ No application found to open file
+ Could not verify fingerprint
+ Manually verify
+ Are you sure that you want to verify your contacts OTR fingerprint?
+ Show dynamic tags
+ Display read-only tags underneath contacts
+ Enable notifications
+ Create conference with…
+ No conference server found
+ Conference creation failed!
+ Conference created!
+ Secret accepted!
+ Reset
+ Account avatar
+ Copy OTR fingerprint to clipboard
+ Fetching history from server
+ No more history on server
+ Updating…
+ Password changed!
+ Could not change password
+ Send a message to start an encrypted chat
+ Ask 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.
+ 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.
+ Your hint should not be empty
+ Your shared secret can not be empty
+ 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.
Change password
Current password
New password
@@ -442,19 +442,19 @@
Disable foreground service
Touch to open Conversations
Avatar has been published!
- Sending %s
- Offering %s
- Hide offline
- Disable Account
- %s is typing...
- %s has stopped typing
- Typing notifications
- Let your contact know when you are writing a new message
- Send location
- Show location
- No application found to display location
- Location
- Received location
+ Sending %s
+ Offering %s
+ Hide offline
+ Disable Account
+ %s is typing...
+ %s has stopped typing
+ Typing notifications
+ Let your contact know when you are writing a new message
+ Send location
+ Show location
+ No application found to display location
+ Location
+ Received location
Conversation closed
Left conference
Certificate options
@@ -474,4 +474,9 @@
- Select %d contact
- Select %d contacts
+ Replace send button with quick action
+ Quick Action
+ None
+ Most recently used
+ Choose quick action
diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml
index 417e60a43774284dd1108793aeeb5b290ae8d030..5b7d69040a294b4e9bd081be58822e183a0591bf 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" />
+