Merge tag '2.5.12' into develop
This commit is contained in:
commit
72be751568
|
@ -1,5 +1,9 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
### Version 2.5.12
|
||||||
|
* Jingle file transfer fixes
|
||||||
|
* Fixed OMEMO self healing (after backup restore) on servers w/o MAM
|
||||||
|
|
||||||
### Version 2.5.11
|
### Version 2.5.11
|
||||||
* Fixed crash on Android <5.0
|
* Fixed crash on Android <5.0
|
||||||
|
|
||||||
|
|
|
@ -84,8 +84,8 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 342
|
versionCode 346
|
||||||
versionName "2.5.11.1"
|
versionName "2.5.12"
|
||||||
archivesBaseName += "-$versionName"
|
archivesBaseName += "-$versionName"
|
||||||
applicationId "eu.sum7.conversations"
|
applicationId "eu.sum7.conversations"
|
||||||
resValue "string", "applicationId", applicationId
|
resValue "string", "applicationId", applicationId
|
||||||
|
|
|
@ -41,7 +41,7 @@ public final class Config {
|
||||||
public static final String MAGIC_CREATE_DOMAIN = "chat.sum7.eu";
|
public static final String MAGIC_CREATE_DOMAIN = "chat.sum7.eu";
|
||||||
public static final String QUICKSY_DOMAIN = "quicksy.im";
|
public static final String QUICKSY_DOMAIN = "quicksy.im";
|
||||||
|
|
||||||
public static final String CHANNEL_DISCOVERY = "https://search.jabbercat.org";
|
public static final String CHANNEL_DISCOVERY = "https://search.jabber.network";
|
||||||
|
|
||||||
public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox
|
public static final boolean DISALLOW_REGISTRATION_IN_UI = false; //hide the register checkbox
|
||||||
|
|
||||||
|
@ -99,6 +99,7 @@ public final class Config {
|
||||||
public static final boolean OMEMO_PADDING = false;
|
public static final boolean OMEMO_PADDING = false;
|
||||||
public static final boolean PUT_AUTH_TAG_INTO_KEY = true;
|
public static final boolean PUT_AUTH_TAG_INTO_KEY = true;
|
||||||
|
|
||||||
|
public static final boolean USE_BOOKMARKS2 = false;
|
||||||
|
|
||||||
public static final boolean DISABLE_PROXY_LOOKUP = false; //useful to debug ibb
|
public static final boolean DISABLE_PROXY_LOOKUP = false; //useful to debug ibb
|
||||||
public static final boolean USE_DIRECT_JINGLE_CANDIDATES = true;
|
public static final boolean USE_DIRECT_JINGLE_CANDIDATES = true;
|
||||||
|
|
|
@ -839,6 +839,13 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void deleteOmemoIdentity() {
|
||||||
|
final String node = AxolotlService.PEP_BUNDLES + ":" + getOwnDeviceId();
|
||||||
|
final IqPacket deleteBundleNode = mXmppConnectionService.getIqGenerator().deleteNode(node);
|
||||||
|
mXmppConnectionService.sendIqPacket(account, deleteBundleNode, null);
|
||||||
|
publishDeviceIdsAndRefineAccessModel(getOwnDeviceIds());
|
||||||
|
}
|
||||||
|
|
||||||
public List<Jid> getCryptoTargets(Conversation conversation) {
|
public List<Jid> getCryptoTargets(Conversation conversation) {
|
||||||
final List<Jid> jids;
|
final List<Jid> jids;
|
||||||
if (conversation.getMode() == Conversation.MODE_SINGLE) {
|
if (conversation.getMode() == Conversation.MODE_SINGLE) {
|
||||||
|
|
|
@ -11,8 +11,10 @@ import org.json.JSONObject;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
@ -84,7 +86,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
|
||||||
private PgpDecryptionService pgpDecryptionService = null;
|
private PgpDecryptionService pgpDecryptionService = null;
|
||||||
private XmppConnection xmppConnection = null;
|
private XmppConnection xmppConnection = null;
|
||||||
private long mEndGracePeriod = 0L;
|
private long mEndGracePeriod = 0L;
|
||||||
private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
|
private final Map<Jid, Bookmark> bookmarks = new HashMap<>();
|
||||||
private Presence.Status presenceStatus = Presence.Status.ONLINE;
|
private Presence.Status presenceStatus = Presence.Status.ONLINE;
|
||||||
private String presenceStatusMessage = null;
|
private String presenceStatusMessage = null;
|
||||||
|
|
||||||
|
@ -469,37 +471,52 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
|
||||||
return this.roster;
|
return this.roster;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Bookmark> getBookmarks() {
|
public Collection<Bookmark> getBookmarks() {
|
||||||
return this.bookmarks;
|
return this.bookmarks.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBookmarks(final CopyOnWriteArrayList<Bookmark> bookmarks) {
|
public void setBookmarks(Map<Jid, Bookmark> bookmarks) {
|
||||||
this.bookmarks = bookmarks;
|
synchronized (this.bookmarks) {
|
||||||
|
this.bookmarks.clear();
|
||||||
|
this.bookmarks.putAll(bookmarks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putBookmark(Bookmark bookmark) {
|
||||||
|
synchronized (this.bookmarks) {
|
||||||
|
this.bookmarks.put(bookmark.getJid(), bookmark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBookmark(Bookmark bookmark) {
|
||||||
|
synchronized (this.bookmarks) {
|
||||||
|
this.bookmarks.remove(bookmark.getJid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBookmark(Jid jid) {
|
||||||
|
synchronized (this.bookmarks) {
|
||||||
|
this.bookmarks.remove(jid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Jid> getBookmarkedJids() {
|
public Set<Jid> getBookmarkedJids() {
|
||||||
final Set<Jid> jids = new HashSet<>();
|
synchronized (this.bookmarks) {
|
||||||
for(final Bookmark bookmark : this.bookmarks) {
|
return new HashSet<>(this.bookmarks.keySet());
|
||||||
final Jid jid = bookmark.getJid();
|
|
||||||
if (jid != null) {
|
|
||||||
jids.add(jid.asBareJid());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return jids;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasBookmarkFor(final Jid conferenceJid) {
|
public boolean hasBookmarkFor(final Jid jid) {
|
||||||
return getBookmark(conferenceJid) != null;
|
synchronized (this.bookmarks) {
|
||||||
|
return this.bookmarks.containsKey(jid.asBareJid());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Bookmark getBookmark(final Jid jid) {
|
Bookmark getBookmark(final Jid jid) {
|
||||||
for (final Bookmark bookmark : this.bookmarks) {
|
synchronized (this.bookmarks) {
|
||||||
if (bookmark.getJid() != null && jid.asBareJid().equals(bookmark.getJid().asBareJid())) {
|
return this.bookmarks.get(jid.asBareJid());
|
||||||
return bookmark;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean setAvatar(final String filename) {
|
public boolean setAvatar(final String filename) {
|
||||||
if (this.avatar != null && this.avatar.equals(filename)) {
|
if (this.avatar != null && this.avatar.equals(filename)) {
|
||||||
|
|
|
@ -6,12 +6,16 @@ import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import eu.siacs.conversations.utils.StringUtils;
|
import eu.siacs.conversations.utils.StringUtils;
|
||||||
import eu.siacs.conversations.utils.UIHelper;
|
import eu.siacs.conversations.utils.UIHelper;
|
||||||
import eu.siacs.conversations.xml.Element;
|
import eu.siacs.conversations.xml.Element;
|
||||||
|
import eu.siacs.conversations.xml.Namespace;
|
||||||
import eu.siacs.conversations.xmpp.InvalidJid;
|
import eu.siacs.conversations.xmpp.InvalidJid;
|
||||||
import rocks.xmpp.addr.Jid;
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
|
@ -33,11 +37,69 @@ public class Bookmark extends Element implements ListItem {
|
||||||
this.account = account;
|
this.account = account;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Map<Jid, Bookmark> parseFromStorage(Element storage, Account account) {
|
||||||
|
if (storage == null) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
final HashMap<Jid, Bookmark> bookmarks = new HashMap<>();
|
||||||
|
for (final Element item : storage.getChildren()) {
|
||||||
|
if (item.getName().equals("conference")) {
|
||||||
|
final Bookmark bookmark = Bookmark.parse(item, account);
|
||||||
|
if (bookmark != null) {
|
||||||
|
final Bookmark old = bookmarks.put(bookmark.jid, bookmark);
|
||||||
|
if (old != null && old.getBookmarkName() != null && bookmark.getBookmarkName() == null) {
|
||||||
|
bookmark.setBookmarkName(old.getBookmarkName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bookmarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<Jid, Bookmark> parseFromPubsub(Element pubsub, Account account) {
|
||||||
|
if (pubsub == null) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
final Element items = pubsub.findChild("items");
|
||||||
|
if (items != null && Namespace.BOOKMARKS2.equals(items.getAttribute("node"))) {
|
||||||
|
final Map<Jid, Bookmark> bookmarks = new HashMap<>();
|
||||||
|
for(Element item : items.getChildren()) {
|
||||||
|
if (item.getName().equals("item")) {
|
||||||
|
final Bookmark bookmark = Bookmark.parseFromItem(item, account);
|
||||||
|
if (bookmark != null) {
|
||||||
|
bookmarks.put(bookmark.jid, bookmark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bookmarks;
|
||||||
|
}
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
public static Bookmark parse(Element element, Account account) {
|
public static Bookmark parse(Element element, Account account) {
|
||||||
Bookmark bookmark = new Bookmark(account);
|
Bookmark bookmark = new Bookmark(account);
|
||||||
bookmark.setAttributes(element.getAttributes());
|
bookmark.setAttributes(element.getAttributes());
|
||||||
bookmark.setChildren(element.getChildren());
|
bookmark.setChildren(element.getChildren());
|
||||||
bookmark.jid = InvalidJid.getNullForInvalid(bookmark.getAttributeAsJid("jid"));
|
bookmark.jid = InvalidJid.getNullForInvalid(bookmark.getAttributeAsJid("jid"));
|
||||||
|
if (bookmark.jid == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Bookmark parseFromItem(Element item, Account account) {
|
||||||
|
final Element conference = item.findChild("conference", Namespace.BOOKMARKS2);
|
||||||
|
if (conference == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Bookmark bookmark = new Bookmark(account);
|
||||||
|
bookmark.jid = InvalidJid.getNullForInvalid(item.getAttributeAsJid("id"));
|
||||||
|
if (bookmark.jid == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
bookmark.setBookmarkName(conference.getAttribute("name"));
|
||||||
|
bookmark.setAutojoin(conference.getAttributeAsBoolean("autojoin"));
|
||||||
|
bookmark.setNick(conference.findChildContent("nick"));
|
||||||
return bookmark;
|
return bookmark;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -307,8 +307,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
|
||||||
synchronized (this.messages) {
|
synchronized (this.messages) {
|
||||||
for (int i = this.messages.size() - 1; i >= 0; --i) {
|
for (int i = this.messages.size() - 1; i >= 0; --i) {
|
||||||
final Message message = messages.get(i);
|
final Message message = messages.get(i);
|
||||||
if (counterpart.equals(message.getCounterpart())
|
final boolean counterpartMatch = mode == MODE_SINGLE ?
|
||||||
&& ((message.getStatus() == Message.STATUS_RECEIVED) == received)
|
counterpart.asBareJid().equals(message.getCounterpart().asBareJid()) :
|
||||||
|
counterpart.equals(message.getCounterpart());
|
||||||
|
if (counterpartMatch && ((message.getStatus() == Message.STATUS_RECEIVED) == received)
|
||||||
&& (carbon == message.isCarbon() || received)) {
|
&& (carbon == message.isCarbon() || received)) {
|
||||||
final boolean idMatch = id.equals(message.getRemoteMsgId()) || message.remoteMsgIdMatchInEdit(id);
|
final boolean idMatch = id.equals(message.getRemoteMsgId()) || message.remoteMsgIdMatchInEdit(id);
|
||||||
if (idMatch && !message.isFileOrImage() && !message.treatAsDownloadable()) {
|
if (idMatch && !message.isFileOrImage() && !message.treatAsDownloadable()) {
|
||||||
|
|
|
@ -417,7 +417,7 @@ public class MucOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getProposedNick() {
|
public String getProposedNick() {
|
||||||
final Bookmark bookmark = this.conversation.getBookmark();
|
final Bookmark bookmark = this.conversation.getBookmark();
|
||||||
final String bookmarkedNick = normalize(account.getJid(), bookmark == null ? null : bookmark.getNick());
|
final String bookmarkedNick = normalize(account.getJid(), bookmark == null ? null : bookmark.getNick());
|
||||||
if (bookmarkedNick != null) {
|
if (bookmarkedNick != null) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ public interface Transferable {
|
||||||
int STATUS_DOWNLOADING = 0x204;
|
int STATUS_DOWNLOADING = 0x204;
|
||||||
int STATUS_OFFER_CHECK_FILESIZE = 0x206;
|
int STATUS_OFFER_CHECK_FILESIZE = 0x206;
|
||||||
int STATUS_UPLOADING = 0x207;
|
int STATUS_UPLOADING = 0x207;
|
||||||
|
int STATUS_CANCELLED = 0x208;
|
||||||
|
|
||||||
|
|
||||||
boolean start();
|
boolean start();
|
||||||
|
|
|
@ -19,6 +19,7 @@ import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService;
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
import eu.siacs.conversations.utils.PhoneHelper;
|
import eu.siacs.conversations.utils.PhoneHelper;
|
||||||
import eu.siacs.conversations.xml.Namespace;
|
import eu.siacs.conversations.xml.Namespace;
|
||||||
|
import eu.siacs.conversations.xmpp.XmppConnection;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
|
||||||
|
|
||||||
public abstract class AbstractGenerator {
|
public abstract class AbstractGenerator {
|
||||||
|
@ -38,7 +39,6 @@ public abstract class AbstractGenerator {
|
||||||
"http://jabber.org/protocol/disco#info",
|
"http://jabber.org/protocol/disco#info",
|
||||||
"urn:xmpp:avatar:metadata+notify",
|
"urn:xmpp:avatar:metadata+notify",
|
||||||
Namespace.NICK+"+notify",
|
Namespace.NICK+"+notify",
|
||||||
Namespace.BOOKMARKS+"+notify",
|
|
||||||
"urn:xmpp:ping",
|
"urn:xmpp:ping",
|
||||||
"jabber:iq:version",
|
"jabber:iq:version",
|
||||||
"http://jabber.org/protocol/chatstates"
|
"http://jabber.org/protocol/chatstates"
|
||||||
|
@ -109,7 +109,8 @@ public abstract class AbstractGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getFeatures(Account account) {
|
public List<String> getFeatures(Account account) {
|
||||||
ArrayList<String> features = new ArrayList<>(Arrays.asList(FEATURES));
|
final XmppConnection connection = account.getXmppConnection();
|
||||||
|
final ArrayList<String> features = new ArrayList<>(Arrays.asList(FEATURES));
|
||||||
if (mXmppConnectionService.confirmMessages()) {
|
if (mXmppConnectionService.confirmMessages()) {
|
||||||
features.addAll(Arrays.asList(MESSAGE_CONFIRMATION_FEATURES));
|
features.addAll(Arrays.asList(MESSAGE_CONFIRMATION_FEATURES));
|
||||||
}
|
}
|
||||||
|
@ -125,6 +126,12 @@ public abstract class AbstractGenerator {
|
||||||
if (mXmppConnectionService.broadcastLastActivity()) {
|
if (mXmppConnectionService.broadcastLastActivity()) {
|
||||||
features.add(Namespace.IDLE);
|
features.add(Namespace.IDLE);
|
||||||
}
|
}
|
||||||
|
if (connection != null && connection.getFeatures().bookmarks2()) {
|
||||||
|
features.add(Namespace.BOOKMARKS2 +"+notify");
|
||||||
|
} else {
|
||||||
|
features.add(Namespace.BOOKMARKS+"+notify");
|
||||||
|
}
|
||||||
|
|
||||||
Collections.sort(features);
|
Collections.sort(features);
|
||||||
return features;
|
return features;
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,10 @@ public class IqGenerator extends AbstractGenerator {
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IqPacket retrieveBookmarks() {
|
||||||
|
return retrieve(Namespace.BOOKMARKS2, null);
|
||||||
|
}
|
||||||
|
|
||||||
public IqPacket publishNick(String nick) {
|
public IqPacket publishNick(String nick) {
|
||||||
final Element item = new Element("item");
|
final Element item = new Element("item");
|
||||||
item.addChild("nick", Namespace.NICK).setContent(nick);
|
item.addChild("nick", Namespace.NICK).setContent(nick);
|
||||||
|
@ -138,6 +142,16 @@ public class IqGenerator extends AbstractGenerator {
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IqPacket deleteItem(final String node, final String id) {
|
||||||
|
IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
|
||||||
|
final Element pubsub = packet.addChild("pubsub", Namespace.PUBSUB);
|
||||||
|
final Element retract = pubsub.addChild("retract");
|
||||||
|
retract.setAttribute("node", node);
|
||||||
|
retract.setAttribute("notify","true");
|
||||||
|
retract.addChild("item").setAttribute("id", id);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
public IqPacket publishAvatar(Avatar avatar, Bundle options) {
|
public IqPacket publishAvatar(Avatar avatar, Bundle options) {
|
||||||
final Element item = new Element("item");
|
final Element item = new Element("item");
|
||||||
item.setAttribute("id", avatar.sha1sum);
|
item.setAttribute("id", avatar.sha1sum);
|
||||||
|
@ -147,8 +161,12 @@ public class IqGenerator extends AbstractGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public IqPacket publishElement(final String namespace, final Element element, final Bundle options) {
|
public IqPacket publishElement(final String namespace, final Element element, final Bundle options) {
|
||||||
|
return publishElement(namespace, element, "curent", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IqPacket publishElement(final String namespace, final Element element, String id, final Bundle options) {
|
||||||
final Element item = new Element("item");
|
final Element item = new Element("item");
|
||||||
item.setAttribute("id","current");
|
item.setAttribute("id", id);
|
||||||
item.addChild(element);
|
item.addChild(element);
|
||||||
return publish(namespace, item, options);
|
return publish(namespace, item, options);
|
||||||
}
|
}
|
||||||
|
@ -222,6 +240,21 @@ public class IqGenerator extends AbstractGenerator {
|
||||||
return publish(AxolotlService.PEP_DEVICE_LIST, item, publishOptions);
|
return publish(AxolotlService.PEP_DEVICE_LIST, item, publishOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Element publishBookmarkItem(final Bookmark bookmark) {
|
||||||
|
final String name = bookmark.getBookmarkName();
|
||||||
|
final String nick = bookmark.getNick();
|
||||||
|
final boolean autojoin = bookmark.autojoin();
|
||||||
|
final Element conference = new Element("conference", Namespace.BOOKMARKS2);
|
||||||
|
if (name != null) {
|
||||||
|
conference.setAttribute("name", name);
|
||||||
|
}
|
||||||
|
if (nick != null) {
|
||||||
|
conference.addChild("nick").setContent(nick);
|
||||||
|
}
|
||||||
|
conference.setAttribute("autojoin",String.valueOf(autojoin));
|
||||||
|
return conference;
|
||||||
|
}
|
||||||
|
|
||||||
public IqPacket publishBundles(final SignedPreKeyRecord signedPreKeyRecord, final IdentityKey identityKey,
|
public IqPacket publishBundles(final SignedPreKeyRecord signedPreKeyRecord, final IdentityKey identityKey,
|
||||||
final Set<PreKeyRecord> preKeyRecords, final int deviceId, Bundle publishOptions) {
|
final Set<PreKeyRecord> preKeyRecords, final int deviceId, Bundle publishOptions) {
|
||||||
final Element item = new Element("item");
|
final Element item = new Element("item");
|
||||||
|
|
|
@ -10,6 +10,7 @@ import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ -20,6 +21,7 @@ import eu.siacs.conversations.crypto.axolotl.BrokenSessionException;
|
||||||
import eu.siacs.conversations.crypto.axolotl.NotEncryptedForThisDeviceException;
|
import eu.siacs.conversations.crypto.axolotl.NotEncryptedForThisDeviceException;
|
||||||
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
|
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
|
import eu.siacs.conversations.entities.Bookmark;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
import eu.siacs.conversations.entities.Conversation;
|
||||||
import eu.siacs.conversations.entities.Conversational;
|
import eu.siacs.conversations.entities.Conversational;
|
||||||
|
@ -110,7 +112,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status, boolean checkedForDuplicates, boolean postpone) {
|
private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status, final boolean checkedForDuplicates, boolean postpone) {
|
||||||
final AxolotlService service = conversation.getAccount().getAxolotlService();
|
final AxolotlService service = conversation.getAccount().getAxolotlService();
|
||||||
final XmppAxolotlMessage xmppAxolotlMessage;
|
final XmppAxolotlMessage xmppAxolotlMessage;
|
||||||
try {
|
try {
|
||||||
|
@ -134,7 +136,6 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG,"ignoring broken session exception because checkForDuplicates failed");
|
Log.d(Config.LOGTAG,"ignoring broken session exception because checkForDuplicates failed");
|
||||||
//TODO should be still emit a failed message?
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (NotEncryptedForThisDeviceException e) {
|
} catch (NotEncryptedForThisDeviceException e) {
|
||||||
|
@ -224,23 +225,55 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
||||||
if (account.getXmppConnection().getFeatures().bookmarksConversion()) {
|
if (account.getXmppConnection().getFeatures().bookmarksConversion()) {
|
||||||
final Element i = items.findChild("item");
|
final Element i = items.findChild("item");
|
||||||
final Element storage = i == null ? null : i.findChild("storage", Namespace.BOOKMARKS);
|
final Element storage = i == null ? null : i.findChild("storage", Namespace.BOOKMARKS);
|
||||||
mXmppConnectionService.processBookmarks(account, storage, true);
|
Map<Jid, Bookmark> bookmarks = Bookmark.parseFromStorage(storage, account);
|
||||||
|
mXmppConnectionService.processBookmarksInitial(account, bookmarks, true);
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": processing bookmark PEP event");
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": processing bookmark PEP event");
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": ignoring bookmark PEP event because bookmark conversion was not detected");
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": ignoring bookmark PEP event because bookmark conversion was not detected");
|
||||||
}
|
}
|
||||||
|
} else if (Namespace.BOOKMARKS2.equals(node) && account.getJid().asBareJid().equals(from)) {
|
||||||
|
final Element item = items.findChild("item");
|
||||||
|
final Element retract = items.findChild("retract");
|
||||||
|
if (item != null) {
|
||||||
|
final Bookmark bookmark = Bookmark.parseFromItem(item, account);
|
||||||
|
if (bookmark != null) {
|
||||||
|
account.putBookmark(bookmark);
|
||||||
|
mXmppConnectionService.processModifiedBookmark(bookmark);
|
||||||
|
mXmppConnectionService.updateConversationUi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (retract != null) {
|
||||||
|
final Jid id = InvalidJid.getNullForInvalid(retract.getAttributeAsJid("id"));
|
||||||
|
if (id != null) {
|
||||||
|
account.removeBookmark(id);
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": deleted bookmark for "+id);
|
||||||
|
mXmppConnectionService.processDeletedBookmark(account, id);
|
||||||
|
mXmppConnectionService.updateConversationUi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().asBareJid()+" received pubsub notification for node="+node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseDeleteEvent(final Element event, final Jid from, final Account account) {
|
private void parseDeleteEvent(final Element event, final Jid from, final Account account) {
|
||||||
final Element delete = event.findChild("delete");
|
final Element delete = event.findChild("delete");
|
||||||
if (delete == null) {
|
final String node = delete == null ? null : delete.getAttribute("node");
|
||||||
return;
|
|
||||||
}
|
|
||||||
String node = delete.getAttribute("node");
|
|
||||||
if (Namespace.NICK.equals(node)) {
|
if (Namespace.NICK.equals(node)) {
|
||||||
Log.d(Config.LOGTAG, "parsing nick delete event from " + from);
|
Log.d(Config.LOGTAG, "parsing nick delete event from " + from);
|
||||||
setNick(account, from, null);
|
setNick(account, from, null);
|
||||||
|
} else if (Namespace.BOOKMARKS2.equals(node) && account.getJid().asBareJid().equals(from)) {
|
||||||
|
account.setBookmarks(Collections.emptyMap());
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": deleted bookmarks node");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parsePurgeEvent(final Element event, final Jid from, final Account account) {
|
||||||
|
final Element purge = event.findChild("purge");
|
||||||
|
final String node = purge == null ? null : purge.getAttribute("node");
|
||||||
|
if (Namespace.BOOKMARKS2.equals(node) && account.getJid().asBareJid().equals(from)) {
|
||||||
|
account.setBookmarks(Collections.emptyMap());
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": purged bookmarks");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,8 +496,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
||||||
origin = from;
|
origin = from;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO either or is probably fine?
|
final boolean liveMessage = query == null && !isTypeGroupChat && mucUserElement == null;
|
||||||
final boolean checkedForDuplicates = serverMsgId != null && remoteMsgId != null && !conversation.possibleDuplicate(serverMsgId, remoteMsgId);
|
final boolean checkedForDuplicates = liveMessage || (serverMsgId != null && remoteMsgId != null && !conversation.possibleDuplicate(serverMsgId, remoteMsgId));
|
||||||
|
|
||||||
if (origin != null) {
|
if (origin != null) {
|
||||||
message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status, checkedForDuplicates,query != null);
|
message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status, checkedForDuplicates,query != null);
|
||||||
|
@ -835,6 +868,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
||||||
parseEvent(event, original.getFrom(), account);
|
parseEvent(event, original.getFrom(), account);
|
||||||
} else if (event.hasChild("delete")) {
|
} else if (event.hasChild("delete")) {
|
||||||
parseDeleteEvent(event, original.getFrom(), account);
|
parseDeleteEvent(event, original.getFrom(), account);
|
||||||
|
} else if (event.hasChild("purge")) {
|
||||||
|
parsePurgeEvent(event, original.getFrom(), account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.entities.DownloadableFile;
|
import eu.siacs.conversations.entities.DownloadableFile;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
|
import eu.siacs.conversations.services.AttachFileToConversationRunnable;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService;
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
import eu.siacs.conversations.ui.RecordingActivity;
|
import eu.siacs.conversations.ui.RecordingActivity;
|
||||||
import eu.siacs.conversations.ui.util.Attachment;
|
import eu.siacs.conversations.ui.util.Attachment;
|
||||||
|
@ -111,6 +112,7 @@ public class FileBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean allFilesUnderSize(Context context, List<Attachment> attachments, long max) {
|
public static boolean allFilesUnderSize(Context context, List<Attachment> attachments, long max) {
|
||||||
|
final boolean compressVideo = !AttachFileToConversationRunnable.getVideoCompression(context).equals("uncompressed");
|
||||||
if (max <= 0) {
|
if (max <= 0) {
|
||||||
Log.d(Config.LOGTAG, "server did not report max file size for http upload");
|
Log.d(Config.LOGTAG, "server did not report max file size for http upload");
|
||||||
return true; //exception to be compatible with HTTP Upload < v0.2
|
return true; //exception to be compatible with HTTP Upload < v0.2
|
||||||
|
@ -120,7 +122,7 @@ public class FileBackend {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String mime = attachment.getMime();
|
String mime = attachment.getMime();
|
||||||
if (mime != null && mime.startsWith("video/")) {
|
if (mime != null && mime.startsWith("video/") && compressVideo) {
|
||||||
try {
|
try {
|
||||||
Dimensions dimensions = FileBackend.getVideoDimensions(context, attachment.getUri());
|
Dimensions dimensions = FileBackend.getVideoDimensions(context, attachment.getUri());
|
||||||
if (dimensions.getMin() > 720) {
|
if (dimensions.getMin() > 720) {
|
||||||
|
@ -191,6 +193,14 @@ public class FileBackend {
|
||||||
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera/";
|
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Uri getUriForUri(Context context, Uri uri) {
|
||||||
|
if ("file".equals(uri.getScheme())) {
|
||||||
|
return getUriForFile(context, new File(uri.getPath()));
|
||||||
|
} else {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Uri getUriForFile(Context context, File file) {
|
public static Uri getUriForFile(Context context, File file) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N || Config.ONLY_INTERNAL_STORAGE) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N || Config.ONLY_INTERNAL_STORAGE) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package eu.siacs.conversations.services;
|
package eu.siacs.conversations.services;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
@ -177,7 +178,11 @@ public class AttachFileToConversationRunnable implements Runnable, MediaTranscod
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getVideoCompression() {
|
private String getVideoCompression() {
|
||||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mXmppConnectionService);
|
return getVideoCompression(mXmppConnectionService);
|
||||||
return preferences.getString("video_compression", mXmppConnectionService.getResources().getString(R.string.video_compression));
|
}
|
||||||
|
|
||||||
|
public static String getVideoCompression(final Context context) {
|
||||||
|
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
return preferences.getString("video_compression", context.getResources().getString(R.string.video_compression));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,6 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -313,7 +312,12 @@ public class XmppConnectionService extends Service {
|
||||||
mJingleConnectionManager.cancelInTransmission();
|
mJingleConnectionManager.cancelInTransmission();
|
||||||
mQuickConversationsService.considerSyncBackground(false);
|
mQuickConversationsService.considerSyncBackground(false);
|
||||||
fetchRosterFromServer(account);
|
fetchRosterFromServer(account);
|
||||||
if (!account.getXmppConnection().getFeatures().bookmarksConversion()) {
|
|
||||||
|
final XmppConnection connection = account.getXmppConnection();
|
||||||
|
|
||||||
|
if (connection.getFeatures().bookmarks2()) {
|
||||||
|
fetchBookmarks2(account);
|
||||||
|
} else if (!account.getXmppConnection().getFeatures().bookmarksConversion()) {
|
||||||
fetchBookmarks(account);
|
fetchBookmarks(account);
|
||||||
}
|
}
|
||||||
final boolean flexible = account.getXmppConnection().getFeatures().flexibleOfflineMessageRetrieval();
|
final boolean flexible = account.getXmppConnection().getFeatures().flexibleOfflineMessageRetrieval();
|
||||||
|
@ -1559,7 +1563,8 @@ public class XmppConnectionService extends Service {
|
||||||
if (response.getType() == IqPacket.TYPE.RESULT) {
|
if (response.getType() == IqPacket.TYPE.RESULT) {
|
||||||
final Element query1 = response.query();
|
final Element query1 = response.query();
|
||||||
final Element storage = query1.findChild("storage", "storage:bookmarks");
|
final Element storage = query1.findChild("storage", "storage:bookmarks");
|
||||||
processBookmarks(a, storage, false);
|
Map<Jid, Bookmark> bookmarks = Bookmark.parseFromStorage(storage, account);
|
||||||
|
processBookmarksInitial(a, bookmarks, false);
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": could not fetch bookmarks");
|
Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": could not fetch bookmarks");
|
||||||
}
|
}
|
||||||
|
@ -1567,55 +1572,101 @@ public class XmppConnectionService extends Service {
|
||||||
sendIqPacket(account, iqPacket, callback);
|
sendIqPacket(account, iqPacket, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void processBookmarks(Account account, Element storage, final boolean pep) {
|
public void fetchBookmarks2(final Account account) {
|
||||||
|
final IqPacket retrieve = mIqGenerator.retrieveBookmarks();
|
||||||
|
sendIqPacket(account, retrieve, new OnIqPacketReceived() {
|
||||||
|
@Override
|
||||||
|
public void onIqPacketReceived(final Account account, final IqPacket response) {
|
||||||
|
if (response.getType() == IqPacket.TYPE.RESULT) {
|
||||||
|
final Element pubsub = response.findChild("pubsub", Namespace.PUBSUB);
|
||||||
|
final Map<Jid, Bookmark> bookmarks = Bookmark.parseFromPubsub(pubsub, account);
|
||||||
|
processBookmarksInitial(account, bookmarks, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processBookmarksInitial(Account account, Map<Jid, Bookmark> bookmarks, final boolean pep) {
|
||||||
final Set<Jid> previousBookmarks = account.getBookmarkedJids();
|
final Set<Jid> previousBookmarks = account.getBookmarkedJids();
|
||||||
final HashMap<Jid, Bookmark> bookmarks = new HashMap<>();
|
|
||||||
final boolean synchronizeWithBookmarks = synchronizeWithBookmarks();
|
final boolean synchronizeWithBookmarks = synchronizeWithBookmarks();
|
||||||
if (storage != null) {
|
for (Bookmark bookmark : bookmarks.values()) {
|
||||||
for (final Element item : storage.getChildren()) {
|
|
||||||
if (item.getName().equals("conference")) {
|
|
||||||
final Bookmark bookmark = Bookmark.parse(item, account);
|
|
||||||
Bookmark old = bookmarks.put(bookmark.getJid(), bookmark);
|
|
||||||
if (old != null && old.getBookmarkName() != null && bookmark.getBookmarkName() == null) {
|
|
||||||
bookmark.setBookmarkName(old.getBookmarkName());
|
|
||||||
}
|
|
||||||
if (bookmark.getJid() == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
previousBookmarks.remove(bookmark.getJid().asBareJid());
|
previousBookmarks.remove(bookmark.getJid().asBareJid());
|
||||||
Conversation conversation = find(bookmark);
|
processModifiedBookmark(bookmark, pep, synchronizeWithBookmarks);
|
||||||
if (conversation != null) {
|
|
||||||
if (conversation.getMode() != Conversation.MODE_MULTI) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
bookmark.setConversation(conversation);
|
|
||||||
if (pep && synchronizeWithBookmarks && !bookmark.autojoin()) {
|
|
||||||
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": archiving conference ("+conversation.getJid()+") after receiving pep");
|
|
||||||
archiveConversation(conversation, false);
|
|
||||||
}
|
|
||||||
} else if (synchronizeWithBookmarks && bookmark.autojoin()) {
|
|
||||||
conversation = findOrCreateConversation(account, bookmark.getFullJid(), true, true, false);
|
|
||||||
bookmark.setConversation(conversation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (pep && synchronizeWithBookmarks) {
|
if (pep && synchronizeWithBookmarks) {
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + previousBookmarks.size() + " bookmarks have been removed");
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + previousBookmarks.size() + " bookmarks have been removed");
|
||||||
for (Jid jid : previousBookmarks) {
|
for (Jid jid : previousBookmarks) {
|
||||||
|
processDeletedBookmark(account, jid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
account.setBookmarks(bookmarks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processDeletedBookmark(Account account, Jid jid) {
|
||||||
final Conversation conversation = find(account, jid);
|
final Conversation conversation = find(account, jid);
|
||||||
if (conversation != null && conversation.getMucOptions().getError() == MucOptions.Error.DESTROYED) {
|
if (conversation != null && conversation.getMucOptions().getError() == MucOptions.Error.DESTROYED) {
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": archiving destroyed conference (" + conversation.getJid() + ") after receiving pep");
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": archiving destroyed conference (" + conversation.getJid() + ") after receiving pep");
|
||||||
archiveConversation(conversation, false);
|
archiveConversation(conversation, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processModifiedBookmark(Bookmark bookmark, final boolean pep, final boolean synchronizeWithBookmarks) {
|
||||||
|
final Account account = bookmark.getAccount();
|
||||||
|
Conversation conversation = find(bookmark);
|
||||||
|
if (conversation != null) {
|
||||||
|
if (conversation.getMode() != Conversation.MODE_MULTI) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bookmark.setConversation(conversation);
|
||||||
|
if (pep && synchronizeWithBookmarks && !bookmark.autojoin()) {
|
||||||
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": archiving conference (" + conversation.getJid() + ") after receiving pep");
|
||||||
|
archiveConversation(conversation, false);
|
||||||
|
} else {
|
||||||
|
final MucOptions mucOptions = conversation.getMucOptions();
|
||||||
|
if (mucOptions.getError() == MucOptions.Error.NICK_IN_USE) {
|
||||||
|
final String current = mucOptions.getActualNick();
|
||||||
|
final String proposed = mucOptions.getProposedNick();
|
||||||
|
if (current != null && !current.equals(proposed)) {
|
||||||
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": proposed nick changed after bookmark push " + current + "->" + proposed);
|
||||||
|
joinMuc(conversation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
account.setBookmarks(new CopyOnWriteArrayList<>(bookmarks.values()));
|
}
|
||||||
|
} else if (synchronizeWithBookmarks && bookmark.autojoin()) {
|
||||||
|
conversation = findOrCreateConversation(account, bookmark.getFullJid(), true, true, false);
|
||||||
|
bookmark.setConversation(conversation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pushBookmarks(Account account) {
|
public void processModifiedBookmark(Bookmark bookmark) {
|
||||||
|
final boolean synchronizeWithBookmarks = synchronizeWithBookmarks();
|
||||||
|
processModifiedBookmark(bookmark, true, synchronizeWithBookmarks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createBookmark(final Account account, final Bookmark bookmark) {
|
||||||
|
account.putBookmark(bookmark);
|
||||||
final XmppConnection connection = account.getXmppConnection();
|
final XmppConnection connection = account.getXmppConnection();
|
||||||
if (connection != null && connection.getFeatures().bookmarksConversion()) {
|
if (connection.getFeatures().bookmarks2()) {
|
||||||
|
final Element item = mIqGenerator.publishBookmarkItem(bookmark);
|
||||||
|
pushNodeAndEnforcePublishOptions(account, Namespace.BOOKMARKS2, item, bookmark.getJid().asBareJid().toEscapedString(), PublishOptions.persistentWhitelistAccessMaxItems());
|
||||||
|
} else if (connection.getFeatures().bookmarksConversion()) {
|
||||||
|
pushBookmarksPep(account);
|
||||||
|
} else {
|
||||||
|
pushBookmarksPrivateXml(account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteBookmark(final Account account, final Bookmark bookmark) {
|
||||||
|
account.removeBookmark(bookmark);
|
||||||
|
final XmppConnection connection = account.getXmppConnection();
|
||||||
|
if (connection.getFeatures().bookmarks2()) {
|
||||||
|
IqPacket request = mIqGenerator.deleteItem(Namespace.BOOKMARKS2, bookmark.getJid().asBareJid().toEscapedString());
|
||||||
|
sendIqPacket(account, request, (a, response) -> {
|
||||||
|
if (response.getType() == IqPacket.TYPE.ERROR) {
|
||||||
|
Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": unable to delete bookmark " + response.getError());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (connection.getFeatures().bookmarksConversion()) {
|
||||||
pushBookmarksPep(account);
|
pushBookmarksPep(account);
|
||||||
} else {
|
} else {
|
||||||
pushBookmarksPrivateXml(account);
|
pushBookmarksPrivateXml(account);
|
||||||
|
@ -1643,14 +1694,18 @@ public class XmppConnectionService extends Service {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void pushNodeAndEnforcePublishOptions(final Account account, final String node, final Element element, final Bundle options) {
|
private void pushNodeAndEnforcePublishOptions(final Account account, final String node, final Element element, final Bundle options) {
|
||||||
pushNodeAndEnforcePublishOptions(account, node, element, options, true);
|
pushNodeAndEnforcePublishOptions(account, node, element, null, options, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pushNodeAndEnforcePublishOptions(final Account account, final String node, final Element element, final Bundle options, final boolean retry) {
|
private void pushNodeAndEnforcePublishOptions(final Account account, final String node, final Element element, final String id, final Bundle options) {
|
||||||
final IqPacket packet = mIqGenerator.publishElement(node, element, options);
|
pushNodeAndEnforcePublishOptions(account, node, element, id, options, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pushNodeAndEnforcePublishOptions(final Account account, final String node, final Element element, final String id, final Bundle options, final boolean retry) {
|
||||||
|
final IqPacket packet = mIqGenerator.publishElement(node, element, id, options);
|
||||||
sendIqPacket(account, packet, (a, response) -> {
|
sendIqPacket(account, packet, (a, response) -> {
|
||||||
if (response.getType() == IqPacket.TYPE.RESULT) {
|
if (response.getType() == IqPacket.TYPE.RESULT) {
|
||||||
return;
|
return;
|
||||||
|
@ -1659,7 +1714,7 @@ public class XmppConnectionService extends Service {
|
||||||
pushNodeConfiguration(account, node, options, new OnConfigurationPushed() {
|
pushNodeConfiguration(account, node, options, new OnConfigurationPushed() {
|
||||||
@Override
|
@Override
|
||||||
public void onPushSucceeded() {
|
public void onPushSucceeded() {
|
||||||
pushNodeAndEnforcePublishOptions(account, node, element, options, false);
|
pushNodeAndEnforcePublishOptions(account, node, element, id, options, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -2037,16 +2092,15 @@ public class XmppConnectionService extends Service {
|
||||||
getMessageArchiveService().kill(conversation);
|
getMessageArchiveService().kill(conversation);
|
||||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||||
if (conversation.getAccount().getStatus() == Account.State.ONLINE) {
|
if (conversation.getAccount().getStatus() == Account.State.ONLINE) {
|
||||||
Bookmark bookmark = conversation.getBookmark();
|
final Bookmark bookmark = conversation.getBookmark();
|
||||||
if (maySynchronizeWithBookmarks && bookmark != null && synchronizeWithBookmarks()) {
|
if (maySynchronizeWithBookmarks && bookmark != null && synchronizeWithBookmarks()) {
|
||||||
if (conversation.getMucOptions().getError() == MucOptions.Error.DESTROYED) {
|
if (conversation.getMucOptions().getError() == MucOptions.Error.DESTROYED) {
|
||||||
Account account = bookmark.getAccount();
|
Account account = bookmark.getAccount();
|
||||||
bookmark.setConversation(null);
|
bookmark.setConversation(null);
|
||||||
account.getBookmarks().remove(bookmark);
|
deleteBookmark(account, bookmark);
|
||||||
pushBookmarks(account);
|
|
||||||
} else if (bookmark.autojoin()) {
|
} else if (bookmark.autojoin()) {
|
||||||
bookmark.setAutojoin(false);
|
bookmark.setAutojoin(false);
|
||||||
pushBookmarks(bookmark.getAccount());
|
createBookmark(bookmark.getAccount(), bookmark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2198,18 +2252,24 @@ public class XmppConnectionService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteAccount(final Account account) {
|
public void deleteAccount(final Account account) {
|
||||||
|
final boolean connected = account.getStatus() == Account.State.ONLINE;
|
||||||
synchronized (this.conversations) {
|
synchronized (this.conversations) {
|
||||||
|
if (connected) {
|
||||||
|
account.getAxolotlService().deleteOmemoIdentity();
|
||||||
|
}
|
||||||
for (final Conversation conversation : conversations) {
|
for (final Conversation conversation : conversations) {
|
||||||
if (conversation.getAccount() == account) {
|
if (conversation.getAccount() == account) {
|
||||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||||
|
if (connected) {
|
||||||
leaveMuc(conversation);
|
leaveMuc(conversation);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
conversations.remove(conversation);
|
conversations.remove(conversation);
|
||||||
mNotificationService.clear(conversation);
|
mNotificationService.clear(conversation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (account.getXmppConnection() != null) {
|
if (account.getXmppConnection() != null) {
|
||||||
new Thread(() -> disconnect(account, true)).start();
|
new Thread(() -> disconnect(account, !connected)).start();
|
||||||
}
|
}
|
||||||
final Runnable runnable = () -> {
|
final Runnable runnable = () -> {
|
||||||
if (!databaseBackend.deleteAccount(account)) {
|
if (!databaseBackend.deleteAccount(account)) {
|
||||||
|
@ -2607,10 +2667,19 @@ public class XmppConnectionService extends Service {
|
||||||
}
|
}
|
||||||
if (mucOptions.isPrivateAndNonAnonymous()) {
|
if (mucOptions.isPrivateAndNonAnonymous()) {
|
||||||
fetchConferenceMembers(conversation);
|
fetchConferenceMembers(conversation);
|
||||||
if (followedInvite && conversation.getBookmark() == null) {
|
|
||||||
|
if (followedInvite) {
|
||||||
|
final Bookmark bookmark = conversation.getBookmark();
|
||||||
|
if (bookmark != null) {
|
||||||
|
if (!bookmark.autojoin()) {
|
||||||
|
bookmark.setAutojoin(true);
|
||||||
|
createBookmark(account, bookmark);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
saveConversationAsBookmark(conversation, null);
|
saveConversationAsBookmark(conversation, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (mucOptions.push()) {
|
if (mucOptions.push()) {
|
||||||
enableMucPush(conversation);
|
enableMucPush(conversation);
|
||||||
}
|
}
|
||||||
|
@ -2761,10 +2830,11 @@ public class XmppConnectionService extends Service {
|
||||||
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
if (conversation.getMode() == Conversation.MODE_MULTI) {
|
||||||
conversation.getMucOptions().setPassword(password);
|
conversation.getMucOptions().setPassword(password);
|
||||||
if (conversation.getBookmark() != null) {
|
if (conversation.getBookmark() != null) {
|
||||||
|
final Bookmark bookmark = conversation.getBookmark();
|
||||||
if (synchronizeWithBookmarks()) {
|
if (synchronizeWithBookmarks()) {
|
||||||
conversation.getBookmark().setAutojoin(true);
|
bookmark.setAutojoin(true);
|
||||||
}
|
}
|
||||||
pushBookmarks(conversation.getAccount());
|
createBookmark(conversation.getAccount(), bookmark);
|
||||||
}
|
}
|
||||||
updateConversation(conversation);
|
updateConversation(conversation);
|
||||||
joinMuc(conversation);
|
joinMuc(conversation);
|
||||||
|
@ -2818,7 +2888,7 @@ public class XmppConnectionService extends Service {
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": persist nick '" + full.getResource() + "' into bookmark for " + conversation.getJid().asBareJid());
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": persist nick '" + full.getResource() + "' into bookmark for " + conversation.getJid().asBareJid());
|
||||||
bookmark.setNick(full.getResource());
|
bookmark.setNick(full.getResource());
|
||||||
pushBookmarks(bookmark.getAccount());
|
createBookmark(bookmark.getAccount(), bookmark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2853,7 +2923,7 @@ public class XmppConnectionService extends Service {
|
||||||
Bookmark bookmark = conversation.getBookmark();
|
Bookmark bookmark = conversation.getBookmark();
|
||||||
if (bookmark != null) {
|
if (bookmark != null) {
|
||||||
bookmark.setNick(nick);
|
bookmark.setNick(nick);
|
||||||
pushBookmarks(bookmark.getAccount());
|
createBookmark(bookmark.getAccount(), bookmark);
|
||||||
}
|
}
|
||||||
joinMuc(conversation);
|
joinMuc(conversation);
|
||||||
}
|
}
|
||||||
|
@ -3021,7 +3091,7 @@ public class XmppConnectionService extends Service {
|
||||||
|
|
||||||
if (bookmark != null && (sameBefore || bookmark.getBookmarkName() == null)) {
|
if (bookmark != null && (sameBefore || bookmark.getBookmarkName() == null)) {
|
||||||
if (bookmark.setBookmarkName(StringUtils.nullOnEmpty(mucOptions.getName()))) {
|
if (bookmark.setBookmarkName(StringUtils.nullOnEmpty(mucOptions.getName()))) {
|
||||||
pushBookmarks(account);
|
createBookmark(account, bookmark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3770,11 +3840,11 @@ public class XmppConnectionService extends Service {
|
||||||
|
|
||||||
|
|
||||||
public void markMessage(Message message, int status, String errorMessage) {
|
public void markMessage(Message message, int status, String errorMessage) {
|
||||||
final int c = message.getStatus();
|
final int oldStatus = message.getStatus();
|
||||||
if (status == Message.STATUS_SEND_FAILED && (c == Message.STATUS_SEND_RECEIVED || c == Message.STATUS_SEND_DISPLAYED)) {
|
if (status == Message.STATUS_SEND_FAILED && (oldStatus == Message.STATUS_SEND_RECEIVED || oldStatus == Message.STATUS_SEND_DISPLAYED)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (status == Message.STATUS_SEND_RECEIVED && c == Message.STATUS_SEND_DISPLAYED) {
|
if (status == Message.STATUS_SEND_RECEIVED && oldStatus == Message.STATUS_SEND_DISPLAYED) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
message.setErrorMessage(errorMessage);
|
message.setErrorMessage(errorMessage);
|
||||||
|
@ -4435,8 +4505,7 @@ public class XmppConnectionService extends Service {
|
||||||
bookmark.setBookmarkName(name);
|
bookmark.setBookmarkName(name);
|
||||||
}
|
}
|
||||||
bookmark.setAutojoin(getPreferences().getBoolean("autojoin", getResources().getBoolean(R.bool.autojoin)));
|
bookmark.setAutojoin(getPreferences().getBoolean("autojoin", getResources().getBoolean(R.bool.autojoin)));
|
||||||
account.getBookmarks().add(bookmark);
|
createBookmark(account, bookmark);
|
||||||
pushBookmarks(account);
|
|
||||||
bookmark.setConversation(conversation);
|
bookmark.setConversation(conversation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.databinding.ActivityChannelDiscoveryBinding;
|
import eu.siacs.conversations.databinding.ActivityChannelDiscoveryBinding;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
|
@ -217,20 +218,21 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void joinChannelSearchResult(String accountJid, MuclumbusService.Room result) {
|
public void joinChannelSearchResult(String selectedAccount, MuclumbusService.Room result) {
|
||||||
final boolean syncAutojoin = getBooleanPreference("autojoin", R.bool.autojoin);
|
final Jid jid = Config.DOMAIN_LOCK == null ? Jid.of(selectedAccount) : Jid.of(selectedAccount, Config.DOMAIN_LOCK, null);
|
||||||
Account account = xmppConnectionService.findAccountByJid(Jid.of(accountJid));
|
final boolean syncAutoJoin = getBooleanPreference("autojoin", R.bool.autojoin);
|
||||||
|
final Account account = xmppConnectionService.findAccountByJid(jid);
|
||||||
final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true);
|
final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, result.getRoom(), true, true, true);
|
||||||
if (conversation.getBookmark() != null) {
|
Bookmark bookmark = conversation.getBookmark();
|
||||||
if (!conversation.getBookmark().autojoin() && syncAutojoin) {
|
if (bookmark != null) {
|
||||||
conversation.getBookmark().setAutojoin(true);
|
if (!bookmark.autojoin() && syncAutoJoin) {
|
||||||
xmppConnectionService.pushBookmarks(account);
|
bookmark.setAutojoin(true);
|
||||||
|
xmppConnectionService.createBookmark(account, bookmark);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final Bookmark bookmark = new Bookmark(account, conversation.getJid().asBareJid());
|
bookmark = new Bookmark(account, conversation.getJid().asBareJid());
|
||||||
bookmark.setAutojoin(syncAutojoin);
|
bookmark.setAutojoin(syncAutoJoin);
|
||||||
account.getBookmarks().add(bookmark);
|
xmppConnectionService.createBookmark(account, bookmark);
|
||||||
xmppConnectionService.pushBookmarks(account);
|
|
||||||
}
|
}
|
||||||
switchToConversation(conversation);
|
switchToConversation(conversation);
|
||||||
}
|
}
|
||||||
|
|
|
@ -386,11 +386,10 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void deleteBookmark() {
|
protected void deleteBookmark() {
|
||||||
Account account = mConversation.getAccount();
|
final Account account = mConversation.getAccount();
|
||||||
Bookmark bookmark = mConversation.getBookmark();
|
final Bookmark bookmark = mConversation.getBookmark();
|
||||||
account.getBookmarks().remove(bookmark);
|
|
||||||
bookmark.setConversation(null);
|
bookmark.setConversation(null);
|
||||||
xmppConnectionService.pushBookmarks(account);
|
xmppConnectionService.deleteBookmark(account, bookmark);
|
||||||
updateView();
|
updateView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1044,6 +1044,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m.getStatus() == Message.STATUS_RECEIVED && t != null && (t.getStatus() == Transferable.STATUS_CANCELLED || t.getStatus() == Transferable.STATUS_FAILED)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final boolean deleted = m.isDeleted();
|
final boolean deleted = m.isDeleted();
|
||||||
final boolean encrypted = m.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED
|
final boolean encrypted = m.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED
|
||||||
|| m.getEncryption() == Message.ENCRYPTION_PGP;
|
|| m.getEncryption() == Message.ENCRYPTION_PGP;
|
||||||
|
|
|
@ -431,7 +431,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
||||||
bookmark.setConversation(conversation);
|
bookmark.setConversation(conversation);
|
||||||
if (!bookmark.autojoin() && getPreferences().getBoolean("autojoin", getResources().getBoolean(R.bool.autojoin))) {
|
if (!bookmark.autojoin() && getPreferences().getBoolean("autojoin", getResources().getBoolean(R.bool.autojoin))) {
|
||||||
bookmark.setAutojoin(true);
|
bookmark.setAutojoin(true);
|
||||||
xmppConnectionService.pushBookmarks(bookmark.getAccount());
|
xmppConnectionService.createBookmark(bookmark.getAccount(), bookmark);
|
||||||
}
|
}
|
||||||
SoftKeyboardUtils.hideSoftKeyboard(this);
|
SoftKeyboardUtils.hideSoftKeyboard(this);
|
||||||
switchToConversation(conversation);
|
switchToConversation(conversation);
|
||||||
|
@ -478,9 +478,8 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
||||||
builder.setMessage(JidDialog.style(this, R.string.remove_bookmark_text, bookmark.getJid().toEscapedString()));
|
builder.setMessage(JidDialog.style(this, R.string.remove_bookmark_text, bookmark.getJid().toEscapedString()));
|
||||||
builder.setPositiveButton(R.string.delete, (dialog, which) -> {
|
builder.setPositiveButton(R.string.delete, (dialog, which) -> {
|
||||||
bookmark.setConversation(null);
|
bookmark.setConversation(null);
|
||||||
Account account = bookmark.getAccount();
|
final Account account = bookmark.getAccount();
|
||||||
account.getBookmarks().remove(bookmark);
|
xmppConnectionService.deleteBookmark(account, bookmark);
|
||||||
xmppConnectionService.pushBookmarks(account);
|
|
||||||
filter(mSearchEditText.getText().toString());
|
filter(mSearchEditText.getText().toString());
|
||||||
});
|
});
|
||||||
builder.create().show();
|
builder.create().show();
|
||||||
|
@ -1041,8 +1040,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
||||||
if (nick != null && !nick.isEmpty() && !nick.equals(MucOptions.defaultNick(account))) {
|
if (nick != null && !nick.isEmpty() && !nick.equals(MucOptions.defaultNick(account))) {
|
||||||
bookmark.setNick(nick);
|
bookmark.setNick(nick);
|
||||||
}
|
}
|
||||||
account.getBookmarks().add(bookmark);
|
xmppConnectionService.createBookmark(account, bookmark);
|
||||||
xmppConnectionService.pushBookmarks(account);
|
|
||||||
final Conversation conversation = xmppConnectionService
|
final Conversation conversation = xmppConnectionService
|
||||||
.findOrCreateConversation(account, conferenceJid, true, true, true);
|
.findOrCreateConversation(account, conferenceJid, true, true, true);
|
||||||
bookmark.setConversation(conversation);
|
bookmark.setConversation(conversation);
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
package eu.siacs.conversations.ui.adapter;
|
package eu.siacs.conversations.ui.adapter;
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.databinding.DataBindingUtil;
|
import android.databinding.DataBindingUtil;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -20,6 +24,7 @@ import java.util.concurrent.RejectedExecutionException;
|
||||||
|
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.databinding.MediaPreviewBinding;
|
import eu.siacs.conversations.databinding.MediaPreviewBinding;
|
||||||
|
import eu.siacs.conversations.persistance.FileBackend;
|
||||||
import eu.siacs.conversations.ui.ConversationFragment;
|
import eu.siacs.conversations.ui.ConversationFragment;
|
||||||
import eu.siacs.conversations.ui.XmppActivity;
|
import eu.siacs.conversations.ui.XmppActivity;
|
||||||
import eu.siacs.conversations.ui.util.Attachment;
|
import eu.siacs.conversations.ui.util.Attachment;
|
||||||
|
@ -54,11 +59,24 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
|
||||||
MediaAdapter.renderPreview(context, attachment, holder.binding.mediaPreview);
|
MediaAdapter.renderPreview(context, attachment, holder.binding.mediaPreview);
|
||||||
}
|
}
|
||||||
holder.binding.deleteButton.setOnClickListener(v -> {
|
holder.binding.deleteButton.setOnClickListener(v -> {
|
||||||
int pos = mediaPreviews.indexOf(attachment);
|
final int pos = mediaPreviews.indexOf(attachment);
|
||||||
mediaPreviews.remove(pos);
|
mediaPreviews.remove(pos);
|
||||||
notifyItemRemoved(pos);
|
notifyItemRemoved(pos);
|
||||||
conversationFragment.toggleInputMethod();
|
conversationFragment.toggleInputMethod();
|
||||||
});
|
});
|
||||||
|
holder.binding.mediaPreview.setOnClickListener(v -> view(context, attachment));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void view(final Context context, Attachment attachment) {
|
||||||
|
final Intent view = new Intent(Intent.ACTION_VIEW);
|
||||||
|
final Uri uri = FileBackend.getUriForUri(context, attachment.getUri());
|
||||||
|
view.setDataAndType(uri, attachment.getMime());
|
||||||
|
view.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
try {
|
||||||
|
context.startActivity(view);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Toast.makeText(context, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addMediaPreviews(List<Attachment> attachments) {
|
public void addMediaPreviews(List<Attachment> attachments) {
|
||||||
|
|
|
@ -187,7 +187,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
if (message.isFileOrImage() || transferable != null) {
|
if (message.isFileOrImage() || transferable != null) {
|
||||||
FileParams params = message.getFileParams();
|
FileParams params = message.getFileParams();
|
||||||
filesize = params.size > 0 ? UIHelper.filesizeToString(params.size) : null;
|
filesize = params.size > 0 ? UIHelper.filesizeToString(params.size) : null;
|
||||||
if (transferable != null && transferable.getStatus() == Transferable.STATUS_FAILED) {
|
if (transferable != null && (transferable.getStatus() == Transferable.STATUS_FAILED || transferable.getStatus() == Transferable.STATUS_CANCELLED)) {
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,10 +206,6 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
info = getContext().getString(R.string.offering);
|
info = getContext().getString(R.string.offering);
|
||||||
break;
|
break;
|
||||||
case Message.STATUS_SEND_RECEIVED:
|
case Message.STATUS_SEND_RECEIVED:
|
||||||
if (mIndicateReceived) {
|
|
||||||
viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Message.STATUS_SEND_DISPLAYED:
|
case Message.STATUS_SEND_DISPLAYED:
|
||||||
if (mIndicateReceived) {
|
if (mIndicateReceived) {
|
||||||
viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
|
viewHolder.indicatorReceived.setVisibility(View.VISIBLE);
|
||||||
|
|
|
@ -53,11 +53,10 @@ public class ShareUtil {
|
||||||
if (message.isGeoUri()) {
|
if (message.isGeoUri()) {
|
||||||
shareIntent.putExtra(Intent.EXTRA_TEXT, message.getBody());
|
shareIntent.putExtra(Intent.EXTRA_TEXT, message.getBody());
|
||||||
shareIntent.setType("text/plain");
|
shareIntent.setType("text/plain");
|
||||||
shareIntent.putExtra(ConversationsActivity.EXTRA_AS_QUOTE, true);
|
|
||||||
} else if (!message.isFileOrImage()) {
|
} else if (!message.isFileOrImage()) {
|
||||||
shareIntent.putExtra(Intent.EXTRA_TEXT, message.getMergedBody().toString());
|
shareIntent.putExtra(Intent.EXTRA_TEXT, message.getMergedBody().toString());
|
||||||
shareIntent.setType("text/plain");
|
shareIntent.setType("text/plain");
|
||||||
shareIntent.putExtra(ConversationsActivity.EXTRA_AS_QUOTE, true);
|
shareIntent.putExtra(ConversationsActivity.EXTRA_AS_QUOTE, message.getStatus() == Message.STATUS_RECEIVED);
|
||||||
} else {
|
} else {
|
||||||
final DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
|
final DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message);
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -14,23 +14,25 @@ public class SocksSocketFactory {
|
||||||
|
|
||||||
private static final byte[] LOCALHOST = new byte[]{127,0,0,1};
|
private static final byte[] LOCALHOST = new byte[]{127,0,0,1};
|
||||||
|
|
||||||
public static void createSocksConnection(Socket socket, String destination, int port) throws IOException {
|
public static void createSocksConnection(final Socket socket, final String destination, final int port) throws IOException {
|
||||||
InputStream proxyIs = socket.getInputStream();
|
final InputStream proxyIs = socket.getInputStream();
|
||||||
OutputStream proxyOs = socket.getOutputStream();
|
final OutputStream proxyOs = socket.getOutputStream();
|
||||||
proxyOs.write(new byte[]{0x05, 0x01, 0x00});
|
proxyOs.write(new byte[]{0x05, 0x01, 0x00});
|
||||||
byte[] response = new byte[2];
|
proxyOs.flush();
|
||||||
proxyIs.read(response);
|
final byte[] handshake = new byte[2];
|
||||||
if (response[0] != 0x05 || response[1] != 0x00) {
|
proxyIs.read(handshake);
|
||||||
|
if (handshake[0] != 0x05 || handshake[1] != 0x00) {
|
||||||
throw new SocksConnectionException("Socks 5 handshake failed");
|
throw new SocksConnectionException("Socks 5 handshake failed");
|
||||||
}
|
}
|
||||||
byte[] dest = destination.getBytes();
|
final byte[] dest = destination.getBytes();
|
||||||
ByteBuffer request = ByteBuffer.allocate(7 + dest.length);
|
final ByteBuffer request = ByteBuffer.allocate(7 + dest.length);
|
||||||
request.put(new byte[]{0x05, 0x01, 0x00, 0x03});
|
request.put(new byte[]{0x05, 0x01, 0x00, 0x03});
|
||||||
request.put((byte) dest.length);
|
request.put((byte) dest.length);
|
||||||
request.put(dest);
|
request.put(dest);
|
||||||
request.putShort((short) port);
|
request.putShort((short) port);
|
||||||
proxyOs.write(request.array());
|
proxyOs.write(request.array());
|
||||||
response = new byte[7 + dest.length];
|
proxyOs.flush();
|
||||||
|
final byte[] response = new byte[7 + dest.length];
|
||||||
proxyIs.read(response);
|
proxyIs.read(response);
|
||||||
if (response[1] != 0x00) {
|
if (response[1] != 0x00) {
|
||||||
if (response[1] == 0x04) {
|
if (response[1] == 0x04) {
|
||||||
|
@ -52,7 +54,7 @@ public class SocksSocketFactory {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Socket createSocket(InetSocketAddress address, String destination, int port) throws IOException {
|
private static Socket createSocket(InetSocketAddress address, String destination, int port) throws IOException {
|
||||||
Socket socket = new Socket();
|
Socket socket = new Socket();
|
||||||
try {
|
try {
|
||||||
socket.connect(address, Config.CONNECT_TIMEOUT * 1000);
|
socket.connect(address, Config.CONNECT_TIMEOUT * 1000);
|
||||||
|
|
|
@ -274,6 +274,8 @@ public class UIHelper {
|
||||||
getFileDescriptionString(context, message)), true);
|
getFileDescriptionString(context, message)), true);
|
||||||
case Transferable.STATUS_FAILED:
|
case Transferable.STATUS_FAILED:
|
||||||
return new Pair<>(context.getString(R.string.file_transmission_failed), true);
|
return new Pair<>(context.getString(R.string.file_transmission_failed), true);
|
||||||
|
case Transferable.STATUS_CANCELLED:
|
||||||
|
return new Pair<>(context.getString(R.string.file_transmission_cancelled), true);
|
||||||
case Transferable.STATUS_UPLOADING:
|
case Transferable.STATUS_UPLOADING:
|
||||||
if (message.getStatus() == Message.STATUS_OFFERED) {
|
if (message.getStatus() == Message.STATUS_OFFERED) {
|
||||||
return new Pair<>(context.getString(R.string.offering_x_file,
|
return new Pair<>(context.getString(R.string.offering_x_file,
|
||||||
|
|
|
@ -33,4 +33,6 @@ public final class Namespace {
|
||||||
public static final String JINGLE_ENCRYPTED_TRANSPORT = "urn:xmpp:jingle:jet:0";
|
public static final String JINGLE_ENCRYPTED_TRANSPORT = "urn:xmpp:jingle:jet:0";
|
||||||
public static final String JINGLE_ENCRYPTED_TRANSPORT_OMEMO = "urn:xmpp:jingle:jet-omemo:0";
|
public static final String JINGLE_ENCRYPTED_TRANSPORT_OMEMO = "urn:xmpp:jingle:jet-omemo:0";
|
||||||
public static final String MUC_USER = "http://jabber.org/protocol/muc#user";
|
public static final String MUC_USER = "http://jabber.org/protocol/muc#user";
|
||||||
|
public static final String BOOKMARKS2 = "urn:xmpp:bookmarks:0";
|
||||||
|
public static final String BOOKMARKS2_COMPAT = BOOKMARKS2+"#compat";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1857,5 +1857,9 @@ public class XmppConnection implements Runnable {
|
||||||
public boolean stanzaIds() {
|
public boolean stanzaIds() {
|
||||||
return hasDiscoFeature(account.getJid().asBareJid(), Namespace.STANZA_IDS);
|
return hasDiscoFeature(account.getJid().asBareJid(), Namespace.STANZA_IDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean bookmarks2() {
|
||||||
|
return Config.USE_BOOKMARKS2 /* || hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT)*/;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,7 +142,7 @@ public class JingleConnection implements Transferable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFileTransferAborted() {
|
public void onFileTransferAborted() {
|
||||||
JingleConnection.this.sendCancel();
|
JingleConnection.this.sendSessionTerminate("connectivity-error");
|
||||||
JingleConnection.this.fail();
|
JingleConnection.this.fail();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -222,27 +222,32 @@ public class JingleConnection implements Transferable {
|
||||||
return this.message.getCounterpart();
|
return this.message.getCounterpart();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deliverPacket(JinglePacket packet) {
|
void deliverPacket(JinglePacket packet) {
|
||||||
boolean returnResult = true;
|
|
||||||
if (packet.isAction("session-terminate")) {
|
if (packet.isAction("session-terminate")) {
|
||||||
Reason reason = packet.getReason();
|
Reason reason = packet.getReason();
|
||||||
if (reason != null) {
|
if (reason != null) {
|
||||||
if (reason.hasChild("cancel")) {
|
if (reason.hasChild("cancel")) {
|
||||||
|
this.cancelled = true;
|
||||||
this.fail();
|
this.fail();
|
||||||
} else if (reason.hasChild("success")) {
|
} else if (reason.hasChild("success")) {
|
||||||
this.receiveSuccess();
|
this.receiveSuccess();
|
||||||
|
} else {
|
||||||
|
final List<Element> children = reason.getChildren();
|
||||||
|
if (children.size() == 1) {
|
||||||
|
this.fail(children.get(0).getName());
|
||||||
} else {
|
} else {
|
||||||
this.fail();
|
this.fail();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.fail();
|
this.fail();
|
||||||
}
|
}
|
||||||
} else if (packet.isAction("session-accept")) {
|
} else if (packet.isAction("session-accept")) {
|
||||||
returnResult = receiveAccept(packet);
|
receiveAccept(packet);
|
||||||
} else if (packet.isAction("session-info")) {
|
} else if (packet.isAction("session-info")) {
|
||||||
Element checksum = packet.getChecksum();
|
final Element checksum = packet.getChecksum();
|
||||||
Element file = checksum == null ? null : checksum.findChild("file");
|
final Element file = checksum == null ? null : checksum.findChild("file");
|
||||||
Element hash = file == null ? null : file.findChild("hash", "urn:xmpp:hashes:2");
|
final Element hash = file == null ? null : file.findChild("hash", "urn:xmpp:hashes:2");
|
||||||
if (hash != null && "sha-1".equalsIgnoreCase(hash.getAttribute("algo"))) {
|
if (hash != null && "sha-1".equalsIgnoreCase(hash.getAttribute("algo"))) {
|
||||||
try {
|
try {
|
||||||
this.expectedHash = Base64.decode(hash.getContent(), Base64.DEFAULT);
|
this.expectedHash = Base64.decode(hash.getContent(), Base64.DEFAULT);
|
||||||
|
@ -250,33 +255,44 @@ public class JingleConnection implements Transferable {
|
||||||
this.expectedHash = new byte[0];
|
this.expectedHash = new byte[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
respondToIq(packet, true);
|
||||||
} else if (packet.isAction("transport-info")) {
|
} else if (packet.isAction("transport-info")) {
|
||||||
returnResult = receiveTransportInfo(packet);
|
receiveTransportInfo(packet);
|
||||||
} else if (packet.isAction("transport-replace")) {
|
} else if (packet.isAction("transport-replace")) {
|
||||||
if (packet.getJingleContent().hasIbbTransport()) {
|
if (packet.getJingleContent().hasIbbTransport()) {
|
||||||
returnResult = this.receiveFallbackToIbb(packet);
|
receiveFallbackToIbb(packet);
|
||||||
} else {
|
} else {
|
||||||
returnResult = false;
|
Log.d(Config.LOGTAG, "trying to fallback to something unknown" + packet.toString());
|
||||||
Log.d(Config.LOGTAG, "trying to fallback to something unknown"
|
respondToIq(packet, false);
|
||||||
+ packet.toString());
|
|
||||||
}
|
}
|
||||||
} else if (packet.isAction("transport-accept")) {
|
} else if (packet.isAction("transport-accept")) {
|
||||||
returnResult = this.receiveTransportAccept(packet);
|
receiveTransportAccept(packet);
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, "packet arrived in connection. action was "
|
Log.d(Config.LOGTAG, "packet arrived in connection. action was " + packet.getAction());
|
||||||
+ packet.getAction());
|
respondToIq(packet, false);
|
||||||
returnResult = false;
|
}
|
||||||
}
|
}
|
||||||
IqPacket response;
|
|
||||||
if (returnResult) {
|
|
||||||
response = packet.generateResponse(IqPacket.TYPE.RESULT);
|
|
||||||
|
|
||||||
|
private void respondToIq(final IqPacket packet, final boolean result) {
|
||||||
|
final IqPacket response;
|
||||||
|
if (result) {
|
||||||
|
response = packet.generateResponse(IqPacket.TYPE.RESULT);
|
||||||
} else {
|
} else {
|
||||||
response = packet.generateResponse(IqPacket.TYPE.ERROR);
|
response = packet.generateResponse(IqPacket.TYPE.ERROR);
|
||||||
|
final Element error = response.addChild("error").setAttribute("type", "cancel");
|
||||||
|
error.addChild("not-acceptable", "urn:ietf:params:xml:ns:xmpp-stanzas");
|
||||||
}
|
}
|
||||||
mXmppConnectionService.sendIqPacket(account, response, null);
|
mXmppConnectionService.sendIqPacket(account, response, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void respondToIqWithOutOfOrder(final IqPacket packet) {
|
||||||
|
final IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
|
||||||
|
final Element error = response.addChild("error").setAttribute("type", "wait");
|
||||||
|
error.addChild("unexpected-request", "urn:ietf:params:xml:ns:xmpp-stanzas");
|
||||||
|
error.addChild("out-of-order", "urn:xmpp:jingle:errors:1");
|
||||||
|
mXmppConnectionService.sendIqPacket(account, response, null);
|
||||||
|
}
|
||||||
|
|
||||||
public void init(final Message message) {
|
public void init(final Message message) {
|
||||||
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
|
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
|
||||||
Conversation conversation = (Conversation) message.getConversation();
|
Conversation conversation = (Conversation) message.getConversation();
|
||||||
|
@ -320,7 +336,7 @@ public class JingleConnection implements Transferable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failed() {
|
public void failed() {
|
||||||
Log.d(Config.LOGTAG, "connection to our own proxy65 candidate failed");
|
Log.d(Config.LOGTAG, String.format("connection to our own proxy65 candidate failed (%s:%d)", candidate.getHost(), candidate.getPort()));
|
||||||
sendInitRequest();
|
sendInitRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,7 +416,6 @@ public class JingleConnection implements Transferable {
|
||||||
this.contentName = content.getAttribute("name");
|
this.contentName = content.getAttribute("name");
|
||||||
this.transportId = content.getTransportId();
|
this.transportId = content.getTransportId();
|
||||||
|
|
||||||
mXmppConnectionService.sendIqPacket(account, packet.generateResponse(IqPacket.TYPE.RESULT), null);
|
|
||||||
|
|
||||||
if (this.initialTransport == Transport.SOCKS) {
|
if (this.initialTransport == Transport.SOCKS) {
|
||||||
this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
|
this.mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
|
||||||
|
@ -411,20 +426,20 @@ public class JingleConnection implements Transferable {
|
||||||
this.ibbBlockSize = Math.min(Integer.parseInt(receivedBlockSize), this.ibbBlockSize);
|
this.ibbBlockSize = Math.min(Integer.parseInt(receivedBlockSize), this.ibbBlockSize);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
Log.d(Config.LOGTAG, "number format exception " + e.getMessage());
|
Log.d(Config.LOGTAG, "number format exception " + e.getMessage());
|
||||||
this.sendCancel();
|
respondToIq(packet, false);
|
||||||
this.fail();
|
this.fail();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, "received block size was null");
|
Log.d(Config.LOGTAG, "received block size was null");
|
||||||
this.sendCancel();
|
respondToIq(packet, false);
|
||||||
this.fail();
|
this.fail();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.ftVersion = content.getVersion();
|
this.ftVersion = content.getVersion();
|
||||||
if (ftVersion == null) {
|
if (ftVersion == null) {
|
||||||
this.sendCancel();
|
respondToIq(packet, false);
|
||||||
this.fail();
|
this.fail();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -486,6 +501,9 @@ public class JingleConnection implements Transferable {
|
||||||
//JET reports the plain text size. however lower levels of our receiving code still
|
//JET reports the plain text size. however lower levels of our receiving code still
|
||||||
//expect the cipher text size. so we just + 16 bytes (auth tag size) here
|
//expect the cipher text size. so we just + 16 bytes (auth tag size) here
|
||||||
this.file.setExpectedSize(size + (remoteIsUsingJet ? 16 : 0));
|
this.file.setExpectedSize(size + (remoteIsUsingJet ? 16 : 0));
|
||||||
|
|
||||||
|
respondToIq(packet, true);
|
||||||
|
|
||||||
if (mJingleConnectionManager.hasStoragePermission()
|
if (mJingleConnectionManager.hasStoragePermission()
|
||||||
&& size < this.mJingleConnectionManager.getAutoAcceptFileSize()
|
&& size < this.mJingleConnectionManager.getAutoAcceptFileSize()
|
||||||
&& mXmppConnectionService.isDataSaverDisabled()) {
|
&& mXmppConnectionService.isDataSaverDisabled()) {
|
||||||
|
@ -503,13 +521,9 @@ public class JingleConnection implements Transferable {
|
||||||
this.mXmppConnectionService.getNotificationService().push(message);
|
this.mXmppConnectionService.getNotificationService().push(message);
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize());
|
Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize());
|
||||||
} else {
|
return;
|
||||||
this.sendCancel();
|
|
||||||
this.fail();
|
|
||||||
}
|
}
|
||||||
} else {
|
respondToIq(packet, false);
|
||||||
this.sendCancel();
|
|
||||||
this.fail();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,14 +571,17 @@ public class JingleConnection implements Transferable {
|
||||||
try {
|
try {
|
||||||
this.mFileInputStream = new FileInputStream(file);
|
this.mFileInputStream = new FileInputStream(file);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
abort();
|
fail(e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
content.setTransportId(this.transportId);
|
content.setTransportId(this.transportId);
|
||||||
if (this.initialTransport == Transport.IBB) {
|
if (this.initialTransport == Transport.IBB) {
|
||||||
content.ibbTransport().setAttribute("block-size", Integer.toString(this.ibbBlockSize));
|
content.ibbTransport().setAttribute("block-size", Integer.toString(this.ibbBlockSize));
|
||||||
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": sending IBB offer");
|
||||||
} else {
|
} else {
|
||||||
content.socks5transport().setChildren(getCandidatesAsElements());
|
final List<Element> candidates = getCandidatesAsElements();
|
||||||
|
Log.d(Config.LOGTAG, String.format("%s: sending S5B offer with %d candidates", account.getJid().asBareJid(), candidates.size()));
|
||||||
|
content.socks5transport().setChildren(candidates);
|
||||||
}
|
}
|
||||||
packet.setContent(content);
|
packet.setContent(content);
|
||||||
this.sendJinglePacket(packet, (account, response) -> {
|
this.sendJinglePacket(packet, (account, response) -> {
|
||||||
|
@ -682,18 +699,19 @@ public class JingleConnection implements Transferable {
|
||||||
mXmppConnectionService.sendIqPacket(account, packet, callback);
|
mXmppConnectionService.sendIqPacket(account, packet, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean receiveAccept(JinglePacket packet) {
|
private void receiveAccept(JinglePacket packet) {
|
||||||
if (this.mJingleStatus != JINGLE_STATUS_INITIATED) {
|
if (this.mJingleStatus != JINGLE_STATUS_INITIATED) {
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received out of order session-accept");
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received out of order session-accept");
|
||||||
return false;
|
respondToIqWithOutOfOrder(packet);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
this.mJingleStatus = JINGLE_STATUS_ACCEPTED;
|
this.mJingleStatus = JINGLE_STATUS_ACCEPTED;
|
||||||
mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
|
mXmppConnectionService.markMessage(message, Message.STATUS_UNSEND);
|
||||||
Content content = packet.getJingleContent();
|
Content content = packet.getJingleContent();
|
||||||
if (content.hasSocks5Transport()) {
|
if (content.hasSocks5Transport()) {
|
||||||
|
respondToIq(packet, true);
|
||||||
mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
|
mergeCandidates(JingleCandidate.parse(content.socks5transport().getChildren()));
|
||||||
this.connectNextCandidate();
|
this.connectNextCandidate();
|
||||||
return true;
|
|
||||||
} else if (content.hasIbbTransport()) {
|
} else if (content.hasIbbTransport()) {
|
||||||
String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size");
|
String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size");
|
||||||
if (receivedBlockSize != null) {
|
if (receivedBlockSize != null) {
|
||||||
|
@ -706,18 +724,19 @@ public class JingleConnection implements Transferable {
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to parse block size in session-accept");
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to parse block size in session-accept");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
respondToIq(packet, true);
|
||||||
this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
|
this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
|
||||||
this.transport.connect(onIbbTransportConnected);
|
this.transport.connect(onIbbTransportConnected);
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
respondToIq(packet, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean receiveTransportInfo(JinglePacket packet) {
|
private void receiveTransportInfo(JinglePacket packet) {
|
||||||
Content content = packet.getJingleContent();
|
final Content content = packet.getJingleContent();
|
||||||
if (content.hasSocks5Transport()) {
|
if (content.hasSocks5Transport()) {
|
||||||
if (content.socks5transport().hasChild("activated")) {
|
if (content.socks5transport().hasChild("activated")) {
|
||||||
|
respondToIq(packet, true);
|
||||||
if ((this.transport != null) && (this.transport instanceof JingleSocks5Transport)) {
|
if ((this.transport != null) && (this.transport instanceof JingleSocks5Transport)) {
|
||||||
onProxyActivated.success();
|
onProxyActivated.success();
|
||||||
} else {
|
} else {
|
||||||
|
@ -729,21 +748,20 @@ public class JingleConnection implements Transferable {
|
||||||
connection.setActivated(true);
|
connection.setActivated(true);
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, "activated connection not found");
|
Log.d(Config.LOGTAG, "activated connection not found");
|
||||||
this.sendCancel();
|
sendSessionTerminate("failed-transport");
|
||||||
this.fail();
|
this.fail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
} else if (content.socks5transport().hasChild("proxy-error")) {
|
} else if (content.socks5transport().hasChild("proxy-error")) {
|
||||||
|
respondToIq(packet, true);
|
||||||
onProxyActivated.failed();
|
onProxyActivated.failed();
|
||||||
return true;
|
|
||||||
} else if (content.socks5transport().hasChild("candidate-error")) {
|
} else if (content.socks5transport().hasChild("candidate-error")) {
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received candidate error");
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received candidate error");
|
||||||
|
respondToIq(packet, true);
|
||||||
this.receivedCandidate = true;
|
this.receivedCandidate = true;
|
||||||
if (mJingleStatus == JINGLE_STATUS_ACCEPTED && this.sentCandidate) {
|
if (mJingleStatus == JINGLE_STATUS_ACCEPTED && this.sentCandidate) {
|
||||||
this.connect();
|
this.connect();
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
} else if (content.socks5transport().hasChild("candidate-used")) {
|
} else if (content.socks5transport().hasChild("candidate-used")) {
|
||||||
String cid = content.socks5transport().findChild("candidate-used").getAttribute("cid");
|
String cid = content.socks5transport().findChild("candidate-used").getAttribute("cid");
|
||||||
if (cid != null) {
|
if (cid != null) {
|
||||||
|
@ -751,8 +769,10 @@ public class JingleConnection implements Transferable {
|
||||||
JingleCandidate candidate = getCandidate(cid);
|
JingleCandidate candidate = getCandidate(cid);
|
||||||
if (candidate == null) {
|
if (candidate == null) {
|
||||||
Log.d(Config.LOGTAG, "could not find candidate with cid=" + cid);
|
Log.d(Config.LOGTAG, "could not find candidate with cid=" + cid);
|
||||||
return false;
|
respondToIq(packet, false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
respondToIq(packet, true);
|
||||||
candidate.flagAsUsedByCounterpart();
|
candidate.flagAsUsedByCounterpart();
|
||||||
this.receivedCandidate = true;
|
this.receivedCandidate = true;
|
||||||
if (mJingleStatus == JINGLE_STATUS_ACCEPTED && this.sentCandidate) {
|
if (mJingleStatus == JINGLE_STATUS_ACCEPTED && this.sentCandidate) {
|
||||||
|
@ -760,15 +780,14 @@ public class JingleConnection implements Transferable {
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, "ignoring because file is already in transmission or we haven't sent our candidate yet status=" + mJingleStatus + " sentCandidate=" + sentCandidate);
|
Log.d(Config.LOGTAG, "ignoring because file is already in transmission or we haven't sent our candidate yet status=" + mJingleStatus + " sentCandidate=" + sentCandidate);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
respondToIq(packet, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
respondToIq(packet, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return true;
|
respondToIq(packet, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -867,11 +886,7 @@ public class JingleConnection implements Transferable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendSuccess() {
|
private void sendSuccess() {
|
||||||
JinglePacket packet = bootstrapPacket("session-terminate");
|
sendSessionTerminate("success");
|
||||||
Reason reason = new Reason();
|
|
||||||
reason.addChild("success");
|
|
||||||
packet.setReason(reason);
|
|
||||||
this.sendJinglePacket(packet);
|
|
||||||
this.disconnectSocks5Connections();
|
this.disconnectSocks5Connections();
|
||||||
this.mJingleStatus = JINGLE_STATUS_FINISHED;
|
this.mJingleStatus = JINGLE_STATUS_FINISHED;
|
||||||
this.message.setStatus(Message.STATUS_RECEIVED);
|
this.message.setStatus(Message.STATUS_RECEIVED);
|
||||||
|
@ -893,7 +908,7 @@ public class JingleConnection implements Transferable {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean receiveFallbackToIbb(JinglePacket packet) {
|
private void receiveFallbackToIbb(JinglePacket packet) {
|
||||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": receiving fallback to ibb");
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": receiving fallback to ibb");
|
||||||
final String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size");
|
final String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size");
|
||||||
if (receivedBlockSize != null) {
|
if (receivedBlockSize != null) {
|
||||||
|
@ -916,6 +931,7 @@ public class JingleConnection implements Transferable {
|
||||||
content.ibbTransport().setAttribute("sid", this.transportId);
|
content.ibbTransport().setAttribute("sid", this.transportId);
|
||||||
answer.setContent(content);
|
answer.setContent(content);
|
||||||
|
|
||||||
|
respondToIq(packet, true);
|
||||||
|
|
||||||
if (initiating()) {
|
if (initiating()) {
|
||||||
this.sendJinglePacket(answer, (account, response) -> {
|
this.sendJinglePacket(answer, (account, response) -> {
|
||||||
|
@ -928,13 +944,13 @@ public class JingleConnection implements Transferable {
|
||||||
this.transport.receive(file, onFileTransmissionStatusChanged);
|
this.transport.receive(file, onFileTransmissionStatusChanged);
|
||||||
this.sendJinglePacket(answer);
|
this.sendJinglePacket(answer);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean receiveTransportAccept(JinglePacket packet) {
|
private void receiveTransportAccept(JinglePacket packet) {
|
||||||
if (packet.getJingleContent().hasIbbTransport()) {
|
if (packet.getJingleContent().hasIbbTransport()) {
|
||||||
String receivedBlockSize = packet.getJingleContent().ibbTransport()
|
final Element ibbTransport = packet.getJingleContent().ibbTransport();
|
||||||
.getAttribute("block-size");
|
final String receivedBlockSize = ibbTransport.getAttribute("block-size");
|
||||||
|
final String sid = ibbTransport.getAttribute("sid");
|
||||||
if (receivedBlockSize != null) {
|
if (receivedBlockSize != null) {
|
||||||
try {
|
try {
|
||||||
int bs = Integer.parseInt(receivedBlockSize);
|
int bs = Integer.parseInt(receivedBlockSize);
|
||||||
|
@ -947,15 +963,19 @@ public class JingleConnection implements Transferable {
|
||||||
}
|
}
|
||||||
this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
|
this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
|
||||||
|
|
||||||
|
if (sid == null || !sid.equals(this.transportId)) {
|
||||||
|
Log.w(Config.LOGTAG, String.format("%s: sid in transport-accept (%s) did not match our sid (%s) ", account.getJid().asBareJid(), sid, transportId));
|
||||||
|
}
|
||||||
|
respondToIq(packet, true);
|
||||||
//might be receive instead if we are not initiating
|
//might be receive instead if we are not initiating
|
||||||
if (initiating()) {
|
if (initiating()) {
|
||||||
this.transport.connect(onIbbTransportConnected);
|
this.transport.connect(onIbbTransportConnected);
|
||||||
} else {
|
} else {
|
||||||
this.transport.receive(file, onFileTransmissionStatusChanged);
|
this.transport.receive(file, onFileTransmissionStatusChanged);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received invalid transport-accept");
|
||||||
|
respondToIq(packet, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -977,18 +997,18 @@ public class JingleConnection implements Transferable {
|
||||||
@Override
|
@Override
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
this.cancelled = true;
|
this.cancelled = true;
|
||||||
abort();
|
abort("cancel");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void abort() {
|
void abort(final String reason) {
|
||||||
this.disconnectSocks5Connections();
|
this.disconnectSocks5Connections();
|
||||||
if (this.transport instanceof JingleInbandTransport) {
|
if (this.transport instanceof JingleInbandTransport) {
|
||||||
this.transport.disconnect();
|
this.transport.disconnect();
|
||||||
}
|
}
|
||||||
this.sendCancel();
|
sendSessionTerminate(reason);
|
||||||
this.mJingleConnectionManager.finishConnection(this);
|
this.mJingleConnectionManager.finishConnection(this);
|
||||||
if (responding()) {
|
if (responding()) {
|
||||||
this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED));
|
this.message.setTransferable(new TransferablePlaceholder(cancelled ? Transferable.STATUS_CANCELLED : Transferable.STATUS_FAILED));
|
||||||
if (this.file != null) {
|
if (this.file != null) {
|
||||||
file.delete();
|
file.delete();
|
||||||
}
|
}
|
||||||
|
@ -1013,7 +1033,7 @@ public class JingleConnection implements Transferable {
|
||||||
FileBackend.close(mFileOutputStream);
|
FileBackend.close(mFileOutputStream);
|
||||||
if (this.message != null) {
|
if (this.message != null) {
|
||||||
if (responding()) {
|
if (responding()) {
|
||||||
this.message.setTransferable(new TransferablePlaceholder(Transferable.STATUS_FAILED));
|
this.message.setTransferable(new TransferablePlaceholder(cancelled ? Transferable.STATUS_CANCELLED : Transferable.STATUS_FAILED));
|
||||||
if (this.file != null) {
|
if (this.file != null) {
|
||||||
file.delete();
|
file.delete();
|
||||||
}
|
}
|
||||||
|
@ -1028,11 +1048,11 @@ public class JingleConnection implements Transferable {
|
||||||
this.mJingleConnectionManager.finishConnection(this);
|
this.mJingleConnectionManager.finishConnection(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendCancel() {
|
private void sendSessionTerminate(String reason) {
|
||||||
JinglePacket packet = bootstrapPacket("session-terminate");
|
final JinglePacket packet = bootstrapPacket("session-terminate");
|
||||||
Reason reason = new Reason();
|
final Reason r = new Reason();
|
||||||
reason.addChild("cancel");
|
r.addChild(reason);
|
||||||
packet.setReason(reason);
|
packet.setReason(r);
|
||||||
this.sendJinglePacket(packet);
|
this.sendJinglePacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
||||||
candidate.setPort(Integer.parseInt(port));
|
candidate.setPort(Integer.parseInt(port));
|
||||||
candidate.setType(JingleCandidate.TYPE_PROXY);
|
candidate.setType(JingleCandidate.TYPE_PROXY);
|
||||||
candidate.setJid(proxy);
|
candidate.setJid(proxy);
|
||||||
candidate.setPriority(655360 + (initiator ? 10 : 20));
|
candidate.setPriority(655360 + (initiator ? 30 : 0));
|
||||||
primaryCandidates.put(account.getJid().asBareJid(),candidate);
|
primaryCandidates.put(account.getJid().asBareJid(),candidate);
|
||||||
listener.onPrimaryCandidateFound(true,candidate);
|
listener.onPrimaryCandidateFound(true,candidate);
|
||||||
} catch (final NumberFormatException e) {
|
} catch (final NumberFormatException e) {
|
||||||
|
@ -166,7 +166,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
||||||
public void cancelInTransmission() {
|
public void cancelInTransmission() {
|
||||||
for (JingleConnection connection : this.connections) {
|
for (JingleConnection connection : this.connections) {
|
||||||
if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
|
if (connection.getJingleStatus() == JingleConnection.JINGLE_STATUS_TRANSMITTING) {
|
||||||
connection.abort();
|
connection.abort("connectivity-error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,10 +48,15 @@ public class JingleInbandTransport extends JingleTransport {
|
||||||
private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() {
|
private OnIqPacketReceived onAckReceived = new OnIqPacketReceived() {
|
||||||
@Override
|
@Override
|
||||||
public void onIqPacketReceived(Account account, IqPacket packet) {
|
public void onIqPacketReceived(Account account, IqPacket packet) {
|
||||||
if (connected && packet.getType() == IqPacket.TYPE.RESULT) {
|
if (!connected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (packet.getType() == IqPacket.TYPE.RESULT) {
|
||||||
if (remainingSize > 0) {
|
if (remainingSize > 0) {
|
||||||
sendNextBlock();
|
sendNextBlock();
|
||||||
}
|
}
|
||||||
|
} else if (packet.getType() == IqPacket.TYPE.ERROR) {
|
||||||
|
onFileTransmissionStatusChanged.onFileTransferAborted();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -80,18 +85,12 @@ public class JingleInbandTransport extends JingleTransport {
|
||||||
open.setAttribute("stanza", "iq");
|
open.setAttribute("stanza", "iq");
|
||||||
open.setAttribute("block-size", Integer.toString(this.blockSize));
|
open.setAttribute("block-size", Integer.toString(this.blockSize));
|
||||||
this.connected = true;
|
this.connected = true;
|
||||||
this.account.getXmppConnection().sendIqPacket(iq,
|
this.account.getXmppConnection().sendIqPacket(iq, (account, packet) -> {
|
||||||
new OnIqPacketReceived() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onIqPacketReceived(Account account,
|
|
||||||
IqPacket packet) {
|
|
||||||
if (packet.getType() != IqPacket.TYPE.RESULT) {
|
if (packet.getType() != IqPacket.TYPE.RESULT) {
|
||||||
callback.failed();
|
callback.failed();
|
||||||
} else {
|
} else {
|
||||||
callback.established();
|
callback.established();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,10 @@ import eu.siacs.conversations.utils.WakeLockHelper;
|
||||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
|
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
|
||||||
|
|
||||||
public class JingleSocks5Transport extends JingleTransport {
|
public class JingleSocks5Transport extends JingleTransport {
|
||||||
|
|
||||||
|
private static final int SOCKET_TIMEOUT_DIRECT = 3000;
|
||||||
|
private static final int SOCKET_TIMEOUT_PROXY = 5000;
|
||||||
|
|
||||||
private final JingleCandidate candidate;
|
private final JingleCandidate candidate;
|
||||||
private final JingleConnection connection;
|
private final JingleConnection connection;
|
||||||
private final String destination;
|
private final String destination;
|
||||||
|
@ -92,8 +96,9 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void acceptIncomingSocketConnection(Socket socket) throws IOException {
|
private void acceptIncomingSocketConnection(final Socket socket) throws IOException {
|
||||||
Log.d(Config.LOGTAG, "accepted connection from " + socket.getInetAddress().getHostAddress());
|
Log.d(Config.LOGTAG, "accepted connection from " + socket.getInetAddress().getHostAddress());
|
||||||
|
socket.setSoTimeout(SOCKET_TIMEOUT_DIRECT);
|
||||||
final byte[] authBegin = new byte[2];
|
final byte[] authBegin = new byte[2];
|
||||||
final InputStream inputStream = socket.getInputStream();
|
final InputStream inputStream = socket.getInputStream();
|
||||||
final OutputStream outputStream = socket.getOutputStream();
|
final OutputStream outputStream = socket.getOutputStream();
|
||||||
|
@ -115,7 +120,8 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
int destinationCount = inputStream.read();
|
int destinationCount = inputStream.read();
|
||||||
final byte[] destination = new byte[destinationCount];
|
final byte[] destination = new byte[destinationCount];
|
||||||
inputStream.read(destination);
|
inputStream.read(destination);
|
||||||
final int port = inputStream.read();
|
final byte[] port = new byte[2];
|
||||||
|
inputStream.read(port);
|
||||||
final String receivedDestination = new String(destination);
|
final String receivedDestination = new String(destination);
|
||||||
final ByteBuffer response = ByteBuffer.allocate(7 + destination.length);
|
final ByteBuffer response = ByteBuffer.allocate(7 + destination.length);
|
||||||
final byte[] responseHeader;
|
final byte[] responseHeader;
|
||||||
|
@ -131,11 +137,12 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
response.put(responseHeader);
|
response.put(responseHeader);
|
||||||
response.put((byte) destination.length);
|
response.put((byte) destination.length);
|
||||||
response.put(destination);
|
response.put(destination);
|
||||||
response.putShort((short) port);
|
response.put(port);
|
||||||
outputStream.write(response.array());
|
outputStream.write(response.array());
|
||||||
outputStream.flush();
|
outputStream.flush();
|
||||||
if (success) {
|
if (success) {
|
||||||
Log.d(Config.LOGTAG,connection.getAccount().getJid().asBareJid()+": successfully processed connection to candidate "+candidate.getHost()+":"+candidate.getPort());
|
Log.d(Config.LOGTAG,connection.getAccount().getJid().asBareJid()+": successfully processed connection to candidate "+candidate.getHost()+":"+candidate.getPort());
|
||||||
|
socket.setSoTimeout(0);
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.inputStream = inputStream;
|
this.inputStream = inputStream;
|
||||||
this.outputStream = outputStream;
|
this.outputStream = outputStream;
|
||||||
|
@ -151,6 +158,7 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
|
|
||||||
public void connect(final OnTransportConnected callback) {
|
public void connect(final OnTransportConnected callback) {
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
|
final int timeout = candidate.getType() == JingleCandidate.TYPE_DIRECT ? SOCKET_TIMEOUT_DIRECT : SOCKET_TIMEOUT_PROXY;
|
||||||
try {
|
try {
|
||||||
final boolean useTor = connection.getAccount().isOnion() || connection.getConnectionManager().getXmppConnectionService().useTorToConnect();
|
final boolean useTor = connection.getAccount().isOnion() || connection.getConnectionManager().getXmppConnectionService().useTorToConnect();
|
||||||
if (useTor) {
|
if (useTor) {
|
||||||
|
@ -158,11 +166,11 @@ public class JingleSocks5Transport extends JingleTransport {
|
||||||
} else {
|
} else {
|
||||||
socket = new Socket();
|
socket = new Socket();
|
||||||
SocketAddress address = new InetSocketAddress(candidate.getHost(), candidate.getPort());
|
SocketAddress address = new InetSocketAddress(candidate.getHost(), candidate.getPort());
|
||||||
socket.connect(address, 5000);
|
socket.connect(address, timeout);
|
||||||
}
|
}
|
||||||
inputStream = socket.getInputStream();
|
inputStream = socket.getInputStream();
|
||||||
outputStream = socket.getOutputStream();
|
outputStream = socket.getOutputStream();
|
||||||
socket.setSoTimeout(5000);
|
socket.setSoTimeout(timeout);
|
||||||
SocksSocketFactory.createSocksConnection(socket, destination, 0);
|
SocksSocketFactory.createSocksConnection(socket, destination, 0);
|
||||||
socket.setSoTimeout(0);
|
socket.setSoTimeout(0);
|
||||||
isEstablished = true;
|
isEstablished = true;
|
||||||
|
|
|
@ -25,6 +25,19 @@ public class PublishOptions {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Bundle persistentWhitelistAccessMaxItems() {
|
||||||
|
final Bundle options = new Bundle();
|
||||||
|
options.putString("pubsub#persist_items", "true");
|
||||||
|
options.putString("pubsub#access_model", "whitelist");
|
||||||
|
options.putString("pubsub#send_last_published_item", "never");
|
||||||
|
options.putString("pubsub#max_items", "128"); //YOLO!
|
||||||
|
|
||||||
|
options.putString("pubsub#notify_delete", "true");
|
||||||
|
options.putString("pubsub#notify_retract", "true"); //one could also set notify=true on the retract
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean preconditionNotMet(IqPacket response) {
|
public static boolean preconditionNotMet(IqPacket response) {
|
||||||
final Element error = response.getType() == IqPacket.TYPE.ERROR ? response.findChild("error") : null;
|
final Element error = response.getType() == IqPacket.TYPE.ERROR ? response.findChild("error") : null;
|
||||||
return error != null && error.hasChild("precondition-not-met", Namespace.PUBSUB_ERROR);
|
return error != null && error.hasChild("precondition-not-met", Namespace.PUBSUB_ERROR);
|
||||||
|
|
|
@ -337,6 +337,7 @@
|
||||||
<string name="x_file_offered_for_download">%s zum Herunterladen angeboten</string>
|
<string name="x_file_offered_for_download">%s zum Herunterladen angeboten</string>
|
||||||
<string name="cancel_transmission">Übertragung abbrechen</string>
|
<string name="cancel_transmission">Übertragung abbrechen</string>
|
||||||
<string name="file_transmission_failed">Übertragung fehlgeschlagen</string>
|
<string name="file_transmission_failed">Übertragung fehlgeschlagen</string>
|
||||||
|
<string name="file_transmission_cancelled">Übertragung abgebrochen</string>
|
||||||
<string name="file_deleted">Datei wurde gelöscht</string>
|
<string name="file_deleted">Datei wurde gelöscht</string>
|
||||||
<string name="no_application_found_to_open_file">Keine Anwendung zum Öffnen der Datei gefunden</string>
|
<string name="no_application_found_to_open_file">Keine Anwendung zum Öffnen der Datei gefunden</string>
|
||||||
<string name="no_application_found_to_open_link">Keine Anwendung zum Öffnen des Links gefunden</string>
|
<string name="no_application_found_to_open_link">Keine Anwendung zum Öffnen des Links gefunden</string>
|
||||||
|
@ -857,7 +858,7 @@
|
||||||
<string name="discover_channels">Channels entdecken</string>
|
<string name="discover_channels">Channels entdecken</string>
|
||||||
<string name="search_channels">Channels suchen</string>
|
<string name="search_channels">Channels suchen</string>
|
||||||
<string name="channel_discovery_opt_in_title">Mögliche Datenschutzverletzung!</string>
|
<string name="channel_discovery_opt_in_title">Mögliche Datenschutzverletzung!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[Die Channel-Entdeckung verwendet einen Drittanbieterservice namens <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Wenn du diese Funktion verwendest, werden deine IP-Adresse und deine Suchbegriffe an diesen Dienst übertragen. Weitere Informationen findest du in der <a href="https://search.jabbercat.org/privacy">Datenschutzerklärung</a>.]]></string>
|
<string name="channel_discover_opt_in_message"><![CDATA[Die Channel-Entdeckung verwendet einen Drittanbieterservice namens <a href="https://search.jabber.network">search.jabber.network</a>.<br><br>Wenn du diese Funktion verwendest, werden deine IP-Adresse und deine Suchbegriffe an diesen Dienst übertragen. Weitere Informationen findest du in der <a href="https://search.jabber.network/privacy">Datenschutzerklärung</a>.]]></string>
|
||||||
<string name="i_already_have_an_account">Ich habe bereits ein Konto</string>
|
<string name="i_already_have_an_account">Ich habe bereits ein Konto</string>
|
||||||
<string name="add_existing_account">Vorhandenes Konto hinzufügen</string>
|
<string name="add_existing_account">Vorhandenes Konto hinzufügen</string>
|
||||||
<string name="register_new_account">Neues Konto erstellen</string>
|
<string name="register_new_account">Neues Konto erstellen</string>
|
||||||
|
|
|
@ -857,7 +857,6 @@
|
||||||
<string name="discover_channels">Ανακάλυψη καναλιών</string>
|
<string name="discover_channels">Ανακάλυψη καναλιών</string>
|
||||||
<string name="search_channels">Αναζήτηση καναλιών</string>
|
<string name="search_channels">Αναζήτηση καναλιών</string>
|
||||||
<string name="channel_discovery_opt_in_title">Πιθανή παραβίαση ιδιωτικότητας!</string>
|
<string name="channel_discovery_opt_in_title">Πιθανή παραβίαση ιδιωτικότητας!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[Η ανακάλυψη καναλιού χρησιμοποιεί μια τρίτη υπηρεσία που λέγεται <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Χρησιμοποιώντας αυτή τη λειτουργία θα μεταβιβαστεί η διεύθυνση IP σας και οι όροι αναζήτησης σε αυτή την υπηρεσία. Δείτε την <a href="https://search.jabbercat.org/privacy">Πολιτική Ιδιωτικότητας</a> της για περισσότερες πληροφορίες.]]></string>
|
|
||||||
<string name="i_already_have_an_account">Έχω ήδη λογαριασμό</string>
|
<string name="i_already_have_an_account">Έχω ήδη λογαριασμό</string>
|
||||||
<string name="add_existing_account">Προσθήκη υπάρχοντος λογαριασμού</string>
|
<string name="add_existing_account">Προσθήκη υπάρχοντος λογαριασμού</string>
|
||||||
<string name="register_new_account">Εγγραφή νέου λογαριασμού</string>
|
<string name="register_new_account">Εγγραφή νέου λογαριασμού</string>
|
||||||
|
|
|
@ -857,7 +857,6 @@
|
||||||
<string name="discover_channels">Descubrir canales</string>
|
<string name="discover_channels">Descubrir canales</string>
|
||||||
<string name="search_channels">Buscar canales</string>
|
<string name="search_channels">Buscar canales</string>
|
||||||
<string name="channel_discovery_opt_in_title">¡Posible violación de privacidad!</string>
|
<string name="channel_discovery_opt_in_title">¡Posible violación de privacidad!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[La búsqueda de canales usa un servicio de terceros llamado search.jabbercat.org<a href="https://search.jabbercat.org">. <br><br>Usando esta funcionalidad transmitirás tu dirección IP y los términos buscados a este servicio. Ver su <a href="https://search.jabbercat.org/privacy">Política de Privacidad</a> para más información.]]></string>
|
|
||||||
<string name="i_already_have_an_account">Ya tengo una cuenta</string>
|
<string name="i_already_have_an_account">Ya tengo una cuenta</string>
|
||||||
<string name="add_existing_account">Añadir una cuenta existente</string>
|
<string name="add_existing_account">Añadir una cuenta existente</string>
|
||||||
<string name="register_new_account">Registrar una cuenta nueva</string>
|
<string name="register_new_account">Registrar una cuenta nueva</string>
|
||||||
|
@ -872,4 +871,5 @@
|
||||||
<string name="account_already_setup">Esta cuenta ya fue configurada</string>
|
<string name="account_already_setup">Esta cuenta ya fue configurada</string>
|
||||||
<string name="please_enter_password">Por favor ingrese la contraseña para esta cuenta</string>
|
<string name="please_enter_password">Por favor ingrese la contraseña para esta cuenta</string>
|
||||||
<string name="unable_to_perform_this_action">No se ha podido realizar esta acción</string>
|
<string name="unable_to_perform_this_action">No se ha podido realizar esta acción</string>
|
||||||
|
<string name="open_join_dialog">Unirse a canal público...</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
<string name="just_now">orain</string>
|
<string name="just_now">orain</string>
|
||||||
<string name="minute_ago">min 1 lehenago</string>
|
<string name="minute_ago">min 1 lehenago</string>
|
||||||
<string name="minutes_ago">%d min lehenago</string>
|
<string name="minutes_ago">%d min lehenago</string>
|
||||||
|
<string name="x_unread_conversations">Irakurrik gabeko %d elkarrizketa</string>
|
||||||
<string name="sending">bidaltzen…</string>
|
<string name="sending">bidaltzen…</string>
|
||||||
<string name="message_decrypting">Mezua desenkriptatzen. Mesedez itxaron…</string>
|
<string name="message_decrypting">Mezua desenkriptatzen. Mesedez itxaron…</string>
|
||||||
<string name="pgp_message">OpenPGPz enkriptatutako mezua</string>
|
<string name="pgp_message">OpenPGPz enkriptatutako mezua</string>
|
||||||
|
@ -336,6 +337,7 @@
|
||||||
<string name="x_file_offered_for_download">%s deskargatzeko eskeinita</string>
|
<string name="x_file_offered_for_download">%s deskargatzeko eskeinita</string>
|
||||||
<string name="cancel_transmission">Transmisioa utzi</string>
|
<string name="cancel_transmission">Transmisioa utzi</string>
|
||||||
<string name="file_transmission_failed">fitxategi transmisioak huts egin du</string>
|
<string name="file_transmission_failed">fitxategi transmisioak huts egin du</string>
|
||||||
|
<string name="file_transmission_cancelled">fitxategiaren transmisioa utzi egin da</string>
|
||||||
<string name="file_deleted">Fitxategia ezabatu egin da</string>
|
<string name="file_deleted">Fitxategia ezabatu egin da</string>
|
||||||
<string name="no_application_found_to_open_file">Fitxategia ireki dezakeen aplikaziorik ez da aurkitu</string>
|
<string name="no_application_found_to_open_file">Fitxategia ireki dezakeen aplikaziorik ez da aurkitu</string>
|
||||||
<string name="no_application_found_to_open_link">Ez da lotura hau ireki dezakeen aplikaziorik aurkitu</string>
|
<string name="no_application_found_to_open_link">Ez da lotura hau ireki dezakeen aplikaziorik aurkitu</string>
|
||||||
|
@ -854,7 +856,7 @@
|
||||||
<string name="discover_channels">Kanalak aurkitu</string>
|
<string name="discover_channels">Kanalak aurkitu</string>
|
||||||
<string name="search_channels">Kanalak bilatu</string>
|
<string name="search_channels">Kanalak bilatu</string>
|
||||||
<string name="channel_discovery_opt_in_title">Balizko pribatutasun urraketa!</string>
|
<string name="channel_discovery_opt_in_title">Balizko pribatutasun urraketa!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[Kanalak aurkitzeko ezaugarriak <a href="https://search.jabbercat.org">search.jabbercat.org</a> izeneko hirugarren zerbitzu bat erabiltzen du.<br><br>Ezaugarri hau erabiltzeak zure IP helbidea eta bilatutako testua zerbitzu horretara bidaltzea dakar. Ikusi beren <a href="https://search.jabbercat.org/privacy">pribatutasun politika</a> informazio gehiago lortzeko.]]></string>
|
<string name="channel_discover_opt_in_message"><![CDATA[Kanalak aurkitzeko ezaugarriak <a href="https://search.jabber.network">search.jabber.network</a>. izeneko hirugarren zerbitzu bat erabiltzen du.<br><br>Ezaugarri hau erabiltzeak zure IP helbidea eta bilatutako testua zerbitzu horretara bidaltzea dakar. Ikusi beren <a href="https://search.jabber.network/privacy">pribatutasun politika</a> informazio gehiago lortzeko.]]></string>
|
||||||
<string name="i_already_have_an_account">Badaukat kontu bat dagoeneko</string>
|
<string name="i_already_have_an_account">Badaukat kontu bat dagoeneko</string>
|
||||||
<string name="add_existing_account">Gehitu existitzen den kontu bat</string>
|
<string name="add_existing_account">Gehitu existitzen den kontu bat</string>
|
||||||
<string name="register_new_account">Kontu berria erregistratu</string>
|
<string name="register_new_account">Kontu berria erregistratu</string>
|
||||||
|
@ -868,4 +870,6 @@
|
||||||
<string name="not_a_backup_file">Hautatu duzun fitxategia ez da Conversations babes-kopia bat</string>
|
<string name="not_a_backup_file">Hautatu duzun fitxategia ez da Conversations babes-kopia bat</string>
|
||||||
<string name="account_already_setup">Kontu hau konfiguratuta dago jada</string>
|
<string name="account_already_setup">Kontu hau konfiguratuta dago jada</string>
|
||||||
<string name="please_enter_password">Mesedez idatzi ezazu kontu honetarako pasahitza</string>
|
<string name="please_enter_password">Mesedez idatzi ezazu kontu honetarako pasahitza</string>
|
||||||
|
<string name="unable_to_perform_this_action">Ezin izan da ekintza hau burutu</string>
|
||||||
|
<string name="open_join_dialog">Kanal publiko batean sartu…</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<string name="action_end_conversation">Fermer cette conversation</string>
|
<string name="action_end_conversation">Fermer cette conversation</string>
|
||||||
<string name="action_contact_details">Détails du contact</string>
|
<string name="action_contact_details">Détails du contact</string>
|
||||||
<string name="action_muc_details">Détails de la conversation de groupe</string>
|
<string name="action_muc_details">Détails de la conversation de groupe</string>
|
||||||
<string name="channel_details">Détails de la chaîne</string>
|
<string name="channel_details">Détails du canal</string>
|
||||||
<string name="action_secure">Conversation sécurisée</string>
|
<string name="action_secure">Conversation sécurisée</string>
|
||||||
<string name="action_add_account">Ajouter un compte</string>
|
<string name="action_add_account">Ajouter un compte</string>
|
||||||
<string name="action_edit_contact">Modifier le nom</string>
|
<string name="action_edit_contact">Modifier le nom</string>
|
||||||
|
@ -17,6 +17,8 @@
|
||||||
<string name="action_unblock_contact">Débloquer le contact</string>
|
<string name="action_unblock_contact">Débloquer le contact</string>
|
||||||
<string name="action_block_domain">Bloquer le domaine</string>
|
<string name="action_block_domain">Bloquer le domaine</string>
|
||||||
<string name="action_unblock_domain">Débloquer le domaine</string>
|
<string name="action_unblock_domain">Débloquer le domaine</string>
|
||||||
|
<string name="action_block_participant">Bloquer le participant</string>
|
||||||
|
<string name="action_unblock_participant">Débloquer le participant</string>
|
||||||
<string name="title_activity_manage_accounts">Gestion des comptes</string>
|
<string name="title_activity_manage_accounts">Gestion des comptes</string>
|
||||||
<string name="title_activity_settings">Paramètres</string>
|
<string name="title_activity_settings">Paramètres</string>
|
||||||
<string name="title_activity_sharewith">Partager avec Conversation</string>
|
<string name="title_activity_sharewith">Partager avec Conversation</string>
|
||||||
|
@ -28,6 +30,7 @@
|
||||||
<string name="just_now">À l\'instant</string>
|
<string name="just_now">À l\'instant</string>
|
||||||
<string name="minute_ago">Il y a 1 minute</string>
|
<string name="minute_ago">Il y a 1 minute</string>
|
||||||
<string name="minutes_ago">Il y a %d minutes</string>
|
<string name="minutes_ago">Il y a %d minutes</string>
|
||||||
|
<string name="x_unread_conversations">%d conversations non lues</string>
|
||||||
<string name="sending">Envoi…</string>
|
<string name="sending">Envoi…</string>
|
||||||
<string name="message_decrypting">Déchiffrement du message. Veuillez patienter...</string>
|
<string name="message_decrypting">Déchiffrement du message. Veuillez patienter...</string>
|
||||||
<string name="pgp_message">Message chiffré avec OpenPGP</string>
|
<string name="pgp_message">Message chiffré avec OpenPGP</string>
|
||||||
|
@ -334,6 +337,7 @@
|
||||||
<string name="x_file_offered_for_download">%s proposé à télécharger</string>
|
<string name="x_file_offered_for_download">%s proposé à télécharger</string>
|
||||||
<string name="cancel_transmission">Annuler l\'envoi</string>
|
<string name="cancel_transmission">Annuler l\'envoi</string>
|
||||||
<string name="file_transmission_failed">Échec de l\'envoi du fichier</string>
|
<string name="file_transmission_failed">Échec de l\'envoi du fichier</string>
|
||||||
|
<string name="file_transmission_cancelled">Transfert de fichier annulé</string>
|
||||||
<string name="file_deleted">Le fichier a été supprimé</string>
|
<string name="file_deleted">Le fichier a été supprimé</string>
|
||||||
<string name="no_application_found_to_open_file">Aucune application disponible pour ouvrir le fichier</string>
|
<string name="no_application_found_to_open_file">Aucune application disponible pour ouvrir le fichier</string>
|
||||||
<string name="no_application_found_to_open_link">Aucune application trouvée pour ouvrir le lien</string>
|
<string name="no_application_found_to_open_link">Aucune application trouvée pour ouvrir le lien</string>
|
||||||
|
@ -701,7 +705,7 @@
|
||||||
<string name="small">Petite</string>
|
<string name="small">Petite</string>
|
||||||
<string name="medium">Moyenne</string>
|
<string name="medium">Moyenne</string>
|
||||||
<string name="large">Grande</string>
|
<string name="large">Grande</string>
|
||||||
<string name="not_encrypted_for_this_device">OMEMO sera utilisé par défaut pour toute nouvelle conversation.</string>
|
<string name="not_encrypted_for_this_device">Le message n\'était pas chiffré pour cet appareil.</string>
|
||||||
<string name="omemo_decryption_failed">Échec de déchiffrement du message OMEMO.</string>
|
<string name="omemo_decryption_failed">Échec de déchiffrement du message OMEMO.</string>
|
||||||
<string name="undo">annuler</string>
|
<string name="undo">annuler</string>
|
||||||
<string name="location_disabled">Le partage de positionnement est désactivé.</string>
|
<string name="location_disabled">Le partage de positionnement est désactivé.</string>
|
||||||
|
@ -845,7 +849,7 @@
|
||||||
<string name="anyone_can_invite_others">N\'importe qui peut inviter d\'autres personnes.</string>
|
<string name="anyone_can_invite_others">N\'importe qui peut inviter d\'autres personnes.</string>
|
||||||
<string name="jabber_ids_are_visible_to_admins">Les adresses XMPP sont visibles par les administrateurs.</string>
|
<string name="jabber_ids_are_visible_to_admins">Les adresses XMPP sont visibles par les administrateurs.</string>
|
||||||
<string name="jabber_ids_are_visible_to_anyone">Les adresses XMPP sont visibles par tous.</string>
|
<string name="jabber_ids_are_visible_to_anyone">Les adresses XMPP sont visibles par tous.</string>
|
||||||
<string name="no_users_hint_channel">Cette chaîne publique n\'a pas de participants. Invitez vos contacts ou utilisez le bouton de partage pour distribuer son adresse XMPP.</string>
|
<string name="no_users_hint_channel">Ce canal publique n\'a pas de participants. Invitez vos contacts ou utilisez le bouton de partage pour distribuer son adresse XMPP.</string>
|
||||||
<string name="no_users_hint_group_chat">Ce chat de groupe privé n\'a aucun participant.</string>
|
<string name="no_users_hint_group_chat">Ce chat de groupe privé n\'a aucun participant.</string>
|
||||||
<string name="manage_permission">Gérer les privilèges</string>
|
<string name="manage_permission">Gérer les privilèges</string>
|
||||||
<string name="search_participants">Rechercher des participants</string>
|
<string name="search_participants">Rechercher des participants</string>
|
||||||
|
@ -854,11 +858,20 @@
|
||||||
<string name="discover_channels">Découverte des canaux</string>
|
<string name="discover_channels">Découverte des canaux</string>
|
||||||
<string name="search_channels">Recherche des canaux</string>
|
<string name="search_channels">Recherche des canaux</string>
|
||||||
<string name="channel_discovery_opt_in_title">Violation possible de la confidentialité !</string>
|
<string name="channel_discovery_opt_in_title">Violation possible de la confidentialité !</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[La découverte de chaînes utilise un service tiers appelé <a href="https://search.jabbercat.org">search.jabbercat.org</a> qui transmet votre adresse IP et les termes de recherche à ce service. Voir leur <a href="https://search.jabbercat.org/privacy">politique de confidentialité</a> pour plus d\'informations]]>.</string>
|
<string name="channel_discover_opt_in_message"><![CDATA[Channel discovery utilise un service tiers appelé <a href="https://search.jabber.network">search.jabber.network</a>.<br><br>L\'utilisation de cette fonction transmettra votre adresse IP et les termes de recherche à ce service. Veuillez consulter leur <a href="https://search.jabber.network/privacy">Politique de confidentialité</a> pour plus d\'information.]]></string>
|
||||||
<string name="i_already_have_an_account">J\'ai déjà un compte</string>
|
<string name="i_already_have_an_account">J\'ai déjà un compte</string>
|
||||||
<string name="add_existing_account">Ajouter un compte existant</string>
|
<string name="add_existing_account">Ajouter un compte existant</string>
|
||||||
<string name="register_new_account">Enregistrer un nouveau compte</string>
|
<string name="register_new_account">Enregistrer un nouveau compte</string>
|
||||||
<string name="this_looks_like_a_domain">Ceci ressemble à une adresse de domaine</string>
|
<string name="this_looks_like_a_domain">Ceci ressemble à une adresse de domaine</string>
|
||||||
<string name="add_anway">Ajouter quand même</string>
|
<string name="add_anway">Ajouter quand même</string>
|
||||||
<string name="this_looks_like_channel">Ceci ressemble à une adresse de canal</string>
|
<string name="this_looks_like_channel">Ceci ressemble à une adresse de canal</string>
|
||||||
|
<string name="share_backup_files">Partager les fichiers de sauvegardes</string>
|
||||||
|
<string name="conversations_backup">Sauvegarder les conversations</string>
|
||||||
|
<string name="event">Événement </string>
|
||||||
|
<string name="open_backup">Ouvrir sauvegarde</string>
|
||||||
|
<string name="not_a_backup_file">Le fichier sélectionné n\'est pas une sauvegarde de Conversations</string>
|
||||||
|
<string name="account_already_setup">Ce compte a déjà été configuré</string>
|
||||||
|
<string name="please_enter_password">Veuillez saisir le mot de passe pour ce compte</string>
|
||||||
|
<string name="unable_to_perform_this_action">Action impossible à réaliser</string>
|
||||||
|
<string name="open_join_dialog">Rejoindre le canal public ...</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -857,7 +857,6 @@
|
||||||
<string name="discover_channels">Descubrir canales</string>
|
<string name="discover_channels">Descubrir canales</string>
|
||||||
<string name="search_channels">Buscar canales</string>
|
<string name="search_channels">Buscar canales</string>
|
||||||
<string name="channel_discovery_opt_in_title">Posible intrusión na intimidade!</string>
|
<string name="channel_discovery_opt_in_title">Posible intrusión na intimidade!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[O descubrimento de canales utiliza un servizo de terceiros chamado <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Ao utilizar esta característica transmitirá o seu enderezo IP e os termos de busca a ese servizo. Lea a súa <a href="https://search.jabbercat.org/privacy">Política de Intimidade</a> para máis información.]]></string>
|
|
||||||
<string name="i_already_have_an_account">Xa teño unha conta</string>
|
<string name="i_already_have_an_account">Xa teño unha conta</string>
|
||||||
<string name="add_existing_account">Engadir conta existente</string>
|
<string name="add_existing_account">Engadir conta existente</string>
|
||||||
<string name="register_new_account">Rexistrar unha nova conta</string>
|
<string name="register_new_account">Rexistrar unha nova conta</string>
|
||||||
|
|
|
@ -856,7 +856,6 @@
|
||||||
<string name="discover_channels">Csatornák felderítése</string>
|
<string name="discover_channels">Csatornák felderítése</string>
|
||||||
<string name="search_channels">Csatornák keresése</string>
|
<string name="search_channels">Csatornák keresése</string>
|
||||||
<string name="channel_discovery_opt_in_title">Magánélet lehetséges megsértése!</string>
|
<string name="channel_discovery_opt_in_title">Magánélet lehetséges megsértése!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[A csatorna felderítés egy harmadik fél által biztosított szolgáltatást használ: <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Ezen funkció használata során át fog kerülni az IP címe és a keresési kifejezés ahhoz a szolgáltatáshoz. További információért tekintse meg az <a href="https://search.jabbercat.org/privacy">Adatvédelmi Irányelveiket</a>.]]></string>
|
|
||||||
<string name="i_already_have_an_account">Már rendelkezem fiókkal</string>
|
<string name="i_already_have_an_account">Már rendelkezem fiókkal</string>
|
||||||
<string name="add_existing_account">Már létező fiók hozzáadása</string>
|
<string name="add_existing_account">Már létező fiók hozzáadása</string>
|
||||||
<string name="register_new_account">Új fiók létrehozása</string>
|
<string name="register_new_account">Új fiók létrehozása</string>
|
||||||
|
|
|
@ -857,7 +857,6 @@
|
||||||
<string name="discover_channels">Individua i canali</string>
|
<string name="discover_channels">Individua i canali</string>
|
||||||
<string name="search_channels">Cerca i canali</string>
|
<string name="search_channels">Cerca i canali</string>
|
||||||
<string name="channel_discovery_opt_in_title">Possibile violazione della privacy!</string>
|
<string name="channel_discovery_opt_in_title">Possibile violazione della privacy!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[La ricerca dei canali usa un servizio di terze parti chiamato <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Usando questa opzione trasmetterai il tuo indirizzo IP e la stringa di ricerca al servizio. Controlla la loro <a href="https://search.jabbercat.org/privacy">Policy per la privacy</a> per maggiori informazioni.]]></string>
|
|
||||||
<string name="i_already_have_an_account">Ho già un account</string>
|
<string name="i_already_have_an_account">Ho già un account</string>
|
||||||
<string name="add_existing_account">Aggiungi un account pre-esistente</string>
|
<string name="add_existing_account">Aggiungi un account pre-esistente</string>
|
||||||
<string name="register_new_account">Registra un nuovo account</string>
|
<string name="register_new_account">Registra un nuovo account</string>
|
||||||
|
|
|
@ -337,6 +337,7 @@
|
||||||
<string name="x_file_offered_for_download">%s aangeboden om te downloaden</string>
|
<string name="x_file_offered_for_download">%s aangeboden om te downloaden</string>
|
||||||
<string name="cancel_transmission">Bestandsoverdracht annuleren</string>
|
<string name="cancel_transmission">Bestandsoverdracht annuleren</string>
|
||||||
<string name="file_transmission_failed">bestandsoverdracht mislukt</string>
|
<string name="file_transmission_failed">bestandsoverdracht mislukt</string>
|
||||||
|
<string name="file_transmission_cancelled">bestandsoverdracht geannuleerd</string>
|
||||||
<string name="file_deleted">Het bestand is verwijderd</string>
|
<string name="file_deleted">Het bestand is verwijderd</string>
|
||||||
<string name="no_application_found_to_open_file">Geen applicatie om bestand te openen</string>
|
<string name="no_application_found_to_open_file">Geen applicatie om bestand te openen</string>
|
||||||
<string name="no_application_found_to_open_link">Geen applicatie om verwijzing te openen</string>
|
<string name="no_application_found_to_open_link">Geen applicatie om verwijzing te openen</string>
|
||||||
|
@ -856,7 +857,7 @@
|
||||||
<string name="discover_channels">Kanalen ontdekken</string>
|
<string name="discover_channels">Kanalen ontdekken</string>
|
||||||
<string name="search_channels">Kanalen doorzoeken</string>
|
<string name="search_channels">Kanalen doorzoeken</string>
|
||||||
<string name="channel_discovery_opt_in_title">Mogelijke privacyschending!</string>
|
<string name="channel_discovery_opt_in_title">Mogelijke privacyschending!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[Kanaalontdekking maakt gebruik van een derdepartijdienst genaamd <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Door deze functie te gebruiken, zullen je IP-adres en zoekopdrachten naar die dienst verstuurd worden. Bekijk hun <a href="https://search.jabbercat.org/privacy">privacybeleid</a> voor meer informatie.]]></string>
|
<string name="channel_discover_opt_in_message"><![CDATA[Kanaalontdekking maakt gebruik van een derdepartijdienst genaamd <a href="https://search.jabber.network">search.jabber.network</a>.<br><br>Door deze functie te gebruiken, zullen je IP-adres en zoekopdrachten naar die dienst verstuurd worden. Bekijk hun <a href="https://search.jabber.network/privacy">privacybeleid</a> voor meer informatie.]]></string>
|
||||||
<string name="i_already_have_an_account">Ik heb al een account</string>
|
<string name="i_already_have_an_account">Ik heb al een account</string>
|
||||||
<string name="add_existing_account">Bestaande account toevoegen</string>
|
<string name="add_existing_account">Bestaande account toevoegen</string>
|
||||||
<string name="register_new_account">Nieuwe account registreren</string>
|
<string name="register_new_account">Nieuwe account registreren</string>
|
||||||
|
@ -871,4 +872,5 @@
|
||||||
<string name="account_already_setup">Deze account is al ingesteld</string>
|
<string name="account_already_setup">Deze account is al ingesteld</string>
|
||||||
<string name="please_enter_password">Voer het wachtwoord voor deze account in</string>
|
<string name="please_enter_password">Voer het wachtwoord voor deze account in</string>
|
||||||
<string name="unable_to_perform_this_action">Kan deze actie niet uitvoeren</string>
|
<string name="unable_to_perform_this_action">Kan deze actie niet uitvoeren</string>
|
||||||
|
<string name="open_join_dialog">Deelnemen aan openbaar kanaal…</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -874,7 +874,6 @@ Administrator twojego serwera będzie mógł czytać twoje wiadomości, ale moż
|
||||||
<string name="discover_channels">Odkryj kanały</string>
|
<string name="discover_channels">Odkryj kanały</string>
|
||||||
<string name="search_channels">Wyszukaj kanał</string>
|
<string name="search_channels">Wyszukaj kanał</string>
|
||||||
<string name="channel_discovery_opt_in_title">Możliwe naruszenie prywatności!</string>
|
<string name="channel_discovery_opt_in_title">Możliwe naruszenie prywatności!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[Odkrywanie kanałów używa usługi firmy trzeciej <a href="https://search.jabbercat.org">search.jabbercat.org</a>. <br><br>Używając tej funkcji twój adres IP oraz kryteria wyszukiwania zostaną wysłane do tej usługi. Sprawdź <a href="https://search.jabbercat.org/privacy">Politykę Prywatności</a> aby uzyskać więcej informacji.]]></string>
|
|
||||||
<string name="i_already_have_an_account">Już mam konto</string>
|
<string name="i_already_have_an_account">Już mam konto</string>
|
||||||
<string name="add_existing_account">Dodaj istniejące konto</string>
|
<string name="add_existing_account">Dodaj istniejące konto</string>
|
||||||
<string name="register_new_account">Zarejestruj nowe konto</string>
|
<string name="register_new_account">Zarejestruj nowe konto</string>
|
||||||
|
|
|
@ -856,7 +856,6 @@
|
||||||
<string name="discover_channels">Descobrir canais</string>
|
<string name="discover_channels">Descobrir canais</string>
|
||||||
<string name="search_channels">Pesquisar canais</string>
|
<string name="search_channels">Pesquisar canais</string>
|
||||||
<string name="channel_discovery_opt_in_title">Provável violação de privacidade!</string>
|
<string name="channel_discovery_opt_in_title">Provável violação de privacidade!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[A descoberta de canais utiliza um serviço de terceiros chamado <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Ao usar esse recurso, você enviará o seu endereço IP e termos de pesquisa para esse serviço. Veja sua <a href="https://search.jabbercat.org/privacy">Política de Privacidade</a> para maiores informações.]]></string>
|
|
||||||
<string name="i_already_have_an_account">Eu já tenho uma conta.</string>
|
<string name="i_already_have_an_account">Eu já tenho uma conta.</string>
|
||||||
<string name="add_existing_account">Adicionar uma conta já existente</string>
|
<string name="add_existing_account">Adicionar uma conta já existente</string>
|
||||||
<string name="register_new_account">Registrar uma nova conta</string>
|
<string name="register_new_account">Registrar uma nova conta</string>
|
||||||
|
|
|
@ -337,6 +337,7 @@
|
||||||
<string name="x_file_offered_for_download">%s - fișier oferit spre descărcare</string>
|
<string name="x_file_offered_for_download">%s - fișier oferit spre descărcare</string>
|
||||||
<string name="cancel_transmission">Anulează transmisiunea</string>
|
<string name="cancel_transmission">Anulează transmisiunea</string>
|
||||||
<string name="file_transmission_failed">transmisie fișier eșuată</string>
|
<string name="file_transmission_failed">transmisie fișier eșuată</string>
|
||||||
|
<string name="file_transmission_cancelled">transmisia fișierului a fost anulată</string>
|
||||||
<string name="file_deleted">Fișierul a fost șters</string>
|
<string name="file_deleted">Fișierul a fost șters</string>
|
||||||
<string name="no_application_found_to_open_file">Nu s-a găsit nici o aplicație care să deschidă fișierul</string>
|
<string name="no_application_found_to_open_file">Nu s-a găsit nici o aplicație care să deschidă fișierul</string>
|
||||||
<string name="no_application_found_to_open_link">Nu s-a găsit nici o aplicație care să deschidă legătura</string>
|
<string name="no_application_found_to_open_link">Nu s-a găsit nici o aplicație care să deschidă legătura</string>
|
||||||
|
@ -866,7 +867,7 @@ sau chiar pierderea mesajelor.\nÎn continuare veți fi rugați să dezactivați
|
||||||
<string name="discover_channels">Descoperă canale publice</string>
|
<string name="discover_channels">Descoperă canale publice</string>
|
||||||
<string name="search_channels">Caută canale publice</string>
|
<string name="search_channels">Caută canale publice</string>
|
||||||
<string name="channel_discovery_opt_in_title">Posibilă încălcare a intimității!</string>
|
<string name="channel_discovery_opt_in_title">Posibilă încălcare a intimității!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[Descoperirea de canale publice folosește un serviciu terț numit <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Folosind această funcție se va transmite adresa dumneavoastră IP și cuvintele căutate către acest serviciu. Pentru mai multe informații citiți <a href="https://search.jabbercat.org/privacy">Politica de confidențialitate</a> a serviciului.]]></string>
|
<string name="channel_discover_opt_in_message"><![CDATA[Descoperirea de canale publice folosește un serviciu terț numit <a href="https://search.jabber.network">search.jabber.network</a>.<br><br>Folosind această funcție se va transmite adresa dumneavoastră IP și cuvintele căutate către acest serviciu. Pentru mai multe informații citiți <a href="https://search.jabber.network/privacy">Politica de confidențialitate</a> a serviciului.]]></string>
|
||||||
<string name="i_already_have_an_account">Eu am deja un cont</string>
|
<string name="i_already_have_an_account">Eu am deja un cont</string>
|
||||||
<string name="add_existing_account">Adaugă un cont existent</string>
|
<string name="add_existing_account">Adaugă un cont existent</string>
|
||||||
<string name="register_new_account">Înregistrează un cont nou</string>
|
<string name="register_new_account">Înregistrează un cont nou</string>
|
||||||
|
|
|
@ -871,7 +871,6 @@
|
||||||
<string name="discover_channels">Знайти канали</string>
|
<string name="discover_channels">Знайти канали</string>
|
||||||
<string name="search_channels">Шукати канали</string>
|
<string name="search_channels">Шукати канали</string>
|
||||||
<string name="channel_discovery_opt_in_title">Можливе порушення приватності!</string>
|
<string name="channel_discovery_opt_in_title">Можливе порушення приватності!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[Пошук каналів використовує сторонній сервіс з назвою <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Використання цієї функції передає Вашу IP адресу та пошукові запити цьому сервісу. Перегляньте їхню <a href="https://search.jabbercat.org/privacy">політику конфіденційності</a>, щоб отримати більше інформації.]]></string>
|
|
||||||
<string name="i_already_have_an_account">Я вже маю обліковий запис</string>
|
<string name="i_already_have_an_account">Я вже маю обліковий запис</string>
|
||||||
<string name="add_existing_account">Додати наявний обліковий запис</string>
|
<string name="add_existing_account">Додати наявний обліковий запис</string>
|
||||||
<string name="register_new_account">Зареєструвати новий обліковий запис</string>
|
<string name="register_new_account">Зареєструвати новий обліковий запис</string>
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<style name="ConversationsTheme.Dark" parent="ConversationsTheme.Dark.Base">
|
|
||||||
<item name="android:navigationBarColor">@color/black</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
|
@ -850,7 +850,6 @@
|
||||||
<string name="discover_channels">发现群聊</string>
|
<string name="discover_channels">发现群聊</string>
|
||||||
<string name="search_channels">搜索群聊</string>
|
<string name="search_channels">搜索群聊</string>
|
||||||
<string name="channel_discovery_opt_in_title">可能侵犯隐私!</string>
|
<string name="channel_discovery_opt_in_title">可能侵犯隐私!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[探索群聊功能使用一个叫<a href="https://search.jabbercat.org">search.jabbercat.org</a>的第三方服务。在探索群聊时,您的IP地址和搜索内容将传送到他们的服务器上。有关更多信息,请参阅他们的<a href="https://search.jabbercat.org/privacy">隐私政策</a>。]]></string>
|
|
||||||
<string name="i_already_have_an_account">我已有账户</string>
|
<string name="i_already_have_an_account">我已有账户</string>
|
||||||
<string name="add_existing_account">添加已有账户</string>
|
<string name="add_existing_account">添加已有账户</string>
|
||||||
<string name="register_new_account">注册新账户</string>
|
<string name="register_new_account">注册新账户</string>
|
||||||
|
|
|
@ -337,6 +337,7 @@
|
||||||
<string name="x_file_offered_for_download">%s offered for download</string>
|
<string name="x_file_offered_for_download">%s offered for download</string>
|
||||||
<string name="cancel_transmission">Cancel transmission</string>
|
<string name="cancel_transmission">Cancel transmission</string>
|
||||||
<string name="file_transmission_failed">file transmission failed</string>
|
<string name="file_transmission_failed">file transmission failed</string>
|
||||||
|
<string name="file_transmission_cancelled">file transmission cancelled</string>
|
||||||
<string name="file_deleted">The file has been deleted</string>
|
<string name="file_deleted">The file has been deleted</string>
|
||||||
<string name="no_application_found_to_open_file">No application found to open file</string>
|
<string name="no_application_found_to_open_file">No application found to open file</string>
|
||||||
<string name="no_application_found_to_open_link">No application found to open link</string>
|
<string name="no_application_found_to_open_link">No application found to open link</string>
|
||||||
|
@ -859,7 +860,7 @@
|
||||||
<string name="discover_channels">Discover channels</string>
|
<string name="discover_channels">Discover channels</string>
|
||||||
<string name="search_channels">Search channels</string>
|
<string name="search_channels">Search channels</string>
|
||||||
<string name="channel_discovery_opt_in_title">Possible privacy violation!</string>
|
<string name="channel_discovery_opt_in_title">Possible privacy violation!</string>
|
||||||
<string name="channel_discover_opt_in_message"><![CDATA[Channel discovery uses a third party service called <a href="https://search.jabbercat.org">search.jabbercat.org</a>.<br><br>Using this feature will transmit your IP address and search terms to that service. See their <a href="https://search.jabbercat.org/privacy">Privacy Policy</a> for more information.]]></string>
|
<string name="channel_discover_opt_in_message"><![CDATA[Channel discovery uses a third party service called <a href="https://search.jabber.network">search.jabber.network</a>.<br><br>Using this feature will transmit your IP address and search terms to that service. See their <a href="https://search.jabber.network/privacy">Privacy Policy</a> for more information.]]></string>
|
||||||
<string name="i_already_have_an_account">I already have an account</string>
|
<string name="i_already_have_an_account">I already have an account</string>
|
||||||
<string name="add_existing_account">Add existing account</string>
|
<string name="add_existing_account">Add existing account</string>
|
||||||
<string name="register_new_account">Register new account</string>
|
<string name="register_new_account">Register new account</string>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<style name="ConversationsTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
<style name="ConversationsTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
<item name="colorPrimary">@color/orange600</item>
|
<item name="colorPrimary">@color/orange600</item>
|
||||||
|
@ -114,13 +114,12 @@
|
||||||
<item type="reference" name="icon_enable_undecided_device">@drawable/ic_new_releases_black_24dp</item>
|
<item type="reference" name="icon_enable_undecided_device">@drawable/ic_new_releases_black_24dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="ConversationsTheme.Dark" parent="ConversationsTheme.Dark.Base" />
|
<style name="ConversationsTheme.Dark" parent="Theme.AppCompat.NoActionBar">
|
||||||
|
|
||||||
<style name="ConversationsTheme.Dark.Base" parent="Theme.AppCompat.NoActionBar">
|
|
||||||
<item name="colorPrimary">@color/orange800</item>
|
<item name="colorPrimary">@color/orange800</item>
|
||||||
<item name="colorPrimaryDark">@color/orange900</item>
|
<item name="colorPrimaryDark">@color/orange900</item>
|
||||||
<item name="colorAccent">@color/blue_a100</item>
|
<item name="colorAccent">@color/blue_a100</item>
|
||||||
<item name="popupOverlayStyle">@style/ThemeOverlay.AppCompat.Dark</item>
|
<item name="popupOverlayStyle">@style/ThemeOverlay.AppCompat.Dark</item>
|
||||||
|
<item name="android:navigationBarColor" tools:targetApi="21">@color/black</item>
|
||||||
|
|
||||||
<item name="color_background_primary">@color/black</item>
|
<item name="color_background_primary">@color/black</item>
|
||||||
<item name="color_background_secondary">@color/black</item>
|
<item name="color_background_secondary">@color/black</item>
|
||||||
|
@ -281,6 +280,7 @@
|
||||||
<item name="android:windowFullscreen">true</item>
|
<item name="android:windowFullscreen">true</item>
|
||||||
<item name="android:windowContentOverlay">@null</item>
|
<item name="android:windowContentOverlay">@null</item>
|
||||||
<item name="android:windowBackground">@android:color/black</item>
|
<item name="android:windowBackground">@android:color/black</item>
|
||||||
|
<item name="android:navigationBarColor" tools:targetApi="21">@color/black</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
|
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
|
||||||
|
|
|
@ -19,4 +19,8 @@
|
||||||
<string name="no_microphone_permission">Quicksy doit avoir accès au microphone</string>
|
<string name="no_microphone_permission">Quicksy doit avoir accès au microphone</string>
|
||||||
<string name="foreground_service_channel_description">Cette catégorie de notification est utilisée pour afficher une notification permanente indiquant que Quicksy est en cours d\'exécution.</string>
|
<string name="foreground_service_channel_description">Cette catégorie de notification est utilisée pour afficher une notification permanente indiquant que Quicksy est en cours d\'exécution.</string>
|
||||||
<string name="set_profile_picture">Photo de profil Quicksy</string>
|
<string name="set_profile_picture">Photo de profil Quicksy</string>
|
||||||
|
<string name="not_available_in_your_country">Quicksy n\'est pas disponible dans votre pays.</string>
|
||||||
|
<string name="unable_to_verify_server_identity">Vérification de l\'identité du serveur impossible.</string>
|
||||||
|
<string name="unknown_security_error">Erreur de sécurité inconnue.</string>
|
||||||
|
<string name="timeout_while_connecting_to_server">Timeout lors de la connexion au serveur.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue