From 199168cf0a2b74c6cb89263a3ca182ccaa8a84ce Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Thu, 13 Dec 2018 10:22:22 +0100 Subject: [PATCH] Implements more userfriendly error messages for connection errors --- .../quasseldroid/ui/chat/ChatActivity.kt | 52 +++ .../quasseldroid/util/backport/OsConstants.kt | 419 ++++++++++++++++++ .../main/java/libcore/io/ErrnoException.java | 32 ++ app/src/main/res/values-de/strings_error.xml | 5 + .../main/res/values-fr-rCA/strings_error.xml | 1 + app/src/main/res/values-lt/strings_error.xml | 1 + app/src/main/res/values-pt/strings_error.xml | 1 + app/src/main/res/values-sr/strings_error.xml | 1 + app/src/main/res/values/strings_error.xml | 5 + .../libquassel/connection/CoreConnection.kt | 6 +- .../connection/ProtocolVersionException.kt | 9 + .../de/kuschku/libquassel/session/ISession.kt | 3 + .../de/kuschku/libquassel/session/Session.kt | 17 +- .../libquassel/session/SessionManager.kt | 5 + .../viewmodel/QuasselViewModel.kt | 4 + 15 files changed, 558 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/de/kuschku/quasseldroid/util/backport/OsConstants.kt create mode 100644 app/src/main/java/libcore/io/ErrnoException.java create mode 100644 lib/src/main/java/de/kuschku/libquassel/connection/ProtocolVersionException.kt diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt index 6eb904134..d6bd0e34c 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt @@ -28,6 +28,7 @@ import android.graphics.Canvas import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle +import android.system.ErrnoException import android.text.Html import android.view.ActionMode import android.view.Menu @@ -36,6 +37,7 @@ import android.view.View import android.widget.EditText import android.widget.LinearLayout import android.widget.TextView +import android.widget.Toast import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.Toolbar @@ -55,6 +57,7 @@ import com.bumptech.glide.request.target.SimpleTarget import com.bumptech.glide.request.transition.Transition import com.google.android.material.bottomsheet.BottomSheetBehavior import de.kuschku.libquassel.connection.ConnectionState +import de.kuschku.libquassel.connection.ProtocolVersionException import de.kuschku.libquassel.connection.QuasselSecurityException import de.kuschku.libquassel.protocol.Buffer_Type import de.kuschku.libquassel.protocol.Message @@ -85,6 +88,7 @@ import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionActiv import de.kuschku.quasseldroid.ui.setup.user.UserSetupActivity import de.kuschku.quasseldroid.util.ColorContext import de.kuschku.quasseldroid.util.avatars.AvatarHelper +import de.kuschku.quasseldroid.util.backport.OsConstants import de.kuschku.quasseldroid.util.helper.* import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer import de.kuschku.quasseldroid.util.missingfeatures.MissingFeaturesDialog @@ -100,6 +104,8 @@ import org.threeten.bp.Instant import org.threeten.bp.ZoneId import org.threeten.bp.format.DateTimeFormatter import org.threeten.bp.format.FormatStyle +import java.net.ConnectException +import java.net.UnknownHostException import java.security.cert.CertificateExpiredException import java.security.cert.CertificateNotYetValidException import javax.inject.Inject @@ -541,6 +547,52 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc } }) + // Connection errors that should show up as toast + viewModel.connectionErrors.toLiveData().observe(this, Observer { error -> + error?.let { + val cause = it.cause + when { + it is UnknownHostException -> { + val host = it.message?.replace("Host is unresolved: ", "") + + Toast.makeText(this, + getString(R.string.label_error_unknown_host, host), + Toast.LENGTH_LONG).show() + } + it is ProtocolVersionException -> { + Toast.makeText(this, + getString(R.string.label_error_invalid_protocol_version, + it.protocol.version), + Toast.LENGTH_LONG).show() + } + it is ConnectException && + cause is libcore.io.ErrnoException -> { + val errorCode = OsConstants.errnoName(cause.errno) + val errorName = OsConstants.strerror(cause.errno) + + Toast.makeText(this, + getString(R.string.label_error_connection, errorName, errorCode), + Toast.LENGTH_LONG).show() + } + it is ConnectException && + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && + cause is ErrnoException -> { + val errorCode = OsConstants.errnoName(cause.errno) + val errorName = OsConstants.strerror(cause.errno) + + Toast.makeText(this, + getString(R.string.label_error_connection, errorName, errorCode), + Toast.LENGTH_LONG).show() + } + else -> { + Toast.makeText(this, + getString(R.string.label_error_connection_closed), + Toast.LENGTH_LONG).show() + } + } + } + }) + // After initial connect, open the drawer viewModel.connectionProgress .filter { (it, _, _) -> it == ConnectionState.CONNECTED } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/backport/OsConstants.kt b/app/src/main/java/de/kuschku/quasseldroid/util/backport/OsConstants.kt new file mode 100644 index 000000000..2d62f5c04 --- /dev/null +++ b/app/src/main/java/de/kuschku/quasseldroid/util/backport/OsConstants.kt @@ -0,0 +1,419 @@ +package de.kuschku.quasseldroid.util.backport + +object OsConstants { + + const val EPERM = 1 + const val ENOENT = 2 + const val ESRCH = 3 + const val EINTR = 4 + const val EIO = 5 + const val ENXIO = 6 + const val E2BIG = 7 + const val ENOEXEC = 8 + const val EBADF = 9 + const val ECHILD = 10 + const val EAGAIN = 11 + const val ENOMEM = 12 + const val EACCES = 13 + const val EFAULT = 14 + const val ENOTBLK = 15 + const val EBUSY = 16 + const val EEXIST = 17 + const val EXDEV = 18 + const val ENODEV = 19 + const val ENOTDIR = 20 + const val EISDIR = 21 + const val EINVAL = 22 + const val ENFILE = 23 + const val EMFILE = 24 + const val ENOTTY = 25 + const val ETXTBSY = 26 + const val EFBIG = 27 + const val ENOSPC = 28 + const val ESPIPE = 29 + const val EROFS = 30 + const val EMLINK = 31 + const val EPIPE = 32 + const val EDOM = 33 + const val ERANGE = 34 + const val EDEADLK = 35 + const val ENAMETOOLONG = 36 + const val ENOLCK = 37 + const val ENOSYS = 38 + const val ENOTEMPTY = 39 + const val ELOOP = 40 + const val EWOULDBLOCK = 11 + const val ENOMSG = 42 + const val EIDRM = 43 + const val ECHRNG = 44 + const val EL2NSYNC = 45 + const val EL3HLT = 46 + const val EL3RST = 47 + const val ELNRNG = 48 + const val EUNATCH = 49 + const val ENOCSI = 50 + const val EL2HLT = 51 + const val EBADE = 52 + const val EBADR = 53 + const val EXFULL = 54 + const val ENOANO = 55 + const val EBADRQC = 56 + const val EBADSLT = 57 + const val EDEADLOCK = 35 + const val EBFONT = 59 + const val ENOSTR = 60 + const val ENODATA = 61 + const val ETIME = 62 + const val ENOSR = 63 + const val ENONET = 64 + const val ENOPKG = 65 + const val EREMOTE = 66 + const val ENOLINK = 67 + const val EADV = 68 + const val ESRMNT = 69 + const val ECOMM = 70 + const val EPROTO = 71 + const val EMULTIHOP = 72 + const val EDOTDOT = 73 + const val EBADMSG = 74 + const val EOVERFLOW = 75 + const val ENOTUNIQ = 76 + const val EBADFD = 77 + const val EREMCHG = 78 + const val ELIBACC = 79 + const val ELIBBAD = 80 + const val ELIBSCN = 81 + const val ELIBMAX = 82 + const val ELIBEXEC = 83 + const val EILSEQ = 84 + const val ERESTART = 85 + const val ESTRPIPE = 86 + const val EUSERS = 87 + const val ENOTSOCK = 88 + const val EDESTADDRREQ = 89 + const val EMSGSIZE = 90 + const val EPROTOTYPE = 91 + const val ENOPROTOOPT = 92 + const val EPROTONOSUPPORT = 93 + const val ESOCKTNOSUPPORT = 94 + const val EOPNOTSUPP = 95 + const val EPFNOSUPPORT = 96 + const val EAFNOSUPPORT = 97 + const val EADDRINUSE = 98 + const val EADDRNOTAVAIL = 99 + const val ENETDOWN = 100 + const val ENETUNREACH = 101 + const val ENETRESET = 102 + const val ECONNABORTED = 103 + const val ECONNRESET = 104 + const val ENOBUFS = 105 + const val EISCONN = 106 + const val ENOTCONN = 107 + const val ESHUTDOWN = 108 + const val ETOOMANYREFS = 109 + const val ETIMEDOUT = 110 + const val ECONNREFUSED = 111 + const val EHOSTDOWN = 112 + const val EHOSTUNREACH = 113 + const val EALREADY = 114 + const val EINPROGRESS = 115 + const val ESTALE = 116 + const val EUCLEAN = 117 + const val ENOTNAM = 118 + const val ENAVAIL = 119 + const val EISNAM = 120 + const val EREMOTEIO = 121 + const val EDQUOT = 122 + const val ENOMEDIUM = 123 + const val EMEDIUMTYPE = 124 + const val ECANCELED = 125 + const val ENOKEY = 126 + const val EKEYEXPIRED = 127 + const val EKEYREVOKED = 128 + const val EKEYREJECTED = 129 + const val EOWNERDEAD = 130 + const val ENOTRECOVERABLE = 131 + const val ERFKILL = 132 + const val EHWPOISON = 133 + const val ENOTSUP = 95 + + fun errnoName(errno: Int): String? { + return when (errno) { + EPERM -> "EPERM" + ENOENT -> "ENOENT" + ESRCH -> "ESRCH" + EINTR -> "EINTR" + EIO -> "EIO" + ENXIO -> "ENXIO" + E2BIG -> "E2BIG" + ENOEXEC -> "ENOEXEC" + EBADF -> "EBADF" + ECHILD -> "ECHILD" + EAGAIN -> "EAGAIN" + ENOMEM -> "ENOMEM" + EACCES -> "EACCES" + EFAULT -> "EFAULT" + ENOTBLK -> "ENOTBLK" + EBUSY -> "EBUSY" + EEXIST -> "EEXIST" + EXDEV -> "EXDEV" + ENODEV -> "ENODEV" + ENOTDIR -> "ENOTDIR" + EISDIR -> "EISDIR" + EINVAL -> "EINVAL" + ENFILE -> "ENFILE" + EMFILE -> "EMFILE" + ENOTTY -> "ENOTTY" + ETXTBSY -> "ETXTBSY" + EFBIG -> "EFBIG" + ENOSPC -> "ENOSPC" + ESPIPE -> "ESPIPE" + EROFS -> "EROFS" + EMLINK -> "EMLINK" + EPIPE -> "EPIPE" + EDOM -> "EDOM" + ERANGE -> "ERANGE" + EDEADLK -> "EDEADLK" + ENAMETOOLONG -> "ENAMETOOLONG" + ENOLCK -> "ENOLCK" + ENOSYS -> "ENOSYS" + ENOTEMPTY -> "ENOTEMPTY" + ELOOP -> "ELOOP" + EWOULDBLOCK -> "EWOULDBLOCK" + ENOMSG -> "ENOMSG" + EIDRM -> "EIDRM" + ECHRNG -> "ECHRNG" + EL2NSYNC -> "EL2NSYNC" + EL3HLT -> "EL3HLT" + EL3RST -> "EL3RST" + ELNRNG -> "ELNRNG" + EUNATCH -> "EUNATCH" + ENOCSI -> "ENOCSI" + EL2HLT -> "EL2HLT" + EBADE -> "EBADE" + EBADR -> "EBADR" + EXFULL -> "EXFULL" + ENOANO -> "ENOANO" + EBADRQC -> "EBADRQC" + EBADSLT -> "EBADSLT" + EDEADLOCK -> "EDEADLOCK" + EBFONT -> "EBFONT" + ENOSTR -> "ENOSTR" + ENODATA -> "ENODATA" + ETIME -> "ETIME" + ENOSR -> "ENOSR" + ENONET -> "ENONET" + ENOPKG -> "ENOPKG" + EREMOTE -> "EREMOTE" + ENOLINK -> "ENOLINK" + EADV -> "EADV" + ESRMNT -> "ESRMNT" + ECOMM -> "ECOMM" + EPROTO -> "EPROTO" + EMULTIHOP -> "EMULTIHOP" + EDOTDOT -> "EDOTDOT" + EBADMSG -> "EBADMSG" + EOVERFLOW -> "EOVERFLOW" + ENOTUNIQ -> "ENOTUNIQ" + EBADFD -> "EBADFD" + EREMCHG -> "EREMCHG" + ELIBACC -> "ELIBACC" + ELIBBAD -> "ELIBBAD" + ELIBSCN -> "ELIBSCN" + ELIBMAX -> "ELIBMAX" + ELIBEXEC -> "ELIBEXEC" + EILSEQ -> "EILSEQ" + ERESTART -> "ERESTART" + ESTRPIPE -> "ESTRPIPE" + EUSERS -> "EUSERS" + ENOTSOCK -> "ENOTSOCK" + EDESTADDRREQ -> "EDESTADDRREQ" + EMSGSIZE -> "EMSGSIZE" + EPROTOTYPE -> "EPROTOTYPE" + ENOPROTOOPT -> "ENOPROTOOPT" + EPROTONOSUPPORT -> "EPROTONOSUPPORT" + ESOCKTNOSUPPORT -> "ESOCKTNOSUPPORT" + EOPNOTSUPP -> "EOPNOTSUPP" + EPFNOSUPPORT -> "EPFNOSUPPORT" + EAFNOSUPPORT -> "EAFNOSUPPORT" + EADDRINUSE -> "EADDRINUSE" + EADDRNOTAVAIL -> "EADDRNOTAVAIL" + ENETDOWN -> "ENETDOWN" + ENETUNREACH -> "ENETUNREACH" + ENETRESET -> "ENETRESET" + ECONNABORTED -> "ECONNABORTED" + ECONNRESET -> "ECONNRESET" + ENOBUFS -> "ENOBUFS" + EISCONN -> "EISCONN" + ENOTCONN -> "ENOTCONN" + ESHUTDOWN -> "ESHUTDOWN" + ETOOMANYREFS -> "ETOOMANYREFS" + ETIMEDOUT -> "ETIMEDOUT" + ECONNREFUSED -> "ECONNREFUSED" + EHOSTDOWN -> "EHOSTDOWN" + EHOSTUNREACH -> "EHOSTUNREACH" + EALREADY -> "EALREADY" + EINPROGRESS -> "EINPROGRESS" + ESTALE -> "ESTALE" + EUCLEAN -> "EUCLEAN" + ENOTNAM -> "ENOTNAM" + ENAVAIL -> "ENAVAIL" + EISNAM -> "EISNAM" + EREMOTEIO -> "EREMOTEIO" + EDQUOT -> "EDQUOT" + ENOMEDIUM -> "ENOMEDIUM" + EMEDIUMTYPE -> "EMEDIUMTYPE" + ECANCELED -> "ECANCELED" + ENOKEY -> "ENOKEY" + EKEYEXPIRED -> "EKEYEXPIRED" + EKEYREVOKED -> "EKEYREVOKED" + EKEYREJECTED -> "EKEYREJECTED" + EOWNERDEAD -> "EOWNERDEAD" + ENOTRECOVERABLE -> "ENOTRECOVERABLE" + ERFKILL -> "ERFKILL" + EHWPOISON -> "EHWPOISON" + ENOTSUP -> "ENOTSUP" + else -> null + } + } + + fun strerror(errno: Int): String? { + return when (errno) { + EPERM -> "Operation not permitted" + ENOENT -> "No such file or directory" + ESRCH -> "No such process" + EINTR -> "Interrupted system call" + EIO -> "Input/output error" + ENXIO -> "No such device or address" + E2BIG -> "Argument list too long" + ENOEXEC -> "Exec format error" + EBADF -> "Bad file descriptor" + ECHILD -> "No child processes" + EAGAIN -> "Resource temporarily unavailable" + ENOMEM -> "Cannot allocate memory" + EACCES -> "Permission denied" + EFAULT -> "Bad address" + ENOTBLK -> "Block device required" + EBUSY -> "Device or resource busy" + EEXIST -> "File exists" + EXDEV -> "Invalid cross-device link" + ENODEV -> "No such device" + ENOTDIR -> "Not a directory" + EISDIR -> "Is a directory" + EINVAL -> "Invalid argument" + ENFILE -> "Too many open files in system" + EMFILE -> "Too many open files" + ENOTTY -> "Inappropriate ioctl for device" + ETXTBSY -> "Text file busy" + EFBIG -> "File too large" + ENOSPC -> "No space left on device" + ESPIPE -> "Illegal seek" + EROFS -> "Read-only file system" + EMLINK -> "Too many links" + EPIPE -> "Broken pipe" + EDOM -> "Numerical argument out of domain" + ERANGE -> "Numerical result out of range" + EDEADLK -> "Resource deadlock avoided" + ENAMETOOLONG -> "File name too long" + ENOLCK -> "No locks available" + ENOSYS -> "Function not implemented" + ENOTEMPTY -> "Directory not empty" + ELOOP -> "Too many levels of symbolic links" + EWOULDBLOCK -> "Resource temporarily unavailable" + ENOMSG -> "No message of desired type" + EIDRM -> "Identifier removed" + ECHRNG -> "Channel number out of range" + EL2NSYNC -> "Level 2 not synchronized" + EL3HLT -> "Level 3 halted" + EL3RST -> "Level 3 reset" + ELNRNG -> "Link number out of range" + EUNATCH -> "Protocol driver not attached" + ENOCSI -> "No CSI structure available" + EL2HLT -> "Level 2 halted" + EBADE -> "Invalid exchange" + EBADR -> "Invalid request descriptor" + EXFULL -> "Exchange full" + ENOANO -> "No anode" + EBADRQC -> "Invalid request code" + EBADSLT -> "Invalid slot" + EDEADLOCK -> "Resource deadlock avoided" + EBFONT -> "Bad font file format" + ENOSTR -> "Device not a stream" + ENODATA -> "No data available" + ETIME -> "Timer expired" + ENOSR -> "Out of streams resources" + ENONET -> "Machine is not on the network" + ENOPKG -> "Package not installed" + EREMOTE -> "Object is remote" + ENOLINK -> "Link has been severed" + EADV -> "Advertise error" + ESRMNT -> "Srmount error" + ECOMM -> "Communication error on send" + EPROTO -> "Protocol error" + EMULTIHOP -> "Multihop attempted" + EDOTDOT -> "RFS specific error" + EBADMSG -> "Bad message" + EOVERFLOW -> "Value too large for defined data type" + ENOTUNIQ -> "Name not unique on network" + EBADFD -> "File descriptor in bad state" + EREMCHG -> "Remote address changed" + ELIBACC -> "Can not access a needed shared library" + ELIBBAD -> "Accessing a corrupted shared library" + ELIBSCN -> ".lib section in a.out corrupted" + ELIBMAX -> "Attempting to link in too many shared libraries" + ELIBEXEC -> "Cannot exec a shared library directly" + EILSEQ -> "Invalid or incomplete multibyte or wide character" + ERESTART -> "Interrupted system call should be restarted" + ESTRPIPE -> "Streams pipe error" + EUSERS -> "Too many users" + ENOTSOCK -> "Socket operation on non-socket" + EDESTADDRREQ -> "Destination address required" + EMSGSIZE -> "Message too long" + EPROTOTYPE -> "Protocol wrong type for socket" + ENOPROTOOPT -> "Protocol not available" + EPROTONOSUPPORT -> "Protocol not supported" + ESOCKTNOSUPPORT -> "Socket type not supported" + EOPNOTSUPP -> "Operation not supported" + EPFNOSUPPORT -> "Protocol family not supported" + EAFNOSUPPORT -> "Address family not supported by protocol" + EADDRINUSE -> "Address already in use" + EADDRNOTAVAIL -> "Cannot assign requested address" + ENETDOWN -> "Network is down" + ENETUNREACH -> "Network is unreachable" + ENETRESET -> "Network dropped connection on reset" + ECONNABORTED -> "Software caused connection abort" + ECONNRESET -> "Connection reset by peer" + ENOBUFS -> "No buffer space available" + EISCONN -> "Transport endpoint is already connected" + ENOTCONN -> "Transport endpoint is not connected" + ESHUTDOWN -> "Cannot send after transport endpoint shutdown" + ETOOMANYREFS -> "Too many references: cannot splice" + ETIMEDOUT -> "Connection timed out" + ECONNREFUSED -> "Connection refused" + EHOSTDOWN -> "Host is down" + EHOSTUNREACH -> "No route to host" + EALREADY -> "Operation already in progress" + EINPROGRESS -> "Operation now in progress" + ESTALE -> "Stale file handle" + EUCLEAN -> "Structure needs cleaning" + ENOTNAM -> "Not a XENIX named type file" + ENAVAIL -> "No XENIX semaphores available" + EISNAM -> "Is a named type file" + EREMOTEIO -> "Remote I/O error" + EDQUOT -> "Disk quota exceeded" + ENOMEDIUM -> "No medium found" + EMEDIUMTYPE -> "Wrong medium type" + ECANCELED -> "Operation canceled" + ENOKEY -> "Required key not available" + EKEYEXPIRED -> "Key has expired" + EKEYREVOKED -> "Key has been revoked" + EKEYREJECTED -> "Key was rejected by service" + EOWNERDEAD -> "Owner died" + ENOTRECOVERABLE -> "State not recoverable" + ERFKILL -> "Operation not possible due to RF-kill" + EHWPOISON -> "Memory page has hardware error" + ENOTSUP -> "Operation not supported" + else -> null + } + } +} diff --git a/app/src/main/java/libcore/io/ErrnoException.java b/app/src/main/java/libcore/io/ErrnoException.java new file mode 100644 index 000000000..16960d6d0 --- /dev/null +++ b/app/src/main/java/libcore/io/ErrnoException.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package libcore.io; + +public final class ErrnoException extends Exception { + public final int errno; + private final String functionName; + + public ErrnoException(String functionName, int errno) { + this.functionName = functionName; + this.errno = errno; + } + + public ErrnoException(String functionName, int errno, Throwable cause) { + super(cause); + this.functionName = functionName; + this.errno = errno; + } +} diff --git a/app/src/main/res/values-de/strings_error.xml b/app/src/main/res/values-de/strings_error.xml index 478f31c54..152a8daad 100644 --- a/app/src/main/res/values-de/strings_error.xml +++ b/app/src/main/res/values-de/strings_error.xml @@ -39,4 +39,9 @@ <code>%1$s</code><br/> ist nicht gültig für %2$s.</p> ]]></string> + + <string name="label_error_unknown_host">Host nicht gefunden: %1$s</string> + <string name="label_error_invalid_protocol_version">Unbekanntes Protokoll: %1$d</string> + <string name="label_error_connection">Fehler beim Herstellen der Verbindung: %1$s (%2$s)</string> + <string name="label_error_connection_closed">Fehler: Verbindung wurde unerwartet getrennt</string> </resources> diff --git a/app/src/main/res/values-fr-rCA/strings_error.xml b/app/src/main/res/values-fr-rCA/strings_error.xml index 07a4007dd..746990039 100644 --- a/app/src/main/res/values-fr-rCA/strings_error.xml +++ b/app/src/main/res/values-fr-rCA/strings_error.xml @@ -38,4 +38,5 @@ <code>%1$s</code><br/> n\'est pas valide pour %2$s.</p> ]]></string> + </resources> diff --git a/app/src/main/res/values-lt/strings_error.xml b/app/src/main/res/values-lt/strings_error.xml index e2f958c7a..dab1e0ed7 100644 --- a/app/src/main/res/values-lt/strings_error.xml +++ b/app/src/main/res/values-lt/strings_error.xml @@ -38,4 +38,5 @@ <code>%1$s</code><br/> negalioja %2$s.</p> ]]></string> + </resources> diff --git a/app/src/main/res/values-pt/strings_error.xml b/app/src/main/res/values-pt/strings_error.xml index 083d35819..425d28f17 100644 --- a/app/src/main/res/values-pt/strings_error.xml +++ b/app/src/main/res/values-pt/strings_error.xml @@ -38,4 +38,5 @@ <code>%1$s</code><br/> não é válido para %2$s.</p> ]]></string> + </resources> diff --git a/app/src/main/res/values-sr/strings_error.xml b/app/src/main/res/values-sr/strings_error.xml index 8c80d94b4..21d16fed2 100644 --- a/app/src/main/res/values-sr/strings_error.xml +++ b/app/src/main/res/values-sr/strings_error.xml @@ -38,4 +38,5 @@ <code>%1$s</code><br/> nije važeći za %2$s.</p> ]]></string> + </resources> diff --git a/app/src/main/res/values/strings_error.xml b/app/src/main/res/values/strings_error.xml index 78f73c922..48336e144 100644 --- a/app/src/main/res/values/strings_error.xml +++ b/app/src/main/res/values/strings_error.xml @@ -38,4 +38,9 @@ <code>%1$s</code><br/> is not valid for %2$s.</p> ]]></string> + + <string name="label_error_unknown_host">Host not found: %1$s</string> + <string name="label_error_invalid_protocol_version">Invalid protocol: %1$d</string> + <string name="label_error_connection">Error encountered during connection: %1$s (%2$s)</string> + <string name="label_error_connection_closed">Error: Connection was unexpectedly closed</string> </resources> diff --git a/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt b/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt index 4a6284f5a..fd70f1edb 100644 --- a/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt +++ b/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt @@ -56,7 +56,8 @@ class CoreConnection( private val hostnameVerifier: HostnameVerifier, private val address: SocketAddress, private val handlerService: HandlerService, - private val securityExceptionCallback: (QuasselSecurityException) -> Unit + private val securityExceptionCallback: (QuasselSecurityException) -> Unit, + private val exceptionCallback: (Throwable) -> Unit ) : Thread(), Closeable { companion object { private const val TAG = "CoreConnection" @@ -146,7 +147,7 @@ class CoreConnection( ) } else -> { - throw IllegalArgumentException("Invalid Protocol Version: $protocol") + throw ProtocolVersionException(protocol) } } } @@ -241,6 +242,7 @@ class CoreConnection( log(WARN, TAG, "Error encountered in connection", e) log(WARN, TAG, "Last sent message: ${MessageRunnable.lastSent.get()}") close() + exceptionCallback(e) } } } diff --git a/lib/src/main/java/de/kuschku/libquassel/connection/ProtocolVersionException.kt b/lib/src/main/java/de/kuschku/libquassel/connection/ProtocolVersionException.kt new file mode 100644 index 000000000..64942c9be --- /dev/null +++ b/lib/src/main/java/de/kuschku/libquassel/connection/ProtocolVersionException.kt @@ -0,0 +1,9 @@ +package de.kuschku.libquassel.connection + +import de.kuschku.libquassel.quassel.ProtocolInfo +import java.net.ConnectException + +class ProtocolVersionException(val protocol: ProtocolInfo) : ConnectException() { + override val message: String? + get() = "Invalid Protocol Version: $protocol" +} diff --git a/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt b/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt index 11e5244d5..d9316a931 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt @@ -61,6 +61,7 @@ interface ISession : Closeable { val proxy: SignalProxy val error: Flowable<Error> + val connectionError: Flowable<Throwable> val lag: Observable<Long> fun login(user: String, pass: String) @@ -69,6 +70,8 @@ interface ISession : Closeable { val NULL = object : ISession { override val proxy: SignalProxy = SignalProxy.NULL override val error = BehaviorSubject.create<Error>().toFlowable(BackpressureStrategy.BUFFER) + override val connectionError = BehaviorSubject.create<Throwable>().toFlowable( + BackpressureStrategy.BUFFER) override val state = BehaviorSubject.createDefault(ConnectionState.DISCONNECTED) override val features: Features = Features( QuasselFeatures.empty(), diff --git a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt index e7710038f..b723f7a86 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt @@ -58,13 +58,24 @@ class Session( get() = coreConnection.sslSession private val coreConnection = CoreConnection( - this, clientData, features, trustManager, hostnameVerifier, address, handlerService, ::handle + this, + clientData, + features, + trustManager, + hostnameVerifier, + address, + handlerService, + ::handle, + ::handleConnectionError ) override val state = coreConnection.state private val _error = PublishSubject.create<Error>() override val error = _error.toFlowable(BackpressureStrategy.BUFFER) + private val _connectionError = PublishSubject.create<Throwable>() + override val connectionError = _connectionError.toFlowable(BackpressureStrategy.LATEST) + override val aliasManager = AliasManager(this) override val backlogManager = BacklogManager(this, backlogStorage) override val bufferViewManager = BufferViewManager(this) @@ -148,6 +159,10 @@ class Session( _error.onNext(Error.SslError(f)) } + fun handleConnectionError(f: Throwable) { + _connectionError.onNext(f) + } + fun addNetwork(networkId: NetworkId) { val network = Network(networkId, this) networks[networkId] = network diff --git a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt index fccae0ba2..9fd414ee3 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt @@ -74,6 +74,11 @@ class SessionManager( .toFlowable(BackpressureStrategy.LATEST) .switchMap(ISession::error) + val connectionError: Flowable<Throwable> + get() = inProgressSession + .toFlowable(BackpressureStrategy.LATEST) + .switchMap(ISession::connectionError) + val connectionProgress: Observable<Triple<ConnectionState, Int, Int>> = Observable.combineLatest( state, initStatus, BiFunction<ConnectionState, Pair<Int, Int>, Triple<ConnectionState, Int, Int>> { t1, t2 -> diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt index e4332e416..48212a5ad 100644 --- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt +++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt @@ -99,6 +99,10 @@ class QuasselViewModel : ViewModel() { it.orNull()?.error ?: Flowable.empty() } + val connectionErrors = sessionManager.toFlowable(BackpressureStrategy.LATEST).switchMap { + it.orNull()?.connectionError ?: Flowable.empty() + } + val sslSession = session.flatMapSwitchMap(ISession::sslSession) val coreInfo = session.mapMapNullable(ISession::coreInfo).mapSwitchMap(CoreInfo::liveInfo) -- GitLab