1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-20 03:49:09 -05:00

Add error view showing on first loading in fragment view (#590)

This commit is contained in:
Mikołaj Pich 2019-11-24 17:05:09 +01:00 committed by Rafał Borcz
parent 41aa326f42
commit 54ab408135
79 changed files with 1736 additions and 208 deletions

View File

@ -22,7 +22,8 @@ import io.github.wulkanowy.utils.FragmentLifecycleLogger
import io.github.wulkanowy.utils.getThemeAttrColor
import javax.inject.Inject
abstract class BaseActivity<T : BasePresenter<out BaseView>> : AppCompatActivity(), BaseView, HasAndroidInjector {
abstract class BaseActivity<T : BasePresenter<out BaseView>> : AppCompatActivity(), BaseView,
HasAndroidInjector {
@Inject
lateinit var androidInjector: DispatchingAndroidInjector<Any>
@ -53,13 +54,15 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>> : AppCompatActivity
override fun showError(text: String, error: Throwable) {
if (messageContainer != null) {
Snackbar.make(messageContainer!!, text, LENGTH_LONG)
.setAction(R.string.all_details) {
ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString())
}
.setAction(R.string.all_details) { showErrorDetailsDialog(error) }
.show()
} else showMessage(text)
}
override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString())
}
override fun showMessage(text: String) {
if (messageContainer != null) Snackbar.make(messageContainer!!, text, LENGTH_LONG).show()
else Toast.makeText(this, text, Toast.LENGTH_LONG).show()

View File

@ -0,0 +1,27 @@
package io.github.wulkanowy.ui.base
import android.widget.Toast
import dagger.android.support.DaggerAppCompatDialogFragment
abstract class BaseDialogFragment : DaggerAppCompatDialogFragment(), BaseView {
override fun showError(text: String, error: Throwable) {
showMessage(text)
}
override fun showMessage(text: String) {
Toast.makeText(context, text, Toast.LENGTH_LONG).show()
}
override fun showExpiredDialog() {
(activity as? BaseActivity<*>)?.showExpiredDialog()
}
override fun openClearLoginView() {
(activity as? BaseActivity<*>)?.openClearLoginView()
}
override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
}

View File

@ -13,15 +13,17 @@ abstract class BaseFragment : DaggerFragment(), BaseView {
override fun showError(text: String, error: Throwable) {
if (messageContainer != null) {
Snackbar.make(messageContainer!!, text, LENGTH_LONG)
.setAction(R.string.all_details) {
if (isAdded) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
.setAction(R.string.all_details) { if (isAdded) showErrorDetailsDialog(error) }
.show()
} else {
(activity as? BaseActivity<*>)?.showError(text, error)
}
}
override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
override fun showMessage(text: String) {
if (messageContainer != null) {
Snackbar.make(messageContainer!!, text, LENGTH_LONG).show()

View File

@ -9,4 +9,6 @@ interface BaseView {
fun showExpiredDialog()
fun openClearLoginView()
fun showErrorDetailsDialog(error: Throwable)
}

View File

@ -7,18 +7,17 @@ import android.view.ViewGroup
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import androidx.appcompat.app.AlertDialog
import dagger.android.support.DaggerAppCompatDialogFragment
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.base.BaseDialogFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.dialog_account.*
import javax.inject.Inject
class AccountDialog : DaggerAppCompatDialogFragment(), AccountView {
class AccountDialog : BaseDialogFragment(), AccountView {
@Inject
lateinit var presenter: AccountPresenter
@ -77,14 +76,6 @@ class AccountDialog : DaggerAppCompatDialogFragment(), AccountView {
}
}
override fun showExpiredDialog() {
(activity as? BaseActivity<*>)?.showExpiredDialog()
}
override fun openClearLoginView() {
(activity as? BaseActivity<*>)?.openClearLoginView()
}
override fun showConfirmDialog() {
context?.let {
AlertDialog.Builder(it)
@ -105,4 +96,3 @@ class AccountDialog : DaggerAppCompatDialogFragment(), AccountView {
super.onDestroy()
}
}

View File

@ -6,6 +6,9 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewGroup
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
import eu.davidea.flexibleadapter.FlexibleAdapter
@ -73,6 +76,9 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
}
attendanceSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
attendanceErrorRetry.setOnClickListener { presenter.onRetry() }
attendanceErrorDetails.setOnClickListener { presenter.onDetailsClick() }
attendancePreviousButton.setOnClickListener { presenter.onPreviousDay() }
attendanceNavDate.setOnClickListener { presenter.onPickDate() }
attendanceNextButton.setOnClickListener { presenter.onNextDay() }
@ -114,11 +120,19 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
}
override fun showEmpty(show: Boolean) {
attendanceEmpty.visibility = if (show) View.VISIBLE else View.GONE
attendanceEmpty.visibility = if (show) VISIBLE else GONE
}
override fun showErrorView(show: Boolean) {
attendanceError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
attendanceErrorMessage.text = message
}
override fun showProgress(show: Boolean) {
attendanceProgress.visibility = if (show) View.VISIBLE else View.GONE
attendanceProgress.visibility = if (show) VISIBLE else GONE
}
override fun enableSwipe(enable: Boolean) {
@ -126,7 +140,7 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
}
override fun showContent(show: Boolean) {
attendanceRecycler.visibility = if (show) View.VISIBLE else View.GONE
attendanceRecycler.visibility = if (show) VISIBLE else GONE
}
override fun hideRefresh() {
@ -134,11 +148,11 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
}
override fun showPreButton(show: Boolean) {
attendancePreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
attendancePreviousButton.visibility = if (show) VISIBLE else INVISIBLE
}
override fun showNextButton(show: Boolean) {
attendanceNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
attendanceNextButton.visibility = if (show) VISIBLE else INVISIBLE
}
override fun showAttendanceDialog(lesson: Attendance) {

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.attendance
import android.annotation.SuppressLint
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
@ -37,10 +38,13 @@ class AttendancePresenter @Inject constructor(
lateinit var currentDate: LocalDate
private set
private lateinit var lastError: Throwable
fun onAttachView(view: AttendanceView, date: Long?) {
super.onAttachView(view)
view.initView()
Timber.i("Attendance view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData(ofEpochDay(date ?: baseDate.toEpochDay()))
if (currentDate.isHolidays) setBaseDateOnHolidays()
reloadView()
@ -70,6 +74,18 @@ class AttendancePresenter @Inject constructor(
loadData(currentDate, true)
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(currentDate, true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
fun onViewReselected() {
Timber.i("Attendance view is reselected")
view?.also { view ->
@ -139,18 +155,29 @@ class AttendancePresenter @Inject constructor(
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showErrorView(false)
showContent(it.isNotEmpty())
}
analytics.logEvent("load_attendance", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.i("Loading attendance result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
}
)
}
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
private fun reloadView() {
Timber.i("Reload attendance view with the date ${currentDate.toFormattedString()}")
view?.apply {
@ -158,11 +185,13 @@ class AttendancePresenter @Inject constructor(
enableSwipe(false)
showContent(false)
showEmpty(false)
showErrorView(false)
clearData()
reloadNavigation()
}
}
@SuppressLint("DefaultLocale")
private fun reloadNavigation() {
view?.apply {
showPreButton(!currentDate.minusDays(1).isHolidays)

View File

@ -24,6 +24,10 @@ interface AttendanceView : BaseView {
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showProgress(show: Boolean)
fun enableSwipe(enable: Boolean)

View File

@ -57,6 +57,8 @@ class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainVie
}
attendanceSummarySwipe.setOnRefreshListener(presenter::onSwipeRefresh)
attendanceSummaryErrorRetry.setOnClickListener { presenter.onRetry() }
attendanceSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() }
subjectsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf())
subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject)
@ -93,6 +95,14 @@ class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainVie
attendanceSummaryEmpty.visibility = if (show) VISIBLE else GONE
}
override fun showErrorView(show: Boolean) {
attendanceSummaryError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
attendanceSummaryErrorMessage.text = message
}
override fun showProgress(show: Boolean) {
attendanceSummaryProgress.visibility = if (show) VISIBLE else GONE
}

View File

@ -33,10 +33,13 @@ class AttendanceSummaryPresenter @Inject constructor(
var currentSubjectId = -1
private set
private lateinit var lastError: Throwable
fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) {
super.onAttachView(view)
view.initView()
Timber.i("Attendance summary view was initialized with subject id ${subjectId ?: -1}")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData(subjectId ?: -1)
loadSubjects()
}
@ -46,6 +49,18 @@ class AttendanceSummaryPresenter @Inject constructor(
loadData(currentSubjectId, true)
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(currentSubjectId, true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
fun onSubjectSelected(name: String?) {
Timber.i("Select attendance summary subject $name")
view?.run {
@ -53,6 +68,7 @@ class AttendanceSummaryPresenter @Inject constructor(
showProgress(true)
enableSwipe(false)
showEmpty(false)
showErrorView(false)
clearView()
}
(subjects.singleOrNull { it.name == name }?.realId ?: -1).let {
@ -89,13 +105,23 @@ class AttendanceSummaryPresenter @Inject constructor(
analytics.logEvent("load_attendance_summary", "items" to it.first.size, "force_refresh" to forceRefresh, "item_id" to subjectId)
}) {
Timber.i("Loading attendance summary result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
}
)
}
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
private fun loadSubjects() {
Timber.i("Loading attendance summary subjects started")
disposable.add(studentRepository.getCurrentStudent()

View File

@ -18,6 +18,10 @@ interface AttendanceSummaryView : BaseView {
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun updateDataSet(data: List<AttendanceSummaryItem>, header: AttendanceSummaryScrollableHeader)
fun updateSubjects(data: ArrayList<String>)

View File

@ -61,6 +61,9 @@ class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView.
}
examSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
examErrorRetry.setOnClickListener { presenter.onRetry() }
examErrorDetails.setOnClickListener { presenter.onDetailsClick() }
examPreviousButton.setOnClickListener { presenter.onPreviousWeek() }
examNextButton.setOnClickListener { presenter.onNextWeek() }
@ -95,6 +98,14 @@ class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView.
examEmpty.visibility = if (show) VISIBLE else GONE
}
override fun showErrorView(show: Boolean) {
examError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
examErrorMessage.text = message
}
override fun showProgress(show: Boolean) {
examProgress.visibility = if (show) VISIBLE else GONE
}

View File

@ -36,10 +36,13 @@ class ExamPresenter @Inject constructor(
lateinit var currentDate: LocalDate
private set
private lateinit var lastError: Throwable
fun onAttachView(view: ExamView, date: Long?) {
super.onAttachView(view)
view.initView()
Timber.i("Exam view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData(ofEpochDay(date ?: baseDate.toEpochDay()))
if (currentDate.isHolidays) setBaseDateOnHolidays()
reloadView()
@ -60,6 +63,18 @@ class ExamPresenter @Inject constructor(
loadData(currentDate, true)
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(currentDate, true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
fun onExamItemSelected(item: AbstractFlexibleItem<*>?) {
if (item is ExamItem) {
Timber.i("Select exam item ${item.exam.id}")
@ -116,17 +131,28 @@ class ExamPresenter @Inject constructor(
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showErrorView(false)
showContent(it.isNotEmpty())
}
analytics.logEvent("load_exam", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.i("Loading exam result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
})
}
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
private fun createExamItems(items: Map<LocalDate, List<Exam>>): List<ExamItem> {
return items.flatMap {
ExamHeader(it.key).let { header ->
@ -142,6 +168,7 @@ class ExamPresenter @Inject constructor(
enableSwipe(false)
showContent(false)
showEmpty(false)
showErrorView(false)
clearData()
reloadNavigation()
}

View File

@ -21,6 +21,10 @@ interface ExamView : BaseView {
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showProgress(show: Boolean)
fun enableSwipe(enable: Boolean)

View File

@ -13,6 +13,7 @@ import androidx.appcompat.app.AlertDialog
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment
import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment
import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment
@ -83,7 +84,8 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainVie
setElevationCompat(context.dpToPx(4f))
}
gradeSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
gradeErrorRetry.setOnClickListener { presenter.onRetry() }
gradeErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -104,22 +106,18 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainVie
gradeProgress.visibility = if (show) VISIBLE else INVISIBLE
}
override fun showEmpty(show: Boolean) {
gradeEmpty.visibility = if (show) VISIBLE else INVISIBLE
override fun showErrorView(show: Boolean) {
gradeError.visibility = if (show) VISIBLE else INVISIBLE
}
override fun showRefresh(show: Boolean) {
gradeSwipe.isRefreshing = show
override fun setErrorDetails(message: String) {
gradeErrorMessage.text = message
}
override fun showSemesterSwitch(show: Boolean) {
semesterSwitchMenu?.isVisible = show
}
override fun enableSwipe(enable: Boolean) {
gradeSwipe.isEnabled = enable
}
override fun showSemesterDialog(selectedIndex: Int) {
val choices = arrayOf(
getString(R.string.grade_semester, 1),

View File

@ -25,14 +25,14 @@ class GradePresenter @Inject constructor(
private val loadedSemesterId = mutableMapOf<Int, Int>()
private lateinit var lastError: Throwable
fun onAttachView(view: GradeView, savedIndex: Int?) {
super.onAttachView(view)
selectedIndex = savedIndex ?: 0
view.run {
initView()
enableSwipe(false)
}
view.initView()
Timber.i("Grade view was initialized with $selectedIndex index")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData()
}
@ -71,7 +71,7 @@ class GradePresenter @Inject constructor(
view?.apply {
showContent(true)
showProgress(false)
showEmpty(false)
showErrorView(false)
loadedSemesterId[currentPageIndex] = semesterId
}
}
@ -80,10 +80,18 @@ class GradePresenter @Inject constructor(
if (semesters.isNotEmpty()) loadChild(index)
}
fun onSwipeRefresh() {
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData()
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
private fun loadData() {
Timber.i("Loading grade data started")
disposable.add(studentRepository.getCurrentStudent()
@ -96,25 +104,28 @@ class GradePresenter @Inject constructor(
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally { view?.showRefresh(false) }
.doFinally { view?.showProgress(false) }
.subscribe({
view?.run {
Timber.i("Loading grade result: Attempt load index $currentPageIndex")
loadChild(currentPageIndex)
enableSwipe(false)
showErrorView(false)
showSemesterSwitch(true)
}
}) {
Timber.i("Loading grade result: An exception occurred")
errorHandler.dispatch(it)
view?.run {
showProgress(false)
showEmpty(true)
enableSwipe(true)
}
})
}
private fun showErrorViewOnError(message: String, error: Throwable) {
lastError = error
view?.run {
showErrorView(true)
setErrorDetails(message)
}
}
private fun loadChild(index: Int, forceRefresh: Boolean = false) {
semesters.first { it.semesterName == selectedIndex }.semesterId.also {
if (forceRefresh || loadedSemesterId[index] != it) {

View File

@ -12,16 +12,14 @@ interface GradeView : BaseView {
fun showProgress(show: Boolean)
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun showRefresh(show: Boolean)
fun setErrorDetails(message: String)
fun showSemesterSwitch(show: Boolean)
fun showSemesterDialog(selectedIndex: Int)
fun enableSwipe(enable: Boolean)
fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean)
fun notifyChildParentReselected(index: Int)

View File

@ -18,6 +18,7 @@ import eu.davidea.flexibleadapter.items.IFlexible
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.grade.GradeView
import io.github.wulkanowy.ui.modules.main.MainActivity
@ -90,6 +91,8 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
)
}
gradeDetailsSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
gradeDetailsErrorRetry.setOnClickListener { presenter.onRetry() }
gradeDetailsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -141,6 +144,14 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
gradeDetailsEmpty.visibility = if (show) VISIBLE else INVISIBLE
}
override fun showErrorView(show: Boolean) {
gradeDetailsError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
gradeDetailsErrorMessage.text = message
}
override fun showRefresh(show: Boolean) {
gradeDetailsSwipe.isRefreshing = show
}

View File

@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.grade.details
import android.widget.Toast
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.repositories.grade.GradeRepository
@ -31,9 +30,12 @@ class GradeDetailsPresenter @Inject constructor(
private var currentSemesterId = 0
private lateinit var lastError: Throwable
override fun onAttachView(view: GradeDetailsView) {
super.onAttachView(view)
view.initView()
errorHandler.showErrorMessage = ::showErrorViewOnError
}
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
@ -90,6 +92,18 @@ class GradeDetailsPresenter @Inject constructor(
view?.notifyParentRefresh()
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
view?.notifyParentRefresh()
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
fun onParentViewReselected() {
view?.run {
if (!isViewEmpty) {
@ -140,21 +154,32 @@ class GradeDetailsPresenter @Inject constructor(
}
.subscribe({
Timber.i("Loading grade details result: Success")
newGradesAmount = it.sumBy { gradeDetailsHeader -> gradeDetailsHeader.newGrades }
newGradesAmount = it.sumBy { gradeDetailsHeader -> gradeDetailsHeader.newGrades }
updateMarkAsDoneButton()
view?.run {
showEmpty(it.isEmpty())
showErrorView(false)
showContent(it.isNotEmpty())
updateData(it)
}
analytics.logEvent("load_grade_details", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.i("Loading grade details result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
})
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
private fun createGradeItems(items: Map<String, List<Grade>>, averages: Map<String, Double>): List<GradeDetailsHeader> {
val isGradeExpandable = preferencesRepository.isGradeExpandable
val gradeColorTheme = preferencesRepository.gradeColorTheme

View File

@ -38,6 +38,10 @@ interface GradeDetailsView : BaseView {
fun showProgress(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun enableSwipe(enable: Boolean)
fun showRefresh(show: Boolean)

View File

@ -111,9 +111,11 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
setOnItemSelectedListener<TextView> { presenter.onSubjectSelected(it?.text?.toString()) }
}
gradeStatisticsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
gradeStatisticsSubjectsContainer.setElevationCompat(requireContext().dpToPx(1f))
gradeStatisticsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
gradeStatisticsErrorRetry.setOnClickListener { presenter.onRetry() }
gradeStatisticsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
override fun updateSubjects(data: ArrayList<String>) {
@ -228,6 +230,14 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
gradeStatisticsEmpty.visibility = if (show) View.VISIBLE else View.INVISIBLE
}
override fun showErrorView(show: Boolean) {
gradeStatisticsError.visibility = if (show) View.VISIBLE else View.GONE
}
override fun setErrorDetails(message: String) {
gradeStatisticsErrorMessage.text = message
}
override fun showProgress(show: Boolean) {
gradeStatisticsProgress.visibility = if (show) View.VISIBLE else View.GONE
}

View File

@ -30,6 +30,8 @@ class GradeStatisticsPresenter @Inject constructor(
private var currentSubjectName: String = "Wszystkie"
private lateinit var lastError: Throwable
var currentType: ViewType = ViewType.PARTIAL
private set
@ -37,6 +39,7 @@ class GradeStatisticsPresenter @Inject constructor(
super.onAttachView(view)
currentType = type ?: ViewType.PARTIAL
view.initView()
errorHandler.showErrorMessage = ::showErrorViewOnError
}
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
@ -51,6 +54,7 @@ class GradeStatisticsPresenter @Inject constructor(
enableSwipe(false)
showRefresh(false)
showBarContent(false)
showErrorView(false)
showEmpty(false)
clearView()
}
@ -62,6 +66,18 @@ class GradeStatisticsPresenter @Inject constructor(
view?.notifyParentRefresh()
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
view?.notifyParentRefresh()
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
fun onSubjectSelected(name: String?) {
Timber.i("Select grade stats subject $name")
view?.run {
@ -70,6 +86,7 @@ class GradeStatisticsPresenter @Inject constructor(
showProgress(true)
enableSwipe(false)
showEmpty(false)
showErrorView(false)
clearView()
}
(subjects.singleOrNull { it.name == name }?.name)?.let {
@ -86,6 +103,7 @@ class GradeStatisticsPresenter @Inject constructor(
showProgress(true)
enableSwipe(false)
showEmpty(false)
showErrorView(false)
clearView()
}
loadDataByType(currentSemesterId, currentSubjectName, type)
@ -146,12 +164,12 @@ class GradeStatisticsPresenter @Inject constructor(
showEmpty(it.isEmpty())
showBarContent(false)
showPieContent(it.isNotEmpty())
showErrorView(false)
updatePieData(it, preferencesRepository.gradeColorTheme)
}
analytics.logEvent("load_grade_statistics", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.e("Loading grade stats result: An exception occurred")
view?.run { showEmpty(isPieViewEmpty) }
errorHandler.dispatch(it)
})
}
@ -177,12 +195,12 @@ class GradeStatisticsPresenter @Inject constructor(
showEmpty(false)
showPieContent(false)
showBarContent(true)
showErrorView(false)
updateBarData(it)
}
analytics.logEvent("load_grade_points_statistics", "force_refresh" to forceRefresh)
}, {
Timber.e("Loading grade points stats result: An exception occurred")
view?.run { showEmpty(isBarViewEmpty) }
errorHandler.dispatch(it)
}, {
Timber.d("Loading grade points stats result: No point stats found")
@ -193,4 +211,15 @@ class GradeStatisticsPresenter @Inject constructor(
})
)
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isBarViewEmpty || isPieViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
}

View File

@ -32,6 +32,10 @@ interface GradeStatisticsView : BaseView {
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showProgress(show: Boolean)
fun enableSwipe(enable: Boolean)

View File

@ -56,6 +56,8 @@ class GradeSummaryFragment : BaseFragment(), GradeSummaryView, GradeView.GradeCh
adapter = gradeSummaryAdapter
}
gradeSummarySwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
gradeSummaryErrorRetry.setOnClickListener { presenter.onRetry() }
gradeSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
override fun updateData(data: List<GradeSummaryItem>, header: GradeSummaryScrollableHeader) {
@ -82,6 +84,14 @@ class GradeSummaryFragment : BaseFragment(), GradeSummaryView, GradeView.GradeCh
gradeSummaryEmpty.visibility = if (show) VISIBLE else INVISIBLE
}
override fun showErrorView(show: Boolean) {
gradeSummaryError.visibility = if (show) VISIBLE else INVISIBLE
}
override fun setErrorDetails(message: String) {
gradeSummaryErrorMessage.text = message
}
override fun showProgress(show: Boolean) {
gradeSummaryProgress.visibility = if (show) VISIBLE else GONE
}

View File

@ -25,9 +25,12 @@ class GradeSummaryPresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<GradeSummaryView>(errorHandler, studentRepository, schedulers) {
private lateinit var lastError: Throwable
override fun onAttachView(view: GradeSummaryView) {
super.onAttachView(view)
view.initView()
errorHandler.showErrorMessage = ::showErrorViewOnError
}
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
@ -56,21 +59,44 @@ class GradeSummaryPresenter @Inject constructor(
view?.run {
showEmpty(gradeSummaryItems.isEmpty())
showContent(gradeSummaryItems.isNotEmpty())
showErrorView(false)
updateData(gradeSummaryItems, gradeSummaryHeader)
}
analytics.logEvent("load_grade_summary", "items" to gradeSummaryItems.size, "force_refresh" to forceRefresh)
}) {
Timber.i("Loading grade summary result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
})
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
fun onSwipeRefresh() {
Timber.i("Force refreshing the grade summary")
view?.notifyParentRefresh()
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
view?.notifyParentRefresh()
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
fun onParentViewReselected() {
view?.run {
if (!isViewEmpty) resetView()

View File

@ -26,6 +26,10 @@ interface GradeSummaryView : BaseView {
fun showContent(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showEmpty(show: Boolean)
fun notifyParentDataLoaded(semesterId: Int)

View File

@ -3,6 +3,8 @@ package io.github.wulkanowy.ui.modules.homework
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
@ -34,6 +36,8 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
override val titleStringId get() = R.string.homework_title
override val isViewEmpty get() = homeworkAdapter.isEmpty
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_homework, container, false)
}
@ -41,7 +45,7 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
messageContainer = homeworkRecycler
presenter.onAttachView(this, savedInstanceState?.getLong(HomeworkFragment.SAVED_DATE_KEY))
presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY))
}
override fun initView() {
@ -56,6 +60,9 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
}
homeworkSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
homeworkErrorRetry.setOnClickListener { presenter.onRetry() }
homeworkErrorDetails.setOnClickListener { presenter.onDetailsClick() }
homeworkPreviousButton.setOnClickListener { presenter.onPreviousDay() }
homeworkNextButton.setOnClickListener { presenter.onNextDay() }
@ -74,18 +81,24 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
homeworkNavDate.text = date
}
override fun isViewEmpty() = homeworkAdapter.isEmpty
override fun hideRefresh() {
homeworkSwipe.isRefreshing = false
}
override fun showEmpty(show: Boolean) {
homeworkEmpty.visibility = if (show) View.VISIBLE else View.GONE
homeworkEmpty.visibility = if (show) VISIBLE else GONE
}
override fun showErrorView(show: Boolean) {
homeworkError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
homeworkErrorMessage.text = message
}
override fun showProgress(show: Boolean) {
homeworkProgress.visibility = if (show) View.VISIBLE else View.GONE
homeworkProgress.visibility = if (show) VISIBLE else GONE
}
override fun enableSwipe(enable: Boolean) {
@ -93,15 +106,15 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
}
override fun showContent(show: Boolean) {
homeworkRecycler.visibility = if (show) View.VISIBLE else View.GONE
homeworkRecycler.visibility = if (show) VISIBLE else GONE
}
override fun showPreButton(show: Boolean) {
homeworkPreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
homeworkPreviousButton.visibility = if (show) VISIBLE else View.INVISIBLE
}
override fun showNextButton(show: Boolean) {
homeworkNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
homeworkNextButton.visibility = if (show) VISIBLE else View.INVISIBLE
}
override fun showTimetableDialog(homework: Homework) {

View File

@ -35,10 +35,13 @@ class HomeworkPresenter @Inject constructor(
lateinit var currentDate: LocalDate
private set
private lateinit var lastError: Throwable
fun onAttachView(view: HomeworkView, date: Long?) {
super.onAttachView(view)
view.initView()
Timber.i("Homework view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData(ofEpochDay(date ?: baseDate.toEpochDay()))
if (currentDate.isHolidays) setBaseDateOnHolidays()
reloadView()
@ -59,6 +62,18 @@ class HomeworkPresenter @Inject constructor(
loadData(currentDate, true)
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(currentDate, true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
fun onHomeworkItemSelected(item: AbstractFlexibleItem<*>?) {
if (item is HomeworkItem) {
Timber.i("Select homework item ${item.homework.id}")
@ -105,17 +120,29 @@ class HomeworkPresenter @Inject constructor(
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showErrorView(false)
showContent(it.isNotEmpty())
}
analytics.logEvent("load_homework", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.i("Loading homework result: An exception occurred")
view?.run { showEmpty(isViewEmpty()) }
errorHandler.dispatch(it)
})
}
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
private fun createHomeworkItem(items: Map<LocalDate, List<Homework>>): List<HomeworkItem> {
return items.flatMap {
HomeworkHeader(it.key).let { header ->
@ -131,6 +158,7 @@ class HomeworkPresenter @Inject constructor(
enableSwipe(false)
showContent(false)
showEmpty(false)
showErrorView(false)
clearData()
reloadNavigation()
}

View File

@ -5,6 +5,8 @@ import io.github.wulkanowy.ui.base.BaseView
interface HomeworkView : BaseView {
val isViewEmpty: Boolean
fun initView()
fun updateData(data: List<HomeworkItem>)
@ -13,12 +15,14 @@ interface HomeworkView : BaseView {
fun updateNavigationWeek(date: String)
fun isViewEmpty(): Boolean
fun hideRefresh()
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showProgress(show: Boolean)
fun enableSwipe(enable: Boolean)

View File

@ -3,6 +3,8 @@ package io.github.wulkanowy.ui.modules.luckynumber
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.LuckyNumber
@ -23,17 +25,22 @@ class LuckyNumberFragment : BaseFragment(), LuckyNumberView, MainView.TitledView
override val titleStringId: Int
get() = R.string.lucky_number_title
override val isViewEmpty get() = luckyNumberText.text.isBlank()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_lucky_number, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
messageContainer = luckyNumberSwipe
presenter.onAttachView(this)
}
override fun initView() {
luckyNumberSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
luckyNumberErrorRetry.setOnClickListener { presenter.onRetry() }
luckyNumberErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
override fun updateData(data: LuckyNumber) {
@ -45,11 +52,19 @@ class LuckyNumberFragment : BaseFragment(), LuckyNumberView, MainView.TitledView
}
override fun showEmpty(show: Boolean) {
luckyNumberEmpty.visibility = if (show) View.VISIBLE else View.GONE
luckyNumberEmpty.visibility = if (show) VISIBLE else GONE
}
override fun showErrorView(show: Boolean) {
luckyNumberError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
luckyNumberErrorMessage.text = message
}
override fun showProgress(show: Boolean) {
luckyNumberProgress.visibility = if (show) View.VISIBLE else View.GONE
luckyNumberProgress.visibility = if (show) VISIBLE else GONE
}
override fun enableSwipe(enable: Boolean) {
@ -57,11 +72,7 @@ class LuckyNumberFragment : BaseFragment(), LuckyNumberView, MainView.TitledView
}
override fun showContent(show: Boolean) {
luckyNumberContent.visibility = if (show) View.VISIBLE else View.GONE
}
override fun isViewEmpty(): Boolean {
return luckyNumberText.text.isBlank()
luckyNumberContent.visibility = if (show) VISIBLE else GONE
}
override fun onDestroyView() {

View File

@ -19,6 +19,8 @@ class LuckyNumberPresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<LuckyNumberView>(errorHandler, studentRepository, schedulers) {
private lateinit var lastError: Throwable
override fun onAttachView(view: LuckyNumberView) {
super.onAttachView(view)
view.run {
@ -27,6 +29,7 @@ class LuckyNumberPresenter @Inject constructor(
enableSwipe(false)
}
Timber.i("Lucky number view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData()
}
@ -52,25 +55,49 @@ class LuckyNumberPresenter @Inject constructor(
updateData(it)
showContent(true)
showEmpty(false)
showErrorView(false)
}
analytics.logEvent("load_lucky_number", "lucky_number" to it.luckyNumber, "force_refresh" to forceRefresh)
}, {
Timber.i("Loading lucky number result: An exception occurred")
view?.run { showEmpty(isViewEmpty()) }
errorHandler.dispatch(it)
}, {
Timber.i("Loading lucky number result: No lucky number found")
view?.run {
showContent(false)
showEmpty(true)
showEmpty(false)
}
})
)
}
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
fun onSwipeRefresh() {
Timber.i("Force refreshing the lucky number")
loadData(true)
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
}

View File

@ -5,6 +5,8 @@ import io.github.wulkanowy.ui.base.BaseView
interface LuckyNumberView : BaseView {
val isViewEmpty: Boolean
fun initView()
fun updateData(data: LuckyNumber)
@ -13,11 +15,13 @@ interface LuckyNumberView : BaseView {
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showProgress(show: Boolean)
fun enableSwipe(enable: Boolean)
fun showContent(show: Boolean)
fun isViewEmpty(): Boolean
}

View File

@ -65,6 +65,10 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl
presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getLong(MESSAGE_ID_KEY) ?: 0L)
}
override fun initView() {
messagePreviewErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.action_menu_message_preview, menu)
menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply)
@ -126,8 +130,16 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl
menuDeleteButton?.setTitle(R.string.message_move_to_bin)
}
override fun showMessageError() {
messagePreviewError.visibility = VISIBLE
override fun showErrorView(show: Boolean) {
messagePreviewError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
messagePreviewErrorMessage.text = message
}
override fun setErrorRetryCallback(callback: () -> Unit) {
messagePreviewErrorRetry.setOnClickListener { callback() }
}
override fun openMessageReply(message: Message?) {

View File

@ -23,11 +23,29 @@ class MessagePreviewPresenter @Inject constructor(
private var message: Message? = null
private lateinit var lastError: Throwable
private var retryCallback: () -> Unit = {}
fun onAttachView(view: MessagePreviewView, id: Long) {
super.onAttachView(view)
view.initView()
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData(id)
}
private fun onMessageLoadRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(messageId)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
private fun loadData(id: Long) {
Timber.i("Loading message $id preview started")
messageId = id
@ -55,7 +73,7 @@ class MessagePreviewPresenter @Inject constructor(
analytics.logEvent("load_message_preview", "length" to message.content?.length)
}) {
Timber.i("Loading message $id preview result: An exception occurred ")
view?.showMessageError()
retryCallback = { onMessageLoadRetry() }
errorHandler.dispatch(it)
})
}
@ -85,6 +103,7 @@ class MessagePreviewPresenter @Inject constructor(
showContent(false)
showProgress(true)
showOptions(false)
showErrorView(false)
}
}
.doFinally {
@ -97,15 +116,24 @@ class MessagePreviewPresenter @Inject constructor(
popView()
}
}, { error ->
view?.showMessageError()
retryCallback = { onMessageDelete() }
errorHandler.dispatch(error)
}, {
view?.showMessageError()
view?.showErrorView(true)
})
)
}
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
lastError = error
setErrorDetails(message)
showErrorView(true)
setErrorRetryCallback { retryCallback() }
}
}
fun onMessageDelete(): Boolean {
deleteMessage()
return true

View File

@ -9,6 +9,8 @@ interface MessagePreviewView : BaseView {
val deleteMessageSuccessString: String
fun initView()
fun setSubject(subject: String)
fun setRecipient(recipient: String)
@ -23,19 +25,23 @@ interface MessagePreviewView : BaseView {
fun showContent(show: Boolean)
fun notifyParentMessageDeleted(message: Message)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun setErrorRetryCallback(callback: () -> Unit)
fun showOptions(show: Boolean)
fun setDeletedOptionsLabels()
fun setNotDeletedOptionsLabels()
fun showMessageError()
fun openMessageReply(message: Message?)
fun openMessageForward(message: Message?)
fun popView()
fun notifyParentMessageDeleted(message: Message)
}

View File

@ -72,6 +72,8 @@ class MessageTabFragment : BaseFragment(), MessageTabView {
)
}
messageTabSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
messageTabErrorRetry.setOnClickListener { presenter.onRetry() }
messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
override fun updateData(data: List<MessageItem>) {
@ -102,6 +104,14 @@ class MessageTabFragment : BaseFragment(), MessageTabView {
messageTabEmpty.visibility = if (show) VISIBLE else INVISIBLE
}
override fun showErrorView(show: Boolean) {
messageTabError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
messageTabErrorMessage.text = message
}
override fun showRefresh(show: Boolean) {
messageTabSwipe.isRefreshing = show
}

View File

@ -23,9 +23,12 @@ class MessageTabPresenter @Inject constructor(
lateinit var folder: MessageFolder
private lateinit var lastError: Throwable
fun onAttachView(view: MessageTabView, folder: MessageFolder) {
super.onAttachView(view)
view.initView()
errorHandler.showErrorMessage = ::showErrorViewOnError
this.folder = folder
}
@ -34,6 +37,18 @@ class MessageTabPresenter @Inject constructor(
onParentViewLoadData(true)
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
fun onDeleteMessage() {
loadData(false)
}
@ -78,17 +93,28 @@ class MessageTabPresenter @Inject constructor(
view?.run {
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
showErrorView(false)
updateData(it)
}
analytics.logEvent("load_messages", "items" to it.size, "folder" to folder.name)
}) {
Timber.i("Loading $folder message result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
})
}
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
private fun updateMessage(message: Message) {
Timber.i("Attempt to update message ${message.id}")
disposable.add(messageRepository.updateMessage(message)

View File

@ -26,6 +26,10 @@ interface MessageTabView : BaseView {
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showRefresh(show: Boolean)
fun openMessage(messageId: Long)

View File

@ -61,6 +61,8 @@ class MobileDeviceFragment : BaseFragment(), MobileDeviceView, MainView.TitledVi
onDeviceUnregisterListener = presenter::onUnregisterDevice
}
mobileDevicesSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
mobileDevicesErrorRetry.setOnClickListener { presenter.onRetry() }
mobileDevicesErrorDetails.setOnClickListener { presenter.onDetailsClick() }
mobileDeviceAddButton.setOnClickListener { presenter.onRegisterDevice() }
}
@ -105,6 +107,14 @@ class MobileDeviceFragment : BaseFragment(), MobileDeviceView, MainView.TitledVi
mobileDevicesEmpty.visibility = if (show) VISIBLE else GONE
}
override fun showErrorView(show: Boolean) {
mobileDevicesError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
mobileDevicesErrorMessage.text = message
}
override fun enableSwipe(enable: Boolean) {
mobileDevicesSwipe.isEnabled = enable
}

View File

@ -20,10 +20,13 @@ class MobileDevicePresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<MobileDeviceView>(errorHandler, studentRepository, schedulers) {
private lateinit var lastError: Throwable
override fun onAttachView(view: MobileDeviceView) {
super.onAttachView(view)
view.initView()
Timber.i("Mobile device view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData()
}
@ -31,6 +34,18 @@ class MobileDevicePresenter @Inject constructor(
loadData(true)
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading mobile devices data started")
disposable.add(studentRepository.getCurrentStudent()
@ -51,15 +66,26 @@ class MobileDevicePresenter @Inject constructor(
updateData(it)
showContent(it.isNotEmpty())
showEmpty(it.isEmpty())
showErrorView(false)
}
analytics.logEvent("load_devices", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.i("Loading mobile devices result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
})
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
fun onRegisterDevice() {
view?.showTokenDialog()
}

View File

@ -25,6 +25,10 @@ interface MobileDeviceView : BaseView {
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showUndo(position: Int, device: MobileDevice)
fun showTokenDialog()

View File

@ -12,15 +12,13 @@ import android.view.View.VISIBLE
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.getSystemService
import dagger.android.support.DaggerDialogFragment
import io.github.wulkanowy.R
import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.data.repositories.mobiledevice.MobileDeviceRemote
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.base.BaseDialogFragment
import kotlinx.android.synthetic.main.dialog_mobile_device.*
import javax.inject.Inject
class MobileDeviceTokenDialog : DaggerDialogFragment(), MobileDeviceTokenVIew {
class MobileDeviceTokenDialog : BaseDialogFragment(), MobileDeviceTokenVIew {
@Inject
lateinit var presenter: MobileDeviceTokenPresenter
@ -66,6 +64,12 @@ class MobileDeviceTokenDialog : DaggerDialogFragment(), MobileDeviceTokenVIew {
})
}
private fun clickCopy(text: String) {
val clip = ClipData.newPlainText("wulkanowy", text)
activity?.getSystemService<ClipboardManager>()?.setPrimaryClip(clip)
Toast.makeText(context, R.string.all_copied, Toast.LENGTH_LONG).show()
}
override fun hideLoading() {
mobileDeviceDialogProgress.visibility = GONE
}
@ -78,30 +82,8 @@ class MobileDeviceTokenDialog : DaggerDialogFragment(), MobileDeviceTokenVIew {
dismiss()
}
override fun showError(text: String, error: Throwable) {
showMessage(text)
}
override fun showMessage(text: String) {
Toast.makeText(context, text, Toast.LENGTH_LONG).show()
}
override fun showExpiredDialog() {
(activity as? BaseActivity<*>)?.showExpiredDialog()
}
override fun openClearLoginView() {
(activity as? BaseActivity<*>)?.openClearLoginView()
}
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
}
fun clickCopy(text: String) {
val clip = ClipData.newPlainText("wulkanowy", text)
activity?.getSystemService<ClipboardManager>()?.setPrimaryClip(clip)
Toast.makeText(context, R.string.all_copied, Toast.LENGTH_LONG).show()
}
}

View File

@ -60,6 +60,8 @@ class NoteFragment : BaseFragment(), NoteView, MainView.TitledView {
)
}
noteSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
noteErrorRetry.setOnClickListener { presenter.onRetry() }
noteErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
override fun showNoteDialog(note: Note) {
@ -82,6 +84,14 @@ class NoteFragment : BaseFragment(), NoteView, MainView.TitledView {
noteEmpty.visibility = if (show) VISIBLE else GONE
}
override fun showErrorView(show: Boolean) {
noteError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
noteErrorMessage.text = message
}
override fun showProgress(show: Boolean) {
noteProgress.visibility = if (show) VISIBLE else GONE
}

View File

@ -21,10 +21,13 @@ class NotePresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<NoteView>(errorHandler, studentRepository, schedulers) {
private lateinit var lastError: Throwable
override fun onAttachView(view: NoteView) {
super.onAttachView(view)
view.initView()
Timber.i("Note view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData()
}
@ -33,6 +36,18 @@ class NotePresenter @Inject constructor(
loadData(true)
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading note data started")
disposable.add(studentRepository.getCurrentStudent()
@ -53,17 +68,28 @@ class NotePresenter @Inject constructor(
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showErrorView(false)
showContent(it.isNotEmpty())
}
analytics.logEvent("load_note", "items" to it.size, "force_refresh" to forceRefresh)
}, {
Timber.i("Loading note result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
})
)
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
fun onNoteItemSelected(item: AbstractFlexibleItem<*>?) {
if (item is NoteItem) {
Timber.i("Select note item ${item.note.id}")

View File

@ -18,6 +18,10 @@ interface NoteView : BaseView {
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showProgress(show: Boolean)
fun enableSwipe(enable: Boolean)

View File

@ -3,6 +3,8 @@ package io.github.wulkanowy.ui.modules.schoolandteachers.school
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.School
@ -22,6 +24,8 @@ class SchoolFragment : BaseFragment(), SchoolView, MainView.TitledView, SchoolAn
override val titleStringId get() = R.string.school_title
override val isViewEmpty get() = schoolName.text.isBlank()
companion object {
fun newInstance() = SchoolFragment()
}
@ -37,6 +41,8 @@ class SchoolFragment : BaseFragment(), SchoolView, MainView.TitledView, SchoolAn
override fun initView() {
schoolSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
schoolErrorRetry.setOnClickListener { presenter.onRetry() }
schoolErrorDetails.setOnClickListener { presenter.onDetailsClick() }
schoolAddressButton.setOnClickListener { presenter.onAddressSelected() }
schoolTelephoneButton.setOnClickListener { presenter.onTelephoneSelected() }
@ -45,24 +51,27 @@ class SchoolFragment : BaseFragment(), SchoolView, MainView.TitledView, SchoolAn
override fun updateData(data: School) {
schoolName.text = data.name
schoolAddress.text = data.address.ifBlank { "-" }
schoolAddressButton.visibility = if (data.address.isNotBlank()) View.VISIBLE else View.GONE
schoolAddressButton.visibility = if (data.address.isNotBlank()) VISIBLE else GONE
schoolTelephone.text = data.contact.ifBlank { "-" }
schoolTelephoneButton.visibility = if (data.contact.isNotBlank()) View.VISIBLE else View.GONE
schoolTelephoneButton.visibility = if (data.contact.isNotBlank()) VISIBLE else GONE
schoolHeadmaster.text = data.headmaster
schoolPedagogue.text = data.pedagogue
}
override fun isViewEmpty(): Boolean {
return schoolName.text.isBlank()
override fun showEmpty(show: Boolean) {
schoolEmpty.visibility = if (show) VISIBLE else GONE
}
override fun showEmpty(show: Boolean) {
schoolEmpty.visibility = if (show) View.VISIBLE else View.GONE
override fun showErrorView(show: Boolean) {
schoolError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
schoolErrorMessage.text = message
}
override fun showProgress(show: Boolean) {
schoolProgress.visibility = if (show) View.VISIBLE else View.GONE
schoolProgress.visibility = if (show) VISIBLE else GONE
}
override fun enableSwipe(enable: Boolean) {
@ -70,7 +79,7 @@ class SchoolFragment : BaseFragment(), SchoolView, MainView.TitledView, SchoolAn
}
override fun showContent(show: Boolean) {
schoolContent.visibility = if (show) View.VISIBLE else View.GONE
schoolContent.visibility = if (show) VISIBLE else GONE
}
override fun hideRefresh() {

View File

@ -23,10 +23,13 @@ class SchoolPresenter @Inject constructor(
private var contact: String? = null
private lateinit var lastError: Throwable
override fun onAttachView(view: SchoolView) {
super.onAttachView(view)
view.initView()
Timber.i("School view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData()
}
@ -34,12 +37,24 @@ class SchoolPresenter @Inject constructor(
loadData(true)
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
fun onParentViewLoadData(forceRefresh: Boolean) {
loadData(forceRefresh)
}
fun onAddressSelected() {
address?.let{ view?.openMapsLocation(it) }
address?.let { view?.openMapsLocation(it) }
}
fun onTelephoneSelected() {
@ -68,21 +83,31 @@ class SchoolPresenter @Inject constructor(
updateData(it)
showContent(true)
showEmpty(false)
showErrorView(false)
}
analytics.logEvent("load_school", "force_refresh" to forceRefresh)
}, {
Timber.i("Loading school result: An exception occurred")
view?.run {
showContent(!isViewEmpty())
showEmpty(isViewEmpty())
}
errorHandler.dispatch(it)
}, {
Timber.i("Loading school result: No school info found")
view?.run {
showContent(!isViewEmpty())
showEmpty(isViewEmpty())
showContent(!isViewEmpty)
showEmpty(isViewEmpty)
showErrorView(false)
}
}))
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
showContent(false)
} else showError(message, error)
}
}
}

View File

@ -6,14 +6,18 @@ import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersChildVi
interface SchoolView : BaseView, SchoolAndTeachersChildView {
val isViewEmpty: Boolean
fun initView()
fun updateData(data: School)
fun isViewEmpty(): Boolean
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showProgress(show: Boolean)
fun enableSwipe(enable: Boolean)

View File

@ -3,6 +3,8 @@ package io.github.wulkanowy.ui.modules.schoolandteachers.teacher
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
@ -16,7 +18,8 @@ import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragmen
import kotlinx.android.synthetic.main.fragment_teacher.*
import javax.inject.Inject
class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView, SchoolAndTeachersChildView {
class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView,
SchoolAndTeachersChildView {
@Inject
lateinit var presenter: TeacherPresenter
@ -55,6 +58,8 @@ class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView, School
)
}
teacherSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
teacherErrorRetry.setOnClickListener { presenter.onRetry() }
teacherErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
override fun updateData(data: List<TeacherItem>) {
@ -70,11 +75,19 @@ class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView, School
}
override fun showEmpty(show: Boolean) {
teacherEmpty.visibility = if (show) View.VISIBLE else View.GONE
teacherEmpty.visibility = if (show) VISIBLE else GONE
}
override fun showErrorView(show: Boolean) {
teacherError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
teacherErrorMessage.text = message
}
override fun showProgress(show: Boolean) {
teacherProgress.visibility = if (show) View.VISIBLE else View.GONE
teacherProgress.visibility = if (show) VISIBLE else GONE
}
override fun enableSwipe(enable: Boolean) {
@ -82,7 +95,7 @@ class TeacherFragment : BaseFragment(), TeacherView, MainView.TitledView, School
}
override fun showContent(show: Boolean) {
teacherRecycler.visibility = if (show) View.VISIBLE else View.GONE
teacherRecycler.visibility = if (show) VISIBLE else GONE
}
override fun hideRefresh() {

View File

@ -19,10 +19,13 @@ class TeacherPresenter @Inject constructor(
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<TeacherView>(errorHandler, studentRepository, schedulers) {
private lateinit var lastError: Throwable
override fun onAttachView(view: TeacherView) {
super.onAttachView(view)
view.initView()
Timber.i("Teacher view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData()
}
@ -30,6 +33,18 @@ class TeacherPresenter @Inject constructor(
loadData(true)
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
fun onParentViewLoadData(forceRefresh: Boolean) {
loadData(forceRefresh)
}
@ -56,12 +71,23 @@ class TeacherPresenter @Inject constructor(
updateData(it)
showContent(it.isNotEmpty())
showEmpty(it.isEmpty())
showErrorView(false)
}
analytics.logEvent("load_teachers", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.i("Loading teachers result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
})
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
}

View File

@ -27,4 +27,8 @@ interface TeacherView : BaseView, SchoolAndTeachersChildView {
fun showContent(show: Boolean)
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
}

View File

@ -9,6 +9,7 @@ import com.yariksoffice.lingver.Lingver
import dagger.android.support.AndroidSupportInjection
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.AppInfo
import javax.inject.Inject
@ -82,6 +83,10 @@ class SettingsFragment : PreferenceFragmentCompat(),
(activity as? BaseActivity<*>)?.openClearLoginView()
}
override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
override fun onResume() {
super.onResume()
preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)

View File

@ -6,6 +6,8 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
import eu.davidea.flexibleadapter.FlexibleAdapter
@ -74,8 +76,11 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView,
}
timetableSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
timetableErrorRetry.setOnClickListener { presenter.onRetry() }
timetableErrorDetails.setOnClickListener { presenter.onDetailsClick() }
timetablePreviousButton.setOnClickListener { presenter.onPreviousDay() }
timetableNavDate.setOnClickListener {presenter.onPickDate() }
timetableNavDate.setOnClickListener { presenter.onPickDate() }
timetableNextButton.setOnClickListener { presenter.onNextDay() }
timetableNavContainer.setElevationCompat(requireContext().dpToPx(8f))
@ -119,11 +124,19 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView,
}
override fun showEmpty(show: Boolean) {
timetableEmpty.visibility = if (show) View.VISIBLE else View.GONE
timetableEmpty.visibility = if (show) VISIBLE else GONE
}
override fun showErrorView(show: Boolean) {
timetableError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
timetableErrorMessage.text = message
}
override fun showProgress(show: Boolean) {
timetableProgress.visibility = if (show) View.VISIBLE else View.GONE
timetableProgress.visibility = if (show) VISIBLE else GONE
}
override fun enableSwipe(enable: Boolean) {
@ -131,15 +144,15 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView,
}
override fun showContent(show: Boolean) {
timetableRecycler.visibility = if (show) View.VISIBLE else View.GONE
timetableRecycler.visibility = if (show) VISIBLE else GONE
}
override fun showPreButton(show: Boolean) {
timetablePreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
timetablePreviousButton.visibility = if (show) VISIBLE else View.INVISIBLE
}
override fun showNextButton(show: Boolean) {
timetableNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
timetableNextButton.visibility = if (show) VISIBLE else View.INVISIBLE
}
override fun showTimetableDialog(lesson: Timetable) {

View File

@ -37,10 +37,13 @@ class TimetablePresenter @Inject constructor(
lateinit var currentDate: LocalDate
private set
private lateinit var lastError: Throwable
fun onAttachView(view: TimetableView, date: Long?) {
super.onAttachView(view)
view.initView()
Timber.i("Timetable was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData(ofEpochDay(date ?: baseDate.toEpochDay()))
if (currentDate.isHolidays) setBaseDateOnHolidays()
reloadView()
@ -70,6 +73,18 @@ class TimetablePresenter @Inject constructor(
loadData(currentDate, true)
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(currentDate, true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
fun onViewReselected() {
Timber.i("Timetable view is reselected")
view?.also { view ->
@ -135,17 +150,28 @@ class TimetablePresenter @Inject constructor(
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showErrorView(false)
showContent(it.isNotEmpty())
}
analytics.logEvent("load_timetable", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.i("Loading timetable result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it)
})
}
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
private fun reloadView() {
Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}")
view?.apply {
@ -153,6 +179,7 @@ class TimetablePresenter @Inject constructor(
enableSwipe(false)
showContent(false)
showEmpty(false)
showErrorView(false)
clearData()
reloadNavigation()
}

View File

@ -24,6 +24,10 @@ interface TimetableView : BaseView {
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showProgress(show: Boolean)
fun enableSwipe(enable: Boolean)

View File

@ -3,6 +3,9 @@ package io.github.wulkanowy.ui.modules.timetable.completed
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewGroup
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
import eu.davidea.flexibleadapter.FlexibleAdapter
@ -58,6 +61,9 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView.
}
completedLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
completedLessonErrorRetry.setOnClickListener { presenter.onRetry() }
completedLessonErrorDetails.setOnClickListener { presenter.onDetailsClick() }
completedLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() }
completedLessonsNavDate.setOnClickListener { presenter.onPickDate() }
completedLessonsNextButton.setOnClickListener { presenter.onNextDay() }
@ -82,7 +88,15 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView.
}
override fun showEmpty(show: Boolean) {
completedLessonsEmpty.visibility = if (show) View.VISIBLE else View.GONE
completedLessonsEmpty.visibility = if (show) VISIBLE else GONE
}
override fun showErrorView(show: Boolean) {
completedLessonError.visibility = if (show) VISIBLE else GONE
}
override fun setErrorDetails(message: String) {
completedLessonErrorMessage.text = message
}
override fun showFeatureDisabled() {
@ -91,7 +105,7 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView.
}
override fun showProgress(show: Boolean) {
completedLessonsProgress.visibility = if (show) View.VISIBLE else View.GONE
completedLessonsProgress.visibility = if (show) VISIBLE else GONE
}
override fun enableSwipe(enable: Boolean) {
@ -99,15 +113,15 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView.
}
override fun showContent(show: Boolean) {
completedLessonsRecycler.visibility = if (show) View.VISIBLE else View.GONE
completedLessonsRecycler.visibility = if (show) VISIBLE else GONE
}
override fun showPreButton(show: Boolean) {
completedLessonsPreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
completedLessonsPreviousButton.visibility = if (show) VISIBLE else INVISIBLE
}
override fun showNextButton(show: Boolean) {
completedLessonsNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
completedLessonsNextButton.visibility = if (show) VISIBLE else INVISIBLE
}
override fun showCompletedLessonDialog(completedLesson: CompletedLesson) {

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.timetable.completed
import android.annotation.SuppressLint
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
@ -34,17 +35,20 @@ class CompletedLessonsPresenter @Inject constructor(
lateinit var currentDate: LocalDate
private set
private lateinit var lastError: Throwable
fun onAttachView(view: CompletedLessonsView, date: Long?) {
super.onAttachView(view)
Timber.i("Completed lessons is attached")
view.initView()
loadData(ofEpochDay(date ?: baseDate.toEpochDay()))
if (currentDate.isHolidays) setBaseDateOnHolidays()
reloadView()
completedLessonsErrorHandler.showErrorMessage = ::showErrorViewOnError
completedLessonsErrorHandler.onFeatureDisabled = {
this.view?.showFeatureDisabled()
Timber.i("Completed lessons feature disabled by school")
}
loadData(ofEpochDay(date ?: baseDate.toEpochDay()))
if (currentDate.isHolidays) setBaseDateOnHolidays()
reloadView()
}
fun onPreviousDay() {
@ -71,6 +75,18 @@ class CompletedLessonsPresenter @Inject constructor(
loadData(currentDate, true)
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(currentDate, true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
fun onCompletedLessonsItemSelected(item: AbstractFlexibleItem<*>?) {
if (item is CompletedLessonItem) {
Timber.i("Select completed lessons item ${item.completedLesson.id}")
@ -117,17 +133,28 @@ class CompletedLessonsPresenter @Inject constructor(
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
showErrorView(false)
showContent(it.isNotEmpty())
}
analytics.logEvent("load_completed_lessons", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.i("Loading completed lessons result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
completedLessonsErrorHandler.dispatch(it)
})
}
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
private fun reloadView() {
Timber.i("Reload completed lessons view with the date ${currentDate.toFormattedString()}")
view?.apply {
@ -135,11 +162,13 @@ class CompletedLessonsPresenter @Inject constructor(
enableSwipe(false)
showContent(false)
showEmpty(false)
showErrorView(false)
clearData()
reloadNavigation()
}
}
@SuppressLint("DefaultLocale")
private fun reloadNavigation() {
view?.apply {
showPreButton(!currentDate.minusDays(1).isHolidays)

View File

@ -20,6 +20,10 @@ interface CompletedLessonsView : BaseView {
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showFeatureDisabled()
fun showProgress(show: Boolean)

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFF"
android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
</vector>

View File

@ -2,7 +2,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:context=".ui.modules.attendance.AttendanceFragment">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
@ -25,7 +26,8 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/attendanceRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:listitem="@layout/item_attendance" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
@ -53,6 +55,57 @@
android:text="@string/attendance_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/attendanceError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/attendanceErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/attendanceErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/attendanceErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<io.github.wulkanowy.ui.widgets.MaterialLinearLayout

View File

@ -82,4 +82,55 @@
android:text="@string/attendance_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/attendanceSummaryError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/attendanceSummaryErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/attendanceSummaryErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/attendanceSummaryErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@ -2,7 +2,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:context=".ui.modules.exam.ExamFragment">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
@ -25,7 +26,8 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/examRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:listitem="@layout/header_exam" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
@ -53,6 +55,57 @@
android:text="@string/exam_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/examError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/examErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/examErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/examErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<io.github.wulkanowy.ui.widgets.MaterialLinearLayout
@ -60,8 +113,8 @@
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom"
android:orientation="horizontal"
android:gravity="center"
android:orientation="horizontal"
tools:ignore="UnusedAttribute">
<ImageButton
@ -70,14 +123,14 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/all_prev"
android:paddingLeft="12dp"
android:paddingTop="8dp"
android:paddingRight="12dp"
android:paddingBottom="8dp"
android:scaleType="fitStart"
android:tint="?colorPrimary"
app:srcCompat="@drawable/ic_chevron_left"
android:contentDescription="@string/all_prev"/>
app:srcCompat="@drawable/ic_chevron_left" />
<TextView
android:id="@+id/examNavDate"
@ -94,13 +147,13 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/all_next"
android:paddingLeft="12dp"
android:paddingTop="8dp"
android:paddingRight="12dp"
android:paddingBottom="8dp"
android:scaleType="fitEnd"
android:tint="?colorPrimary"
app:srcCompat="@drawable/ic_chevron_right"
android:contentDescription="@string/all_next" />
app:srcCompat="@drawable/ic_chevron_right" />
</io.github.wulkanowy.ui.widgets.MaterialLinearLayout>
</FrameLayout>

View File

@ -17,19 +17,13 @@
tools:ignore="UnusedAttribute"
tools:visibility="visible" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/gradeSwipe"
<androidx.viewpager.widget.ViewPager
android:id="@+id/gradeViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="48dp">
<androidx.viewpager.widget.ViewPager
android:id="@+id/gradeViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"
tools:visibility="visible" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
android:layout_marginTop="48dp"
android:visibility="invisible"
tools:visibility="visible" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/gradeProgress"
@ -41,7 +35,7 @@
tools:visibility="invisible" />
<LinearLayout
android:id="@+id/gradeEmpty"
android:id="@+id/gradeError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
@ -53,16 +47,41 @@
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_main_grade"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/gradeErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="@string/grade_no_items"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/gradeErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/gradeErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -13,7 +13,8 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/gradeDetailsRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:listitem="@layout/item_grade_details" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
@ -49,4 +50,55 @@
android:text="@string/grade_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/gradeDetailsError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/gradeDetailsErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/gradeDetailsErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/gradeDetailsErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@ -138,6 +138,58 @@
android:text="@string/grade_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/gradeStatisticsError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/gradeStatisticsErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/gradeStatisticsErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/gradeStatisticsErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -13,7 +13,8 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/gradeSummaryRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:listitem="@layout/item_grade_summary" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
@ -49,4 +50,55 @@
android:text="@string/grade_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/gradeSummaryError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/gradeSummaryErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/gradeSummaryErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/gradeSummaryErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@ -2,7 +2,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:context=".ui.modules.homework.HomeworkFragment">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
@ -25,7 +26,8 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/homeworkRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:listitem="@layout/item_homework" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
@ -53,6 +55,57 @@
android:text="@string/homework_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/homeworkError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/homeworkErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/homeworkErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/homeworkErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<io.github.wulkanowy.ui.widgets.MaterialLinearLayout
@ -60,8 +113,8 @@
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom"
android:orientation="horizontal"
android:gravity="center"
android:orientation="horizontal"
tools:ignore="UnusedAttribute">
<ImageButton
@ -70,14 +123,14 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/all_prev"
android:paddingLeft="12dp"
android:paddingTop="8dp"
android:paddingRight="12dp"
android:paddingBottom="8dp"
android:scaleType="fitStart"
android:tint="?colorPrimary"
app:srcCompat="@drawable/ic_chevron_left"
android:contentDescription="@string/all_prev"/>
app:srcCompat="@drawable/ic_chevron_left" />
<TextView
android:id="@+id/homeworkNavDate"
@ -94,13 +147,13 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/all_next"
android:paddingLeft="12dp"
android:paddingTop="8dp"
android:paddingRight="12dp"
android:paddingBottom="8dp"
android:scaleType="fitEnd"
android:tint="?colorPrimary"
app:srcCompat="@drawable/ic_chevron_right"
android:contentDescription="@string/all_next" />
app:srcCompat="@drawable/ic_chevron_right" />
</io.github.wulkanowy.ui.widgets.MaterialLinearLayout>
</FrameLayout>

View File

@ -67,4 +67,55 @@
android:text="@string/lucky_number_empty"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/luckyNumberError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/luckyNumberErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/luckyNumberErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/luckyNumberErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -54,30 +54,56 @@
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<LinearLayout
android:id="@+id/messagePreviewError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables">
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_more_messages"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/messagePreviewErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="@string/message_preview_error"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/messagePreviewErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/messagePreviewErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
<me.zhanghai.android.materialprogressbar.MaterialProgressBar

View File

@ -12,7 +12,8 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/messageTabRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:listitem="@layout/item_message" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
@ -47,4 +48,55 @@
android:text="@string/message_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/messageTabError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/messageTabErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/messageTabErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/messageTabErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@ -14,6 +14,18 @@
android:indeterminate="true"
tools:visibility="invisible" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/mobileDevicesSwipe"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/mobileDevicesRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_mobile_device" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
android:id="@+id/mobileDevicesEmpty"
android:layout_width="match_parent"
@ -42,17 +54,56 @@
tools:ignore="contentDescription" />
</LinearLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/mobileDevicesSwipe"
<LinearLayout
android:id="@+id/mobileDevicesError"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/mobileDevicesRecycler"
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/mobileDevicesErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_mobile_device" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/mobileDevicesErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/mobileDevicesErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/mobileDeviceAddButton"

View File

@ -48,4 +48,55 @@
android:text="@string/note_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/noteError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/noteErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/noteErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/noteErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@ -232,4 +232,55 @@
android:text="@string/school_no_info"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/schoolError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/schoolErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/schoolErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/schoolErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@ -49,4 +49,55 @@
android:text="@string/teacher_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/teacherError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/teacherErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/teacherErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/teacherErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@ -2,7 +2,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:context=".ui.modules.timetable.TimetableFragment">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
@ -25,7 +26,8 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/timetableRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:listitem="@layout/item_timetable" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
@ -53,6 +55,57 @@
android:text="@string/timetable_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/timetableError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/timetableErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/timetableErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/timetableErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<io.github.wulkanowy.ui.widgets.MaterialLinearLayout

View File

@ -57,6 +57,57 @@
android:text="@string/completed_lessons_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/completedLessonError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/completedLessonErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/completedLessonErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/completedLessonErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<io.github.wulkanowy.ui.widgets.MaterialLinearLayout

View File

@ -32,7 +32,7 @@
android:layout_toRightOf="@id/homeworkItemSubject"
android:gravity="end"
android:textSize="13sp"
tools:text="@tools:sample/lorem" />
tools:text="@tools:sample/full_names" />
<TextView
android:id="@+id/homeworkItemContent"
@ -46,5 +46,5 @@
android:layout_marginBottom="15dp"
android:lineSpacingMultiplier="1.2"
android:textSize="14sp"
tools:text="@tools:sample/lorem/random" />
tools:text="@tools:sample/lorem" />
</RelativeLayout>

View File

@ -270,6 +270,7 @@
<!--Generic-->
<string name="all_content">Treść</string>
<string name="all_retry">Ponów</string>
<string name="all_description">Opis</string>
<string name="all_no_description">Brak opisu</string>
<string name="all_teacher">Nauczyciel</string>

View File

@ -256,6 +256,7 @@
<!--Generic-->
<string name="all_content">Content</string>
<string name="all_retry">Retry</string>
<string name="all_description">Description</string>
<string name="all_no_description">No description</string>
<string name="all_teacher">Teacher</string>