Merge tag '2.8.7' into develop
This commit is contained in:
commit
c57d5375e0
|
@ -11,7 +11,7 @@ android:
|
|||
- '.+'
|
||||
before_script:
|
||||
- mkdir libs
|
||||
- wget -O libs/libwebrtc-m81.aar http://gultsch.de/files/libwebrtc-m81.aar
|
||||
- wget -O libs/libwebrtc-m83.aar http://gultsch.de/files/libwebrtc-m83.aar
|
||||
script:
|
||||
- ./gradlew assembleConversationsFreeSystemRelease
|
||||
- ./gradlew assembleQuicksyFreeCompatRelease
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
# Changelog
|
||||
|
||||
### Version 2.8.7
|
||||
|
||||
* Show help button if A/V call fails
|
||||
* Fixed some annoying crashes
|
||||
* Fixed Jingle connections (file transfer + calls) with bare JIDs
|
||||
|
||||
### Version 2.8.6
|
||||
|
||||
* Offer to record voice message when callee is busy
|
||||
|
|
|
@ -80,7 +80,7 @@ dependencies {
|
|||
implementation 'com.squareup.okhttp3:okhttp:3.12.12'
|
||||
implementation 'com.google.guava:guava:27.1-android'
|
||||
quicksyImplementation 'io.michaelrocks:libphonenumber-android:8.11.1'
|
||||
//implementation fileTree(include: ['libwebrtc-m81.aar'], dir: 'libs')
|
||||
//implementation fileTree(include: ['libwebrtc-m83.aar'], dir: 'libs')
|
||||
implementation 'org.webrtc:google-webrtc:1.0.+'
|
||||
}
|
||||
|
||||
|
@ -96,8 +96,8 @@ android {
|
|||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 25
|
||||
versionCode 390
|
||||
versionName "2.8.6"
|
||||
versionCode 393
|
||||
versionName "2.8.7"
|
||||
archivesBaseName += "-$versionName"
|
||||
applicationId "eu.sum7.conversations"
|
||||
resValue "string", "applicationId", applicationId
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
• Show help button if A/V call fails
|
||||
• Fixed some annoying crashes
|
||||
• Fixed Jingle connections (file transfer + calls) with bare JIDs
|
|
@ -1,6 +1,7 @@
|
|||
package eu.siacs.conversations;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -35,11 +36,11 @@ public final class Config {
|
|||
public static final String LOGTAG = BuildConfig.LOGTAG;
|
||||
|
||||
public static final Jid BUG_REPORTS = Jid.of("bugs@chat.sum7.eu");
|
||||
|
||||
public static final Uri HELP = Uri.parse("https://sum7.eu/chat");
|
||||
|
||||
public static final String DOMAIN_LOCK = null; //only allow account creation for this domain
|
||||
public static final String MAGIC_CREATE_DOMAIN = "chat.sum7.eu";
|
||||
public static final String QUICKSY_DOMAIN = "quicksy.im";
|
||||
public static final Jid QUICKSY_DOMAIN = Jid.of("quicksy.im");
|
||||
|
||||
public static final String CHANNEL_DISCOVERY = "https://search.jabber.network";
|
||||
|
||||
|
|
|
@ -134,7 +134,7 @@ public class Contact implements ListItem, Blockable {
|
|||
return this.systemName;
|
||||
} else if (!TextUtils.isEmpty(this.serverName)) {
|
||||
return this.serverName;
|
||||
} else if (!TextUtils.isEmpty(this.presenceName) && ((QuickConversationsService.isQuicksy() && Config.QUICKSY_DOMAIN.equals(jid.getDomain().toEscapedString())) ||mutualPresenceSubscription())) {
|
||||
} else if (!TextUtils.isEmpty(this.presenceName) && ((QuickConversationsService.isQuicksy() && JidHelper.isQuicksyDomain(jid.getDomain())) ||mutualPresenceSubscription())) {
|
||||
return this.presenceName;
|
||||
} else if (jid.getLocal() != null) {
|
||||
return JidHelper.localPartOrFallback(jid);
|
||||
|
|
|
@ -1006,7 +1006,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
|
|||
&& !contact.isOwnServer()
|
||||
&& !contact.showInContactList()
|
||||
&& !contact.isSelf()
|
||||
&& !Config.QUICKSY_DOMAIN.equals(contact.getJid().toEscapedString())
|
||||
&& !JidHelper.isQuicksyDomain(contact.getJid())
|
||||
&& sentMessagesCount() == 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ import android.graphics.Color;
|
|||
import android.text.SpannableStringBuilder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
@ -21,6 +24,7 @@ import java.util.Set;
|
|||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
|
||||
import eu.siacs.conversations.services.AvatarService;
|
||||
import eu.siacs.conversations.ui.util.PresenceSelector;
|
||||
import eu.siacs.conversations.utils.CryptoHelper;
|
||||
import eu.siacs.conversations.utils.Emoticons;
|
||||
import eu.siacs.conversations.utils.GeoHelper;
|
||||
|
@ -531,7 +535,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
}
|
||||
|
||||
public Set<ReadByMarker> getReadByMarkers() {
|
||||
return Collections.unmodifiableSet(this.readByMarkers);
|
||||
return ImmutableSet.copyOf(this.readByMarkers);
|
||||
}
|
||||
|
||||
boolean similar(Message message) {
|
||||
|
@ -745,19 +749,12 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
}
|
||||
|
||||
public boolean fixCounterpart() {
|
||||
Presences presences = conversation.getContact().getPresences();
|
||||
if (counterpart != null && presences.has(counterpart.getResource())) {
|
||||
final Presences presences = conversation.getContact().getPresences();
|
||||
if (counterpart != null && presences.has(Strings.nullToEmpty(counterpart.getResource()))) {
|
||||
return true;
|
||||
} else if (presences.size() >= 1) {
|
||||
try {
|
||||
counterpart = Jid.of(conversation.getJid().getLocal(),
|
||||
conversation.getJid().getDomain(),
|
||||
presences.toResourceArray()[0]);
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
counterpart = null;
|
||||
return false;
|
||||
}
|
||||
counterpart = PresenceSelector.getNextCounterpart(getContact(),presences.toResourceArray()[0]);
|
||||
return true;
|
||||
} else {
|
||||
counterpart = null;
|
||||
return false;
|
||||
|
|
|
@ -836,6 +836,10 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
for (Element child : packet.getChildren()) {
|
||||
if (Namespace.JINGLE_MESSAGE.equals(child.getNamespace()) && JINGLE_MESSAGE_ELEMENT_NAMES.contains(child.getName())) {
|
||||
final String action = child.getName();
|
||||
final String sessionId = child.getAttribute("id");
|
||||
if (sessionId == null) {
|
||||
break;
|
||||
}
|
||||
if (query == null) {
|
||||
if (serverMsgId == null) {
|
||||
serverMsgId = extractStanzaId(account, packet);
|
||||
|
@ -845,10 +849,6 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
processMessageReceipts(account, packet, query);
|
||||
}
|
||||
} else if (query.isCatchup()) {
|
||||
final String sessionId = child.getAttribute("id");
|
||||
if (sessionId == null) {
|
||||
break;
|
||||
}
|
||||
if ("propose".equals(action)) {
|
||||
final Element description = child.findChild("description");
|
||||
final String namespace = description == null ? null : description.getNamespace();
|
||||
|
@ -872,7 +872,6 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
c.add(message);
|
||||
mXmppConnectionService.databaseBackend.createMessage(message);
|
||||
}
|
||||
|
||||
} else if ("proceed".equals(action)) {
|
||||
//status needs to be flipped to find the original propose
|
||||
final Conversation c = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), false, false);
|
||||
|
@ -890,6 +889,37 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
//MAM reloads (non catchups
|
||||
if ("propose".equals(action)) {
|
||||
final Element description = child.findChild("description");
|
||||
final String namespace = description == null ? null : description.getNamespace();
|
||||
if (Namespace.JINGLE_APPS_RTP.equals(namespace)) {
|
||||
final Conversation c = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), false, false);
|
||||
final Message preExistingMessage = c.findRtpSession(sessionId, status);
|
||||
if (preExistingMessage != null) {
|
||||
preExistingMessage.setServerMsgId(serverMsgId);
|
||||
mXmppConnectionService.updateMessage(preExistingMessage);
|
||||
break;
|
||||
}
|
||||
final Message message = new Message(
|
||||
c,
|
||||
status,
|
||||
Message.TYPE_RTP_SESSION,
|
||||
sessionId
|
||||
);
|
||||
message.setServerMsgId(serverMsgId);
|
||||
message.setTime(timestamp);
|
||||
message.setBody(new RtpSessionStatus(true, 0).toString());
|
||||
if (query.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
|
||||
c.prepend(query.getActualInThisQuery(), message);
|
||||
} else {
|
||||
c.add(message);
|
||||
}
|
||||
query.incrementActualMessageCount();
|
||||
mXmppConnectionService.databaseBackend.createMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -704,7 +704,7 @@ public class FileBackend {
|
|||
return pos > 0 ? filename.substring(pos + 1) : null;
|
||||
}
|
||||
|
||||
private void copyImageToPrivateStorage(File file, Uri image, int sampleSize) throws FileCopyException {
|
||||
private void copyImageToPrivateStorage(File file, Uri image, int sampleSize) throws FileCopyException, NotAnImageFileException {
|
||||
file.getParentFile().mkdirs();
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
|
@ -724,7 +724,7 @@ public class FileBackend {
|
|||
originalBitmap = BitmapFactory.decodeStream(is, null, options);
|
||||
is.close();
|
||||
if (originalBitmap == null) {
|
||||
throw new FileCopyException(R.string.error_not_an_image_file);
|
||||
throw new NotAnImageFileException();
|
||||
}
|
||||
Bitmap scaledBitmap = resize(originalBitmap, Config.IMAGE_SIZE);
|
||||
int rotation = getRotation(image);
|
||||
|
@ -763,12 +763,12 @@ public class FileBackend {
|
|||
}
|
||||
}
|
||||
|
||||
public void copyImageToPrivateStorage(File file, Uri image) throws FileCopyException {
|
||||
public void copyImageToPrivateStorage(File file, Uri image) throws FileCopyException, NotAnImageFileException {
|
||||
Log.d(Config.LOGTAG, "copy image (" + image.toString() + ") to private storage " + file.getAbsolutePath());
|
||||
copyImageToPrivateStorage(file, image, 0);
|
||||
}
|
||||
|
||||
public void copyImageToPrivateStorage(Message message, Uri image) throws FileCopyException {
|
||||
public void copyImageToPrivateStorage(Message message, Uri image) throws FileCopyException, NotAnImageFileException {
|
||||
switch (Config.IMAGE_FORMAT) {
|
||||
case JPEG:
|
||||
message.setRelativeFilePath(message.getUuid() + ".jpg");
|
||||
|
@ -1420,11 +1420,14 @@ public class FileBackend {
|
|||
}
|
||||
}
|
||||
|
||||
public class FileCopyException extends Exception {
|
||||
private static final long serialVersionUID = -1010013599132881427L;
|
||||
public static class NotAnImageFileException extends Exception {
|
||||
|
||||
}
|
||||
|
||||
public static class FileCopyException extends Exception {
|
||||
private int resId;
|
||||
|
||||
public FileCopyException(int resId) {
|
||||
private FileCopyException(int resId) {
|
||||
this.resId = resId;
|
||||
}
|
||||
|
||||
|
|
|
@ -567,19 +567,23 @@ public class XmppConnectionService extends Service {
|
|||
mFileAddingExecutor.execute(() -> {
|
||||
try {
|
||||
getFileBackend().copyImageToPrivateStorage(message, uri);
|
||||
if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
|
||||
final PgpEngine pgpEngine = getPgpEngine();
|
||||
if (pgpEngine != null) {
|
||||
pgpEngine.encrypt(message, callback);
|
||||
} else if (callback != null) {
|
||||
callback.error(R.string.unable_to_connect_to_keychain, null);
|
||||
}
|
||||
} else {
|
||||
sendMessage(message);
|
||||
callback.success(message);
|
||||
}
|
||||
} catch (FileBackend.NotAnImageFileException e) {
|
||||
attachFileToConversation(conversation, uri, mimeType, callback);
|
||||
return;
|
||||
} catch (final FileBackend.FileCopyException e) {
|
||||
callback.error(e.getResId(), message);
|
||||
return;
|
||||
}
|
||||
if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP) {
|
||||
final PgpEngine pgpEngine = getPgpEngine();
|
||||
if (pgpEngine != null) {
|
||||
pgpEngine.encrypt(message, callback);
|
||||
} else if (callback != null) {
|
||||
callback.error(R.string.unable_to_connect_to_keychain, null);
|
||||
}
|
||||
} else {
|
||||
sendMessage(message);
|
||||
callback.success(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -2774,6 +2778,7 @@ public class XmppConnectionService extends Service {
|
|||
updateConversationUi();
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchConferenceMembers(final Conversation conversation) {
|
||||
final Account account = conversation.getAccount();
|
||||
final AxolotlService axolotlService = account.getAxolotlService();
|
||||
|
@ -3299,6 +3304,10 @@ public class XmppConnectionService extends Service {
|
|||
updateConversationUi();
|
||||
}
|
||||
|
||||
public void createMessageAsync(final Message message) {
|
||||
mDatabaseWriterExecutor.execute(() -> databaseBackend.createMessage(message));
|
||||
}
|
||||
|
||||
public void updateMessage(Message message, String uuid) {
|
||||
if (!databaseBackend.updateMessage(message, uuid)) {
|
||||
Log.e(Config.LOGTAG, "error updated message in DB after edit");
|
||||
|
@ -4137,7 +4146,7 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
}
|
||||
if (Config.QUICKSY_DOMAIN != null) {
|
||||
hosts.remove(Config.QUICKSY_DOMAIN); //we only want to show this when we type a e164 number
|
||||
hosts.remove(Config.QUICKSY_DOMAIN.toEscapedString()); //we only want to show this when we type a e164 number
|
||||
}
|
||||
if (Config.DOMAIN_LOCK != null) {
|
||||
hosts.add(Config.DOMAIN_LOCK);
|
||||
|
@ -4436,34 +4445,38 @@ public class XmppConnectionService extends Service {
|
|||
|
||||
public void fetchCaps(Account account, final Jid jid, final Presence presence) {
|
||||
final Pair<String, String> key = new Pair<>(presence.getHash(), presence.getVer());
|
||||
ServiceDiscoveryResult disco = getCachedServiceDiscoveryResult(key);
|
||||
final ServiceDiscoveryResult disco = getCachedServiceDiscoveryResult(key);
|
||||
if (disco != null) {
|
||||
presence.setServiceDiscoveryResult(disco);
|
||||
} else {
|
||||
if (!account.inProgressDiscoFetches.contains(key)) {
|
||||
account.inProgressDiscoFetches.add(key);
|
||||
IqPacket request = new IqPacket(IqPacket.TYPE.GET);
|
||||
request.setTo(jid);
|
||||
final String node = presence.getNode();
|
||||
final String ver = presence.getVer();
|
||||
final Element query = request.query(Namespace.DISCO_INFO);
|
||||
if (node != null && ver != null) {
|
||||
query.setAttribute("node", node + "#" + ver);
|
||||
}
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": making disco request for " + key.second + " to " + jid);
|
||||
sendIqPacket(account, request, (a, response) -> {
|
||||
if (response.getType() == IqPacket.TYPE.RESULT) {
|
||||
ServiceDiscoveryResult discoveryResult = new ServiceDiscoveryResult(response);
|
||||
if (presence.getVer().equals(discoveryResult.getVer())) {
|
||||
databaseBackend.insertDiscoveryResult(discoveryResult);
|
||||
injectServiceDiscoveryResult(a.getRoster(), presence.getHash(), presence.getVer(), discoveryResult);
|
||||
} else {
|
||||
Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + discoveryResult.getVer());
|
||||
}
|
||||
}
|
||||
a.inProgressDiscoFetches.remove(key);
|
||||
});
|
||||
if (account.inProgressDiscoFetches.contains(key)) {
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": skipping duplicate disco request for " + key.second + " to " + jid);
|
||||
return;
|
||||
}
|
||||
account.inProgressDiscoFetches.add(key);
|
||||
final IqPacket request = new IqPacket(IqPacket.TYPE.GET);
|
||||
request.setTo(jid);
|
||||
final String node = presence.getNode();
|
||||
final String ver = presence.getVer();
|
||||
final Element query = request.query(Namespace.DISCO_INFO);
|
||||
if (node != null && ver != null) {
|
||||
query.setAttribute("node", node + "#" + ver);
|
||||
}
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": making disco request for " + key.second + " to " + jid);
|
||||
sendIqPacket(account, request, (a, response) -> {
|
||||
if (response.getType() == IqPacket.TYPE.RESULT) {
|
||||
ServiceDiscoveryResult discoveryResult = new ServiceDiscoveryResult(response);
|
||||
if (presence.getVer().equals(discoveryResult.getVer())) {
|
||||
databaseBackend.insertDiscoveryResult(discoveryResult);
|
||||
injectServiceDiscoveryResult(a.getRoster(), presence.getHash(), presence.getVer(), discoveryResult);
|
||||
} else {
|
||||
Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + discoveryResult.getVer());
|
||||
}
|
||||
} else {
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to fetch caps from " + jid);
|
||||
}
|
||||
a.inProgressDiscoFetches.remove(key);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -263,7 +263,7 @@ public class ChannelDiscoveryActivity extends XmppActivity implements MenuItem.O
|
|||
}
|
||||
|
||||
public void joinChannelSearchResult(String selectedAccount, Room result) {
|
||||
final Jid jid = Config.DOMAIN_LOCK == null ? Jid.ofEscaped(selectedAccount) : Jid.ofEscaped(selectedAccount, Config.DOMAIN_LOCK, null);
|
||||
final Jid jid = Config.DOMAIN_LOCK == null ? Jid.ofEscaped(selectedAccount) : Jid.ofLocalAndDomainEscaped(selectedAccount, Config.DOMAIN_LOCK);
|
||||
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);
|
||||
|
|
|
@ -3,6 +3,7 @@ package eu.siacs.conversations.ui;
|
|||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.PictureInPictureParams;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
|
@ -17,6 +18,8 @@ import android.support.annotation.RequiresApi;
|
|||
import android.support.annotation.StringRes;
|
||||
import android.util.Log;
|
||||
import android.util.Rational;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
@ -53,6 +56,7 @@ import eu.siacs.conversations.utils.PermissionUtils;
|
|||
import eu.siacs.conversations.utils.TimeFrameUtils;
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
|
||||
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
|
||||
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
|
||||
import eu.siacs.conversations.xmpp.jingle.Media;
|
||||
import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
|
||||
|
@ -77,8 +81,13 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
|||
RtpEndUserState.APPLICATION_ERROR,
|
||||
RtpEndUserState.DECLINED_OR_BUSY,
|
||||
RtpEndUserState.CONNECTIVITY_ERROR,
|
||||
RtpEndUserState.CONNECTIVITY_LOST_ERROR,
|
||||
RtpEndUserState.RETRACTED
|
||||
);
|
||||
private static final List<RtpEndUserState> STATES_SHOWING_HELP_BUTTON = Arrays.asList(
|
||||
RtpEndUserState.APPLICATION_ERROR,
|
||||
RtpEndUserState.CONNECTIVITY_ERROR
|
||||
);
|
||||
private static final String PROXIMITY_WAKE_LOCK_TAG = "conversations:in-rtp-session";
|
||||
private static final int REQUEST_ACCEPT_CALL = 0x1111;
|
||||
private WeakReference<JingleRtpConnection> rtpConnectionReference;
|
||||
|
@ -122,6 +131,45 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
|||
setSupportActionBar(binding.toolbar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(final Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.activity_rtp_session, menu);
|
||||
final MenuItem help = menu.findItem(R.id.action_help);
|
||||
help.setVisible(isHelpButtonVisible());
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
private boolean isHelpButtonVisible() {
|
||||
try {
|
||||
return STATES_SHOWING_HELP_BUTTON.contains(requireRtpConnection().getEndUserState());
|
||||
} catch (IllegalStateException e) {
|
||||
final Intent intent = getIntent();
|
||||
final String state = intent != null ? intent.getStringExtra(EXTRA_LAST_REPORTED_STATE) : null;
|
||||
if (state != null) {
|
||||
return STATES_SHOWING_HELP_BUTTON.contains(RtpEndUserState.valueOf(state));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
if (item.getItemId() == R.id.action_help) {
|
||||
launchHelpInBrowser();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void launchHelpInBrowser() {
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, Config.HELP);
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (final ActivityNotFoundException e) {
|
||||
Toast.makeText(this, R.string.no_application_found_to_open_link, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void endCall(View view) {
|
||||
endCall();
|
||||
}
|
||||
|
@ -233,8 +281,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
|||
}
|
||||
}
|
||||
|
||||
private void putProximityWakeLockInProperState() {
|
||||
if (requireRtpConnection().getAudioManager().getSelectedAudioDevice() == AppRTCAudioManager.AudioDevice.EARPIECE) {
|
||||
private void putProximityWakeLockInProperState(final AppRTCAudioManager.AudioDevice audioDevice) {
|
||||
if (audioDevice == AppRTCAudioManager.AudioDevice.EARPIECE) {
|
||||
acquireProximityWakeLock();
|
||||
} else {
|
||||
releaseProximityWakeLock();
|
||||
|
@ -300,6 +348,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
|||
updateButtonConfiguration(state);
|
||||
updateStateDisplay(state);
|
||||
updateProfilePicture(state);
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
binding.with.setText(account.getRoster().getContact(with).getDisplayName());
|
||||
}
|
||||
|
@ -427,7 +476,13 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
|||
final WeakReference<JingleRtpConnection> reference = xmppConnectionService.getJingleConnectionManager()
|
||||
.findJingleRtpConnection(account, with, sessionId);
|
||||
if (reference == null || reference.get() == null) {
|
||||
throw new IllegalStateException("failed to initialize activity with running rtp session. session not found");
|
||||
final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession = xmppConnectionService
|
||||
.getJingleConnectionManager().getTerminalSessionState(with, sessionId);
|
||||
if (terminatedRtpSession == null) {
|
||||
throw new IllegalStateException("failed to initialize activity with running rtp session. session not found");
|
||||
}
|
||||
initializeWithTerminatedSessionState(account, with, terminatedRtpSession);
|
||||
return true;
|
||||
}
|
||||
this.rtpConnectionReference = reference;
|
||||
final RtpEndUserState currentState = requireRtpConnection().getEndUserState();
|
||||
|
@ -448,9 +503,26 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
|||
updateStateDisplay(currentState, media);
|
||||
updateButtonConfiguration(currentState, media);
|
||||
updateProfilePicture(currentState);
|
||||
invalidateOptionsMenu();
|
||||
return false;
|
||||
}
|
||||
|
||||
private void initializeWithTerminatedSessionState(final Account account, final Jid with, final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession) {
|
||||
Log.d(Config.LOGTAG, "initializeWithTerminatedSessionState()");
|
||||
if (terminatedRtpSession.state == RtpEndUserState.ENDED) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
RtpEndUserState state = terminatedRtpSession.state;
|
||||
resetIntent(account, with, terminatedRtpSession.state, terminatedRtpSession.media);
|
||||
updateButtonConfiguration(state);
|
||||
updateStateDisplay(state);
|
||||
updateProfilePicture(state);
|
||||
updateCallDuration();
|
||||
invalidateOptionsMenu();
|
||||
binding.with.setText(account.getRoster().getContact(with).getDisplayName());
|
||||
}
|
||||
|
||||
private void reInitializeActivityWithRunningRtpSession(final Account account, Jid with, String sessionId) {
|
||||
runOnUiThread(() -> initializeActivityWithRunningRtpSession(account, with, sessionId));
|
||||
resetIntent(account, with, sessionId);
|
||||
|
@ -512,6 +584,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
|||
case CONNECTIVITY_ERROR:
|
||||
setTitle(R.string.rtp_state_connectivity_error);
|
||||
break;
|
||||
case CONNECTIVITY_LOST_ERROR:
|
||||
setTitle(R.string.rtp_state_connectivity_lost_error);
|
||||
break;
|
||||
case RETRACTED:
|
||||
setTitle(R.string.rtp_state_retracted);
|
||||
break;
|
||||
|
@ -577,7 +652,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
|||
this.binding.acceptCall.setOnClickListener(this::recordVoiceMail);
|
||||
this.binding.acceptCall.setImageResource(R.drawable.ic_voicemail_white_24dp);
|
||||
this.binding.acceptCall.setVisibility(View.VISIBLE);
|
||||
} else if (asList(RtpEndUserState.CONNECTIVITY_ERROR, RtpEndUserState.APPLICATION_ERROR, RtpEndUserState.RETRACTED).contains(state)) {
|
||||
} else if (asList(
|
||||
RtpEndUserState.CONNECTIVITY_ERROR,
|
||||
RtpEndUserState.CONNECTIVITY_LOST_ERROR,
|
||||
RtpEndUserState.APPLICATION_ERROR,
|
||||
RtpEndUserState.RETRACTED
|
||||
).contains(state)) {
|
||||
this.binding.rejectCall.setOnClickListener(this::exit);
|
||||
this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp);
|
||||
this.binding.rejectCall.setVisibility(View.VISIBLE);
|
||||
|
@ -926,6 +1006,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
|||
updateButtonConfiguration(state, media);
|
||||
updateVideoViews(state);
|
||||
updateProfilePicture(state, contact);
|
||||
invalidateOptionsMenu();
|
||||
});
|
||||
if (END_CARD.contains(state)) {
|
||||
final JingleRtpConnection rtpConnection = requireRtpConnection();
|
||||
|
@ -956,7 +1037,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
|||
} else if (END_CARD.contains(endUserState)) {
|
||||
Log.d(Config.LOGTAG, "onAudioDeviceChanged() nothing to do because end card has been reached");
|
||||
} else {
|
||||
putProximityWakeLockInProperState();
|
||||
putProximityWakeLockInProperState(selectedAudioDevice);
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
Log.d(Config.LOGTAG, "RTP connection was not available when audio device changed");
|
||||
|
@ -974,6 +1055,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
|
|||
updateStateDisplay(state);
|
||||
updateButtonConfiguration(state);
|
||||
updateProfilePicture(state);
|
||||
invalidateOptionsMenu();
|
||||
});
|
||||
resetIntent(account, with, state, actionToMedia(currentIntent.getAction()));
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -27,7 +27,7 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
|
|||
if (split.length == 1) {
|
||||
final String local = split[0].toLowerCase(Locale.ENGLISH);
|
||||
if (Config.QUICKSY_DOMAIN != null && E164_PATTERN.matcher(local).matches()) {
|
||||
suggestions.add(local + '@' + Config.QUICKSY_DOMAIN);
|
||||
suggestions.add(local + '@' + Config.QUICKSY_DOMAIN.toEscapedString());
|
||||
} else {
|
||||
for (String domain : domains) {
|
||||
suggestions.add(local + '@' + domain);
|
||||
|
|
|
@ -35,7 +35,6 @@ import android.content.Intent;
|
|||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
@ -43,7 +42,6 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.utils.Compatibility;
|
||||
import eu.siacs.conversations.utils.MimeUtils;
|
||||
|
||||
|
@ -113,7 +111,7 @@ public class Attachment implements Parcelable {
|
|||
}
|
||||
|
||||
public static boolean canBeSendInband(final List<Attachment> attachments) {
|
||||
for(Attachment attachment : attachments) {
|
||||
for (Attachment attachment : attachments) {
|
||||
if (attachment.type != Type.LOCATION) {
|
||||
return false;
|
||||
}
|
||||
|
@ -122,21 +120,21 @@ public class Attachment implements Parcelable {
|
|||
}
|
||||
|
||||
public static List<Attachment> of(final Context context, Uri uri, Type type) {
|
||||
final String mime = type == Type.LOCATION ?null :MimeUtils.guessMimeTypeFromUri(context, uri);
|
||||
final String mime = type == Type.LOCATION ? null : MimeUtils.guessMimeTypeFromUri(context, uri);
|
||||
return Collections.singletonList(new Attachment(uri, type, mime));
|
||||
}
|
||||
|
||||
public static List<Attachment> of(final Context context, List<Uri> uris) {
|
||||
List<Attachment> attachments = new ArrayList<>();
|
||||
for(Uri uri : uris) {
|
||||
for (Uri uri : uris) {
|
||||
final String mime = MimeUtils.guessMimeTypeFromUri(context, uri);
|
||||
attachments.add(new Attachment(uri, mime != null && mime.startsWith("image/") ? Type.IMAGE : Type.FILE,mime));
|
||||
attachments.add(new Attachment(uri, mime != null && isImage(mime) ? Type.IMAGE : Type.FILE, mime));
|
||||
}
|
||||
return attachments;
|
||||
}
|
||||
|
||||
public static Attachment of(UUID uuid, final File file, String mime) {
|
||||
return new Attachment(uuid, Uri.fromFile(file),mime != null && (mime.startsWith("image/") || mime.startsWith("video/")) ? Type.IMAGE : Type.FILE, mime);
|
||||
return new Attachment(uuid, Uri.fromFile(file), mime != null && (isImage(mime) || mime.startsWith("video/")) ? Type.IMAGE : Type.FILE, mime);
|
||||
}
|
||||
|
||||
public static List<Attachment> extractAttachments(final Context context, final Intent intent, Type type) {
|
||||
|
@ -151,9 +149,7 @@ public class Attachment implements Parcelable {
|
|||
if (clipData != null) {
|
||||
for (int i = 0; i < clipData.getItemCount(); ++i) {
|
||||
final Uri uri = clipData.getItemAt(i).getUri();
|
||||
Log.d(Config.LOGTAG,"uri="+uri+" contentType="+contentType);
|
||||
final String mime = MimeUtils.guessMimeTypeFromUriAndMime(context, uri, contentType);
|
||||
Log.d(Config.LOGTAG,"mime="+mime);
|
||||
uris.add(new Attachment(uri, type, mime));
|
||||
}
|
||||
}
|
||||
|
@ -165,12 +161,12 @@ public class Attachment implements Parcelable {
|
|||
}
|
||||
|
||||
public boolean renderThumbnail() {
|
||||
return type == Type.IMAGE || (type == Type.FILE && mime != null && renderFileThumbnail(mime));
|
||||
return type == Type.IMAGE || (type == Type.FILE && mime != null && renderFileThumbnail(mime));
|
||||
}
|
||||
|
||||
private static boolean renderFileThumbnail(final String mime) {
|
||||
return mime.startsWith("video/")
|
||||
|| mime.startsWith("image/")
|
||||
|| isImage(mime)
|
||||
|| (Compatibility.runsTwentyOne() && "application/pdf".equals(mime));
|
||||
}
|
||||
|
||||
|
@ -181,4 +177,8 @@ public class Attachment implements Parcelable {
|
|||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
private static boolean isImage(final String mime) {
|
||||
return mime.startsWith("image/") && !mime.equals("image/svg+xml");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,12 +106,24 @@ public class PresenceSelector {
|
|||
builder.setPositiveButton(
|
||||
R.string.ok,
|
||||
(dialog, which) -> onFullJidSelected.onFullJidSelected(
|
||||
Jid.of(contact.getJid().getLocal(), contact.getJid().getDomain(), resourceArray[selectedResource.get()])
|
||||
getNextCounterpart(contact, resourceArray[selectedResource.get()])
|
||||
)
|
||||
);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
public static Jid getNextCounterpart(final Contact contact, final String resource) {
|
||||
return getNextCounterpart(contact.getJid(), resource);
|
||||
}
|
||||
|
||||
public static Jid getNextCounterpart(final Jid jid, final String resource) {
|
||||
if (resource.isEmpty()) {
|
||||
return jid.asBareJid();
|
||||
} else {
|
||||
return jid.withResource(resource);
|
||||
}
|
||||
}
|
||||
|
||||
public static void warnMutualPresenceSubscription(Activity activity, final Conversation conversation, final OnPresenceSelected listener) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setTitle(conversation.getContact().getJid().toString());
|
||||
|
|
|
@ -39,7 +39,7 @@ public class AccountUtils {
|
|||
for (Account account : service.getAccounts()) {
|
||||
if (account.getStatus() != Account.State.DISABLED) {
|
||||
if (Config.DOMAIN_LOCK != null) {
|
||||
accounts.add(account.getJid().toEscapedString());
|
||||
accounts.add(account.getJid().getEscapedLocal());
|
||||
} else {
|
||||
accounts.add(account.getJid().asBareJid().toEscapedString());
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import java.util.Arrays;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.xmpp.InvalidJid;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
|
||||
|
@ -59,4 +60,8 @@ public class JidHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean isQuicksyDomain(final Jid jid) {
|
||||
return Config.QUICKSY_DOMAIN != null && Config.QUICKSY_DOMAIN.equals(jid.getDomain());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,10 +16,10 @@ public abstract class AbstractJingleConnection {
|
|||
public static final String JINGLE_MESSAGE_PROPOSE_ID_PREFIX = "jm-propose-";
|
||||
public static final String JINGLE_MESSAGE_PROCEED_ID_PREFIX = "jm-proceed-";
|
||||
|
||||
protected final JingleConnectionManager jingleConnectionManager;
|
||||
final JingleConnectionManager jingleConnectionManager;
|
||||
protected final XmppConnectionService xmppConnectionService;
|
||||
protected final Id id;
|
||||
protected final Jid initiator;
|
||||
private final Jid initiator;
|
||||
|
||||
AbstractJingleConnection(final JingleConnectionManager jingleConnectionManager, final Id id, final Jid initiator) {
|
||||
this.jingleConnectionManager = jingleConnectionManager;
|
||||
|
@ -47,8 +47,9 @@ public abstract class AbstractJingleConnection {
|
|||
public final String sessionId;
|
||||
|
||||
private Id(final Account account, final Jid with, final String sessionId) {
|
||||
Preconditions.checkNotNull(account);
|
||||
Preconditions.checkNotNull(with);
|
||||
Preconditions.checkArgument(with.isFullJid());
|
||||
Preconditions.checkNotNull(sessionId);
|
||||
this.account = account;
|
||||
this.with = with;
|
||||
this.sessionId = sessionId;
|
||||
|
|
|
@ -147,7 +147,6 @@ public class JingleCandidate {
|
|||
}
|
||||
|
||||
public String toString() {
|
||||
return this.getHost() + ":" + this.getPort() + " (prio="
|
||||
+ this.getPriority() + ")";
|
||||
return String.format("%s:%s (priority=%s,ours=%s)", getHost(), getPort(), getPriority(), isOurs());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import com.google.common.cache.CacheBuilder;
|
|||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.j2objc.annotations.Weak;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.security.SecureRandom;
|
||||
|
@ -57,8 +56,8 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
|||
private final HashMap<RtpSessionProposal, DeviceDiscoveryState> rtpSessionProposals = new HashMap<>();
|
||||
private final ConcurrentHashMap<AbstractJingleConnection.Id, AbstractJingleConnection> connections = new ConcurrentHashMap<>();
|
||||
|
||||
private final Cache<PersistableSessionId, JingleRtpConnection.State> endedSessions = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(30, TimeUnit.MINUTES)
|
||||
private final Cache<PersistableSessionId, TerminatedRtpSession> terminatedSessions = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(24, TimeUnit.HOURS)
|
||||
.build();
|
||||
|
||||
private HashMap<Jid, JingleCandidate> primaryCandidates = new HashMap<>();
|
||||
|
@ -92,7 +91,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
|||
if (FileTransferDescription.NAMESPACES.contains(descriptionNamespace)) {
|
||||
connection = new JingleFileTransferConnection(this, id, from);
|
||||
} else if (Namespace.JINGLE_APPS_RTP.equals(descriptionNamespace) && !usesTor(account)) {
|
||||
final boolean sessionEnded = this.endedSessions.asMap().containsKey(PersistableSessionId.of(id));
|
||||
final boolean sessionEnded = this.terminatedSessions.asMap().containsKey(PersistableSessionId.of(id));
|
||||
final boolean stranger = isWithStrangerAndStrangerNotificationsAreOff(account, id.with);
|
||||
if (isBusy() || sessionEnded || stranger) {
|
||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": rejected session with " + id.with + " because busy. sessionEnded=" + sessionEnded + ", stranger=" + stranger);
|
||||
|
@ -447,6 +446,10 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
|||
}
|
||||
|
||||
void finishConnection(final AbstractJingleConnection connection) {
|
||||
this.connections.remove(connection.getId());
|
||||
}
|
||||
|
||||
void finishConnectionOrThrow(final AbstractJingleConnection connection) {
|
||||
final AbstractJingleConnection.Id id = connection.getId();
|
||||
if (this.connections.remove(id) == null) {
|
||||
throw new IllegalStateException(String.format("Unable to finish connection with id=%s", id.toString()));
|
||||
|
@ -680,8 +683,12 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
|||
throw e;
|
||||
}
|
||||
|
||||
void endSession(AbstractJingleConnection.Id id, final AbstractJingleConnection.State state) {
|
||||
this.endedSessions.put(PersistableSessionId.of(id), state);
|
||||
void setTerminalSessionState(AbstractJingleConnection.Id id, final RtpEndUserState state, final Set<Media> media) {
|
||||
this.terminatedSessions.put(PersistableSessionId.of(id), new TerminatedRtpSession(state, media));
|
||||
}
|
||||
|
||||
public TerminatedRtpSession getTerminalSessionState(final Jid with, final String sessionId) {
|
||||
return this.terminatedSessions.getIfPresent(new PersistableSessionId(with, sessionId));
|
||||
}
|
||||
|
||||
private static class PersistableSessionId {
|
||||
|
@ -712,6 +719,16 @@ public class JingleConnectionManager extends AbstractConnectionManager {
|
|||
}
|
||||
}
|
||||
|
||||
public static class TerminatedRtpSession {
|
||||
public final RtpEndUserState state;
|
||||
public final Set<Media> media;
|
||||
|
||||
TerminatedRtpSession(RtpEndUserState state, Set<Media> media) {
|
||||
this.state = state;
|
||||
this.media = media;
|
||||
}
|
||||
}
|
||||
|
||||
public enum DeviceDiscoveryState {
|
||||
SEARCHING, DISCOVERED, FAILED;
|
||||
|
||||
|
|
|
@ -4,7 +4,10 @@ import android.util.Base64;
|
|||
import android.util.Log;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
@ -197,7 +200,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
|||
}
|
||||
};
|
||||
|
||||
public JingleFileTransferConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) {
|
||||
JingleFileTransferConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) {
|
||||
super(jingleConnectionManager, id, initiator);
|
||||
}
|
||||
|
||||
|
@ -414,15 +417,10 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
|||
}
|
||||
|
||||
private List<String> getRemoteFeatures() {
|
||||
final Jid jid = this.id.with;
|
||||
String resource = jid != null ? jid.getResource() : null;
|
||||
if (resource != null) {
|
||||
Presence presence = this.id.account.getRoster().getContact(jid).getPresences().get(resource);
|
||||
ServiceDiscoveryResult result = presence != null ? presence.getServiceDiscoveryResult() : null;
|
||||
return result == null ? Collections.emptyList() : result.getFeatures();
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final String resource = Strings.nullToEmpty(this.id.with.getResource());
|
||||
final Presence presence = this.id.account.getRoster().getContact(id.with).getPresences().get(resource);
|
||||
final ServiceDiscoveryResult result = presence != null ? presence.getServiceDiscoveryResult() : null;
|
||||
return result == null ? Collections.emptyList() : result.getFeatures();
|
||||
}
|
||||
|
||||
private void init(JinglePacket packet) { //should move to deliverPacket
|
||||
|
@ -442,7 +440,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
|||
try {
|
||||
senders = content.getSenders();
|
||||
} catch (final Exception e) {
|
||||
senders = Content.Senders.INITIATOR;
|
||||
senders = Content.Senders.INITIATOR;
|
||||
}
|
||||
this.contentSenders = senders;
|
||||
this.contentName = content.getAttribute("name");
|
||||
|
@ -632,10 +630,14 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
|||
|
||||
final JinglePacket packet = this.bootstrapPacket(JinglePacket.Action.SESSION_INFO);
|
||||
packet.addJingleChild(checksum);
|
||||
this.sendJinglePacket(packet);
|
||||
xmppConnectionService.sendIqPacket(id.account, packet, (account, response) -> {
|
||||
if (response.getType() == IqPacket.TYPE.ERROR) {
|
||||
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": ignoring error response to our session-info (hash transmission)");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Collection<JingleCandidate> getOurCandidates() {
|
||||
private Collection<JingleCandidate> getOurCandidates() {
|
||||
return Collections2.filter(this.candidates, c -> c != null && c.isOurs());
|
||||
}
|
||||
|
||||
|
@ -825,8 +827,9 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
|||
this.sendFallbackToIbb();
|
||||
}
|
||||
} else {
|
||||
//TODO at this point we can already close other connections to free some resources
|
||||
final JingleCandidate candidate = connection.getCandidate();
|
||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": elected candidate " + candidate.getHost() + ":" + candidate.getPort());
|
||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": elected candidate " + candidate.toString());
|
||||
this.mJingleStatus = JINGLE_STATUS_TRANSMITTING;
|
||||
if (connection.needsActivation()) {
|
||||
if (connection.getCandidate().isOurs()) {
|
||||
|
@ -875,38 +878,23 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
|||
}
|
||||
|
||||
private JingleSocks5Transport chooseConnection() {
|
||||
JingleSocks5Transport connection = null;
|
||||
for (Entry<String, JingleSocks5Transport> cursor : connections
|
||||
.entrySet()) {
|
||||
JingleSocks5Transport currentConnection = cursor.getValue();
|
||||
// Log.d(Config.LOGTAG,"comparing candidate: "+currentConnection.getCandidate().toString());
|
||||
if (currentConnection.isEstablished()
|
||||
&& (currentConnection.getCandidate().isUsedByCounterpart() || (!currentConnection
|
||||
.getCandidate().isOurs()))) {
|
||||
// Log.d(Config.LOGTAG,"is usable");
|
||||
if (connection == null) {
|
||||
connection = currentConnection;
|
||||
} else {
|
||||
if (connection.getCandidate().getPriority() < currentConnection
|
||||
.getCandidate().getPriority()) {
|
||||
connection = currentConnection;
|
||||
} else if (connection.getCandidate().getPriority() == currentConnection
|
||||
.getCandidate().getPriority()) {
|
||||
// Log.d(Config.LOGTAG,"found two candidates with same priority");
|
||||
final List<JingleSocks5Transport> establishedConnections = FluentIterable.from(connections.entrySet())
|
||||
.transform(Entry::getValue)
|
||||
.filter(c -> (c != null && c.isEstablished() && (c.getCandidate().isUsedByCounterpart() || !c.getCandidate().isOurs())))
|
||||
.toSortedList((a, b) -> {
|
||||
final int compare = Integer.compare(b.getCandidate().getPriority(), a.getCandidate().getPriority());
|
||||
if (compare == 0) {
|
||||
if (isInitiator()) {
|
||||
if (currentConnection.getCandidate().isOurs()) {
|
||||
connection = currentConnection;
|
||||
}
|
||||
//pick the one we sent a candidate-used for (meaning not ours)
|
||||
return a.getCandidate().isOurs() ? 1 : -1;
|
||||
} else {
|
||||
if (!currentConnection.getCandidate().isOurs()) {
|
||||
connection = currentConnection;
|
||||
}
|
||||
//pick the one they sent a candidate-used for (meaning ours)
|
||||
return a.getCandidate().isOurs() ? -1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return connection;
|
||||
return compare;
|
||||
});
|
||||
return Iterables.getFirst(establishedConnections, null);
|
||||
}
|
||||
|
||||
private void sendSuccess() {
|
||||
|
@ -1035,7 +1023,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
|||
abort(Reason.CANCEL);
|
||||
}
|
||||
|
||||
void abort(final Reason reason) {
|
||||
private void abort(final Reason reason) {
|
||||
this.disconnectSocks5Connections();
|
||||
if (this.transport instanceof JingleInBandTransport) {
|
||||
this.transport.disconnect();
|
||||
|
@ -1179,7 +1167,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
|||
}
|
||||
}
|
||||
|
||||
public int getJingleStatus() {
|
||||
private int getJingleStatus() {
|
||||
return this.mJingleStatus;
|
||||
}
|
||||
|
||||
|
@ -1222,11 +1210,11 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
|||
jingleConnectionManager.updateConversationUi(false);
|
||||
}
|
||||
|
||||
public String getTransportId() {
|
||||
String getTransportId() {
|
||||
return this.transportId;
|
||||
}
|
||||
|
||||
public FileTransferDescription.Version getFtVersion() {
|
||||
FileTransferDescription.Version getFtVersion() {
|
||||
return this.description.getVersion();
|
||||
}
|
||||
|
||||
|
|
|
@ -814,7 +814,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
|||
} else if (state == PeerConnection.PeerConnectionState.CLOSED) {
|
||||
return RtpEndUserState.ENDING_CALL;
|
||||
} else {
|
||||
return RtpEndUserState.CONNECTIVITY_ERROR;
|
||||
return rtpConnectionStarted == 0 ? RtpEndUserState.CONNECTIVITY_ERROR : RtpEndUserState.CONNECTIVITY_LOST_ERROR;
|
||||
}
|
||||
case REJECTED:
|
||||
case TERMINATED_DECLINED_OR_BUSY:
|
||||
|
@ -831,7 +831,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
|||
case RETRACTED_RACED:
|
||||
return RtpEndUserState.RETRACTED;
|
||||
case TERMINATED_CONNECTIVITY_ERROR:
|
||||
return RtpEndUserState.CONNECTIVITY_ERROR;
|
||||
return rtpConnectionStarted == 0 ? RtpEndUserState.CONNECTIVITY_ERROR : RtpEndUserState.CONNECTIVITY_LOST_ERROR;
|
||||
case TERMINATED_APPLICATION_FAILURE:
|
||||
return RtpEndUserState.APPLICATION_ERROR;
|
||||
}
|
||||
|
@ -912,7 +912,6 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
|||
}
|
||||
if (isInState(State.PROCEED)) {
|
||||
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ending call while in state PROCEED just means ending the connection");
|
||||
this.jingleConnectionManager.endSession(id, State.TERMINATED_SUCCESS);
|
||||
this.webRTCWrapper.close();
|
||||
transitionOrThrow(State.TERMINATED_SUCCESS); //arguably this wasn't success; but not a real failure either
|
||||
this.finish();
|
||||
|
@ -1189,7 +1188,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
|||
if (isTerminated()) {
|
||||
this.cancelRingingTimeout();
|
||||
this.webRTCWrapper.verifyClosed();
|
||||
this.jingleConnectionManager.finishConnection(this);
|
||||
this.jingleConnectionManager.setTerminalSessionState(id, getEndUserState(), getMedia());
|
||||
this.jingleConnectionManager.finishConnectionOrThrow(this);
|
||||
} else {
|
||||
throw new IllegalStateException(String.format("Unable to call finish from %s", this.state));
|
||||
}
|
||||
|
@ -1219,7 +1219,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
|
|||
final Conversational conversational = message.getConversation();
|
||||
if (conversational instanceof Conversation) {
|
||||
((Conversation) conversational).add(this.message);
|
||||
xmppConnectionService.databaseBackend.createMessage(message);
|
||||
xmppConnectionService.createMessageAsync(message);
|
||||
xmppConnectionService.updateConversationUi();
|
||||
} else {
|
||||
throw new IllegalStateException("Somehow the conversation in a message was a stub");
|
||||
|
|
|
@ -11,6 +11,7 @@ public enum RtpEndUserState {
|
|||
ENDED, //close UI
|
||||
DECLINED_OR_BUSY, //other party declined; no retry button
|
||||
CONNECTIVITY_ERROR, //network error; retry button
|
||||
CONNECTIVITY_LOST_ERROR, //network error but for call duration > 0
|
||||
RETRACTED, //user pressed home or power button during 'ringing' - shows retry button
|
||||
APPLICATION_ERROR //something rather bad happened; libwebrtc failed or we got in IQ-error
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 476 B |
Binary file not shown.
After Width: | Height: | Size: 304 B |
Binary file not shown.
After Width: | Height: | Size: 585 B |
Binary file not shown.
After Width: | Height: | Size: 842 B |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_help"
|
||||
android:icon="?attr/icon_help"
|
||||
android:title="@string/help"
|
||||
app:showAsAction="always"/>
|
||||
</menu>
|
|
@ -41,7 +41,7 @@
|
|||
<string name="moderator">Moderator</string>
|
||||
<string name="participant">Teilnehmer</string>
|
||||
<string name="visitor">Besucher</string>
|
||||
<string name="remove_contact_text">Möchtest du %svon deiner Kontaktliste entfernen? Unterhaltungen mit diesem Kontakt werden dabei nicht entfernt.</string>
|
||||
<string name="remove_contact_text">Möchtest du %s von deiner Kontaktliste entfernen? Unterhaltungen mit diesem Kontakt werden dabei nicht entfernt.</string>
|
||||
<string name="block_contact_text">Möchtest du %s sperren und keine Nachrichten mehr erhalten?</string>
|
||||
<string name="unblock_contact_text">Möchtest du %s entsperren und wieder Nachrichten empfangen?</string>
|
||||
<string name="block_domain_text">Alle Kontakte von %s sperren?</string>
|
||||
|
@ -901,7 +901,8 @@
|
|||
<string name="rtp_state_ringing">Klingelt</string>
|
||||
<string name="rtp_state_declined_or_busy">Besetzt</string>
|
||||
<string name="rtp_state_connectivity_error">Verbindungsaufbau fehlgeschlagen</string>
|
||||
<string name="rtp_state_retracted">Rückrufruf</string>
|
||||
<string name="rtp_state_connectivity_lost_error">Verbindung unterbrochen</string>
|
||||
<string name="rtp_state_retracted">Anruf zurückgenommen</string>
|
||||
<string name="rtp_state_application_failure">App-Fehler</string>
|
||||
<string name="hang_up">Auflegen</string>
|
||||
<string name="ongoing_call">Laufender Anruf</string>
|
||||
|
@ -914,6 +915,7 @@
|
|||
<string name="missed_call">Entgangener Anruf</string>
|
||||
<string name="audio_call">Audioanruf</string>
|
||||
<string name="video_call">Videoanruf</string>
|
||||
<string name="help">Hilfe</string>
|
||||
<string name="microphone_unavailable">Dein Mikrofon ist nicht verfügbar</string>
|
||||
<string name="only_one_call_at_a_time">Du kannst immer nur einen Anruf zur gleichen Zeit machen.</string>
|
||||
<string name="return_to_ongoing_call">Zurück zum laufenden Aufruf</string>
|
||||
|
|
|
@ -901,6 +901,7 @@
|
|||
<string name="rtp_state_ringing">Llamando</string>
|
||||
<string name="rtp_state_declined_or_busy">Ocupado</string>
|
||||
<string name="rtp_state_connectivity_error">No se ha podido realizar la llamada</string>
|
||||
<string name="rtp_state_connectivity_lost_error">Conexión perdida</string>
|
||||
<string name="rtp_state_retracted">Llamada rechazada</string>
|
||||
<string name="rtp_state_application_failure">Fallo en la aplicación</string>
|
||||
<string name="hang_up">Colgar</string>
|
||||
|
@ -914,6 +915,7 @@
|
|||
<string name="missed_call">Llamada perdida</string>
|
||||
<string name="audio_call">Audio llamada</string>
|
||||
<string name="video_call">Video llamada</string>
|
||||
<string name="help">Ayuda</string>
|
||||
<string name="microphone_unavailable">Tu micrófono no está disponible</string>
|
||||
<string name="only_one_call_at_a_time">Solo puedes hacer una llamada a la vez</string>
|
||||
<string name="return_to_ongoing_call">Volver a la llamada en curso</string>
|
||||
|
|
|
@ -901,6 +901,7 @@
|
|||
<string name="rtp_state_ringing">Sonando</string>
|
||||
<string name="rtp_state_declined_or_busy">Ocupado</string>
|
||||
<string name="rtp_state_connectivity_error">Non se pode establecer a chamada</string>
|
||||
<string name="rtp_state_connectivity_lost_error">Perdeuse a conexión</string>
|
||||
<string name="rtp_state_retracted">Chamada cortada</string>
|
||||
<string name="rtp_state_application_failure">Fallo na aplicación</string>
|
||||
<string name="hang_up">Colgar</string>
|
||||
|
@ -914,6 +915,7 @@
|
|||
<string name="missed_call">Chamada perdida</string>
|
||||
<string name="audio_call">Chamada de audio</string>
|
||||
<string name="video_call">Chamada de vídeo</string>
|
||||
<string name="help">Axuda</string>
|
||||
<string name="microphone_unavailable">O micrófono non está dispoñible</string>
|
||||
<string name="only_one_call_at_a_time">Só podes manter unha chamada en cada momento.</string>
|
||||
<string name="return_to_ongoing_call">Voltar á chamada activa</string>
|
||||
|
|
|
@ -901,6 +901,7 @@
|
|||
<string name="rtp_state_ringing">Sta squillando</string>
|
||||
<string name="rtp_state_declined_or_busy">Occupato</string>
|
||||
<string name="rtp_state_connectivity_error">Impossibile connettere la chiamata</string>
|
||||
<string name="rtp_state_connectivity_lost_error">Connessione persa</string>
|
||||
<string name="rtp_state_retracted">Chiamata ritirata</string>
|
||||
<string name="rtp_state_application_failure">Errore dell\'app</string>
|
||||
<string name="hang_up">Riaggancia</string>
|
||||
|
@ -914,6 +915,7 @@
|
|||
<string name="missed_call">Chiamata persa</string>
|
||||
<string name="audio_call">Chiamata vocale</string>
|
||||
<string name="video_call">Chiamata video</string>
|
||||
<string name="help">Aiuto</string>
|
||||
<string name="microphone_unavailable">Il tuo microfono non è disponibile</string>
|
||||
<string name="only_one_call_at_a_time">Puoi fare solo una chiamata alla volta.</string>
|
||||
<string name="return_to_ongoing_call">Torna alla chiamata in corso</string>
|
||||
|
|
|
@ -919,6 +919,7 @@ Administrator twojego serwera będzie mógł czytać twoje wiadomości, ale moż
|
|||
<string name="rtp_state_ringing">Dzwonienie</string>
|
||||
<string name="rtp_state_declined_or_busy">Zajęty</string>
|
||||
<string name="rtp_state_connectivity_error">Nie można wykonać połączenia</string>
|
||||
<string name="rtp_state_connectivity_lost_error">Utracono połączenie</string>
|
||||
<string name="rtp_state_retracted">Anulowane połączenie</string>
|
||||
<string name="rtp_state_application_failure">Błąd aplikacji</string>
|
||||
<string name="hang_up">Rozłącz</string>
|
||||
|
@ -932,6 +933,7 @@ Administrator twojego serwera będzie mógł czytać twoje wiadomości, ale moż
|
|||
<string name="missed_call">Nieodebrane połączenie</string>
|
||||
<string name="audio_call">Połączenie audio</string>
|
||||
<string name="video_call">Połączenie wideo</string>
|
||||
<string name="help">Pomoc</string>
|
||||
<string name="microphone_unavailable">Twój mikrofon jest niedostępny</string>
|
||||
<string name="only_one_call_at_a_time">Możesz mieć tylko jedno połączenie na raz.</string>
|
||||
<string name="return_to_ongoing_call">Powróć do trwającego połączenia</string>
|
||||
|
|
|
@ -909,6 +909,7 @@
|
|||
<string name="rtp_state_ringing">Sună</string>
|
||||
<string name="rtp_state_declined_or_busy">Ocupat</string>
|
||||
<string name="rtp_state_connectivity_error">Nu s-a putut conecta apelul</string>
|
||||
<string name="rtp_state_connectivity_lost_error">Conexiune pierdută</string>
|
||||
<string name="rtp_state_retracted">Apel anulat</string>
|
||||
<string name="rtp_state_application_failure">Eroare de aplicație</string>
|
||||
<string name="hang_up">Închide</string>
|
||||
|
@ -922,6 +923,7 @@
|
|||
<string name="missed_call">Apel pierdut</string>
|
||||
<string name="audio_call">Apel audio</string>
|
||||
<string name="video_call">Apel video</string>
|
||||
<string name="help">Ajutor</string>
|
||||
<string name="microphone_unavailable">Microfonul nu este disponibil</string>
|
||||
<string name="only_one_call_at_a_time">Puteți avea un singur apel simultan.</string>
|
||||
<string name="return_to_ongoing_call">Reveniți la apelul în curs</string>
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
<string name="just_now">только что</string>
|
||||
<string name="minute_ago">1 минуту назад</string>
|
||||
<string name="minutes_ago">%d мин. назад</string>
|
||||
<string name="x_unread_conversations">%d непрочитанных бесед</string>
|
||||
<string name="sending">отправка…</string>
|
||||
<string name="message_decrypting">Расшифровка сообщения. Подождите…</string>
|
||||
<string name="pgp_message">OpenPGP зашифр. сообщение</string>
|
||||
|
@ -40,12 +41,14 @@
|
|||
<string name="moderator">Модератор</string>
|
||||
<string name="participant">Участник</string>
|
||||
<string name="visitor">Посетитель</string>
|
||||
<string name="remove_contact_text">Вы хотите удалить %s из своего списка контактов? Беседы, связанные с этим контактом, будут сохранены.</string>
|
||||
<string name="block_contact_text">Вы хотите заблокировать дальнейшие сообщения от %s?</string>
|
||||
<string name="unblock_contact_text">Вы хотите разблокировать пользователя %s?</string>
|
||||
<string name="block_domain_text">Заблокировать всех пользователей домена %s?</string>
|
||||
<string name="unblock_domain_text">Разблокировать всех пользователей домена %s?</string>
|
||||
<string name="contact_blocked">Контакт заблокирован</string>
|
||||
<string name="blocked">Заблокирован</string>
|
||||
<string name="remove_bookmark_text">Вы хотите удалить %s из избранного? Беседы, связанные с данной закладкой, будут сохранены.</string>
|
||||
<string name="register_account">Создать новый аккаунт на сервере</string>
|
||||
<string name="change_password_on_server">Изменить пароль на сервере</string>
|
||||
<string name="share_with">Поделиться с</string>
|
||||
|
@ -64,14 +67,22 @@
|
|||
<string name="save">Сохранить</string>
|
||||
<string name="ok">ОК</string>
|
||||
<string name="crash_report_title">Conversations был неожиданно остановлен</string>
|
||||
<string name="crash_report_message">Отправляя отчёты об ошибках, вы помогаете совершенствованию Conversations.</string>
|
||||
<string name="send_now">Отправить сейчас</string>
|
||||
<string name="send_never">Больше не спрашивать</string>
|
||||
<string name="problem_connecting_to_account">Не удалось подключиться к учетной записи</string>
|
||||
<string name="problem_connecting_to_accounts">Не удалось подключиться к учетным записям</string>
|
||||
<string name="touch_to_fix">Нажмите, чтобы настроить учетные записи</string>
|
||||
<string name="attach_file">Прикрепить файл</string>
|
||||
<string name="not_in_roster">Контакт не находится в вашем списке контактов. Хотите добавить его?</string>
|
||||
<string name="add_contact">Добавить контакт</string>
|
||||
<string name="send_failed">доставка не удалась</string>
|
||||
<string name="preparing_image">Подготовка к передаче изображения</string>
|
||||
<string name="preparing_images">Подготовка к передаче изображений</string>
|
||||
<string name="sharing_files_please_wait">Обмен файлами. Пожалуйста, подождите…</string>
|
||||
<string name="action_clear_history">Очистить историю</string>
|
||||
<string name="clear_conversation_history">Очистить историю</string>
|
||||
<string name="clear_histor_msg">Вы хотите удалить все сообщения в этой беседе?\n\n<b>Внимание:</b> Данная операция не повлияет на сообщения, хранящиеся на других устройствах или серверах.</string>
|
||||
<string name="delete_file_dialog">Удалить файл</string>
|
||||
<string name="delete_file_dialog_msg">Вы уверены, что хотите удалить этот файл?\n\n<b>Предупреждение:</b> Данная операция не удалит копии этого файла, хранящиеся на других устройствах или серверах.</string>
|
||||
<string name="also_end_conversation">Закрыть эту беседу</string>
|
||||
|
@ -82,16 +93,20 @@
|
|||
<string name="send_omemo_message">OMEMO зашифр. сообщение</string>
|
||||
<string name="send_omemo_x509_message">v\\OMEMO зашифр. сообщение</string>
|
||||
<string name="send_pgp_message">OpenPGP зашифр. сообщение</string>
|
||||
<string name="your_nick_has_been_changed">Имя уже используется</string>
|
||||
<string name="send_unencrypted">Отправить в незашифрованном виде</string>
|
||||
<string name="decryption_failed">Расшифровка не удалась. Вероятно, что у вас нет надлежащего ключа.</string>
|
||||
<string name="openkeychain_required">Установите OpenKeychain</string>
|
||||
<string name="openkeychain_required_long">Conversations использует <b>OpenKeychain</b> для шифрования и дешифрования сообщений и управления открытыми ключами.\n\nOpenKeychain распространяется под лицензией GPLv3 и доступна для загрузки через F-Droid или Google Play.\n\n<small>(Потребуется перезапуск Conversations после установки.)</small></string>
|
||||
<string name="restart">Перезапуск</string>
|
||||
<string name="install">Установка</string>
|
||||
<string name="openkeychain_not_installed">Пожалуйста, установите OpenKeychain</string>
|
||||
<string name="offering">предложение…</string>
|
||||
<string name="waiting">ожидание…</string>
|
||||
<string name="no_pgp_key">Нет OpenPGP ключа</string>
|
||||
<string name="contact_has_no_pgp_key">Conversations не может зашифровать сообщение, потому что ваш собеседник не анонсирует свой открытый ключ.\n\n<small>Пожалуйста, попросите вашего собеседника настроить OpenPGP.</small></string>
|
||||
<string name="no_pgp_keys">Нет OpenPGP ключей</string>
|
||||
<string name="contacts_have_no_pgp_keys">Conversations не может зашифровать сообщение, потому что ваши собеседники не анонсируют свои открытые ключи.\n\n<small>Пожалуйста, попросите ваших собеседников настроить OpenPGP.</small></string>
|
||||
<string name="pref_general">Общие</string>
|
||||
<string name="pref_accept_files">Принимать файлы</string>
|
||||
<string name="pref_accept_files_summary">Автоматический приём файлов…</string>
|
||||
|
@ -101,6 +116,10 @@
|
|||
<string name="pref_vibrate_summary">Вибрировать, когда приходят новые сообщения</string>
|
||||
<string name="pref_led">Светодиодное уведомление</string>
|
||||
<string name="pref_led_summary">Мерцание индикатора при получении нового сообщения</string>
|
||||
<string name="pref_ringtone">Мелодия звонка</string>
|
||||
<string name="pref_notification_sound">Звук уведомления</string>
|
||||
<string name="pref_notification_sound_summary">Звук уведомления о новых сообщениях</string>
|
||||
<string name="pref_call_ringtone_summary">Мелодия входящего звонка</string>
|
||||
<string name="pref_notification_grace_period">Грейс-период</string>
|
||||
<string name="pref_advanced_options">Дополнительно</string>
|
||||
<string name="pref_never_send_crash">Не отправлять отчёты об ошибках</string>
|
||||
|
@ -258,8 +277,6 @@
|
|||
<string name="hosted_on">размещено на %s</string>
|
||||
<string name="checking_x">Проверка %s на сервере HTTP</string>
|
||||
<string name="not_connected_try_again">Вы неподключены. Попробуйте позже</string>
|
||||
<string name="check_x_filesize">Проверить размер %s</string>
|
||||
<string name="check_x_filesize_on_host">Проверить размер %1$s на %2$s</string>
|
||||
<string name="message_options">Опции сообщения</string>
|
||||
<string name="quote">Цитировать</string>
|
||||
<string name="paste_as_quote">Вставить как цитату</string>
|
||||
|
@ -676,7 +693,7 @@
|
|||
<string name="pref_omemo_setting_summary_default_off">OMEMO нужно будет явно включать для новых бесед.</string>
|
||||
<string name="create_shortcut">Создать ярлык</string>
|
||||
<string name="pref_font_size">Размер шрифта</string>
|
||||
<string name="pref_font_size_summary">Относительный размер шрифта используемый в приложении.</string>
|
||||
<string name="pref_font_size_summary">Относительный размер шрифта, используемый в приложении.</string>
|
||||
<string name="default_on">Включено по умолчанию</string>
|
||||
<string name="default_off">Выключено по умолчанию</string>
|
||||
<string name="small">Маленький</string>
|
||||
|
@ -737,7 +754,7 @@
|
|||
<string name="media_browser">Просмотр медиафайлов</string>
|
||||
<string name="security_violation_not_attaching_file">Файл не прикреплен из соображений безопасности.</string>
|
||||
<string name="pref_video_compression">Качество видео</string>
|
||||
<string name="pref_video_compression_summary">Низкое качество означает меньшие файлы</string>
|
||||
<string name="pref_video_compression_summary">Чем ниже качество, тем меньше объем файлов</string>
|
||||
<string name="video_360p">Среднее (360p)</string>
|
||||
<string name="video_720p">Высокое (720р)</string>
|
||||
<string name="cancelled">отменено</string>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<string name="action_settings">Inställningar</string>
|
||||
<string name="action_add">Ny konversation</string>
|
||||
<string name="action_accounts">Kontoinställningar</string>
|
||||
<string name="action_account">Hantera konto</string>
|
||||
<string name="action_end_conversation">Stäng denna konversation</string>
|
||||
<string name="action_contact_details">Kontaktdetaljer</string>
|
||||
<string name="action_muc_details">Gruppchattdetaljer</string>
|
||||
|
@ -34,18 +35,20 @@
|
|||
<string name="message_decrypting">Avkrypterar meddelande. Vänta…</string>
|
||||
<string name="pgp_message">OpenPGP-krypterat meddelande</string>
|
||||
<string name="nick_in_use">Nick används redan</string>
|
||||
<string name="invalid_muc_nick">Ogiltigt nick</string>
|
||||
<string name="invalid_muc_nick">Ogiltigt smeknamn</string>
|
||||
<string name="admin">Admin</string>
|
||||
<string name="owner">Ägare</string>
|
||||
<string name="moderator">Moderator</string>
|
||||
<string name="participant">Deltagare</string>
|
||||
<string name="visitor">Besökare</string>
|
||||
<string name="remove_contact_text">Vill du ta bort %s från din kontaktlista? Konversationer med denna kontakt kommer inte tas bort.</string>
|
||||
<string name="block_contact_text">Vill du blockera %s från att skicka dig meddelanden?</string>
|
||||
<string name="unblock_contact_text">Vill du avblockera %s och tillåta denne att skicka dig meddelanden?</string>
|
||||
<string name="block_domain_text">Blockera alla kontakter från %s?</string>
|
||||
<string name="unblock_domain_text">Avblockera alla kontakter från %s?</string>
|
||||
<string name="contact_blocked">Kontakt blockerad</string>
|
||||
<string name="blocked">Blockerad</string>
|
||||
<string name="remove_bookmark_text">Vill du ta bort %s som ett bokmärke? Konversationer med detta bokmärke kommer inte tas bort.</string>
|
||||
<string name="register_account">Registrera nytt konto på servern</string>
|
||||
<string name="change_password_on_server">Byt lösenord på server</string>
|
||||
<string name="share_with">Dela med…</string>
|
||||
|
@ -66,9 +69,13 @@
|
|||
<string name="crash_report_title">Conversations har kraschat</string>
|
||||
<string name="send_now">Skicka nu</string>
|
||||
<string name="send_never">Fråga aldrig igen</string>
|
||||
<string name="problem_connecting_to_account">Kunde inte ansluta till konto</string>
|
||||
<string name="problem_connecting_to_accounts">Kunde inte ansluta till flera konton</string>
|
||||
<string name="attach_file">Bifoga fil</string>
|
||||
<string name="add_contact">Lägg till kontakt</string>
|
||||
<string name="send_failed">sändning misslyckades</string>
|
||||
<string name="preparing_image">Förbereder att skicka bild</string>
|
||||
<string name="preparing_images">Förbereder att skicka bilder</string>
|
||||
<string name="sharing_files_please_wait">Delar filer. Vänta...</string>
|
||||
<string name="action_clear_history">Rensa historik</string>
|
||||
<string name="clear_conversation_history">Rensa konversationshistorik</string>
|
||||
|
@ -81,6 +88,7 @@
|
|||
<string name="send_omemo_message">Skicka OMEMO-krypterat meddelande</string>
|
||||
<string name="send_omemo_x509_message">Skicka v\\OMEMO-krypterat meddelande</string>
|
||||
<string name="send_pgp_message">Skicka OpenPGP-krypterat meddelande</string>
|
||||
<string name="your_nick_has_been_changed">Nytt smeknamn används</string>
|
||||
<string name="send_unencrypted">Skicka okrypterat</string>
|
||||
<string name="decryption_failed">Avkryptering misslyckades. Du har kanske kanske inte rätt privat nyckel.</string>
|
||||
<string name="openkeychain_required">OpenKeychain</string>
|
||||
|
@ -304,6 +312,7 @@
|
|||
<string name="ban_now">Bannlys nu</string>
|
||||
<string name="could_not_change_role">Kunde inte ändra rollen för %s</string>
|
||||
<string name="members_only">Privat, medlemsskap krävs</string>
|
||||
<string name="non_anonymous">Gör XMPP-adresser synliga för alla</string>
|
||||
<string name="you_are_not_participating">Du deltar ej</string>
|
||||
<string name="never">Aldrig</string>
|
||||
<string name="until_further_notice">Tills vidare</string>
|
||||
|
@ -389,6 +398,8 @@
|
|||
<item quantity="other">%d meddelanden</item>
|
||||
</plurals>
|
||||
<string name="load_more_messages">Ladda fler meddelanden</string>
|
||||
<string name="no_storage_permission">Ge Conversations tillgång till extern lagring</string>
|
||||
<string name="no_camera_permission">Ge Conversations tillgång till kameran</string>
|
||||
<string name="sync_with_contacts">Synkronisera med kontakter</string>
|
||||
<string name="notify_on_all_messages">Notifiera för alla meddelanden</string>
|
||||
<string name="notify_never">Notifieringar deaktiverade</string>
|
||||
|
@ -442,6 +453,7 @@
|
|||
<string name="allow">Tillåt</string>
|
||||
<string name="no_permission_to_access_x">Saknar rättigheter för access till %s</string>
|
||||
<string name="remote_server_not_found">Fjärrserver hittas inte</string>
|
||||
<string name="unable_to_update_account">Kunde inte uppdatera konto</string>
|
||||
<string name="pref_delete_omemo_identities">Ta bort OMEMO identiteter</string>
|
||||
<string name="delete_selected_keys">Ta bort valda nycklar</string>
|
||||
<string name="error_publish_avatar_offline">Du måste vara ansluten för att publicera din avatarbild</string>
|
||||
|
@ -451,6 +463,7 @@
|
|||
<string name="device_does_not_support_data_saver">Din enhet stödjer inte att deaktivera databesparing för Conversations.</string>
|
||||
<string name="this_device_has_been_verified">Denna enhet har verifierats</string>
|
||||
<string name="copy_fingerprint">Kopiera fingeravtryck</string>
|
||||
<string name="barcode_does_not_contain_fingerprints_for_this_conversation">Streckkoden innehåller inte fingeravtryck för denna konversation.</string>
|
||||
<string name="verified_fingerprints">Verifierade fingeravtryck</string>
|
||||
<string name="use_camera_icon_to_scan_barcode">Använd kameran för att scanna en kontakts streckkod</string>
|
||||
<string name="please_wait_for_keys_to_be_fetched">Vänta medans nycklar hämtas</string>
|
||||
|
@ -554,6 +567,7 @@
|
|||
<string name="ebook">e-bok</string>
|
||||
<string name="open_with">Öppna med...</string>
|
||||
<string name="choose_account">Välj konto</string>
|
||||
<string name="enter_password_to_restore">Ange ditt lösenord till kontot %s för att återställa säkerhetskopian.</string>
|
||||
<string name="create_group_chat">Skapa gruppchatt</string>
|
||||
<string name="create_private_group_chat">Skapa sluten gruppchatt</string>
|
||||
<string name="create_dialog_channel_name">Kanalnamn</string>
|
||||
|
@ -576,5 +590,7 @@
|
|||
<string name="this_looks_like_a_domain">Detta verkar vara ett domännamn</string>
|
||||
<string name="add_anway">Lägg till ändå</string>
|
||||
<string name="this_looks_like_channel">Detta ser ut som en kanaladress</string>
|
||||
<string name="not_a_backup_file">Filen du valde är inte en säkerhetskopia till Conversations</string>
|
||||
<string name="category_about">Om</string>
|
||||
<string name="please_enable_an_account">Aktivera ett konto</string>
|
||||
</resources>
|
||||
|
|
|
@ -96,6 +96,7 @@
|
|||
<attr name="icon_refresh" format="reference" />
|
||||
<attr name="icon_remove" format="reference" />
|
||||
<attr name="icon_search" format="reference" />
|
||||
<attr name="icon_help" format="reference" />
|
||||
<attr name="icon_secure" format="reference" />
|
||||
<attr name="icon_settings" format="reference" />
|
||||
<attr name="icon_share" format="reference" />
|
||||
|
|
|
@ -903,6 +903,7 @@
|
|||
<string name="rtp_state_ringing">Ringing</string>
|
||||
<string name="rtp_state_declined_or_busy">Busy</string>
|
||||
<string name="rtp_state_connectivity_error">Could not connect call</string>
|
||||
<string name="rtp_state_connectivity_lost_error">Connection lost</string>
|
||||
<string name="rtp_state_retracted">Retracted call</string>
|
||||
<string name="rtp_state_application_failure">App failure</string>
|
||||
<string name="hang_up">Hang up</string>
|
||||
|
@ -916,6 +917,7 @@
|
|||
<string name="missed_call">Missed call</string>
|
||||
<string name="audio_call">Audio call</string>
|
||||
<string name="video_call">Video call</string>
|
||||
<string name="help">Help</string>
|
||||
<string name="microphone_unavailable">Your microphone is unavailable</string>
|
||||
<string name="only_one_call_at_a_time">You can only have one call at a time.</string>
|
||||
<string name="return_to_ongoing_call">Return to ongoing call</string>
|
||||
|
|
|
@ -114,6 +114,7 @@
|
|||
</item>
|
||||
<item name="icon_remove" type="reference">@drawable/ic_delete_black_24dp</item>
|
||||
<item name="icon_search" type="reference">@drawable/ic_search_white_24dp</item>
|
||||
<item name="icon_help" type="reference">@drawable/ic_help_white_24dp</item>
|
||||
<item name="icon_secure" type="reference">@drawable/ic_lock_open_white_24dp</item>
|
||||
<item name="icon_settings" type="reference">@drawable/ic_settings_black_24dp</item>
|
||||
<item name="icon_share" type="reference">@drawable/ic_share_white_24dp</item>
|
||||
|
@ -267,6 +268,7 @@
|
|||
</item>
|
||||
<item name="icon_remove" type="reference">@drawable/ic_delete_white_24dp</item>
|
||||
<item name="icon_search" type="reference">@drawable/ic_search_white_24dp</item>
|
||||
<item name="icon_help" type="reference">@drawable/ic_help_white_24dp</item>
|
||||
<item name="icon_secure" type="reference">@drawable/ic_lock_open_white_24dp</item>
|
||||
<item name="icon_settings" type="reference">@drawable/ic_settings_white_24dp</item>
|
||||
<item name="icon_share" type="reference">@drawable/ic_share_white_24dp</item>
|
||||
|
|
Loading…
Reference in New Issue