From 54ab4081351a02a9b590f6f94cdf3b3c87470c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 24 Nov 2019 17:05:09 +0100 Subject: [PATCH] Add error view showing on first loading in fragment view (#590) --- .../github/wulkanowy/ui/base/BaseActivity.kt | 11 +-- .../wulkanowy/ui/base/BaseDialogFragment.kt | 27 ++++++++ .../github/wulkanowy/ui/base/BaseFragment.kt | 8 ++- .../io/github/wulkanowy/ui/base/BaseView.kt | 2 + .../ui/modules/account/AccountDialog.kt | 14 +--- .../modules/attendance/AttendanceFragment.kt | 24 +++++-- .../modules/attendance/AttendancePresenter.kt | 31 ++++++++- .../ui/modules/attendance/AttendanceView.kt | 4 ++ .../summary/AttendanceSummaryFragment.kt | 10 +++ .../summary/AttendanceSummaryPresenter.kt | 28 +++++++- .../summary/AttendanceSummaryView.kt | 4 ++ .../wulkanowy/ui/modules/exam/ExamFragment.kt | 11 +++ .../ui/modules/exam/ExamPresenter.kt | 29 +++++++- .../wulkanowy/ui/modules/exam/ExamView.kt | 4 ++ .../ui/modules/grade/GradeFragment.kt | 16 ++--- .../ui/modules/grade/GradePresenter.kt | 37 ++++++---- .../wulkanowy/ui/modules/grade/GradeView.kt | 6 +- .../grade/details/GradeDetailsFragment.kt | 11 +++ .../grade/details/GradeDetailsPresenter.kt | 31 ++++++++- .../modules/grade/details/GradeDetailsView.kt | 4 ++ .../statistics/GradeStatisticsFragment.kt | 14 +++- .../statistics/GradeStatisticsPresenter.kt | 33 ++++++++- .../grade/statistics/GradeStatisticsView.kt | 4 ++ .../grade/summary/GradeSummaryFragment.kt | 10 +++ .../grade/summary/GradeSummaryPresenter.kt | 28 +++++++- .../modules/grade/summary/GradeSummaryView.kt | 4 ++ .../ui/modules/homework/HomeworkFragment.kt | 29 +++++--- .../ui/modules/homework/HomeworkPresenter.kt | 30 ++++++++- .../ui/modules/homework/HomeworkView.kt | 8 ++- .../luckynumber/LuckyNumberFragment.kt | 25 +++++-- .../luckynumber/LuckyNumberPresenter.kt | 29 +++++++- .../ui/modules/luckynumber/LuckyNumberView.kt | 8 ++- .../message/preview/MessagePreviewFragment.kt | 16 ++++- .../preview/MessagePreviewPresenter.kt | 34 +++++++++- .../message/preview/MessagePreviewView.kt | 14 ++-- .../modules/message/tab/MessageTabFragment.kt | 10 +++ .../message/tab/MessageTabPresenter.kt | 28 +++++++- .../ui/modules/message/tab/MessageTabView.kt | 4 ++ .../mobiledevice/MobileDeviceFragment.kt | 10 +++ .../mobiledevice/MobileDevicePresenter.kt | 28 +++++++- .../modules/mobiledevice/MobileDeviceView.kt | 4 ++ .../token/MobileDeviceTokenDialog.kt | 34 +++------- .../wulkanowy/ui/modules/note/NoteFragment.kt | 10 +++ .../ui/modules/note/NotePresenter.kt | 28 +++++++- .../wulkanowy/ui/modules/note/NoteView.kt | 4 ++ .../school/SchoolFragment.kt | 27 +++++--- .../school/SchoolPresenter.kt | 39 +++++++++-- .../schoolandteachers/school/SchoolView.kt | 8 ++- .../teacher/TeacherFragment.kt | 21 ++++-- .../teacher/TeacherPresenter.kt | 28 +++++++- .../schoolandteachers/teacher/TeacherView.kt | 4 ++ .../ui/modules/settings/SettingsFragment.kt | 5 ++ .../ui/modules/timetable/TimetableFragment.kt | 25 +++++-- .../modules/timetable/TimetablePresenter.kt | 29 +++++++- .../ui/modules/timetable/TimetableView.kt | 4 ++ .../completed/CompletedLessonsFragment.kt | 24 +++++-- .../completed/CompletedLessonsPresenter.kt | 37 ++++++++-- .../completed/CompletedLessonsView.kt | 4 ++ app/src/main/res/drawable/ic_error.xml | 9 +++ .../main/res/layout/fragment_attendance.xml | 57 +++++++++++++++- .../layout/fragment_attendance_summary.xml | 51 ++++++++++++++ app/src/main/res/layout/fragment_exam.xml | 67 +++++++++++++++++-- app/src/main/res/layout/fragment_grade.xml | 47 +++++++++---- .../res/layout/fragment_grade_details.xml | 54 ++++++++++++++- .../res/layout/fragment_grade_statistics.xml | 52 ++++++++++++++ .../res/layout/fragment_grade_summary.xml | 54 ++++++++++++++- app/src/main/res/layout/fragment_homework.xml | 67 +++++++++++++++++-- .../main/res/layout/fragment_lucky_number.xml | 51 ++++++++++++++ .../res/layout/fragment_message_preview.xml | 34 ++++++++-- .../main/res/layout/fragment_message_tab.xml | 54 ++++++++++++++- .../res/layout/fragment_mobile_device.xml | 67 ++++++++++++++++--- app/src/main/res/layout/fragment_note.xml | 51 ++++++++++++++ app/src/main/res/layout/fragment_school.xml | 51 ++++++++++++++ app/src/main/res/layout/fragment_teacher.xml | 51 ++++++++++++++ .../main/res/layout/fragment_timetable.xml | 57 +++++++++++++++- .../layout/fragment_timetable_completed.xml | 51 ++++++++++++++ app/src/main/res/layout/item_homework.xml | 4 +- app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 79 files changed, 1736 insertions(+), 208 deletions(-) create mode 100644 app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt create mode 100644 app/src/main/res/drawable/ic_error.xml diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt index f0e51e314..ab67d8acb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt @@ -22,7 +22,8 @@ import io.github.wulkanowy.utils.FragmentLifecycleLogger import io.github.wulkanowy.utils.getThemeAttrColor import javax.inject.Inject -abstract class BaseActivity> : AppCompatActivity(), BaseView, HasAndroidInjector { +abstract class BaseActivity> : AppCompatActivity(), BaseView, + HasAndroidInjector { @Inject lateinit var androidInjector: DispatchingAndroidInjector @@ -53,13 +54,15 @@ abstract class BaseActivity> : 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() diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt new file mode 100644 index 000000000..fdc463714 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt @@ -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()) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt index 4b2ad053d..2f5878d0d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt @@ -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() diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt index 7681263b0..0f4df92cd 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt @@ -9,4 +9,6 @@ interface BaseView { fun showExpiredDialog() fun openClearLoginView() + + fun showErrorDetailsDialog(error: Throwable) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt index f23a1eb52..cfff31c98 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt @@ -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() } } - diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt index b57bf7d06..bc7c1cacd 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt @@ -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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt index 0e1f50403..1490ee6e3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt index 20b3cd9e8..a6d0d4ba9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt index 4fe490d5e..fc3601847 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt @@ -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 } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt index cd64a11ca..3ce85d815 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt @@ -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() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt index 50f03e20a..b86f6590d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt @@ -18,6 +18,10 @@ interface AttendanceSummaryView : BaseView { fun showEmpty(show: Boolean) + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + fun updateDataSet(data: List, header: AttendanceSummaryScrollableHeader) fun updateSubjects(data: ArrayList) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt index c762fa154..b880f4650 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt @@ -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 } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt index 93109922c..35cf5b945 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt @@ -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>): List { 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() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt index 888cb05e5..5f4a74306 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt index 68ae57eca..884b9f8c4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt @@ -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), diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt index 2ee69480c..d8c202381 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt @@ -25,14 +25,14 @@ class GradePresenter @Inject constructor( private val loadedSemesterId = mutableMapOf() + 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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt index a37e6d67f..bbdbc32ea 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt index 8993760d6..b3dd35878 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt @@ -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 } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt index 96b556864..647047c69 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -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>, averages: Map): List { val isGradeExpandable = preferencesRepository.isGradeExpandable val gradeColorTheme = preferencesRepository.gradeColorTheme diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt index fb806351c..dad4ac6eb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt index 94b5f474b..5bcf167e5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt @@ -111,9 +111,11 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G setOnItemSelectedListener { 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) { @@ -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 } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt index 1e0537016..9795c130c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt @@ -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) + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt index d6e66fac3..a214744c3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt index 7699a6411..05fde5227 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt @@ -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, 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 } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt index 3f8fd0edb..1aaa9d097 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt @@ -25,9 +25,12 @@ class GradeSummaryPresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(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() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt index 9e9c6e58f..cf3184873 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt index 8195dd20b..3f8f1359a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt @@ -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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt index 6829031c1..7e1da314b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt @@ -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>): List { 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() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt index 977a5b73f..1d241df46 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt @@ -5,6 +5,8 @@ import io.github.wulkanowy.ui.base.BaseView interface HomeworkView : BaseView { + val isViewEmpty: Boolean + fun initView() fun updateData(data: List) @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt index 00204a876..12bf1a132 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt @@ -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() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt index ee7260e6f..63ced4b89 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt @@ -19,6 +19,8 @@ class LuckyNumberPresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(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) + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt index 9ead2b1f7..a680c83eb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt @@ -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 } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt index 4f881e8ff..22231e429 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -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?) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt index a230c0b3a..c32bd6119 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -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 diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt index 373dee11e..d57766273 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt @@ -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) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt index 326cd6a75..d6065983d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt @@ -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) { @@ -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 } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index af5762939..1123e7e41 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt index 967863de4..e5705c739 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt index 1d9104a81..d47574f60 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt @@ -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 } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt index 4ff71dc14..e95d2ce0e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt @@ -20,10 +20,13 @@ class MobileDevicePresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(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() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt index 58804e245..869b59bb1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt @@ -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() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt index a1812ece7..8b81156b1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt @@ -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()?.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()?.setPrimaryClip(clip) - Toast.makeText(context, R.string.all_copied, Toast.LENGTH_LONG).show() - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt index 6117ae244..e5335e459 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt @@ -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 } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt index df98ab889..7acf37a4c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt @@ -21,10 +21,13 @@ class NotePresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(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}") diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt index 9a2c12ebf..a9c9f4f2f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolFragment.kt index 398a58a60..5a7c4cade 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolFragment.kt @@ -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() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt index c1ea4fd04..d3299d90d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt @@ -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) + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolView.kt index af0c7bd2c..c42c2f91b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolView.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt index 842e50fe1..b6bb9c101 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherFragment.kt @@ -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) { @@ -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() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt index a6bdec591..6aa4871d2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt @@ -19,10 +19,13 @@ class TeacherPresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(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) + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt index 2cc1139cc..b16be8ff9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherView.kt @@ -27,4 +27,8 @@ interface TeacherView : BaseView, SchoolAndTeachersChildView { fun showContent(show: Boolean) fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt index 172749809..03a89d274 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt index e1d7db8d6..ef6057dfa 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt @@ -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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt index be80fbd4a..4c30ecb7f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -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() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt index 749f11e0b..f730a2712 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt index 67d358247..60c16b2dc 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt @@ -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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt index 3a439e1ab..e2b569508 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt index e1e167cda..a6a327e2d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt @@ -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) diff --git a/app/src/main/res/drawable/ic_error.xml b/app/src/main/res/drawable/ic_error.xml new file mode 100644 index 000000000..bb4cb3025 --- /dev/null +++ b/app/src/main/res/drawable/ic_error.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_attendance.xml b/app/src/main/res/layout/fragment_attendance.xml index b88066e53..daf7e67f9 100644 --- a/app/src/main/res/layout/fragment_attendance.xml +++ b/app/src/main/res/layout/fragment_attendance.xml @@ -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"> + android:layout_height="match_parent" + tools:listitem="@layout/item_attendance" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_exam.xml b/app/src/main/res/layout/fragment_exam.xml index adfbee32f..1e1b0c99e 100644 --- a/app/src/main/res/layout/fragment_exam.xml +++ b/app/src/main/res/layout/fragment_exam.xml @@ -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"> + android:layout_height="match_parent" + tools:listitem="@layout/header_exam" /> + + + + + + + + + + + + + + + app:srcCompat="@drawable/ic_chevron_left" /> + app:srcCompat="@drawable/ic_chevron_right" /> diff --git a/app/src/main/res/layout/fragment_grade.xml b/app/src/main/res/layout/fragment_grade.xml index b9b86f7db..0bc864b2d 100644 --- a/app/src/main/res/layout/fragment_grade.xml +++ b/app/src/main/res/layout/fragment_grade.xml @@ -17,19 +17,13 @@ tools:ignore="UnusedAttribute" tools:visibility="visible" /> - - - - + android:layout_marginTop="48dp" + android:visibility="invisible" + tools:visibility="visible" /> + + + + + + + diff --git a/app/src/main/res/layout/fragment_grade_details.xml b/app/src/main/res/layout/fragment_grade_details.xml index 125d43a95..a1faefe7a 100644 --- a/app/src/main/res/layout/fragment_grade_details.xml +++ b/app/src/main/res/layout/fragment_grade_details.xml @@ -13,7 +13,8 @@ + android:layout_height="match_parent" + tools:listitem="@layout/item_grade_details" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_grade_statistics.xml b/app/src/main/res/layout/fragment_grade_statistics.xml index 6e88f8a89..599af8d71 100644 --- a/app/src/main/res/layout/fragment_grade_statistics.xml +++ b/app/src/main/res/layout/fragment_grade_statistics.xml @@ -138,6 +138,58 @@ android:text="@string/grade_no_items" android:textSize="20sp" /> + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_grade_summary.xml b/app/src/main/res/layout/fragment_grade_summary.xml index 2d9e7dc6f..3f21e61c7 100644 --- a/app/src/main/res/layout/fragment_grade_summary.xml +++ b/app/src/main/res/layout/fragment_grade_summary.xml @@ -13,7 +13,8 @@ + android:layout_height="match_parent" + tools:listitem="@layout/item_grade_summary" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_homework.xml b/app/src/main/res/layout/fragment_homework.xml index 799d3f42c..0fcde9c07 100644 --- a/app/src/main/res/layout/fragment_homework.xml +++ b/app/src/main/res/layout/fragment_homework.xml @@ -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"> + android:layout_height="match_parent" + tools:listitem="@layout/item_homework" /> + + + + + + + + + + + + + + + app:srcCompat="@drawable/ic_chevron_left" /> + app:srcCompat="@drawable/ic_chevron_right" /> diff --git a/app/src/main/res/layout/fragment_lucky_number.xml b/app/src/main/res/layout/fragment_lucky_number.xml index 5d3afda26..a6b9b8221 100644 --- a/app/src/main/res/layout/fragment_lucky_number.xml +++ b/app/src/main/res/layout/fragment_lucky_number.xml @@ -67,4 +67,55 @@ android:text="@string/lucky_number_empty" android:textSize="20sp" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_message_preview.xml b/app/src/main/res/layout/fragment_message_preview.xml index eedba669d..4a6f7e9a7 100644 --- a/app/src/main/res/layout/fragment_message_preview.xml +++ b/app/src/main/res/layout/fragment_message_preview.xml @@ -54,30 +54,56 @@ + + tools:ignore="UseCompoundDrawables" + tools:visibility="invisible"> + + + + + + + + android:layout_height="match_parent" + tools:listitem="@layout/item_message" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_mobile_device.xml b/app/src/main/res/layout/fragment_mobile_device.xml index b78dc067b..cf11c2831 100644 --- a/app/src/main/res/layout/fragment_mobile_device.xml +++ b/app/src/main/res/layout/fragment_mobile_device.xml @@ -14,6 +14,18 @@ android:indeterminate="true" tools:visibility="invisible" /> + + + + + - + android:layout_height="match_parent" + android:gravity="center" + android:orientation="vertical" + android:visibility="invisible" + tools:ignore="UseCompoundDrawables" + tools:visibility="invisible"> - + + + + - + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:gravity="center" + android:orientation="horizontal"> + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_school.xml b/app/src/main/res/layout/fragment_school.xml index df2df1bda..a7e9a213b 100644 --- a/app/src/main/res/layout/fragment_school.xml +++ b/app/src/main/res/layout/fragment_school.xml @@ -232,4 +232,55 @@ android:text="@string/school_no_info" android:textSize="20sp" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_teacher.xml b/app/src/main/res/layout/fragment_teacher.xml index 01cf87260..484bf0651 100644 --- a/app/src/main/res/layout/fragment_teacher.xml +++ b/app/src/main/res/layout/fragment_teacher.xml @@ -49,4 +49,55 @@ android:text="@string/teacher_no_items" android:textSize="20sp" /> + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_timetable.xml b/app/src/main/res/layout/fragment_timetable.xml index 27c5c4c1c..98ccabbff 100644 --- a/app/src/main/res/layout/fragment_timetable.xml +++ b/app/src/main/res/layout/fragment_timetable.xml @@ -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"> + android:layout_height="match_parent" + tools:listitem="@layout/item_timetable" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tools:text="@tools:sample/full_names" /> + tools:text="@tools:sample/lorem" /> diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index edbad4ac0..30168a5ad 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -270,6 +270,7 @@ Treść + Ponów Opis Brak opisu Nauczyciel diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7140cb8fc..9673355e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -256,6 +256,7 @@ Content + Retry Description No description Teacher