From aff1a7030d2d6203a3ae86b14e832660dec14fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 1 Jan 2022 15:46:08 +0100 Subject: [PATCH] Add a custom error message for ssl errors due to invalid clock setting (#1742) --- .../github/wulkanowy/ui/base/ErrorDialog.kt | 24 +----- .../github/wulkanowy/ui/base/ErrorHandler.kt | 4 +- .../ui/modules/dashboard/DashboardFragment.kt | 9 +-- .../modules/dashboard/DashboardPresenter.kt | 1 + .../ui/modules/dashboard/DashboardView.kt | 2 +- .../wulkanowy/utils/ExceptionExtension.kt | 74 +++++++++++++++++++ .../wulkanowy/utils/ResourcesExtension.kt | 34 --------- app/src/main/res/values/strings.xml | 1 + 8 files changed, 84 insertions(+), 65 deletions(-) create mode 100644 app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt delete mode 100644 app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt index c2ffff1f5..48c003b7e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt @@ -16,15 +16,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.databinding.DialogErrorBinding -import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException -import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException -import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException import io.github.wulkanowy.utils.* -import okhttp3.internal.http2.StreamResetException -import java.io.InterruptedIOException -import java.net.ConnectException -import java.net.SocketTimeoutException -import java.net.UnknownHostException import javax.inject.Inject @AndroidEntryPoint @@ -67,7 +59,7 @@ class ErrorDialog : DialogFragment() { private fun DialogErrorBinding.bindErrorDetails(error: Throwable) { return with(this) { - errorDialogHumanizedMessage.text = resources.getString(error) + errorDialogHumanizedMessage.text = resources.getErrorString(error) errorDialogErrorMessage.text = error.localizedMessage errorDialogErrorMessage.isGone = error.localizedMessage.isNullOrBlank() errorDialogContent.text = error.stackTraceToString() @@ -77,22 +69,10 @@ class ErrorDialog : DialogFragment() { private fun AlertDialog.enableReportButtonIfErrorIsReportable(error: Throwable) { setOnShowListener { - getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = isErrorShouldBeReported(error) + getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = error.isShouldBeReported() } } - private fun isErrorShouldBeReported(error: Throwable): Boolean = when (error) { - is UnknownHostException, - is InterruptedIOException, - is ConnectException, - is StreamResetException, - is SocketTimeoutException, - is ServiceUnavailableException, - is FeatureDisabledException, - is FeatureNotAvailableException -> false - else -> true - } - private fun copyErrorToClipboard(errorStacktrace: String) { val clip = ClipData.newPlainText("Error details", errorStacktrace) requireActivity().getSystemService()?.setPrimaryClip(clip) diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt index fbc994e2c..afe200e9a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt @@ -5,7 +5,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException -import io.github.wulkanowy.utils.getString +import io.github.wulkanowy.utils.getErrorString import io.github.wulkanowy.utils.security.ScramblerException import timber.log.Timber import javax.inject.Inject @@ -26,7 +26,7 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co } protected open fun proceed(error: Throwable) { - showErrorMessage(context.resources.getString(error), error) + showErrorMessage(context.resources.getErrorString(error), error) when (error) { is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl) is ScramblerException, is BadCredentialsException -> onSessionExpired() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt index 12a28ea73..88c281ecd 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt @@ -28,10 +28,7 @@ import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.notificationscenter.NotificationsCenterFragment import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment import io.github.wulkanowy.ui.modules.timetable.TimetableFragment -import io.github.wulkanowy.utils.capitalise -import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.openInternetBrowser -import io.github.wulkanowy.utils.toFormattedString +import io.github.wulkanowy.utils.* import java.time.LocalDate import javax.inject.Inject @@ -178,8 +175,8 @@ class DashboardFragment : BaseFragment(R.layout.fragme binding.dashboardErrorContainer.isVisible = show } - override fun setErrorDetails(message: String) { - binding.dashboardErrorMessage.text = message + override fun setErrorDetails(error: Throwable) { + binding.dashboardErrorMessage.text = requireContext().resources.getErrorString(error) } override fun resetView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt index 360d6be84..a1845ab59 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt @@ -714,6 +714,7 @@ class DashboardPresenter @Inject constructor( if ((forceRefresh && wasGeneralError) || !forceRefresh) { showContent(false) showErrorView(true) + setErrorDetails(lastError) } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt index 730e19a35..2cc2f1d2d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt @@ -18,7 +18,7 @@ interface DashboardView : BaseView { fun showErrorView(show: Boolean) - fun setErrorDetails(message: String) + fun setErrorDetails(error: Throwable) fun resetView() diff --git a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt new file mode 100644 index 000000000..43cecd400 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt @@ -0,0 +1,74 @@ +package io.github.wulkanowy.utils + +import android.content.res.Resources +import io.github.wulkanowy.R +import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException +import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException +import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException +import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException +import io.github.wulkanowy.sdk.scrapper.exception.VulcanException +import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException +import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException +import okhttp3.internal.http2.StreamResetException +import java.io.InterruptedIOException +import java.net.ConnectException +import java.net.SocketException +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import java.security.cert.CertificateExpiredException +import java.security.cert.CertificateNotYetValidException +import javax.net.ssl.SSLHandshakeException + +fun Resources.getErrorString(error: Throwable): String = when (error) { + is UnknownHostException -> R.string.error_no_internet + is SocketException, + is SocketTimeoutException, + is InterruptedIOException, + is ConnectException, + is StreamResetException -> R.string.error_timeout + is NotLoggedInException -> R.string.error_login_failed + is PasswordChangeRequiredException -> R.string.error_password_change_required + is ServiceUnavailableException -> R.string.error_service_unavailable + is FeatureDisabledException -> R.string.error_feature_disabled + is FeatureNotAvailableException -> R.string.error_feature_not_available + is VulcanException -> R.string.error_unknown_uonet + is ScrapperException -> R.string.error_unknown_app + is SSLHandshakeException -> when { + error.isCausedByCertificateNotValidNow() -> R.string.error_invalid_device_datetime + else -> R.string.error_timeout + } + else -> R.string.error_unknown +}.let { getString(it) } + +fun Throwable.isShouldBeReported(): Boolean = when (this) { + is UnknownHostException, + is SocketException, + is SocketTimeoutException, + is InterruptedIOException, + is ConnectException, + is StreamResetException, + is ServiceUnavailableException, + is FeatureDisabledException, + is FeatureNotAvailableException -> false + is SSLHandshakeException -> when { + isCausedByCertificateNotValidNow() -> false + else -> true + } + else -> true +} + +private fun Throwable?.isCausedByCertificateNotValidNow(): Boolean { + var exception = this + do { + if (exception.isCertificateNotValidNow()) return true + + exception = exception?.cause + } while (exception != null) + return false +} + +private fun Throwable?.isCertificateNotValidNow(): Boolean { + val isNotYetValid = this is CertificateNotYetValidException + val isExpired = this is CertificateExpiredException + return isNotYetValid || isExpired +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt deleted file mode 100644 index 71d3fd173..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.utils - -import android.content.res.Resources -import io.github.wulkanowy.R -import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException -import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException -import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException -import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException -import io.github.wulkanowy.sdk.scrapper.exception.VulcanException -import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException -import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException -import okhttp3.internal.http2.StreamResetException -import java.io.InterruptedIOException -import java.net.ConnectException -import java.net.SocketException -import java.net.SocketTimeoutException -import java.net.UnknownHostException - -fun Resources.getString(error: Throwable) = when (error) { - is UnknownHostException -> getString(R.string.error_no_internet) - is SocketException, - is SocketTimeoutException, - is InterruptedIOException, - is ConnectException, - is StreamResetException -> getString(R.string.error_timeout) - is NotLoggedInException -> getString(R.string.error_login_failed) - is PasswordChangeRequiredException -> getString(R.string.error_password_change_required) - is ServiceUnavailableException -> getString(R.string.error_service_unavailable) - is FeatureDisabledException -> getString(R.string.error_feature_disabled) - is FeatureNotAvailableException -> getString(R.string.error_feature_not_available) - is VulcanException -> getString(R.string.error_unknown_uonet) - is ScrapperException -> getString(R.string.error_unknown_app) - else -> getString(R.string.error_unknown) -} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bdf2935bd..9c63073e2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -767,6 +767,7 @@ No internet connection + An error occurred. Check your device clock Connection to register failed. Servers can be overloaded. Please try again later Loading data failed. Please try again later Register password change required