diff --git a/CHANGELOG.md b/CHANGELOG.md index c57da8926..6f484990f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/build.gradle b/build.gradle index c976b3b13..e2f592ee4 100644 --- a/build.gradle +++ b/build.gradle @@ -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 diff --git a/src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java b/src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java index d8246a6af..66ebd7c3a 100644 --- a/src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java +++ b/src/conversations/java/eu/siacs/conversations/services/ImportBackupService.java @@ -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); } } diff --git a/src/conversations/java/eu/siacs/conversations/ui/ImportBackupActivity.java b/src/conversations/java/eu/siacs/conversations/ui/ImportBackupActivity.java index d6dbfd222..da4512465 100644 --- a/src/conversations/java/eu/siacs/conversations/ui/ImportBackupActivity.java +++ b/src/conversations/java/eu/siacs/conversations/ui/ImportBackupActivity.java @@ -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(); } } diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java index 9de4f3fc7..d64c81e1d 100644 --- a/src/main/java/eu/siacs/conversations/Config.java +++ b/src/main/java/eu/siacs/conversations/Config.java @@ -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; diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java index 62777f194..a70c67198 100644 --- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java +++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java @@ -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; } } diff --git a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java index 826723fb7..3e2761101 100644 --- a/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java +++ b/src/main/java/eu/siacs/conversations/entities/ServiceDiscoveryResult.java @@ -86,6 +86,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( diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java index dc47fc4ed..1270c1019 100644 --- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java @@ -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() { diff --git a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java index df6b9eaca..d3143f01e 100644 --- a/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/PresenceGenerator.java @@ -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) { diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java index b5aed7212..8c366558d 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java +++ b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java @@ -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()) { diff --git a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java index 294af5d15..432c70390 100644 --- a/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/services/AbstractConnectionManager.java @@ -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); + } + } } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 0c6df6d74..d40a3ccd5 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -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); } diff --git a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java index ee2a15379..863fd5780 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java @@ -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); } diff --git a/src/main/java/eu/siacs/conversations/ui/LocationActivity.java b/src/main/java/eu/siacs/conversations/ui/LocationActivity.java index 2bddf5b2d..80fb8983a 100644 --- a/src/main/java/eu/siacs/conversations/ui/LocationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/LocationActivity.java @@ -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); diff --git a/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java b/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java index 710969334..3cd5e8ed3 100644 --- a/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RecordingActivity.java @@ -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; + } + } } diff --git a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java index ce64af856..83b0bebcc 100644 --- a/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ShareWithActivity.java @@ -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 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); diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index a48dd230b..4de40206c 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -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); diff --git a/src/main/java/eu/siacs/conversations/utils/BackupFileHeader.java b/src/main/java/eu/siacs/conversations/utils/BackupFileHeader.java index bc86a5d24..5e8b80f81 100644 --- a/src/main/java/eu/siacs/conversations/utils/BackupFileHeader.java +++ b/src/main/java/eu/siacs/conversations/utils/BackupFileHeader.java @@ -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); } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 68afc030f..add83d0b7 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -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; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index 1ec5f2714..e44d85056 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -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); diff --git a/src/main/res/layout/activity_muc_users.xml b/src/main/res/layout/activity_muc_users.xml index 679c5156e..c195a0a1a 100644 --- a/src/main/res/layout/activity_muc_users.xml +++ b/src/main/res/layout/activity_muc_users.xml @@ -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" /> diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 1304adf48..8fba03091 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -864,4 +864,12 @@ Dies sieht aus wie eine Domain-Adresse Trotzdem hinzufügen Dies sieht aus wie eine Channel-Adresse + Sicherungsdateien teilen + Sicherung für Conversations + Ereignis + Sicherung öffnen + Die von dir ausgewählte Datei ist keine Sicherungsdatei von Conversations + Dieses Konto wurde bereits eingerichtet + Bitte gib das Passwort für dieses Konto ein + Diese Aktion kann nicht ausgeführt werden diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 7d74e4c44..a67508cce 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -864,4 +864,11 @@ Esto parece una dirección de dominio Añadir de todas formas Esto parece una dirección de un canal + Compartir ficheros de respaldo + Respaldo de Conversations + Evento + Abrir respaldo + El fichero seleccionado no es un respaldo de Conversations + Esta cuenta ya fue configurada + Por favor ingrese la contraseña para esta cuenta diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index 01d0b0a36..2d3bc1e2d 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -17,6 +17,8 @@ Kontaktua desblokeatu Domeinua blokeatu Domeinua desblokeatu + Parte-hartzailea blokeatu + Parte-hartzaileari blokeoa kendu Kontuak kudeatu Ezarpenak Elkarrizketa batekin partekatu @@ -860,4 +862,11 @@ Honek domeinu helbide baten itxura dauka Gehitu hala ere Honek kanal helbide baten itxura dauka + Babes-kopia fitxategiak partekatu + Conversations babes-kopia + Gertaera + Babes-kopia ireki + Hautatu duzun fitxategia ez da Conversations babes-kopia bat + Kontu hau konfiguratuta dago jada + Mesedez idatzi ezazu kontu honetarako pasahitza diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index 7801384e1..f4d9c6864 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -864,4 +864,12 @@ Esto semella un enderezo de dominio Engadir igualmente Esto semella o enderezo de un canal + Compartir ficheiros de respaldo + Respaldar Conversations + Evento + Abrir respaldo + O ficheiro seleccionado non é un ficheiro de respaldo Conversations + Esta conta xa foi configurada + Introduza o contrasinal de esta conta + Non se puido completar a acción diff --git a/src/main/res/values-hu/strings.xml b/src/main/res/values-hu/strings.xml index 1c5cf15dd..af1a9acff 100644 --- a/src/main/res/values-hu/strings.xml +++ b/src/main/res/values-hu/strings.xml @@ -17,6 +17,8 @@ Tiltás feloldása Tartomány tiltása Tartomány feloldása + Résztvevő letiltása + Résztvevő feloldása Fiókok kezelése Beállítások Megosztás Conversation-nel @@ -862,4 +864,11 @@ Ez egy domain címnek tűnik Akkor is adja hozzá Ez egy csatorna címnek tűnik + Biztonsági mentések megosztása + Conversations biztonsági mentés + Esemény + Biztonsági mentés megnyitása + A kiválasztott fájl nem a Conversations biztonsági mentése + Ez a fiók már be lett állítva + Kérem, adja meg a fiókhoz tartozó jelszót diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index cd77172fe..460751b5b 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -17,6 +17,8 @@ Sblocca contatto Blocca dominio Sblocca dominio + Blocca partecipante + Sblocca partecipante Gestisci account Impostazioni Condividi con Conversation diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 3f2e76dcb..b1e74c29b 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -17,6 +17,8 @@ Contact deblokkeren Domein blokkeren Domein deblokkeren + Deelnemer blokkeren + Deelnemer deblokkeren Accounts beheren Instellingen Delen in gesprek @@ -861,4 +863,12 @@ Dit lijkt op een domeinadres Tóch toevoegen Dit lijkt op een kanaaladres + Back-upbestanden delen + Back-up van Conversations + Gebeurtenis + Back-up openen + Het geselecteerde bestand is geen Conversations-back-upbestand + Deze account is al ingesteld + Voer het wachtwoord voor deze account in + Kan deze actie niet uitvoeren diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 7bc92e1d9..bb9b85a0d 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -881,4 +881,12 @@ Administrator twojego serwera będzie mógł czytać twoje wiadomości, ale moż To wygląda jak nazwa domeny Dodaj i tak To wygląda jak adres kanału + Udostępnij pliki kopii zapasowych + Kopia zapasowa Conversations + Zdarzenie + Otwórz kopię zapasową + Plik który otworzyłeś nie jest plikiem kopii zapasowej Conversations + To konto zostało już ustawione + Proszę podać hasło dla tego konta + Nie można wykonać tej akcji diff --git a/src/main/res/values-ro-rRO/strings.xml b/src/main/res/values-ro-rRO/strings.xml index bbd064227..bcce3ce36 100644 --- a/src/main/res/values-ro-rRO/strings.xml +++ b/src/main/res/values-ro-rRO/strings.xml @@ -873,4 +873,12 @@ sau chiar pierderea mesajelor.\nÎn continuare veți fi rugați să dezactivați Aceasta pare să fie o adresă de domeniu Adaugă oricum Aceasta pare o adresă de canal + Partajează fișierele copiei de siguranță + Copie de siguranță Conversations + Eveniment + Deschide o copie de siguranță + Fișierul selectat nu este o copie de siguranța Conversations + Acest cont a fost deja configurat + Va rugăm să introduceți parola pentru acest cont + Nu se poate realiza această acțiune diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 3159c119d..57dd78de4 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -873,4 +873,5 @@ The file you selected is not a Conversations backup file This account has already been setup Please enter the password for this account + Unable to perform this action diff --git a/src/playstore/java/eu/siacs/conversations/services/PushManagementService.java b/src/playstore/java/eu/siacs/conversations/services/PushManagementService.java index 1fc8e58fe..e99de1b14 100644 --- a/src/playstore/java/eu/siacs/conversations/services/PushManagementService.java +++ b/src/playstore/java/eu/siacs/conversations/services/PushManagementService.java @@ -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()); } diff --git a/src/quicksy/java/eu/siacs/conversations/services/QuickConversationsService.java b/src/quicksy/java/eu/siacs/conversations/services/QuickConversationsService.java index cfa2f7775..319f160f7 100644 --- a/src/quicksy/java/eu/siacs/conversations/services/QuickConversationsService.java +++ b/src/quicksy/java/eu/siacs/conversations/services/QuickConversationsService.java @@ -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; diff --git a/src/quicksy/java/eu/siacs/conversations/ui/util/ApiDialogHelper.java b/src/quicksy/java/eu/siacs/conversations/ui/util/ApiDialogHelper.java index d1f987bff..995fe3fca 100644 --- a/src/quicksy/java/eu/siacs/conversations/ui/util/ApiDialogHelper.java +++ b/src/quicksy/java/eu/siacs/conversations/ui/util/ApiDialogHelper.java @@ -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; diff --git a/src/quicksy/res/layout/activity_enter_number.xml b/src/quicksy/res/layout/activity_enter_number.xml index 78d183dd4..ba2e3c1ff 100644 --- a/src/quicksy/res/layout/activity_enter_number.xml +++ b/src/quicksy/res/layout/activity_enter_number.xml @@ -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" /> diff --git a/src/quicksy/res/values-bg/strings.xml b/src/quicksy/res/values-bg/strings.xml index 1ec1e155d..236567885 100644 --- a/src/quicksy/res/values-bg/strings.xml +++ b/src/quicksy/res/values-bg/strings.xml @@ -20,4 +20,4 @@ Тази категория известия се използва за показване на постоянно известие, което показва, че Quicksy работи. Профилна снимка за Quicksy Quicksy не може да се използва във Вашата страна. - + diff --git a/src/quicksy/res/values-de/strings.xml b/src/quicksy/res/values-de/strings.xml index 9fed2e699..2e8a87a29 100644 --- a/src/quicksy/res/values-de/strings.xml +++ b/src/quicksy/res/values-de/strings.xml @@ -20,4 +20,7 @@ Diese Benachrichtigungsart wird verwendet, um eine permanente Benachrichtigung anzuzeigen, die anzeigt, dass Quicksy gerade ausgeführt wird. Quicksy Profilbild Quicksy ist in deinem Land nicht verfügbar. + Die Überprüfung der Serveridentität ist nicht möglich. + Unbekannter Sicherheitsfehler. + Zeitüberschreitung bei der Verbindung zum Server. diff --git a/src/quicksy/res/values-es/strings.xml b/src/quicksy/res/values-es/strings.xml index 2c4a1b0b7..c00850a2d 100644 --- a/src/quicksy/res/values-es/strings.xml +++ b/src/quicksy/res/values-es/strings.xml @@ -20,4 +20,7 @@ Esta categoría de notificación se usa para mostrar una notificación permantente indicando que Quicksy está ejecutándose. Foto de perfil en Quicksy Quicksy no está disponible en tu país. + No se ha podido verificar la identidad del servidor. + Error de seguridad desconocido. + Se ha superado el tiempo máximo de espera conectando al servidor. diff --git a/src/quicksy/res/values-gl/strings.xml b/src/quicksy/res/values-gl/strings.xml index c17eb3bfc..b1675f6e0 100644 --- a/src/quicksy/res/values-gl/strings.xml +++ b/src/quicksy/res/values-gl/strings.xml @@ -20,4 +20,7 @@ Esta categoría de notificacións utilízase para mostrar unha notificación permanente que indica que Quicksy está funcionando. Imaxe de perfil Quicksy Quicksy non está dispoñible no seu país. + Non se puido validar a identidade do servidor. + Fallo de seguridade descoñecido. + Caducou a conexión mentras conectaba co servidor. diff --git a/src/quicksy/res/values-hu/strings.xml b/src/quicksy/res/values-hu/strings.xml index af2244f39..45bfbb6c0 100644 --- a/src/quicksy/res/values-hu/strings.xml +++ b/src/quicksy/res/values-hu/strings.xml @@ -19,4 +19,8 @@ A Quicksy-nek hozzáférésre lenne szüksége a mikrofonhoz Ez az értesítési kategória állandó értesítést jelenít meg arról, hogy a Quicksy fut. Quicksy profilkép - + A Quicksy nem érhető el az Ön országában. + Nem sikerült ellenőrizni a szerver azonosságát. + Ismeretlen biztonsági hiba. + Időtúllépés történt a szerverhez való csatlakozás közben. + diff --git a/src/quicksy/res/values-it/strings.xml b/src/quicksy/res/values-it/strings.xml index 4d61c3c6b..ff5985ec0 100644 --- a/src/quicksy/res/values-it/strings.xml +++ b/src/quicksy/res/values-it/strings.xml @@ -19,4 +19,5 @@ Quicksy ha bisogno di accedere al microfono Questa categoria di notifiche è usata per mostrare una notifica permanente per indicare che Quicksy è in esecuzione. Immagine profilo di Quicksy + Quicksy non è disponibile nella tua nazione. diff --git a/src/quicksy/res/values-nl/strings.xml b/src/quicksy/res/values-nl/strings.xml index a34babb9d..1c3a1c4f4 100644 --- a/src/quicksy/res/values-nl/strings.xml +++ b/src/quicksy/res/values-nl/strings.xml @@ -19,4 +19,8 @@ Quicksy heeft toegang nodig tot de microfoon Deze meldingscategorie wordt gebruikt om een permanente melding weer te geven dat Quicksy wordt uitgevoerd. Quicksy-profielafbeelding - + Quicksy is niet beschikbaar in je land. + Kan serveridentiteit niet verifiëren. + Onbekende beveiligingsfout. + Time-out bij verbinden met server. + diff --git a/src/quicksy/res/values-pl/strings.xml b/src/quicksy/res/values-pl/strings.xml index 9b9961c6e..6fd3d98f7 100644 --- a/src/quicksy/res/values-pl/strings.xml +++ b/src/quicksy/res/values-pl/strings.xml @@ -20,4 +20,7 @@ Ta kategoria powiadomień jest używana do wyświetlania ciągłego powiadomienia o tym, że Quicksy działa. Obrazek profilowy Quicksy Quicksy nie jest dostępne w twoim kraju + Nie udało się sprawdzić tożsamości serwera. + Nieznany błąd bezpieczeństwa. + Błąd czasu oczekiwania na połączenie z serwerem. diff --git a/src/quicksy/res/values-ro-rRO/strings.xml b/src/quicksy/res/values-ro-rRO/strings.xml index bc6ca871f..1b6504387 100644 --- a/src/quicksy/res/values-ro-rRO/strings.xml +++ b/src/quicksy/res/values-ro-rRO/strings.xml @@ -22,4 +22,7 @@ sau chiar pierderea mesajelor.\nÎn continuare veți fi rugați să dezactivați Această categorie de notificări este folosită pentru a arăta o notificare permanentă ce indică rularea Quicksy Poză profil Quicksy Quicksy nu este disponibilă în țara dumneavoastră. + Nu s-a putut verifica identitatea serverului. + Eroare de securitate necunoscută. + A expirat timpul de așteptare conexiune server. diff --git a/src/quicksy/res/values/strings.xml b/src/quicksy/res/values/strings.xml index e9cfa3cae..54f7b1481 100644 --- a/src/quicksy/res/values/strings.xml +++ b/src/quicksy/res/values/strings.xml @@ -20,4 +20,7 @@ This notification category is used to display a permanent notification indicating that Quicksy is running. Quicksy profile picture Quicksy is not available in your country. + Unable to verify server identity. + Unknown security error. + Timeout while connecting to server.