Merge tag '2.5.6' into develop
This commit is contained in:
commit
e304b6b152
|
@ -1,5 +1,9 @@
|
|||
# Changelog
|
||||
|
||||
### Version 2.5.6
|
||||
* fixes for Jingle file transfer
|
||||
* fixed some rare crashes
|
||||
|
||||
### Version 2.5.5
|
||||
* allow backups to be restored from anywhere
|
||||
* bug fixes
|
||||
|
|
|
@ -81,8 +81,8 @@ android {
|
|||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 25
|
||||
versionCode 334
|
||||
versionName "2.5.5"
|
||||
versionCode 336
|
||||
versionName "2.5.6"
|
||||
archivesBaseName += "-$versionName"
|
||||
applicationId "eu.sum7.conversations"
|
||||
resValue "string", "applicationId", applicationId
|
||||
|
|
|
@ -137,7 +137,7 @@ public class ImportBackupService extends Service {
|
|||
} else {
|
||||
backupFiles.add(backupFile);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Log.d(Config.LOGTAG, "unable to read backup file ", e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ public class ImportBackupActivity extends ActionBarActivity implements ServiceCo
|
|||
try {
|
||||
final ImportBackupService.BackupFile backupFile = ImportBackupService.BackupFile.read(this, uri);
|
||||
showEnterPasswordDialog(backupFile, finishOnCancel);
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Snackbar.make(binding.coordinator, R.string.not_a_backup_file, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,6 +113,7 @@ public final class Config {
|
|||
public static final boolean ONLY_INTERNAL_STORAGE = false; //use internal storage instead of sdcard to save attachments
|
||||
|
||||
public static final boolean IGNORE_ID_REWRITE_IN_MUC = true;
|
||||
public static final boolean MUC_LEAVE_BEFORE_JOIN = true;
|
||||
|
||||
public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY * 5;
|
||||
public static final int MAM_MAX_MESSAGES = 750;
|
||||
|
|
|
@ -426,12 +426,16 @@ public class MucOptions {
|
|||
} else if (!conversation.getJid().isBareJid()) {
|
||||
return conversation.getJid().getResource();
|
||||
} else {
|
||||
final String displayName = normalize(account.getJid(), account.getDisplayName());
|
||||
if (displayName == null) {
|
||||
return JidHelper.localPartOrFallback(account.getJid());
|
||||
} else {
|
||||
return displayName;
|
||||
}
|
||||
return defaultNick(account);
|
||||
}
|
||||
}
|
||||
|
||||
public static String defaultNick(final Account account) {
|
||||
final String displayName = normalize(account.getJid(), account.getDisplayName());
|
||||
if (displayName == null) {
|
||||
return JidHelper.localPartOrFallback(account.getJid());
|
||||
} else {
|
||||
return displayName;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -87,6 +87,18 @@ public class ServiceDiscoveryResult {
|
|||
}
|
||||
}
|
||||
|
||||
private ServiceDiscoveryResult() {
|
||||
this.hash = "sha-1";
|
||||
this.features = Collections.emptyList();
|
||||
this.identities = Collections.emptyList();
|
||||
this.ver = null;
|
||||
this.forms = Collections.emptyList();
|
||||
}
|
||||
|
||||
public static ServiceDiscoveryResult empty() {
|
||||
return new ServiceDiscoveryResult();
|
||||
}
|
||||
|
||||
public ServiceDiscoveryResult(Cursor cursor) throws JSONException {
|
||||
this(
|
||||
cursor.getString(cursor.getColumnIndex(HASH)),
|
||||
|
|
|
@ -68,8 +68,8 @@ public abstract class AbstractGenerator {
|
|||
return this.mVersion;
|
||||
}
|
||||
|
||||
public String getIdentityName() {
|
||||
return mXmppConnectionService.getString(R.string.app_name) + ' ' + getIdentityVersion();
|
||||
String getIdentityName() {
|
||||
return mXmppConnectionService.getString(R.string.app_name);
|
||||
}
|
||||
|
||||
public String getUserAgent() {
|
||||
|
|
|
@ -50,15 +50,20 @@ public class PresenceGenerator extends AbstractGenerator {
|
|||
return selfPresence(account, status, true);
|
||||
}
|
||||
|
||||
public PresencePacket selfPresence(Account account, Presence.Status status, boolean includePgpAnnouncement) {
|
||||
PresencePacket packet = new PresencePacket();
|
||||
if(status.toShowString() != null) {
|
||||
packet.addChild("show").setContent(status.toShowString());
|
||||
}
|
||||
packet.setFrom(account.getJid());
|
||||
final String sig = account.getPgpSignature();
|
||||
if (includePgpAnnouncement && sig != null && mXmppConnectionService.getPgpEngine() != null) {
|
||||
packet.addChild("x", "jabber:x:signed").setContent(sig);
|
||||
public PresencePacket selfPresence(final Account account, final Presence.Status status, final boolean personal) {
|
||||
final PresencePacket packet = new PresencePacket();
|
||||
if (personal) {
|
||||
final String sig = account.getPgpSignature();
|
||||
final String message = account.getPresenceStatusMessage();
|
||||
if(status.toShowString() != null) {
|
||||
packet.addChild("show").setContent(status.toShowString());
|
||||
}
|
||||
if (!TextUtils.isEmpty(message)) {
|
||||
packet.addChild(new Element("status").setContent(message));
|
||||
}
|
||||
if (sig != null && mXmppConnectionService.getPgpEngine() != null) {
|
||||
packet.addChild("x", "jabber:x:signed").setContent(sig);
|
||||
}
|
||||
}
|
||||
final String capHash = getCapHash(account);
|
||||
if (capHash != null) {
|
||||
|
|
|
@ -22,7 +22,6 @@ import eu.siacs.conversations.entities.Account;
|
|||
import eu.siacs.conversations.entities.DownloadableFile;
|
||||
import eu.siacs.conversations.entities.Message;
|
||||
import eu.siacs.conversations.entities.Transferable;
|
||||
import eu.siacs.conversations.entities.TransferablePlaceholder;
|
||||
import eu.siacs.conversations.persistance.FileBackend;
|
||||
import eu.siacs.conversations.services.AbstractConnectionManager;
|
||||
import eu.siacs.conversations.services.XmppConnectionService;
|
||||
|
@ -77,22 +76,20 @@ public class HttpDownloadConnection implements Transferable {
|
|||
} else {
|
||||
mUrl = CryptoHelper.toHttpsUrl(new URL(message.getBody().split("\n")[0]));
|
||||
}
|
||||
String[] parts = mUrl.getPath().toLowerCase().split("\\.");
|
||||
String lastPart = parts.length >= 1 ? parts[parts.length - 1] : null;
|
||||
String secondToLast = parts.length >= 2 ? parts[parts.length - 2] : null;
|
||||
if ("pgp".equals(lastPart) || "gpg".equals(lastPart)) {
|
||||
final AbstractConnectionManager.Extension extension = AbstractConnectionManager.Extension.of(mUrl.getPath());
|
||||
if (VALID_CRYPTO_EXTENSIONS.contains(extension.main)) {
|
||||
this.message.setEncryption(Message.ENCRYPTION_PGP);
|
||||
} else if (message.getEncryption() != Message.ENCRYPTION_OTR
|
||||
&& message.getEncryption() != Message.ENCRYPTION_AXOLOTL) {
|
||||
this.message.setEncryption(Message.ENCRYPTION_NONE);
|
||||
}
|
||||
String extension;
|
||||
if (VALID_CRYPTO_EXTENSIONS.contains(lastPart)) {
|
||||
extension = secondToLast;
|
||||
final String ext;
|
||||
if (VALID_CRYPTO_EXTENSIONS.contains(extension.main)) {
|
||||
ext = extension.secondary;
|
||||
} else {
|
||||
extension = lastPart;
|
||||
ext = extension.main;
|
||||
}
|
||||
message.setRelativeFilePath(message.getUuid() + (extension != null ? ("." + extension) : ""));
|
||||
message.setRelativeFilePath(message.getUuid() + (ext != null ? ("." + ext) : ""));
|
||||
this.file = mXmppConnectionService.getFileBackend().getFile(message, false);
|
||||
final String reference = mUrl.getRef();
|
||||
if (reference != null && AesGcmURLStreamHandler.IV_KEY.matcher(reference).matches()) {
|
||||
|
|
|
@ -109,4 +109,23 @@ public class AbstractConnectionManager {
|
|||
PowerManager powerManager = (PowerManager) mXmppConnectionService.getSystemService(Context.POWER_SERVICE);
|
||||
return powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
|
||||
}
|
||||
|
||||
public static class Extension {
|
||||
public final String main;
|
||||
public final String secondary;
|
||||
|
||||
private Extension(String main, String secondary) {
|
||||
this.main = main;
|
||||
this.secondary = secondary;
|
||||
}
|
||||
|
||||
public static Extension of(String path) {
|
||||
final int pos = path.lastIndexOf('/');
|
||||
final String filename = path.substring(pos + 1).toLowerCase();
|
||||
final String[] parts = filename.split("\\.");
|
||||
final String main = parts.length >= 2 ? parts[parts.length - 1] : null;
|
||||
final String secondary = parts.length >= 3 ? parts[parts.length - 2] : null;
|
||||
return new Extension(main, secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,13 +68,9 @@ import java.util.Set;
|
|||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.R;
|
||||
import eu.siacs.conversations.android.JabberIdContact;
|
||||
|
@ -101,9 +97,8 @@ import eu.siacs.conversations.generator.AbstractGenerator;
|
|||
import eu.siacs.conversations.generator.IqGenerator;
|
||||
import eu.siacs.conversations.generator.MessageGenerator;
|
||||
import eu.siacs.conversations.generator.PresenceGenerator;
|
||||
import eu.siacs.conversations.http.HttpConnectionManager;
|
||||
import eu.siacs.conversations.http.CustomURLStreamHandlerFactory;
|
||||
import eu.siacs.conversations.http.services.MuclumbusService;
|
||||
import eu.siacs.conversations.http.HttpConnectionManager;
|
||||
import eu.siacs.conversations.parser.AbstractParser;
|
||||
import eu.siacs.conversations.parser.IqParser;
|
||||
import eu.siacs.conversations.parser.MessageParser;
|
||||
|
@ -129,9 +124,9 @@ import eu.siacs.conversations.utils.Resolver;
|
|||
import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
|
||||
import eu.siacs.conversations.utils.StringUtils;
|
||||
import eu.siacs.conversations.utils.WakeLockHelper;
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import eu.siacs.conversations.utils.XmppUri;
|
||||
import eu.siacs.conversations.xml.Element;
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import eu.siacs.conversations.xmpp.OnBindListener;
|
||||
import eu.siacs.conversations.xmpp.OnContactStatusChanged;
|
||||
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
|
||||
|
@ -155,11 +150,6 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
|||
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
||||
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
|
||||
import me.leolin.shortcutbadger.ShortcutBadger;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
import rocks.xmpp.addr.Jid;
|
||||
|
||||
public class XmppConnectionService extends Service {
|
||||
|
@ -428,11 +418,11 @@ public class XmppConnectionService extends Service {
|
|||
final int next = connection.getTimeToNextAttempt();
|
||||
final boolean lowPingTimeoutMode = isInLowPingTimeoutMode(account);
|
||||
if (next <= 0) {
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": error connecting account. reconnecting now. lowPingTimeout=" + Boolean.toString(lowPingTimeoutMode));
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": error connecting account. reconnecting now. lowPingTimeout=" + lowPingTimeoutMode);
|
||||
reconnectAccount(account, true, false);
|
||||
} else {
|
||||
final int attempt = connection.getAttempt() + 1;
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": error connecting account. try again in " + next + "s for the " + attempt + " time. lowPingTimeout=" + Boolean.toString(lowPingTimeoutMode));
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": error connecting account. try again in " + next + "s for the " + attempt + " time. lowPingTimeout=" + lowPingTimeoutMode);
|
||||
scheduleWakeUpCall(next, account.getUuid().hashCode());
|
||||
}
|
||||
}
|
||||
|
@ -1395,7 +1385,7 @@ public class XmppConnectionService extends Service {
|
|||
|
||||
final boolean inProgressJoin;
|
||||
synchronized (account.inProgressConferenceJoins) {
|
||||
inProgressJoin = conversation.getMode() == Conversational.MODE_MULTI && account.inProgressConferenceJoins.contains(conversation);
|
||||
inProgressJoin = conversation.getMode() == Conversational.MODE_MULTI && (account.inProgressConferenceJoins.contains(conversation) || account.pendingConferenceJoins.contains(conversation));
|
||||
}
|
||||
|
||||
if (account.isOnlineAndConnected() && !inProgressJoin) {
|
||||
|
@ -1522,7 +1512,6 @@ public class XmppConnectionService extends Service {
|
|||
packet.addChild(ChatState.toElement(conversation.getOutgoingChatState()));
|
||||
}
|
||||
}
|
||||
Log.d(Config.LOGTAG,packet.toString());
|
||||
sendMessagePacket(account, packet);
|
||||
}
|
||||
}
|
||||
|
@ -2546,8 +2535,10 @@ public class XmppConnectionService extends Service {
|
|||
synchronized (account.inProgressConferenceJoins) {
|
||||
account.inProgressConferenceJoins.add(conversation);
|
||||
}
|
||||
sendPresencePacket(account, mPresenceGenerator.leave(conversation.getMucOptions()));
|
||||
conversation.resetMucOptions();
|
||||
if (Config.MUC_LEAVE_BEFORE_JOIN) {
|
||||
sendPresencePacket(account, mPresenceGenerator.leave(conversation.getMucOptions()));
|
||||
}
|
||||
conversation.resetMucOptions();
|
||||
if (onConferenceJoined != null) {
|
||||
conversation.getMucOptions().flagNoAutoPushConfiguration();
|
||||
}
|
||||
|
@ -2800,7 +2791,13 @@ public class XmppConnectionService extends Service {
|
|||
final Bookmark bookmark = conversation.getBookmark();
|
||||
final String bookmarkedNick = bookmark == null ? null : bookmark.getNick();
|
||||
if (bookmark != null && (tookProposedNickFromBookmark || TextUtils.isEmpty(bookmarkedNick)) && !full.getResource().equals(bookmarkedNick)) {
|
||||
Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": persist nick '" + full.getResource() + "' into bookmark for " + conversation.getJid().asBareJid());
|
||||
final Account account = conversation.getAccount();
|
||||
final String defaultNick = MucOptions.defaultNick(account);
|
||||
if (TextUtils.isEmpty(bookmarkedNick) && full.getResource().equals(defaultNick)) {
|
||||
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": do not overwrite empty bookmark nick with default nick for "+conversation.getJid().asBareJid());
|
||||
return;
|
||||
}
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": persist nick '" + full.getResource() + "' into bookmark for " + conversation.getJid().asBareJid());
|
||||
bookmark.setNick(full.getResource());
|
||||
pushBookmarks(bookmark.getAccount());
|
||||
}
|
||||
|
@ -2827,15 +2824,8 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
});
|
||||
|
||||
PresencePacket packet = new PresencePacket();
|
||||
packet.setTo(joinJid);
|
||||
packet.setFrom(conversation.getAccount().getJid());
|
||||
|
||||
String sig = account.getPgpSignature();
|
||||
if (sig != null) {
|
||||
packet.addChild("status").setContent("online");
|
||||
packet.addChild("x", "jabber:x:signed").setContent(sig);
|
||||
}
|
||||
final PresencePacket packet = mPresenceGenerator.selfPresence(account, Presence.Status.ONLINE, options.nonanonymous());
|
||||
packet.setTo(joinJid);
|
||||
sendPresencePacket(account, packet);
|
||||
} else {
|
||||
conversation.setContactJid(joinJid);
|
||||
|
@ -4105,11 +4095,7 @@ public class XmppConnectionService extends Service {
|
|||
} else {
|
||||
status = getTargetPresence();
|
||||
}
|
||||
PresencePacket packet = mPresenceGenerator.selfPresence(account, status);
|
||||
String message = account.getPresenceStatusMessage();
|
||||
if (message != null && !message.isEmpty()) {
|
||||
packet.addChild(new Element("status").setContent(message));
|
||||
}
|
||||
final PresencePacket packet = mPresenceGenerator.selfPresence(account, status);
|
||||
if (mLastActivity > 0 && includeIdleTimestamp) {
|
||||
long since = Math.min(mLastActivity, System.currentTimeMillis()); //don't send future dates
|
||||
packet.addChild("idle", Namespace.IDLE).setAttribute("since", AbstractGenerator.getTimestamp(since));
|
||||
|
@ -4419,11 +4405,12 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
|
||||
public void saveConversationAsBookmark(Conversation conversation, String name) {
|
||||
Account account = conversation.getAccount();
|
||||
Bookmark bookmark = new Bookmark(account, conversation.getJid().asBareJid());
|
||||
if (!conversation.getJid().isBareJid()) {
|
||||
bookmark.setNick(conversation.getJid().getResource());
|
||||
}
|
||||
final Account account = conversation.getAccount();
|
||||
final Bookmark bookmark = new Bookmark(account, conversation.getJid().asBareJid());
|
||||
final String nick = conversation.getJid().getResource();
|
||||
if (nick != null && !nick.isEmpty() && !nick.equals(MucOptions.defaultNick(account))) {
|
||||
bookmark.setNick(nick);
|
||||
}
|
||||
if (!TextUtils.isEmpty(name)) {
|
||||
bookmark.setBookmarkName(name);
|
||||
}
|
||||
|
|
|
@ -257,6 +257,11 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
|
|||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
final User user = mUserPreviewAdapter.getSelectedUser();
|
||||
if (user == null) {
|
||||
Toast.makeText(this, R.string.unable_to_perform_this_action, Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
}
|
||||
if (!MucDetailsContextMenuHelper.onContextItemSelected(item, mUserPreviewAdapter.getSelectedUser(), this)) {
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
|
|
|
@ -24,13 +24,11 @@ import org.osmdroid.api.IMapController;
|
|||
import org.osmdroid.config.Configuration;
|
||||
import org.osmdroid.config.IConfigurationProvider;
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
|
||||
import org.osmdroid.tileprovider.tilesource.XYTileSource;
|
||||
import org.osmdroid.util.GeoPoint;
|
||||
import org.osmdroid.views.CustomZoomButtonsController;
|
||||
import org.osmdroid.views.MapView;
|
||||
import org.osmdroid.views.overlay.Overlay;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import eu.siacs.conversations.BuildConfig;
|
||||
|
@ -41,7 +39,6 @@ import eu.siacs.conversations.services.QuickConversationsService;
|
|||
import eu.siacs.conversations.ui.util.LocationHelper;
|
||||
import eu.siacs.conversations.ui.widget.Marker;
|
||||
import eu.siacs.conversations.ui.widget.MyLocation;
|
||||
import eu.siacs.conversations.utils.LocationProvider;
|
||||
import eu.siacs.conversations.utils.ThemeHelper;
|
||||
|
||||
public abstract class LocationActivity extends ActionBarActivity implements LocationListener {
|
||||
|
@ -136,7 +133,7 @@ public abstract class LocationActivity extends ActionBarActivity implements Loca
|
|||
map.setTileSource(TileSourceFactory.MAPNIK);
|
||||
map.getZoomController().setVisibility(CustomZoomButtonsController.Visibility.NEVER);
|
||||
map.setMultiTouchControls(true);
|
||||
map.setTilesScaledToDpi(true);
|
||||
map.setTilesScaledToDpi(false);
|
||||
mapController = map.getController();
|
||||
mapController.setZoom(Config.Map.INITIAL_ZOOM_LEVEL);
|
||||
mapController.setCenter(pos);
|
||||
|
|
|
@ -21,6 +21,8 @@ import java.io.IOException;
|
|||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.R;
|
||||
|
@ -30,167 +32,180 @@ import eu.siacs.conversations.utils.ThemeHelper;
|
|||
|
||||
public class RecordingActivity extends Activity implements View.OnClickListener {
|
||||
|
||||
public static String STORAGE_DIRECTORY_TYPE_NAME = "Recordings";
|
||||
public static String STORAGE_DIRECTORY_TYPE_NAME = "Recordings";
|
||||
|
||||
private ActivityRecordingBinding binding;
|
||||
private ActivityRecordingBinding binding;
|
||||
|
||||
private MediaRecorder mRecorder;
|
||||
private long mStartTime = 0;
|
||||
private MediaRecorder mRecorder;
|
||||
private long mStartTime = 0;
|
||||
|
||||
private Handler mHandler = new Handler();
|
||||
private Runnable mTickExecutor = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
tick();
|
||||
mHandler.postDelayed(mTickExecutor, 100);
|
||||
}
|
||||
};
|
||||
private CountDownLatch outputFileWrittenLatch = new CountDownLatch(1);
|
||||
|
||||
private File mOutputFile;
|
||||
private boolean mShouldFinishAfterWrite = false;
|
||||
private Handler mHandler = new Handler();
|
||||
private Runnable mTickExecutor = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
tick();
|
||||
mHandler.postDelayed(mTickExecutor, 100);
|
||||
}
|
||||
};
|
||||
|
||||
private FileObserver mFileObserver;
|
||||
private File mOutputFile;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(ThemeHelper.findDialog(this));
|
||||
super.onCreate(savedInstanceState);
|
||||
this.binding = DataBindingUtil.setContentView(this,R.layout.activity_recording);
|
||||
this.binding.cancelButton.setOnClickListener(this);
|
||||
this.binding.shareButton.setOnClickListener(this);
|
||||
this.setFinishOnTouchOutside(false);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
private FileObserver mFileObserver;
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
if (!startRecording()) {
|
||||
this.binding.shareButton.setEnabled(false);
|
||||
this.binding.timer.setTextAppearance(this, R.style.TextAppearance_Conversations_Title);
|
||||
this.binding.timer.setText(R.string.unable_to_start_recording);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(ThemeHelper.findDialog(this));
|
||||
super.onCreate(savedInstanceState);
|
||||
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_recording);
|
||||
this.binding.cancelButton.setOnClickListener(this);
|
||||
this.binding.shareButton.setOnClickListener(this);
|
||||
this.setFinishOnTouchOutside(false);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (mRecorder != null) {
|
||||
mHandler.removeCallbacks(mTickExecutor);
|
||||
stopRecording(false);
|
||||
}
|
||||
if (mFileObserver != null) {
|
||||
mFileObserver.stopWatching();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
if (!startRecording()) {
|
||||
this.binding.shareButton.setEnabled(false);
|
||||
this.binding.timer.setTextAppearance(this, R.style.TextAppearance_Conversations_Title);
|
||||
this.binding.timer.setText(R.string.unable_to_start_recording);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean startRecording() {
|
||||
mRecorder = new MediaRecorder();
|
||||
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
|
||||
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
|
||||
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
|
||||
mRecorder.setAudioEncodingBitRate(96000);
|
||||
mRecorder.setAudioSamplingRate(22050);
|
||||
setupOutputFile();
|
||||
mRecorder.setOutputFile(mOutputFile.getAbsolutePath());
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (mRecorder != null) {
|
||||
mHandler.removeCallbacks(mTickExecutor);
|
||||
stopRecording(false);
|
||||
}
|
||||
if (mFileObserver != null) {
|
||||
mFileObserver.stopWatching();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
mRecorder.prepare();
|
||||
mRecorder.start();
|
||||
mStartTime = SystemClock.elapsedRealtime();
|
||||
mHandler.postDelayed(mTickExecutor, 100);
|
||||
Log.d("Voice Recorder", "started recording to " + mOutputFile.getAbsolutePath());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e("Voice Recorder", "prepare() failed " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private boolean startRecording() {
|
||||
mRecorder = new MediaRecorder();
|
||||
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
|
||||
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
|
||||
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
|
||||
mRecorder.setAudioEncodingBitRate(96000);
|
||||
mRecorder.setAudioSamplingRate(22050);
|
||||
setupOutputFile();
|
||||
mRecorder.setOutputFile(mOutputFile.getAbsolutePath());
|
||||
|
||||
protected void stopRecording(boolean saveFile) {
|
||||
mShouldFinishAfterWrite = saveFile;
|
||||
try {
|
||||
mRecorder.stop();
|
||||
mRecorder.release();
|
||||
} catch (Exception e) {
|
||||
if (saveFile) {
|
||||
Toast.makeText(this,R.string.unable_to_save_recording, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} finally {
|
||||
mRecorder = null;
|
||||
mStartTime = 0;
|
||||
}
|
||||
if (!saveFile && mOutputFile != null) {
|
||||
if (mOutputFile.delete()) {
|
||||
Log.d(Config.LOGTAG,"deleted canceled recording");
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
mRecorder.prepare();
|
||||
mRecorder.start();
|
||||
mStartTime = SystemClock.elapsedRealtime();
|
||||
mHandler.postDelayed(mTickExecutor, 100);
|
||||
Log.d("Voice Recorder", "started recording to " + mOutputFile.getAbsolutePath());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e("Voice Recorder", "prepare() failed " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static File generateOutputFilename(Context context) {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
|
||||
String filename = "RECORDING_" + dateFormat.format(new Date()) + ".m4a";
|
||||
return new File(FileBackend.getConversationsDirectory(context, STORAGE_DIRECTORY_TYPE_NAME) + "/" + filename);
|
||||
}
|
||||
protected void stopRecording(final boolean saveFile) {
|
||||
try {
|
||||
mRecorder.stop();
|
||||
mRecorder.release();
|
||||
} catch (Exception e) {
|
||||
if (saveFile) {
|
||||
Toast.makeText(this, R.string.unable_to_save_recording, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
mRecorder = null;
|
||||
mStartTime = 0;
|
||||
}
|
||||
if (!saveFile && mOutputFile != null) {
|
||||
if (mOutputFile.delete()) {
|
||||
Log.d(Config.LOGTAG, "deleted canceled recording");
|
||||
}
|
||||
}
|
||||
if (saveFile) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
if (!outputFileWrittenLatch.await(2, TimeUnit.SECONDS)) {
|
||||
Log.d(Config.LOGTAG, "time out waiting for output file to be written");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Log.d(Config.LOGTAG, "interrupted while waiting for output file to be written" ,e);
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
setResult(Activity.RESULT_OK, new Intent().setData(Uri.fromFile(mOutputFile)));
|
||||
finish();
|
||||
});
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupOutputFile() {
|
||||
mOutputFile = generateOutputFilename(this);
|
||||
File parentDirectory = mOutputFile.getParentFile();
|
||||
if (parentDirectory.mkdirs()) {
|
||||
Log.d(Config.LOGTAG, "created " + parentDirectory.getAbsolutePath());
|
||||
}
|
||||
File noMedia = new File(parentDirectory, ".nomedia");
|
||||
if (!noMedia.exists()) {
|
||||
try {
|
||||
if (noMedia.createNewFile()) {
|
||||
Log.d(Config.LOGTAG, "created nomedia file in " + parentDirectory.getAbsolutePath());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.d(Config.LOGTAG, "unable to create nomedia file in " + parentDirectory.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
setupFileObserver(parentDirectory);
|
||||
}
|
||||
private static File generateOutputFilename(Context context) {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
|
||||
String filename = "RECORDING_" + dateFormat.format(new Date()) + ".m4a";
|
||||
return new File(FileBackend.getConversationsDirectory(context, STORAGE_DIRECTORY_TYPE_NAME) + "/" + filename);
|
||||
}
|
||||
|
||||
private void setupFileObserver(File directory) {
|
||||
mFileObserver = new FileObserver(directory.getAbsolutePath()) {
|
||||
@Override
|
||||
public void onEvent(int event, String s) {
|
||||
if (s != null && s.equals(mOutputFile.getName()) && event == FileObserver.CLOSE_WRITE) {
|
||||
if (mShouldFinishAfterWrite) {
|
||||
setResult(Activity.RESULT_OK, new Intent().setData(Uri.fromFile(mOutputFile)));
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
mFileObserver.startWatching();
|
||||
}
|
||||
private void setupOutputFile() {
|
||||
mOutputFile = generateOutputFilename(this);
|
||||
File parentDirectory = mOutputFile.getParentFile();
|
||||
if (parentDirectory.mkdirs()) {
|
||||
Log.d(Config.LOGTAG, "created " + parentDirectory.getAbsolutePath());
|
||||
}
|
||||
File noMedia = new File(parentDirectory, ".nomedia");
|
||||
if (!noMedia.exists()) {
|
||||
try {
|
||||
if (noMedia.createNewFile()) {
|
||||
Log.d(Config.LOGTAG, "created nomedia file in " + parentDirectory.getAbsolutePath());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.d(Config.LOGTAG, "unable to create nomedia file in " + parentDirectory.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
setupFileObserver(parentDirectory);
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private void tick() {
|
||||
long time = (mStartTime < 0) ? 0 : (SystemClock.elapsedRealtime() - mStartTime);
|
||||
int minutes = (int) (time / 60000);
|
||||
int seconds = (int) (time / 1000) % 60;
|
||||
int milliseconds = (int) (time / 100) % 10;
|
||||
this.binding.timer.setText(minutes + ":" + (seconds < 10 ? "0" + seconds : seconds) + "." + milliseconds);
|
||||
}
|
||||
private void setupFileObserver(File directory) {
|
||||
mFileObserver = new FileObserver(directory.getAbsolutePath()) {
|
||||
@Override
|
||||
public void onEvent(int event, String s) {
|
||||
if (s != null && s.equals(mOutputFile.getName()) && event == FileObserver.CLOSE_WRITE) {
|
||||
outputFileWrittenLatch.countDown();
|
||||
}
|
||||
}
|
||||
};
|
||||
mFileObserver.startWatching();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.cancel_button:
|
||||
mHandler.removeCallbacks(mTickExecutor);
|
||||
stopRecording(false);
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
break;
|
||||
case R.id.share_button:
|
||||
this.binding.shareButton.setEnabled(false);
|
||||
this.binding.shareButton.setText(R.string.please_wait);
|
||||
mHandler.removeCallbacks(mTickExecutor);
|
||||
mHandler.postDelayed(() -> stopRecording(true), 500);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@SuppressLint("SetTextI18n")
|
||||
private void tick() {
|
||||
long time = (mStartTime < 0) ? 0 : (SystemClock.elapsedRealtime() - mStartTime);
|
||||
int minutes = (int) (time / 60000);
|
||||
int seconds = (int) (time / 1000) % 60;
|
||||
int milliseconds = (int) (time / 100) % 10;
|
||||
this.binding.timer.setText(minutes + ":" + (seconds < 10 ? "0" + seconds : seconds) + "." + milliseconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
switch (view.getId()) {
|
||||
case R.id.cancel_button:
|
||||
mHandler.removeCallbacks(mTickExecutor);
|
||||
stopRecording(false);
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
break;
|
||||
case R.id.share_button:
|
||||
this.binding.shareButton.setEnabled(false);
|
||||
this.binding.shareButton.setText(R.string.please_wait);
|
||||
mHandler.removeCallbacks(mTickExecutor);
|
||||
mHandler.postDelayed(() -> stopRecording(true), 500);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import android.view.MenuItem;
|
|||
import android.widget.Toast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
|
@ -142,7 +143,8 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
|
|||
this.share.text = text;
|
||||
}
|
||||
} else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
|
||||
this.share.uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||
final ArrayList<Uri> uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||
this.share.uris = uris == null ? new ArrayList<>() : uris;
|
||||
}
|
||||
if (xmppConnectionServiceBound) {
|
||||
xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0, false);
|
||||
|
|
|
@ -62,6 +62,7 @@ import eu.siacs.conversations.entities.Bookmark;
|
|||
import eu.siacs.conversations.entities.Contact;
|
||||
import eu.siacs.conversations.entities.Conversation;
|
||||
import eu.siacs.conversations.entities.ListItem;
|
||||
import eu.siacs.conversations.entities.MucOptions;
|
||||
import eu.siacs.conversations.entities.Presence;
|
||||
import eu.siacs.conversations.services.QuickConversationsService;
|
||||
import eu.siacs.conversations.services.XmppConnectionService;
|
||||
|
@ -1021,8 +1022,8 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
|||
} else {
|
||||
final Bookmark bookmark = new Bookmark(account, conferenceJid.asBareJid());
|
||||
bookmark.setAutojoin(getBooleanPreference("autojoin", R.bool.autojoin));
|
||||
String nick = conferenceJid.getResource();
|
||||
if (nick != null && !nick.isEmpty()) {
|
||||
final String nick = conferenceJid.getResource();
|
||||
if (nick != null && !nick.isEmpty() && !nick.equals(MucOptions.defaultNick(account))) {
|
||||
bookmark.setNick(nick);
|
||||
}
|
||||
account.getBookmarks().add(bookmark);
|
||||
|
|
|
@ -49,7 +49,7 @@ public class BackupFileHeader {
|
|||
public static BackupFileHeader read(DataInputStream inputStream) throws IOException {
|
||||
final int version = inputStream.readInt();
|
||||
if (version > VERSION) {
|
||||
throw new IllegalArgumentException("Backup File version was "+version+" but app only supports up to version "+VERSION);
|
||||
throw new IllegalArgumentException("Backup File version was " + version + " but app only supports up to version " + VERSION);
|
||||
}
|
||||
String app = inputStream.readUTF();
|
||||
String jid = inputStream.readUTF();
|
||||
|
@ -59,7 +59,7 @@ public class BackupFileHeader {
|
|||
byte[] salt = new byte[16];
|
||||
inputStream.readFully(salt);
|
||||
|
||||
return new BackupFileHeader(app,Jid.of(jid),timestamp,iv,salt);
|
||||
return new BackupFileHeader(app, Jid.of(jid), timestamp, iv, salt);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1184,8 +1184,21 @@ public class XmppConnection implements Runnable {
|
|||
if (advancedStreamFeaturesLoaded && (jid.equals(Jid.of(account.getServer())) || jid.equals(account.getJid().asBareJid()))) {
|
||||
enableAdvancedStreamFeatures();
|
||||
}
|
||||
} else {
|
||||
} else if (packet.getType() == IqPacket.TYPE.ERROR) {
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": could not query disco info for " + jid.toString());
|
||||
final boolean serverOrAccount = jid.equals(Jid.of(account.getServer())) || jid.equals(account.getJid().asBareJid());
|
||||
final boolean advancedStreamFeaturesLoaded;
|
||||
if (serverOrAccount) {
|
||||
synchronized (XmppConnection.this.disco) {
|
||||
disco.put(jid, ServiceDiscoveryResult.empty());
|
||||
advancedStreamFeaturesLoaded = disco.containsKey(Jid.of(account.getServer())) && disco.containsKey(account.getJid().asBareJid());
|
||||
}
|
||||
} else {
|
||||
advancedStreamFeaturesLoaded = false;
|
||||
}
|
||||
if (advancedStreamFeaturesLoaded) {
|
||||
enableAdvancedStreamFeatures();
|
||||
}
|
||||
}
|
||||
if (packet.getType() != IqPacket.TYPE.TIMEOUT) {
|
||||
if (mPendingServiceDiscoveries.decrementAndGet() == 0
|
||||
|
@ -1294,11 +1307,10 @@ public class XmppConnection implements Runnable {
|
|||
throw new IOException();
|
||||
} else if (streamError.hasChild("host-unknown")) {
|
||||
throw new StateChangingException(Account.State.HOST_UNKNOWN);
|
||||
} else if (streamError.hasChild("policy-violation")) { ;
|
||||
} else if (streamError.hasChild("policy-violation")) {
|
||||
this.lastConnect = SystemClock.elapsedRealtime();
|
||||
final String text = streamError.findChildContent("text");
|
||||
if (text != null) {
|
||||
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": policy violation. "+text);
|
||||
}
|
||||
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": policy violation. "+text);
|
||||
throw new StateChangingException(Account.State.POLICY_VIOLATION);
|
||||
} else {
|
||||
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": stream error " + streamError.toString());
|
||||
|
@ -1545,7 +1557,8 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
|
||||
public int getTimeToNextAttempt() {
|
||||
final int interval = Math.min((int) (25 * Math.pow(1.3, attempt)), 300);
|
||||
final int additionalTime = account.getLastErrorStatus() == Account.State.POLICY_VIOLATION ? 3 : 0;
|
||||
final int interval = Math.min((int) (25 * Math.pow(1.3, (additionalTime + attempt))), 300);
|
||||
final int secondsSinceLast = (int) ((SystemClock.elapsedRealtime() - this.lastConnect) / 1000);
|
||||
return interval - secondsSinceLast;
|
||||
}
|
||||
|
|
|
@ -119,6 +119,8 @@ public class JingleConnection implements Transferable {
|
|||
}
|
||||
Log.d(Config.LOGTAG, "successfully transmitted file:" + file.getAbsolutePath() + " (" + CryptoHelper.bytesToHex(file.getSha1Sum()) + ")");
|
||||
return;
|
||||
} else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
|
||||
account.getPgpDecryptionService().decrypt(message, true);
|
||||
}
|
||||
} else {
|
||||
if (ftVersion == Content.Version.FT_5) { //older Conversations will break when receiving a session-info
|
||||
|
@ -414,41 +416,26 @@ public class JingleConnection implements Transferable {
|
|||
this.mXmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, packet.getFrom().asBareJid());
|
||||
}
|
||||
Element fileSize = fileOffer.findChild("size");
|
||||
Element fileNameElement = fileOffer.findChild("name");
|
||||
if (fileNameElement != null) {
|
||||
String[] filename = fileNameElement.getContent()
|
||||
.toLowerCase(Locale.US).toLowerCase().split("\\.");
|
||||
String extension = filename[filename.length - 1];
|
||||
if (VALID_IMAGE_EXTENSIONS.contains(extension)) {
|
||||
final String path = fileOffer.findChildContent("name");
|
||||
if (path != null) {
|
||||
AbstractConnectionManager.Extension extension = AbstractConnectionManager.Extension.of(path);
|
||||
if (VALID_IMAGE_EXTENSIONS.contains(extension.main)) {
|
||||
message.setType(Message.TYPE_IMAGE);
|
||||
message.setRelativeFilePath(message.getUuid() + "." + extension);
|
||||
} else if (VALID_CRYPTO_EXTENSIONS.contains(
|
||||
filename[filename.length - 1])) {
|
||||
if (filename.length == 3) {
|
||||
extension = filename[filename.length - 2];
|
||||
if (VALID_IMAGE_EXTENSIONS.contains(extension)) {
|
||||
message.setType(Message.TYPE_IMAGE);
|
||||
message.setRelativeFilePath(message.getUuid() + "." + extension);
|
||||
} else {
|
||||
message.setType(Message.TYPE_FILE);
|
||||
}
|
||||
message.setEncryption(Message.ENCRYPTION_PGP);
|
||||
message.setRelativeFilePath(message.getUuid() + "." + extension.main);
|
||||
} else if (VALID_CRYPTO_EXTENSIONS.contains(extension.main)) {
|
||||
if (VALID_IMAGE_EXTENSIONS.contains(extension.secondary)) {
|
||||
message.setType(Message.TYPE_IMAGE);
|
||||
message.setRelativeFilePath(message.getUuid() + "." + extension.secondary);
|
||||
} else {
|
||||
message.setType(Message.TYPE_FILE);
|
||||
message.setRelativeFilePath(message.getUuid() + (extension.secondary != null ? ("." + extension.secondary) : ""));
|
||||
}
|
||||
message.setEncryption(Message.ENCRYPTION_PGP);
|
||||
} else {
|
||||
message.setType(Message.TYPE_FILE);
|
||||
message.setRelativeFilePath(message.getUuid() + (extension.main != null ? ("." + extension.main) : ""));
|
||||
}
|
||||
if (message.getType() == Message.TYPE_FILE) {
|
||||
String suffix = "";
|
||||
if (!fileNameElement.getContent().isEmpty()) {
|
||||
String parts[] = fileNameElement.getContent().split("/");
|
||||
suffix = parts[parts.length - 1];
|
||||
if (message.getEncryption() == Message.ENCRYPTION_PGP && (suffix.endsWith(".pgp") || suffix.endsWith(".gpg"))) {
|
||||
suffix = suffix.substring(0, suffix.length() - 4);
|
||||
}
|
||||
}
|
||||
message.setRelativeFilePath(message.getUuid() + "_" + suffix);
|
||||
}
|
||||
long size = Long.parseLong(fileSize.getContent());
|
||||
long size = parseLong(fileSize, 0);
|
||||
message.setBody(Long.toString(size));
|
||||
conversation.add(message);
|
||||
mJingleConnectionManager.updateConversationUi(true);
|
||||
|
@ -493,6 +480,18 @@ public class JingleConnection implements Transferable {
|
|||
}
|
||||
}
|
||||
|
||||
private static long parseLong(final Element element, final long l) {
|
||||
final String input = element == null ? null : element.getContent();
|
||||
if (input == null) {
|
||||
return l;
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(input);
|
||||
} catch (Exception e) {
|
||||
return l;
|
||||
}
|
||||
}
|
||||
|
||||
private void sendInitRequest() {
|
||||
JinglePacket packet = this.bootstrapPacket("session-initiate");
|
||||
Content content = new Content(this.contentCreator, this.contentName);
|
||||
|
@ -841,18 +840,17 @@ public class JingleConnection implements Transferable {
|
|||
|
||||
private boolean receiveFallbackToIbb(JinglePacket packet) {
|
||||
Log.d(Config.LOGTAG, "receiving fallack to ibb");
|
||||
String receivedBlockSize = packet.getJingleContent().ibbTransport()
|
||||
.getAttribute("block-size");
|
||||
final String receivedBlockSize = packet.getJingleContent().ibbTransport().getAttribute("block-size");
|
||||
if (receivedBlockSize != null) {
|
||||
int bs = Integer.parseInt(receivedBlockSize);
|
||||
if (bs > this.ibbBlockSize) {
|
||||
final int bs = Integer.parseInt(receivedBlockSize);
|
||||
if (bs < this.ibbBlockSize) {
|
||||
this.ibbBlockSize = bs;
|
||||
}
|
||||
}
|
||||
this.transportId = packet.getJingleContent().getTransportId();
|
||||
this.transport = new JingleInbandTransport(this, this.transportId, this.ibbBlockSize);
|
||||
|
||||
JinglePacket answer = bootstrapPacket("transport-accept");
|
||||
final JinglePacket answer = bootstrapPacket("transport-accept");
|
||||
|
||||
final Content content = new Content(contentCreator, contentName);
|
||||
content.setFileOffer(fileOffer, ftVersion);
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:background="?attr/color_background_primary"
|
||||
android:orientation="vertical"
|
||||
android:scrollbars="vertical"
|
||||
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
|
||||
|
|
|
@ -864,4 +864,12 @@
|
|||
<string name="this_looks_like_a_domain">Dies sieht aus wie eine Domain-Adresse</string>
|
||||
<string name="add_anway">Trotzdem hinzufügen</string>
|
||||
<string name="this_looks_like_channel">Dies sieht aus wie eine Channel-Adresse</string>
|
||||
<string name="share_backup_files">Sicherungsdateien teilen</string>
|
||||
<string name="conversations_backup">Sicherung für Conversations</string>
|
||||
<string name="event">Ereignis</string>
|
||||
<string name="open_backup">Sicherung öffnen</string>
|
||||
<string name="not_a_backup_file">Die von dir ausgewählte Datei ist keine Sicherungsdatei von Conversations</string>
|
||||
<string name="account_already_setup">Dieses Konto wurde bereits eingerichtet</string>
|
||||
<string name="please_enter_password">Bitte gib das Passwort für dieses Konto ein</string>
|
||||
<string name="unable_to_perform_this_action">Diese Aktion kann nicht ausgeführt werden</string>
|
||||
</resources>
|
||||
|
|
|
@ -864,4 +864,11 @@
|
|||
<string name="this_looks_like_a_domain">Esto parece una dirección de dominio</string>
|
||||
<string name="add_anway">Añadir de todas formas</string>
|
||||
<string name="this_looks_like_channel">Esto parece una dirección de un canal</string>
|
||||
<string name="share_backup_files">Compartir ficheros de respaldo</string>
|
||||
<string name="conversations_backup">Respaldo de Conversations</string>
|
||||
<string name="event">Evento</string>
|
||||
<string name="open_backup">Abrir respaldo</string>
|
||||
<string name="not_a_backup_file">El fichero seleccionado no es un respaldo de Conversations</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>
|
||||
</resources>
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
<string name="action_unblock_contact">Kontaktua desblokeatu</string>
|
||||
<string name="action_block_domain">Domeinua blokeatu</string>
|
||||
<string name="action_unblock_domain">Domeinua desblokeatu</string>
|
||||
<string name="action_block_participant">Parte-hartzailea blokeatu</string>
|
||||
<string name="action_unblock_participant">Parte-hartzaileari blokeoa kendu</string>
|
||||
<string name="title_activity_manage_accounts">Kontuak kudeatu</string>
|
||||
<string name="title_activity_settings">Ezarpenak</string>
|
||||
<string name="title_activity_sharewith">Elkarrizketa batekin partekatu</string>
|
||||
|
@ -860,4 +862,11 @@
|
|||
<string name="this_looks_like_a_domain">Honek domeinu helbide baten itxura dauka</string>
|
||||
<string name="add_anway">Gehitu hala ere</string>
|
||||
<string name="this_looks_like_channel">Honek kanal helbide baten itxura dauka</string>
|
||||
<string name="share_backup_files">Babes-kopia fitxategiak partekatu</string>
|
||||
<string name="conversations_backup">Conversations babes-kopia</string>
|
||||
<string name="event">Gertaera</string>
|
||||
<string name="open_backup">Babes-kopia ireki</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="please_enter_password">Mesedez idatzi ezazu kontu honetarako pasahitza</string>
|
||||
</resources>
|
||||
|
|
|
@ -864,4 +864,12 @@
|
|||
<string name="this_looks_like_a_domain">Esto semella un enderezo de dominio</string>
|
||||
<string name="add_anway">Engadir igualmente</string>
|
||||
<string name="this_looks_like_channel">Esto semella o enderezo de un canal</string>
|
||||
<string name="share_backup_files">Compartir ficheiros de respaldo</string>
|
||||
<string name="conversations_backup">Respaldar Conversations</string>
|
||||
<string name="event">Evento</string>
|
||||
<string name="open_backup">Abrir respaldo</string>
|
||||
<string name="not_a_backup_file">O ficheiro seleccionado non é un ficheiro de respaldo Conversations</string>
|
||||
<string name="account_already_setup">Esta conta xa foi configurada</string>
|
||||
<string name="please_enter_password">Introduza o contrasinal de esta conta</string>
|
||||
<string name="unable_to_perform_this_action">Non se puido completar a acción</string>
|
||||
</resources>
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
<string name="action_unblock_contact">Tiltás feloldása</string>
|
||||
<string name="action_block_domain">Tartomány tiltása</string>
|
||||
<string name="action_unblock_domain">Tartomány feloldása</string>
|
||||
<string name="action_block_participant">Résztvevő letiltása</string>
|
||||
<string name="action_unblock_participant">Résztvevő feloldása</string>
|
||||
<string name="title_activity_manage_accounts">Fiókok kezelése</string>
|
||||
<string name="title_activity_settings">Beállítások</string>
|
||||
<string name="title_activity_sharewith">Megosztás Conversation-nel</string>
|
||||
|
@ -862,4 +864,11 @@
|
|||
<string name="this_looks_like_a_domain">Ez egy domain címnek tűnik</string>
|
||||
<string name="add_anway">Akkor is adja hozzá</string>
|
||||
<string name="this_looks_like_channel">Ez egy csatorna címnek tűnik</string>
|
||||
<string name="share_backup_files">Biztonsági mentések megosztása</string>
|
||||
<string name="conversations_backup">Conversations biztonsági mentés</string>
|
||||
<string name="event">Esemény</string>
|
||||
<string name="open_backup">Biztonsági mentés megnyitása</string>
|
||||
<string name="not_a_backup_file">A kiválasztott fájl nem a Conversations biztonsági mentése</string>
|
||||
<string name="account_already_setup">Ez a fiók már be lett állítva</string>
|
||||
<string name="please_enter_password">Kérem, adja meg a fiókhoz tartozó jelszót</string>
|
||||
</resources>
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
<string name="action_unblock_contact">Sblocca contatto</string>
|
||||
<string name="action_block_domain">Blocca dominio</string>
|
||||
<string name="action_unblock_domain">Sblocca dominio</string>
|
||||
<string name="action_block_participant">Blocca partecipante</string>
|
||||
<string name="action_unblock_participant">Sblocca partecipante</string>
|
||||
<string name="title_activity_manage_accounts">Gestisci account</string>
|
||||
<string name="title_activity_settings">Impostazioni</string>
|
||||
<string name="title_activity_sharewith">Condividi con Conversation</string>
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
<string name="action_unblock_contact">Contact deblokkeren</string>
|
||||
<string name="action_block_domain">Domein blokkeren</string>
|
||||
<string name="action_unblock_domain">Domein deblokkeren</string>
|
||||
<string name="action_block_participant">Deelnemer blokkeren</string>
|
||||
<string name="action_unblock_participant">Deelnemer deblokkeren</string>
|
||||
<string name="title_activity_manage_accounts">Accounts beheren</string>
|
||||
<string name="title_activity_settings">Instellingen</string>
|
||||
<string name="title_activity_sharewith">Delen in gesprek</string>
|
||||
|
@ -861,4 +863,12 @@
|
|||
<string name="this_looks_like_a_domain">Dit lijkt op een domeinadres</string>
|
||||
<string name="add_anway">Tóch toevoegen</string>
|
||||
<string name="this_looks_like_channel">Dit lijkt op een kanaaladres</string>
|
||||
<string name="share_backup_files">Back-upbestanden delen</string>
|
||||
<string name="conversations_backup">Back-up van Conversations</string>
|
||||
<string name="event">Gebeurtenis</string>
|
||||
<string name="open_backup">Back-up openen</string>
|
||||
<string name="not_a_backup_file">Het geselecteerde bestand is geen Conversations-back-upbestand</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="unable_to_perform_this_action">Kan deze actie niet uitvoeren</string>
|
||||
</resources>
|
||||
|
|
|
@ -881,4 +881,12 @@ Administrator twojego serwera będzie mógł czytać twoje wiadomości, ale moż
|
|||
<string name="this_looks_like_a_domain">To wygląda jak nazwa domeny</string>
|
||||
<string name="add_anway">Dodaj i tak</string>
|
||||
<string name="this_looks_like_channel">To wygląda jak adres kanału</string>
|
||||
<string name="share_backup_files">Udostępnij pliki kopii zapasowych</string>
|
||||
<string name="conversations_backup">Kopia zapasowa Conversations</string>
|
||||
<string name="event">Zdarzenie</string>
|
||||
<string name="open_backup">Otwórz kopię zapasową</string>
|
||||
<string name="not_a_backup_file">Plik który otworzyłeś nie jest plikiem kopii zapasowej Conversations</string>
|
||||
<string name="account_already_setup">To konto zostało już ustawione</string>
|
||||
<string name="please_enter_password">Proszę podać hasło dla tego konta</string>
|
||||
<string name="unable_to_perform_this_action">Nie można wykonać tej akcji</string>
|
||||
</resources>
|
||||
|
|
|
@ -873,4 +873,12 @@ sau chiar pierderea mesajelor.\nÎn continuare veți fi rugați să dezactivați
|
|||
<string name="this_looks_like_a_domain">Aceasta pare să fie o adresă de domeniu</string>
|
||||
<string name="add_anway">Adaugă oricum</string>
|
||||
<string name="this_looks_like_channel">Aceasta pare o adresă de canal</string>
|
||||
<string name="share_backup_files">Partajează fișierele copiei de siguranță</string>
|
||||
<string name="conversations_backup">Copie de siguranță Conversations</string>
|
||||
<string name="event">Eveniment</string>
|
||||
<string name="open_backup">Deschide o copie de siguranță</string>
|
||||
<string name="not_a_backup_file">Fișierul selectat nu este o copie de siguranța Conversations</string>
|
||||
<string name="account_already_setup">Acest cont a fost deja configurat</string>
|
||||
<string name="please_enter_password">Va rugăm să introduceți parola pentru acest cont</string>
|
||||
<string name="unable_to_perform_this_action">Nu se poate realiza această acțiune</string>
|
||||
</resources>
|
||||
|
|
|
@ -873,4 +873,5 @@
|
|||
<string name="not_a_backup_file">The file you selected is not a Conversations backup file</string>
|
||||
<string name="account_already_setup">This account has already been setup</string>
|
||||
<string name="please_enter_password">Please enter the password for this account</string>
|
||||
<string name="unable_to_perform_this_action">Unable to perform this action</string>
|
||||
</resources>
|
||||
|
|
|
@ -147,7 +147,14 @@ public class PushManagementService {
|
|||
}
|
||||
|
||||
private void retrieveFcmInstanceToken(final OnGcmInstanceTokenRetrieved instanceTokenRetrieved) {
|
||||
FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(task -> {
|
||||
final FirebaseInstanceId firebaseInstanceId;
|
||||
try {
|
||||
firebaseInstanceId = FirebaseInstanceId.getInstance();
|
||||
} catch (IllegalStateException e) {
|
||||
Log.d(Config.LOGTAG, "unable to get firebase instance token ",e);
|
||||
return;
|
||||
}
|
||||
firebaseInstanceId.getInstanceId().addOnCompleteListener(task -> {
|
||||
if (!task.isSuccessful()) {
|
||||
Log.d(Config.LOGTAG, "unable to get Firebase instance token", task.getException());
|
||||
}
|
||||
|
|
|
@ -8,13 +8,17 @@ import android.preference.PreferenceManager;
|
|||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.ConnectException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -29,10 +33,11 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.android.JabberIdContact;
|
||||
import eu.siacs.conversations.android.PhoneNumberContact;
|
||||
import eu.siacs.conversations.crypto.sasl.Plain;
|
||||
import eu.siacs.conversations.entities.Account;
|
||||
|
@ -44,8 +49,6 @@ import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
|
|||
import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
|
||||
import eu.siacs.conversations.xml.Element;
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
|
||||
import eu.siacs.conversations.xmpp.XmppConnection;
|
||||
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
||||
import io.michaelrocks.libphonenumber.android.Phonenumber;
|
||||
import rocks.xmpp.addr.Jid;
|
||||
|
@ -58,6 +61,9 @@ public class QuickConversationsService extends AbstractQuickConversationsService
|
|||
public static final int API_ERROR_CONNECT = -3;
|
||||
public static final int API_ERROR_SSL_HANDSHAKE = -4;
|
||||
public static final int API_ERROR_AIRPLANE_MODE = -5;
|
||||
public static final int API_ERROR_SSL_CERTIFICATE = -6;
|
||||
public static final int API_ERROR_SSL_GENERAL = -7;
|
||||
public static final int API_ERROR_TIMEOUT = -8;
|
||||
|
||||
private static final String API_DOMAIN = "api." + Config.QUICKSY_DOMAIN;
|
||||
|
||||
|
@ -135,7 +141,7 @@ public class QuickConversationsService extends AbstractQuickConversationsService
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (IOException e) {
|
||||
final int code = getApiErrorCode(e);
|
||||
synchronized (mOnVerificationRequested) {
|
||||
for (OnVerificationRequested onVerificationRequested : mOnVerificationRequested) {
|
||||
|
@ -232,7 +238,7 @@ public class QuickConversationsService extends AbstractQuickConversationsService
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (IOException e) {
|
||||
final int code = getApiErrorCode(e);
|
||||
synchronized (mOnVerification) {
|
||||
for (OnVerification onVerification : mOnVerification) {
|
||||
|
@ -265,7 +271,7 @@ public class QuickConversationsService extends AbstractQuickConversationsService
|
|||
|
||||
}
|
||||
|
||||
private int getApiErrorCode(Exception e) {
|
||||
private int getApiErrorCode(final Exception e) {
|
||||
if (!service.hasInternetConnection()) {
|
||||
return API_ERROR_AIRPLANE_MODE;
|
||||
} else if (e instanceof UnknownHostException) {
|
||||
|
@ -274,6 +280,12 @@ public class QuickConversationsService extends AbstractQuickConversationsService
|
|||
return API_ERROR_CONNECT;
|
||||
} else if (e instanceof SSLHandshakeException) {
|
||||
return API_ERROR_SSL_HANDSHAKE;
|
||||
} else if (e instanceof SSLPeerUnverifiedException || e instanceof CertificateException) {
|
||||
return API_ERROR_SSL_CERTIFICATE;
|
||||
} else if (e instanceof SSLException || e instanceof GeneralSecurityException) {
|
||||
return API_ERROR_SSL_GENERAL;
|
||||
} else if (e instanceof SocketTimeoutException) {
|
||||
return API_ERROR_TIMEOUT;
|
||||
} else {
|
||||
Log.d(Config.LOGTAG, e.getClass().getName());
|
||||
return API_ERROR_OTHER;
|
||||
|
|
|
@ -33,6 +33,15 @@ public class ApiDialogHelper {
|
|||
case QuickConversationsService.API_ERROR_UNKNOWN_HOST:
|
||||
res = R.string.unable_to_find_server;
|
||||
break;
|
||||
case QuickConversationsService.API_ERROR_SSL_CERTIFICATE:
|
||||
res = R.string.unable_to_verify_server_identity;
|
||||
break;
|
||||
case QuickConversationsService.API_ERROR_SSL_GENERAL:
|
||||
res = R.string.unknown_security_error;
|
||||
break;
|
||||
case QuickConversationsService.API_ERROR_TIMEOUT:
|
||||
res = R.string.timeout_while_connecting_to_server;
|
||||
break;
|
||||
case 400:
|
||||
res = R.string.invalid_user_input;
|
||||
break;
|
||||
|
|
|
@ -46,7 +46,9 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:cursorVisible="false"
|
||||
android:inputType="textNoSuggestions"
|
||||
android:drawableEnd="@drawable/ic_arrow_drop_down_black_18dp"
|
||||
android:drawableRight="@drawable/ic_arrow_drop_down_black_18dp"
|
||||
android:focusable="false"
|
||||
android:gravity="bottom|center_horizontal"
|
||||
android:longClickable="false" />
|
||||
|
|
|
@ -20,4 +20,4 @@
|
|||
<string name="foreground_service_channel_description">Тази категория известия се използва за показване на постоянно известие, което показва, че Quicksy работи.</string>
|
||||
<string name="set_profile_picture">Профилна снимка за Quicksy</string>
|
||||
<string name="not_available_in_your_country">Quicksy не може да се използва във Вашата страна.</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -20,4 +20,7 @@
|
|||
<string name="foreground_service_channel_description">Diese Benachrichtigungsart wird verwendet, um eine permanente Benachrichtigung anzuzeigen, die anzeigt, dass Quicksy gerade ausgeführt wird.</string>
|
||||
<string name="set_profile_picture">Quicksy Profilbild</string>
|
||||
<string name="not_available_in_your_country">Quicksy ist in deinem Land nicht verfügbar.</string>
|
||||
<string name="unable_to_verify_server_identity">Die Überprüfung der Serveridentität ist nicht möglich.</string>
|
||||
<string name="unknown_security_error">Unbekannter Sicherheitsfehler.</string>
|
||||
<string name="timeout_while_connecting_to_server">Zeitüberschreitung bei der Verbindung zum Server.</string>
|
||||
</resources>
|
||||
|
|
|
@ -20,4 +20,7 @@
|
|||
<string name="foreground_service_channel_description">Esta categoría de notificación se usa para mostrar una notificación permantente indicando que Quicksy está ejecutándose.</string>
|
||||
<string name="set_profile_picture">Foto de perfil en Quicksy</string>
|
||||
<string name="not_available_in_your_country">Quicksy no está disponible en tu país.</string>
|
||||
<string name="unable_to_verify_server_identity">No se ha podido verificar la identidad del servidor.</string>
|
||||
<string name="unknown_security_error">Error de seguridad desconocido.</string>
|
||||
<string name="timeout_while_connecting_to_server">Se ha superado el tiempo máximo de espera conectando al servidor.</string>
|
||||
</resources>
|
||||
|
|
|
@ -20,4 +20,7 @@
|
|||
<string name="foreground_service_channel_description">Esta categoría de notificacións utilízase para mostrar unha notificación permanente que indica que Quicksy está funcionando.</string>
|
||||
<string name="set_profile_picture">Imaxe de perfil Quicksy</string>
|
||||
<string name="not_available_in_your_country">Quicksy non está dispoñible no seu país.</string>
|
||||
<string name="unable_to_verify_server_identity">Non se puido validar a identidade do servidor.</string>
|
||||
<string name="unknown_security_error">Fallo de seguridade descoñecido.</string>
|
||||
<string name="timeout_while_connecting_to_server">Caducou a conexión mentras conectaba co servidor.</string>
|
||||
</resources>
|
||||
|
|
|
@ -19,4 +19,8 @@
|
|||
<string name="no_microphone_permission">A Quicksy-nek hozzáférésre lenne szüksége a mikrofonhoz</string>
|
||||
<string name="foreground_service_channel_description">Ez az értesítési kategória állandó értesítést jelenít meg arról, hogy a Quicksy fut.</string>
|
||||
<string name="set_profile_picture">Quicksy profilkép</string>
|
||||
</resources>
|
||||
<string name="not_available_in_your_country">A Quicksy nem érhető el az Ön országában.</string>
|
||||
<string name="unable_to_verify_server_identity">Nem sikerült ellenőrizni a szerver azonosságát.</string>
|
||||
<string name="unknown_security_error">Ismeretlen biztonsági hiba.</string>
|
||||
<string name="timeout_while_connecting_to_server">Időtúllépés történt a szerverhez való csatlakozás közben.</string>
|
||||
</resources>
|
||||
|
|
|
@ -19,4 +19,5 @@
|
|||
<string name="no_microphone_permission">Quicksy ha bisogno di accedere al microfono</string>
|
||||
<string name="foreground_service_channel_description">Questa categoria di notifiche è usata per mostrare una notifica permanente per indicare che Quicksy è in esecuzione.</string>
|
||||
<string name="set_profile_picture">Immagine profilo di Quicksy</string>
|
||||
<string name="not_available_in_your_country">Quicksy non è disponibile nella tua nazione.</string>
|
||||
</resources>
|
||||
|
|
|
@ -19,4 +19,8 @@
|
|||
<string name="no_microphone_permission">Quicksy heeft toegang nodig tot de microfoon</string>
|
||||
<string name="foreground_service_channel_description">Deze meldingscategorie wordt gebruikt om een permanente melding weer te geven dat Quicksy wordt uitgevoerd.</string>
|
||||
<string name="set_profile_picture">Quicksy-profielafbeelding</string>
|
||||
</resources>
|
||||
<string name="not_available_in_your_country">Quicksy is niet beschikbaar in je land.</string>
|
||||
<string name="unable_to_verify_server_identity">Kan serveridentiteit niet verifiëren.</string>
|
||||
<string name="unknown_security_error">Onbekende beveiligingsfout.</string>
|
||||
<string name="timeout_while_connecting_to_server">Time-out bij verbinden met server.</string>
|
||||
</resources>
|
||||
|
|
|
@ -20,4 +20,7 @@
|
|||
<string name="foreground_service_channel_description">Ta kategoria powiadomień jest używana do wyświetlania ciągłego powiadomienia o tym, że Quicksy działa.</string>
|
||||
<string name="set_profile_picture">Obrazek profilowy Quicksy</string>
|
||||
<string name="not_available_in_your_country">Quicksy nie jest dostępne w twoim kraju</string>
|
||||
<string name="unable_to_verify_server_identity">Nie udało się sprawdzić tożsamości serwera.</string>
|
||||
<string name="unknown_security_error">Nieznany błąd bezpieczeństwa.</string>
|
||||
<string name="timeout_while_connecting_to_server">Błąd czasu oczekiwania na połączenie z serwerem.</string>
|
||||
</resources>
|
||||
|
|
|
@ -22,4 +22,7 @@ sau chiar pierderea mesajelor.\nÎn continuare veți fi rugați să dezactivați
|
|||
<string name="foreground_service_channel_description">Această categorie de notificări este folosită pentru a arăta o notificare permanentă ce indică rularea Quicksy</string>
|
||||
<string name="set_profile_picture">Poză profil Quicksy</string>
|
||||
<string name="not_available_in_your_country">Quicksy nu este disponibilă în țara dumneavoastră.</string>
|
||||
<string name="unable_to_verify_server_identity">Nu s-a putut verifica identitatea serverului.</string>
|
||||
<string name="unknown_security_error">Eroare de securitate necunoscută.</string>
|
||||
<string name="timeout_while_connecting_to_server">A expirat timpul de așteptare conexiune server.</string>
|
||||
</resources>
|
||||
|
|
|
@ -20,4 +20,7 @@
|
|||
<string name="foreground_service_channel_description">This notification category is used to display a permanent notification indicating that Quicksy is running.</string>
|
||||
<string name="set_profile_picture">Quicksy profile picture</string>
|
||||
<string name="not_available_in_your_country">Quicksy is not available in your country.</string>
|
||||
<string name="unable_to_verify_server_identity">Unable to verify server identity.</string>
|
||||
<string name="unknown_security_error">Unknown security error.</string>
|
||||
<string name="timeout_while_connecting_to_server">Timeout while connecting to server.</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue