1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2025-02-22 09:54:45 +01:00

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -57,6 +57,8 @@ class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainVie
} }
attendanceSummarySwipe.setOnRefreshListener(presenter::onSwipeRefresh) attendanceSummarySwipe.setOnRefreshListener(presenter::onSwipeRefresh)
attendanceSummaryErrorRetry.setOnClickListener { presenter.onRetry() }
attendanceSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() }
subjectsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf()) subjectsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf())
subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject) subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject)
@ -93,6 +95,14 @@ class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainVie
attendanceSummaryEmpty.visibility = if (show) VISIBLE else GONE 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) { override fun showProgress(show: Boolean) {
attendanceSummaryProgress.visibility = if (show) VISIBLE else GONE attendanceSummaryProgress.visibility = if (show) VISIBLE else GONE
} }

View File

@ -33,10 +33,13 @@ class AttendanceSummaryPresenter @Inject constructor(
var currentSubjectId = -1 var currentSubjectId = -1
private set private set
private lateinit var lastError: Throwable
fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) { fun onAttachView(view: AttendanceSummaryView, subjectId: Int?) {
super.onAttachView(view) super.onAttachView(view)
view.initView() view.initView()
Timber.i("Attendance summary view was initialized with subject id ${subjectId ?: -1}") Timber.i("Attendance summary view was initialized with subject id ${subjectId ?: -1}")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData(subjectId ?: -1) loadData(subjectId ?: -1)
loadSubjects() loadSubjects()
} }
@ -46,6 +49,18 @@ class AttendanceSummaryPresenter @Inject constructor(
loadData(currentSubjectId, true) loadData(currentSubjectId, true)
} }
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(currentSubjectId, true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
fun onSubjectSelected(name: String?) { fun onSubjectSelected(name: String?) {
Timber.i("Select attendance summary subject $name") Timber.i("Select attendance summary subject $name")
view?.run { view?.run {
@ -53,6 +68,7 @@ class AttendanceSummaryPresenter @Inject constructor(
showProgress(true) showProgress(true)
enableSwipe(false) enableSwipe(false)
showEmpty(false) showEmpty(false)
showErrorView(false)
clearView() clearView()
} }
(subjects.singleOrNull { it.name == name }?.realId ?: -1).let { (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) 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") Timber.i("Loading attendance summary result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }
errorHandler.dispatch(it) 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() { private fun loadSubjects() {
Timber.i("Loading attendance summary subjects started") Timber.i("Loading attendance summary subjects started")
disposable.add(studentRepository.getCurrentStudent() disposable.add(studentRepository.getCurrentStudent()

View File

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

View File

@ -61,6 +61,9 @@ class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView.
} }
examSwipe.setOnRefreshListener(presenter::onSwipeRefresh) examSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
examErrorRetry.setOnClickListener { presenter.onRetry() }
examErrorDetails.setOnClickListener { presenter.onDetailsClick() }
examPreviousButton.setOnClickListener { presenter.onPreviousWeek() } examPreviousButton.setOnClickListener { presenter.onPreviousWeek() }
examNextButton.setOnClickListener { presenter.onNextWeek() } examNextButton.setOnClickListener { presenter.onNextWeek() }
@ -95,6 +98,14 @@ class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView.
examEmpty.visibility = if (show) VISIBLE else GONE 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) { override fun showProgress(show: Boolean) {
examProgress.visibility = if (show) VISIBLE else GONE examProgress.visibility = if (show) VISIBLE else GONE
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -12,16 +12,14 @@ interface GradeView : BaseView {
fun showProgress(show: Boolean) 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 showSemesterSwitch(show: Boolean)
fun showSemesterDialog(selectedIndex: Int) fun showSemesterDialog(selectedIndex: Int)
fun enableSwipe(enable: Boolean)
fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean)
fun notifyChildParentReselected(index: Int) fun notifyChildParentReselected(index: Int)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -65,6 +65,10 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl
presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getLong(MESSAGE_ID_KEY) ?: 0L) presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getLong(MESSAGE_ID_KEY) ?: 0L)
} }
override fun initView() {
messagePreviewErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.action_menu_message_preview, menu) inflater.inflate(R.menu.action_menu_message_preview, menu)
menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply) menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply)
@ -126,8 +130,16 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl
menuDeleteButton?.setTitle(R.string.message_move_to_bin) menuDeleteButton?.setTitle(R.string.message_move_to_bin)
} }
override fun showMessageError() { override fun showErrorView(show: Boolean) {
messagePreviewError.visibility = VISIBLE 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?) { override fun openMessageReply(message: Message?) {

View File

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

View File

@ -9,6 +9,8 @@ interface MessagePreviewView : BaseView {
val deleteMessageSuccessString: String val deleteMessageSuccessString: String
fun initView()
fun setSubject(subject: String) fun setSubject(subject: String)
fun setRecipient(recipient: String) fun setRecipient(recipient: String)
@ -23,19 +25,23 @@ interface MessagePreviewView : BaseView {
fun showContent(show: Boolean) 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 showOptions(show: Boolean)
fun setDeletedOptionsLabels() fun setDeletedOptionsLabels()
fun setNotDeletedOptionsLabels() fun setNotDeletedOptionsLabels()
fun showMessageError()
fun openMessageReply(message: Message?) fun openMessageReply(message: Message?)
fun openMessageForward(message: Message?) fun openMessageForward(message: Message?)
fun popView() fun popView()
fun notifyParentMessageDeleted(message: Message)
} }

View File

@ -72,6 +72,8 @@ class MessageTabFragment : BaseFragment(), MessageTabView {
) )
} }
messageTabSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } messageTabSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
messageTabErrorRetry.setOnClickListener { presenter.onRetry() }
messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() }
} }
override fun updateData(data: List<MessageItem>) { override fun updateData(data: List<MessageItem>) {
@ -102,6 +104,14 @@ class MessageTabFragment : BaseFragment(), MessageTabView {
messageTabEmpty.visibility = if (show) VISIBLE else INVISIBLE 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) { override fun showRefresh(show: Boolean) {
messageTabSwipe.isRefreshing = show messageTabSwipe.isRefreshing = show
} }

View File

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

View File

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

View File

@ -61,6 +61,8 @@ class MobileDeviceFragment : BaseFragment(), MobileDeviceView, MainView.TitledVi
onDeviceUnregisterListener = presenter::onUnregisterDevice onDeviceUnregisterListener = presenter::onUnregisterDevice
} }
mobileDevicesSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } mobileDevicesSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
mobileDevicesErrorRetry.setOnClickListener { presenter.onRetry() }
mobileDevicesErrorDetails.setOnClickListener { presenter.onDetailsClick() }
mobileDeviceAddButton.setOnClickListener { presenter.onRegisterDevice() } mobileDeviceAddButton.setOnClickListener { presenter.onRegisterDevice() }
} }
@ -105,6 +107,14 @@ class MobileDeviceFragment : BaseFragment(), MobileDeviceView, MainView.TitledVi
mobileDevicesEmpty.visibility = if (show) VISIBLE else GONE 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) { override fun enableSwipe(enable: Boolean) {
mobileDevicesSwipe.isEnabled = enable mobileDevicesSwipe.isEnabled = enable
} }

