1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-20 02:19:08 -05:00

Add error message and bug report button to error dialog (#778)

This commit is contained in:
Mikołaj Pich 2020-04-19 23:20:55 +02:00 committed by GitHub
parent 4bd0459155
commit 366ebc781d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 118 additions and 32 deletions

View File

@ -6,19 +6,32 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.HorizontalScrollView
import android.widget.Toast import android.widget.Toast
import android.widget.Toast.LENGTH_LONG import android.widget.Toast.LENGTH_LONG
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.fragment.app.DialogFragment
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.sdk.exception.FeatureDisabledException
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
import io.github.wulkanowy.sdk.exception.ServiceUnavailableException
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.getString
import io.github.wulkanowy.utils.openEmailClient
import io.github.wulkanowy.utils.openInternetBrowser
import kotlinx.android.synthetic.main.dialog_error.* import kotlinx.android.synthetic.main.dialog_error.*
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.inject.Inject
class ErrorDialog : DialogFragment() { class ErrorDialog : BaseDialogFragment() {
private lateinit var error: Throwable private lateinit var error: Throwable
@Inject
lateinit var appInfo: AppInfo
companion object { companion object {
private const val ARGUMENT_KEY = "Data" private const val ARGUMENT_KEY = "Data"
@ -49,6 +62,9 @@ class ErrorDialog : DialogFragment() {
} }
errorDialogContent.text = stringWriter.toString() errorDialogContent.text = stringWriter.toString()
with(errorDialogHorizontalScroll) {
post { fullScroll(HorizontalScrollView.FOCUS_LEFT) }
}
errorDialogCopy.setOnClickListener { errorDialogCopy.setOnClickListener {
val clip = ClipData.newPlainText("wulkanowy", stringWriter.toString()) val clip = ClipData.newPlainText("wulkanowy", stringWriter.toString())
activity?.getSystemService<ClipboardManager>()?.setPrimaryClip(clip) activity?.getSystemService<ClipboardManager>()?.setPrimaryClip(clip)
@ -56,6 +72,29 @@ class ErrorDialog : DialogFragment() {
Toast.makeText(context, R.string.all_copied, LENGTH_LONG).show() Toast.makeText(context, R.string.all_copied, LENGTH_LONG).show()
} }
errorDialogCancel.setOnClickListener { dismiss() } errorDialogCancel.setOnClickListener { dismiss() }
errorDialogReport.setOnClickListener { openEmailClient(stringWriter.toString()) }
errorDialogMessage.text = resources.getString(error)
errorDialogReport.isEnabled = when (error) {
is UnknownHostException,
is SocketTimeoutException,
is ServiceUnavailableException,
is FeatureDisabledException,
is FeatureNotAvailableException -> false
else -> true
} }
} }
private fun openEmailClient(content: String) {
requireContext().openEmailClient(
chooserTitle = getString(R.string.about_feedback),
email = "wulkanowyinc@gmail.com",
subject = "Zgłoszenie błędu",
body = requireContext().getString(R.string.about_feedback_template,
"${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), appInfo.versionName
) + "\n" + content,
onActivityNotFound = {
requireContext().openInternetBrowser("https://github.com/wulkanowy/wulkanowy/issues", ::showMessage)
}
)
}
}

View File

@ -2,17 +2,11 @@ package io.github.wulkanowy.ui.base
import android.content.res.Resources import android.content.res.Resources
import com.chuckerteam.chucker.api.ChuckerCollector import com.chuckerteam.chucker.api.ChuckerCollector
import io.github.wulkanowy.R
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.sdk.exception.BadCredentialsException import io.github.wulkanowy.sdk.exception.BadCredentialsException
import io.github.wulkanowy.sdk.exception.FeatureDisabledException import io.github.wulkanowy.utils.getString
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
import io.github.wulkanowy.sdk.exception.NotLoggedInException
import io.github.wulkanowy.sdk.exception.ServiceUnavailableException
import io.github.wulkanowy.utils.security.ScramblerException import io.github.wulkanowy.utils.security.ScramblerException
import timber.log.Timber import timber.log.Timber
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
open class ErrorHandler @Inject constructor(protected val resources: Resources, private val chuckerCollector: ChuckerCollector) { open class ErrorHandler @Inject constructor(protected val resources: Resources, private val chuckerCollector: ChuckerCollector) {
@ -30,18 +24,10 @@ open class ErrorHandler @Inject constructor(protected val resources: Resources,
} }
protected open fun proceed(error: Throwable) { protected open fun proceed(error: Throwable) {
resources.run {
when (error) { when (error) {
is UnknownHostException -> showErrorMessage(getString(R.string.error_no_internet), error)
is SocketTimeoutException -> showErrorMessage(getString(R.string.error_timeout), error)
is NotLoggedInException -> showErrorMessage(getString(R.string.error_login_failed), error)
is ServiceUnavailableException -> showErrorMessage(getString(R.string.error_service_unavailable), error)
is FeatureDisabledException -> showErrorMessage(getString(R.string.error_feature_disabled), error)
is ScramblerException, is BadCredentialsException -> onSessionExpired() is ScramblerException, is BadCredentialsException -> onSessionExpired()
is NoCurrentStudentException -> onNoCurrentStudent() is NoCurrentStudentException -> onNoCurrentStudent()
is FeatureNotAvailableException -> showErrorMessage(getString(R.string.error_feature_not_available), error) else -> showErrorMessage(resources.getString(error), error)
else -> showErrorMessage(getString(R.string.error_unknown), error)
}
} }
} }

View File

@ -7,6 +7,7 @@ import dagger.Provides
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.di.scopes.PerFragment import io.github.wulkanowy.di.scopes.PerFragment
import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.ui.modules.about.AboutFragment import io.github.wulkanowy.ui.modules.about.AboutFragment
import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
@ -115,6 +116,10 @@ abstract class MainModule {
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindAccountDialog(): AccountDialog abstract fun bindAccountDialog(): AccountDialog
@PerFragment
@ContributesAndroidInjector
abstract fun bindErrorDialog(): ErrorDialog
@PerFragment @PerFragment
@ContributesAndroidInjector(modules = [MobileDeviceModule::class]) @ContributesAndroidInjector(modules = [MobileDeviceModule::class])
abstract fun bindMobileDevices(): MobileDeviceFragment abstract fun bindMobileDevices(): MobileDeviceFragment
@ -132,7 +137,7 @@ abstract class MainModule {
abstract fun bindLogViewerFragment(): LogViewerFragment abstract fun bindLogViewerFragment(): LogViewerFragment
@PerFragment @PerFragment
@ContributesAndroidInjector() @ContributesAndroidInjector
abstract fun bindContributorFragment(): ContributorFragment abstract fun bindContributorFragment(): ContributorFragment
@PerFragment @PerFragment

View File

@ -0,0 +1,20 @@
package io.github.wulkanowy.utils
import android.content.res.Resources
import io.github.wulkanowy.R
import io.github.wulkanowy.sdk.exception.FeatureDisabledException
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
import io.github.wulkanowy.sdk.exception.NotLoggedInException
import io.github.wulkanowy.sdk.exception.ServiceUnavailableException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
fun Resources.getString(error: Throwable) = when (error) {
is UnknownHostException -> getString(R.string.error_no_internet)
is SocketTimeoutException -> getString(R.string.error_timeout)
is NotLoggedInException -> getString(R.string.error_login_failed)
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)
else -> getString(R.string.error_unknown)
}

View File

@ -1,26 +1,49 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minWidth="300dp" android:minWidth="300dp"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="20dp"
android:paddingTop="20dp"
android:text="@string/all_details"
android:textSize="20sp" />
<TextView
android:id="@+id/errorDialogMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="20dp"
android:paddingVertical="10dp"
android:textSize="21sp"
tools:text="@tools:sample/lorem" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
android:orientation="vertical"> android:orientation="vertical">
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/errorDialogNestedScroll"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:overScrollMode="ifContentScrolls"> android:overScrollMode="ifContentScrolls"
app:layout_constrainedHeight="true"
app:layout_constraintHeight_max="300dp"
app:layout_constraintHeight_min="200dp"
app:layout_constraintTop_toTopOf="parent">
<HorizontalScrollView <HorizontalScrollView
android:id="@+id/errorDialogHorizontalScroll"
android:layout_width="350dp" android:layout_width="350dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingLeft="24dp" android:paddingLeft="24dp"
android:paddingTop="24dp"
android:paddingRight="24dp"> android:paddingRight="24dp">
<TextView <TextView
@ -33,16 +56,31 @@
tools:text="@tools:sample/lorem/random" /> tools:text="@tools:sample/lorem/random" />
</HorizontalScrollView> </HorizontalScrollView>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="24dp" android:background="@drawable/ic_all_divider"
android:gravity="end" android:gravity="end"
android:minHeight="52dp" android:minHeight="52dp"
android:orientation="horizontal"> android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/errorDialogReport"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="@string/about_feedback" />
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/errorDialogCancel" android:id="@+id/errorDialogCancel"
style="@style/Widget.MaterialComponents.Button.TextButton" style="@style/Widget.MaterialComponents.Button.TextButton"
@ -50,7 +88,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@android:string/cancel" /> android:text="@android:string/cancel" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
@ -60,7 +97,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@android:string/copy" /> android:text="@android:string/copy" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -421,5 +421,5 @@
<string name="error_service_unavailable">Trwa przerwa techniczna dziennika UONET+. Spróbuj ponownie później</string> <string name="error_service_unavailable">Trwa przerwa techniczna dziennika UONET+. Spróbuj ponownie później</string>
<string name="error_unknown">Wystąpił nieoczekiwany błąd</string> <string name="error_unknown">Wystąpił nieoczekiwany błąd</string>
<string name="error_feature_disabled">Funkcja wyłączona przez szkołę</string> <string name="error_feature_disabled">Funkcja wyłączona przez szkołę</string>
<string name="error_feature_not_available">Funkcja niedostępna. Zaloguj się w innym trybie niż Mobilne API</string> <string name="error_feature_not_available">Funkcja niedostępna. Zaloguj się w trybie innym niż Mobilne API</string>
</resources> </resources>