forked from github/wulkanowy-mirror
Add details to error message (#186)
This commit is contained in:
parent
7a3c0de7ad
commit
7686228e01
@ -2,29 +2,30 @@ package io.github.wulkanowy.data
|
|||||||
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.api.interceptor.ServiceUnavailableException
|
||||||
import io.github.wulkanowy.api.login.NotLoggedInException
|
import io.github.wulkanowy.api.login.NotLoggedInException
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.IOException
|
|
||||||
import java.net.SocketTimeoutException
|
import java.net.SocketTimeoutException
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
open class ErrorHandler @Inject constructor(protected val resources: Resources) {
|
open class ErrorHandler @Inject constructor(protected val resources: Resources) {
|
||||||
|
|
||||||
var showErrorMessage: (String) -> Unit = {}
|
var showErrorMessage: (String, Throwable) -> Unit = { _, _ -> }
|
||||||
|
|
||||||
open fun proceed(error: Throwable) {
|
open fun proceed(error: Throwable) {
|
||||||
Timber.e(error, "An exception occurred while the Wulkanowy was running")
|
Timber.e(error, "An exception occurred while the Wulkanowy was running")
|
||||||
|
|
||||||
showErrorMessage((when (error) {
|
showErrorMessage((when (error) {
|
||||||
is UnknownHostException -> resources.getString(R.string.all_no_internet)
|
is UnknownHostException -> resources.getString(R.string.error_no_internet)
|
||||||
is SocketTimeoutException -> resources.getString(R.string.all_timeout)
|
is SocketTimeoutException -> resources.getString(R.string.error_timeout)
|
||||||
is NotLoggedInException, is IOException -> resources.getString(R.string.all_login_failed)
|
is NotLoggedInException -> resources.getString(R.string.error_login_failed)
|
||||||
else -> error.localizedMessage
|
is ServiceUnavailableException -> resources.getString(R.string.error_service_unavaible)
|
||||||
}))
|
else -> resources.getString(R.string.error_unknown)
|
||||||
|
}), error)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun clear() {
|
open fun clear() {
|
||||||
showErrorMessage = {}
|
showErrorMessage = { _, _ -> }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import androidx.appcompat.app.AppCompatDelegate
|
|||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
|
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
|
||||||
import dagger.android.support.DaggerAppCompatActivity
|
import dagger.android.support.DaggerAppCompatActivity
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
|
||||||
abstract class BaseActivity : DaggerAppCompatActivity(), BaseView {
|
abstract class BaseActivity : DaggerAppCompatActivity(), BaseView {
|
||||||
|
|
||||||
@ -16,6 +17,12 @@ abstract class BaseActivity : DaggerAppCompatActivity(), BaseView {
|
|||||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun showError(text: String, error: Throwable) {
|
||||||
|
Snackbar.make(messageContainer, text, LENGTH_LONG).setAction(R.string.all_details) {
|
||||||
|
ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString())
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
|
||||||
override fun showMessage(text: String) {
|
override fun showMessage(text: String) {
|
||||||
Snackbar.make(messageContainer, text, LENGTH_LONG).show()
|
Snackbar.make(messageContainer, text, LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,24 @@ package io.github.wulkanowy.ui.base
|
|||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
|
|
||||||
import dagger.android.support.DaggerFragment
|
import dagger.android.support.DaggerFragment
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
|
||||||
abstract class BaseFragment : DaggerFragment(), BaseView {
|
abstract class BaseFragment : DaggerFragment(), BaseView {
|
||||||
|
|
||||||
protected var messageContainer: View? = null
|
protected var messageContainer: View? = null
|
||||||
|
|
||||||
|
override fun showError(text: String, error: Throwable) {
|
||||||
|
if (messageContainer == null) (activity as? BaseActivity)?.showError(text, error)
|
||||||
|
else messageContainer?.also {
|
||||||
|
Snackbar.make(it, text, Snackbar.LENGTH_LONG).setAction(R.string.all_details) {
|
||||||
|
ErrorDialog.newInstance(error).show(fragmentManager, error.toString())
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun showMessage(text: String) {
|
override fun showMessage(text: String) {
|
||||||
if (messageContainer == null) (activity as? BaseActivity)?.showMessage(text)
|
if (messageContainer == null) (activity as? BaseActivity)?.showMessage(text)
|
||||||
else messageContainer?.also { Snackbar.make(it, text, LENGTH_LONG).show() }
|
else messageContainer?.also { Snackbar.make(it, text, Snackbar.LENGTH_LONG).show() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package io.github.wulkanowy.ui.base
|
package io.github.wulkanowy.ui.base
|
||||||
|
|
||||||
import io.github.wulkanowy.data.ErrorHandler
|
import io.github.wulkanowy.data.ErrorHandler
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
|
|
||||||
open class BasePresenter<T : BaseView>(private val errorHandler: ErrorHandler) {
|
open class BasePresenter<T : BaseView>(private val errorHandler: ErrorHandler) {
|
||||||
@ -12,7 +11,7 @@ open class BasePresenter<T : BaseView>(private val errorHandler: ErrorHandler) {
|
|||||||
|
|
||||||
open fun onAttachView(view: T) {
|
open fun onAttachView(view: T) {
|
||||||
this.view = view
|
this.view = view
|
||||||
errorHandler.showErrorMessage = { view.showMessage(it) }
|
errorHandler.showErrorMessage = { text, error -> view.showError(text, error) }
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun onDetachView() {
|
open fun onDetachView() {
|
||||||
|
@ -2,5 +2,7 @@ package io.github.wulkanowy.ui.base
|
|||||||
|
|
||||||
interface BaseView {
|
interface BaseView {
|
||||||
|
|
||||||
|
fun showError(text: String, error: Throwable)
|
||||||
|
|
||||||
fun showMessage(text: String)
|
fun showMessage(text: String)
|
||||||
}
|
}
|
||||||
|
60
app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
Normal file
60
app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package io.github.wulkanowy.ui.base
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import android.content.Context.CLIPBOARD_SERVICE
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Toast
|
||||||
|
import android.widget.Toast.LENGTH_LONG
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import io.github.wulkanowy.R
|
||||||
|
import kotlinx.android.synthetic.main.dialog_error.*
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.io.StringWriter
|
||||||
|
|
||||||
|
class ErrorDialog : DialogFragment() {
|
||||||
|
|
||||||
|
private lateinit var error: Throwable
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val ARGUMENT_KEY = "Data"
|
||||||
|
|
||||||
|
fun newInstance(error: Throwable): ErrorDialog {
|
||||||
|
return ErrorDialog().apply {
|
||||||
|
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, error) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setStyle(STYLE_NO_TITLE, 0)
|
||||||
|
arguments?.run {
|
||||||
|
error = getSerializable(ARGUMENT_KEY) as Throwable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.dialog_error, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
StringWriter().let { writer ->
|
||||||
|
error.printStackTrace(PrintWriter(writer))
|
||||||
|
|
||||||
|
errorDialogContent.text = writer.toString()
|
||||||
|
errorDialogCopy.setOnClickListener {
|
||||||
|
ClipData.newPlainText("wulkanowyError", writer.toString()).let { clip ->
|
||||||
|
(activity?.getSystemService(CLIPBOARD_SERVICE) as? ClipboardManager)?.primaryClip = clip
|
||||||
|
}
|
||||||
|
Toast.makeText(context, R.string.all_copied, LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errorDialogCancel.setOnClickListener { dismiss() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -59,6 +59,10 @@ class AccountDialog : DaggerAppCompatDialogFragment(), AccountView {
|
|||||||
accountAdapter.updateDataSet(data)
|
accountAdapter.updateDataSet(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun showError(text: String, error: Throwable) {
|
||||||
|
showMessage(text)
|
||||||
|
}
|
||||||
|
|
||||||
override fun showMessage(text: String) {
|
override fun showMessage(text: String) {
|
||||||
Toast.makeText(context, text, LENGTH_LONG).show()
|
Toast.makeText(context, text, LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,12 @@ class LoginErrorHandler(resources: Resources) : ErrorHandler(resources) {
|
|||||||
|
|
||||||
var onBadCredentials: () -> Unit = {}
|
var onBadCredentials: () -> Unit = {}
|
||||||
|
|
||||||
|
var onStudentDuplicate: (String) -> Unit = {}
|
||||||
|
|
||||||
override fun proceed(error: Throwable) {
|
override fun proceed(error: Throwable) {
|
||||||
when (error) {
|
when (error) {
|
||||||
is BadCredentialsException -> onBadCredentials()
|
is BadCredentialsException -> onBadCredentials()
|
||||||
is SQLiteConstraintException -> showErrorMessage(resources.getString(R.string.login_duplicate_student))
|
is SQLiteConstraintException -> onStudentDuplicate(resources.getString(R.string.login_duplicate_student))
|
||||||
else -> super.proceed(error)
|
else -> super.proceed(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,10 @@ class LoginOptionsPresenter @Inject constructor(
|
|||||||
|
|
||||||
override fun onAttachView(view: LoginOptionsView) {
|
override fun onAttachView(view: LoginOptionsView) {
|
||||||
super.onAttachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.run {
|
||||||
|
initView()
|
||||||
|
errorHandler.onStudentDuplicate = { showMessage(it) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onParentViewLoadData() {
|
fun onParentViewLoadData() {
|
||||||
|
@ -3,11 +3,11 @@ package io.github.wulkanowy.ui.modules.settings
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import com.takisoft.preferencex.PreferenceFragmentCompat
|
import com.takisoft.preferencex.PreferenceFragmentCompat
|
||||||
import dagger.android.support.AndroidSupportInjection
|
import dagger.android.support.AndroidSupportInjection
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.ui.base.BaseActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -54,8 +54,12 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun showError(text: String, error: Throwable) {
|
||||||
|
(activity as? BaseActivity)?.showError(text, error)
|
||||||
|
}
|
||||||
|
|
||||||
override fun showMessage(text: String) {
|
override fun showMessage(text: String) {
|
||||||
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
|
(activity as? BaseActivity)?.showMessage(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
70
app/src/main/res/layout/dialog_error.xml
Normal file
70
app/src/main/res/layout/dialog_error.xml
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minWidth="300dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:overScrollMode="ifContentScrolls"
|
||||||
|
tools:ignore="UselessParent">
|
||||||
|
|
||||||
|
<HorizontalScrollView
|
||||||
|
android:layout_width="350dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="24dp"
|
||||||
|
android:paddingTop="24dp"
|
||||||
|
android:paddingRight="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/errorDialogContent"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scrollHorizontally="true"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</HorizontalScrollView>
|
||||||
|
</ScrollView>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:gravity="end"
|
||||||
|
android:minHeight="52dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/errorDialogCancel"
|
||||||
|
style="@style/Widget.AppCompat.Button.Borderless"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@android:string/cancel"
|
||||||
|
android:textColor="@color/colorPrimary" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/errorDialogCopy"
|
||||||
|
style="@style/Widget.AppCompat.Button.Borderless"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@android:string/copy"
|
||||||
|
android:textColor="@color/colorPrimary" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
@ -205,9 +205,12 @@
|
|||||||
|
|
||||||
|
|
||||||
<!--Others-->
|
<!--Others-->
|
||||||
<string name="all_no_internet">Brak połączenia z internetem</string>
|
<string name="all_copied">Skopiowano</string>
|
||||||
<string name="all_sync_finish">Synchronizacja zakończona</string>
|
|
||||||
<string name="all_sync_fail">Podczas synchronizacji wystąpił błąd</string>
|
<!--Errors-->
|
||||||
<string name="all_timeout">Zbyt długie oczekiwanie na połączenie</string>
|
<string name="error_no_internet">Brak połączenia z internetem</string>
|
||||||
<string name="all_login_failed">Logowanie nie powiodło się. Spróbuj ponownie lub zrestartuj aplikację</string>
|
<string name="error_timeout">Zbyt długie oczekiwanie na połączenie</string>
|
||||||
|
<string name="error_login_failed">Logowanie nie powiodło się. Spróbuj ponownie lub zrestartuj aplikację</string>
|
||||||
|
<string name="error_service_unavaible">Dziennik jest niedostępny. Spróbuj ponownie później</string>
|
||||||
|
<string name="error_unknown">Wystąpił nieoczekiwany błąd</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -190,9 +190,12 @@
|
|||||||
|
|
||||||
|
|
||||||
<!--Others-->
|
<!--Others-->
|
||||||
<string name="all_no_internet">No internet connection</string>
|
<string name="all_copied">Copied</string>
|
||||||
<string name="all_sync_finish">Synchronization complete</string>
|
|
||||||
<string name="all_sync_fail">There was an error during synchronization</string>
|
<!--Errors-->
|
||||||
<string name="all_timeout">Too long wait for connection</string>
|
<string name="error_no_internet">No internet connection</string>
|
||||||
<string name="all_login_failed">Login is failed. Try again or restart the app</string>
|
<string name="error_timeout">Too long wait for connection</string>
|
||||||
|
<string name="error_login_failed">Login is failed. Try again or restart the app</string>
|
||||||
|
<string name="error_service_unavaible">The log is not available. Try again later</string>
|
||||||
|
<string name="error_unknown">An unexpected error occurred</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user