View File

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

View File

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

View File

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

View File

@ -60,6 +60,8 @@ class NoteFragment : BaseFragment(), NoteView, MainView.TitledView {
) )
} }
noteSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } noteSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
noteErrorRetry.setOnClickListener { presenter.onRetry() }
noteErrorDetails.setOnClickListener { presenter.onDetailsClick() }
} }
override fun showNoteDialog(note: Note) { override fun showNoteDialog(note: Note) {
@ -82,6 +84,14 @@ class NoteFragment : BaseFragment(), NoteView, MainView.TitledView {
noteEmpty.visibility = if (show) VISIBLE else GONE 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) { override fun showProgress(show: Boolean) {
noteProgress.visibility = if (show) VISIBLE else GONE noteProgress.visibility = if (show) VISIBLE else GONE
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,6 +6,8 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
@ -74,8 +76,11 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView,
} }
timetableSwipe.setOnRefreshListener(presenter::onSwipeRefresh) timetableSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
timetableErrorRetry.setOnClickListener { presenter.onRetry() }
timetableErrorDetails.setOnClickListener { presenter.onDetailsClick() }
timetablePreviousButton.setOnClickListener { presenter.onPreviousDay() } timetablePreviousButton.setOnClickListener { presenter.onPreviousDay() }
timetableNavDate.setOnClickListener {presenter.onPickDate() } timetableNavDate.setOnClickListener { presenter.onPickDate() }
timetableNextButton.setOnClickListener { presenter.onNextDay() } timetableNextButton.setOnClickListener { presenter.onNextDay() }
timetableNavContainer.setElevationCompat(requireContext().dpToPx(8f)) timetableNavContainer.setElevationCompat(requireContext().dpToPx(8f))
@ -119,11 +124,19 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView,
} }
override fun showEmpty(show: Boolean) { 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) { 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) { override fun enableSwipe(enable: Boolean) {
@ -131,15 +144,15 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView,
} }
override fun showContent(show: Boolean) { 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) { 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) { 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) { override fun showTimetableDialog(lesson: Timetable) {

View File

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

View File

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

View File

@ -3,6 +3,9 @@ package io.github.wulkanowy.ui.modules.timetable.completed
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
@ -58,6 +61,9 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView.
} }
completedLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) completedLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
completedLessonErrorRetry.setOnClickListener { presenter.onRetry() }
completedLessonErrorDetails.setOnClickListener { presenter.onDetailsClick() }
completedLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() } completedLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() }
completedLessonsNavDate.setOnClickListener { presenter.onPickDate() } completedLessonsNavDate.setOnClickListener { presenter.onPickDate() }
completedLessonsNextButton.setOnClickListener { presenter.onNextDay() } completedLessonsNextButton.setOnClickListener { presenter.onNextDay() }
@ -82,7 +88,15 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView.
} }
override fun showEmpty(show: Boolean) { 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() { override fun showFeatureDisabled() {
@ -91,7 +105,7 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView.
} }
override fun showProgress(show: Boolean) { 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) { override fun enableSwipe(enable: Boolean) {
@ -99,15 +113,15 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView.
} }
override fun showContent(show: Boolean) { 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) { 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) { 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) { override fun showCompletedLessonDialog(completedLesson: CompletedLesson) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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