From e557021ad9793954563a4cf3c844f3f8227db060 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 29 Aug 2021 19:37:01 +0000 Subject: [PATCH 01/15] Bump huawei-publish-gradle-plugin from 1.2.4 to 1.3.0 (#1458) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index afe0285c8..840d90f42 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ buildscript { classpath 'com.huawei.agconnect:agcp:1.6.0.300' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1' classpath "com.github.triplet.gradle:play-publisher:2.8.0" - classpath "ru.cian:huawei-publish-gradle-plugin:1.2.4" + classpath "ru.cian:huawei-publish-gradle-plugin:1.3.0" classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3" classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0" classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries" From d139c22782660abb9452d1a6f6b7d51a4ba797d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 29 Aug 2021 19:37:18 +0000 Subject: [PATCH 02/15] Bump hianalytics from 6.1.1.300 to 6.2.0.300 (#1457) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 61e47534c..7b2b2e695 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -218,7 +218,7 @@ dependencies { playImplementation 'com.google.android.play:core:1.10.0' playImplementation 'com.google.android.play:core-ktx:1.8.1' - hmsImplementation 'com.huawei.hms:hianalytics:6.1.1.300' + hmsImplementation 'com.huawei.hms:hianalytics:6.2.0.300' hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.0.300' releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" From d87283eb3151780fb85adc6f3458ec4d12f12b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Mon, 30 Aug 2021 00:20:13 +0200 Subject: [PATCH 03/15] Fix opening twitter link from about on android 11 (#1460) --- .../java/io/github/wulkanowy/utils/ContextExtension.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt index cb31389e0..68d3afe83 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt @@ -1,6 +1,7 @@ package io.github.wulkanowy.utils import android.annotation.SuppressLint +import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent import android.graphics.Bitmap @@ -58,8 +59,11 @@ fun Context.getCompatBitmap(@DrawableRes drawableRes: Int, @ColorRes colorRes: I fun Context.openInternetBrowser(uri: String, onActivityNotFound: (uri: String) -> Unit = {}) { Intent.parseUri(uri, 0).let { - if (it.resolveActivity(packageManager) != null) startActivity(it) - else onActivityNotFound(uri) + try { + startActivity(it) + } catch (e: ActivityNotFoundException) { + onActivityNotFound(uri) + } } } From c3adb9b6d6f6b972656262f63a52efe1e9db80e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Fri, 3 Sep 2021 22:54:29 +0200 Subject: [PATCH 04/15] Bump agp to 7.0.2 (#1469) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 840d90f42..7fb02eb9d 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ buildscript { } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.android.tools.build:gradle:7.0.1' + classpath 'com.android.tools.build:gradle:7.0.2' classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.10' classpath 'com.huawei.agconnect:agcp:1.6.0.300' From 8d7b611c4483326ee7e2cb999f811b80e0f3ba83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 4 Sep 2021 15:54:05 +0200 Subject: [PATCH 05/15] Fix showing error view in timetable (#1472) --- app/build.gradle | 2 +- .../github/wulkanowy/ui/modules/timetable/TimetableFragment.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7b2b2e695..4513be16d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -157,7 +157,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:1.2.0" + implementation "io.github.wulkanowy:sdk:7c399ffaea" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt index a65d69211..a79661901 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt @@ -49,7 +49,7 @@ class TimetableFragment : BaseFragment(R.layout.fragme override val titleStringId get() = R.string.timetable_title - override val isViewEmpty get() = timetableAdapter.itemCount > 0 + override val isViewEmpty get() = timetableAdapter.itemCount == 0 override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize From 45d1727dbeccaa66f08bd1822e665737c9d4cc25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 4 Sep 2021 15:54:37 +0200 Subject: [PATCH 06/15] Add missing school announcement dialog (#1470) --- .../SchoolAnnouncementAdapter.kt | 10 +- .../SchoolAnnouncementDialog.kt | 54 +++++++ .../SchoolAnnouncementFragment.kt | 9 +- .../SchoolAnnouncementPresenter.kt | 5 + .../SchoolAnnouncementView.kt | 2 + .../ui/modules/timetable/TimetableFragment.kt | 6 +- .../res/layout/dialog_school_announcement.xml | 148 ++++++++++++++++++ 7 files changed, 225 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt create mode 100644 app/src/main/res/layout/dialog_school_announcement.xml diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt index e7c202676..62f6251ec 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt @@ -2,7 +2,7 @@ package io.github.wulkanowy.ui.modules.schoolannouncement import android.view.LayoutInflater import android.view.ViewGroup -import androidx.core.text.HtmlCompat +import androidx.core.text.parseAsHtml import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.data.db.entities.SchoolAnnouncement import io.github.wulkanowy.databinding.ItemSchoolAnnouncementBinding @@ -14,6 +14,8 @@ class SchoolAnnouncementAdapter @Inject constructor() : var items = emptyList() + var onItemClickListener: (SchoolAnnouncement) -> Unit = {} + override fun getItemCount() = items.size override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( @@ -26,9 +28,9 @@ class SchoolAnnouncementAdapter @Inject constructor() : with(holder.binding) { schoolAnnouncementItemDate.text = item.date.toFormattedString() schoolAnnouncementItemType.text = item.subject - schoolAnnouncementItemContent.text = HtmlCompat.fromHtml( - item.content, HtmlCompat.FROM_HTML_MODE_COMPACT - ) + schoolAnnouncementItemContent.text = item.content.parseAsHtml() + + root.setOnClickListener { onItemClickListener(item) } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt new file mode 100644 index 000000000..ed4b0ac98 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.ui.modules.schoolannouncement + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.text.parseAsHtml +import androidx.fragment.app.DialogFragment +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.databinding.DialogSchoolAnnouncementBinding +import io.github.wulkanowy.utils.lifecycleAwareVariable +import io.github.wulkanowy.utils.toFormattedString + +class SchoolAnnouncementDialog : DialogFragment() { + + private var binding: DialogSchoolAnnouncementBinding by lifecycleAwareVariable() + + private lateinit var announcement: SchoolAnnouncement + + companion object { + + private const val ARGUMENT_KEY = "item" + + fun newInstance(exam: SchoolAnnouncement) = SchoolAnnouncementDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + announcement = getSerializable(ARGUMENT_KEY) as SchoolAnnouncement + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogSchoolAnnouncementBinding.inflate(inflater).apply { binding = this }.root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(binding) { + announcementDialogSubjectValue.text = announcement.subject + announcementDialogDateValue.text = announcement.date.toFormattedString() + announcementDialogDescriptionValue.text = announcement.content.parseAsHtml() + + announcementDialogClose.setOnClickListener { dismiss() } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementFragment.kt index a5a53aefc..baf2824ba 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementFragment.kt @@ -8,6 +8,7 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.SchoolAnnouncement import io.github.wulkanowy.databinding.FragmentSchoolAnnouncementBinding import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.widgets.DividerItemDecoration import io.github.wulkanowy.utils.getThemeAttrColor @@ -43,7 +44,9 @@ class SchoolAnnouncementFragment : override fun initView() { with(binding.directorInformationRecycler) { layoutManager = LinearLayoutManager(context) - adapter = schoolAnnouncementAdapter + adapter = schoolAnnouncementAdapter.apply { + onItemClickListener = presenter::onItemClickListener + } addItemDecoration(DividerItemDecoration(context)) } with(binding) { @@ -99,6 +102,10 @@ class SchoolAnnouncementFragment : binding.directorInformationSwipe.isRefreshing = show } + override fun openSchoolAnnouncementDialog(item: SchoolAnnouncement) { + (activity as? MainActivity)?.showDialogFragment(SchoolAnnouncementDialog.newInstance(item)) + } + override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt index ba89ab1f4..88ad81465 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt @@ -1,6 +1,7 @@ package io.github.wulkanowy.ui.modules.schoolannouncement import io.github.wulkanowy.data.Status +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter @@ -46,6 +47,10 @@ class SchoolAnnouncementPresenter @Inject constructor( view?.showErrorDetailsDialog(lastError) } + fun onItemClickListener(item: SchoolAnnouncement) { + view?.openSchoolAnnouncementDialog(item) + } + private fun loadData(forceRefresh: Boolean = false) { Timber.i("Loading School announcement data started") diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementView.kt index 0553df1e7..383d0f294 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementView.kt @@ -19,6 +19,8 @@ interface SchoolAnnouncementView : BaseView { fun setErrorDetails(message: String) + fun openSchoolAnnouncementDialog(item: SchoolAnnouncement) + fun showProgress(show: Boolean) fun enableSwipe(enable: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt index a79661901..1e1084a8d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt @@ -7,7 +7,7 @@ import android.view.MenuItem import android.view.View import android.view.View.GONE import android.view.View.VISIBLE -import androidx.core.text.HtmlCompat +import androidx.core.text.parseAsHtml import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.datepicker.CalendarConstraints import com.google.android.material.datepicker.MaterialDatePicker @@ -147,9 +147,7 @@ class TimetableFragment : BaseFragment(R.layout.fragme override fun setDayHeaderMessage(message: String?) { binding.timetableEmptyMessage.visibility = if (message.isNullOrEmpty()) GONE else VISIBLE - binding.timetableEmptyMessage.text = HtmlCompat.fromHtml( - message.orEmpty(), HtmlCompat.FROM_HTML_MODE_COMPACT - ) + binding.timetableEmptyMessage.text = message.orEmpty().parseAsHtml() } override fun showErrorView(show: Boolean) { diff --git a/app/src/main/res/layout/dialog_school_announcement.xml b/app/src/main/res/layout/dialog_school_announcement.xml new file mode 100644 index 000000000..cbce4e025 --- /dev/null +++ b/app/src/main/res/layout/dialog_school_announcement.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + From 3b9451184c60ff9feb1bad8b036f08b6603c85b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 5 Sep 2021 03:15:40 +0200 Subject: [PATCH 07/15] Fix preview of second student guardian when first guardian is null (#1473) --- .../modules/studentinfo/StudentInfoAdapter.kt | 4 +- .../studentinfo/StudentInfoFragment.kt | 69 ++++++++----------- .../ui/modules/studentinfo/StudentInfoItem.kt | 3 +- .../studentinfo/StudentInfoPresenter.kt | 25 ++++--- .../ui/modules/studentinfo/StudentInfoView.kt | 4 +- 5 files changed, 48 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt index 2d8387491..60912200c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt @@ -13,7 +13,7 @@ class StudentInfoAdapter @Inject constructor() : var items = listOf() - var onItemClickListener: (position: Int) -> Unit = {} + var onItemClickListener: (StudentInfoView.Type?) -> Unit = {} var onItemLongClickListener: (text: String) -> Unit = {} @@ -32,7 +32,7 @@ class StudentInfoAdapter @Inject constructor() : studentInfoItemArrow.visibility = if (item.showArrow) VISIBLE else GONE with(root) { - setOnClickListener { onItemClickListener(position) } + setOnClickListener { onItemClickListener(item.viewType) } setOnLongClickListener { onItemLongClickListener(studentInfoItemSubtitle.text.toString()) true diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt index e9ef41372..361a59440 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules.studentinfo -import android.annotation.SuppressLint import android.content.ClipData import android.content.ClipboardManager import android.os.Bundle @@ -130,9 +129,9 @@ class StudentInfoFragment : getString(R.string.student_info_parents_name) to studentInfo.parentsNames ).map { StudentInfoItem( - it.first, - it.second.ifBlank { getString(R.string.all_no_data) }, - false, + title = it.first, + subtitle = it.second.ifBlank { getString(R.string.all_no_data) }, + showArrow = false, ) } ) @@ -146,25 +145,33 @@ class StudentInfoFragment : getString(R.string.student_info_email) to studentInfo.email ).map { StudentInfoItem( - it.first, - it.second.ifBlank { getString(R.string.all_no_data) }, - false, + title = it.first, + subtitle = it.second.ifBlank { getString(R.string.all_no_data) }, + showArrow = false, ) } ) } - @SuppressLint("DefaultLocale") + @OptIn(ExperimentalStdlibApi::class) override fun showFamilyTypeData(studentInfo: StudentInfo) { + val items = buildList { + add(studentInfo.firstGuardian?.let { + Triple(it.kinship.capitalise(), it.fullName, StudentInfoView.Type.FIRST_GUARDIAN) + }) + + add(studentInfo.secondGuardian?.let { + Triple(it.kinship.capitalise(), it.fullName, StudentInfoView.Type.SECOND_GUARDIAN) + }) + }.filterNotNull() + updateData( - listOfNotNull( - studentInfo.firstGuardian?.let { it.kinship.capitalise() to it.fullName }, - studentInfo.secondGuardian?.let { it.kinship.capitalise() to it.fullName }, - ).map { (title, value) -> + items.map { (title, value, type) -> StudentInfoItem( - title.ifBlank { getString(R.string.all_no_data) }, - value.ifBlank { getString(R.string.all_no_data) }, - true, + title = title.ifBlank { getString(R.string.all_no_data) }, + subtitle = value.ifBlank { getString(R.string.all_no_data) }, + showArrow = true, + viewType = type, ) } ) @@ -178,15 +185,15 @@ class StudentInfoFragment : getString(R.string.student_info_correspondence_address) to studentInfo.correspondenceAddress ).map { StudentInfoItem( - it.first, - it.second.ifBlank { getString(R.string.all_no_data) }, - false, + title = it.first, + subtitle = it.second.ifBlank { getString(R.string.all_no_data) }, + showArrow = false, ) } ) } - override fun showFirstGuardianTypeData(studentGuardian: StudentGuardian) { + override fun showGuardianTypeData(studentGuardian: StudentGuardian) { updateData( listOf( getString(R.string.student_info_full_name) to studentGuardian.fullName, @@ -196,27 +203,9 @@ class StudentInfoFragment : getString(R.string.student_info_email) to studentGuardian.email ).map { StudentInfoItem( - it.first, - it.second.ifBlank { getString(R.string.all_no_data) }, - false, - ) - } - ) - } - - override fun showSecondGuardianTypeData(studentGuardian: StudentGuardian) { - updateData( - listOf( - getString(R.string.student_info_full_name) to studentGuardian.fullName, - getString(R.string.student_info_kinship) to studentGuardian.kinship, - getString(R.string.student_info_guardian_address) to studentGuardian.address, - getString(R.string.student_info_phones) to studentGuardian.phones, - getString(R.string.student_info_email) to studentGuardian.email - ).map { - StudentInfoItem( - it.first, - it.second.ifBlank { getString(R.string.all_no_data) }, - false, + title = it.first, + subtitle = it.second.ifBlank { getString(R.string.all_no_data) }, + showArrow = false, ) } ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoItem.kt index bb149b2b1..21226539b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoItem.kt @@ -3,5 +3,6 @@ package io.github.wulkanowy.ui.modules.studentinfo data class StudentInfoItem( val title: String, val subtitle: String, - val showArrow: Boolean + val showArrow: Boolean, + val viewType: StudentInfoView.Type? = null, ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt index 55ac66d0b..80798b11e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt @@ -58,13 +58,12 @@ class StudentInfoPresenter @Inject constructor( view?.showErrorDetailsDialog(lastError) } - fun onItemSelected(position: Int) { - if (infoType != StudentInfoView.Type.FAMILY) return + fun onItemSelected(viewType: StudentInfoView.Type?) { + viewType ?: return view?.openStudentInfoView( - if (position == 0) StudentInfoView.Type.FIRST_GUARDIAN - else StudentInfoView.Type.SECOND_GUARDIAN, - studentWithSemesters + studentWithSemesters = studentWithSemesters, + infoType = viewType, ) } @@ -76,15 +75,19 @@ class StudentInfoPresenter @Inject constructor( flowWithResourceIn { val semester = studentWithSemesters.semesters.getCurrentOrLast() studentInfoRepository.getStudentInfo( - studentWithSemesters.student, - semester, - forceRefresh + student = studentWithSemesters.student, + semester = semester, + forceRefresh = forceRefresh ) }.onEach { when (it.status) { Status.LOADING -> Timber.i("Loading student info $infoType started") Status.SUCCESS -> { - if (it.data != null && !(infoType == StudentInfoView.Type.FAMILY && it.data.firstGuardian == null && it.data.secondGuardian == null)) { + val isFamily = infoType == StudentInfoView.Type.FAMILY + val isFirstGuardianEmpty = it.data?.firstGuardian == null + val isSecondGuardianEmpty = it.data?.secondGuardian == null + + if (it.data != null && !(isFamily && isFirstGuardianEmpty && isSecondGuardianEmpty)) { Timber.i("Loading student info $infoType result: Success") showCorrectData(it.data) view?.run { @@ -122,8 +125,8 @@ class StudentInfoPresenter @Inject constructor( StudentInfoView.Type.CONTACT -> view?.showContactTypeData(studentInfo) StudentInfoView.Type.ADDRESS -> view?.showAddressTypeData(studentInfo) StudentInfoView.Type.FAMILY -> view?.showFamilyTypeData(studentInfo) - StudentInfoView.Type.SECOND_GUARDIAN -> view?.showSecondGuardianTypeData(studentInfo.secondGuardian!!) - StudentInfoView.Type.FIRST_GUARDIAN -> view?.showFirstGuardianTypeData(studentInfo.firstGuardian!!) + StudentInfoView.Type.SECOND_GUARDIAN -> view?.showGuardianTypeData(studentInfo.secondGuardian!!) + StudentInfoView.Type.FIRST_GUARDIAN -> view?.showGuardianTypeData(studentInfo.firstGuardian!!) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt index 87613e626..c20359df1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt @@ -25,9 +25,7 @@ interface StudentInfoView : BaseView { fun showFamilyTypeData(studentInfo: StudentInfo) - fun showFirstGuardianTypeData(studentGuardian: StudentGuardian) - - fun showSecondGuardianTypeData(studentGuardian: StudentGuardian) + fun showGuardianTypeData(studentGuardian: StudentGuardian) fun openStudentInfoView(infoType: Type, studentWithSemesters: StudentWithSemesters) From e6c9abb4e5e7552a32a2c89f8088be709539d119 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Sep 2021 18:09:42 +0000 Subject: [PATCH 08/15] Bump core from 1.10.0 to 1.10.1 (#1480) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 4513be16d..b175a274c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -215,7 +215,7 @@ dependencies { playImplementation 'com.google.firebase:firebase-analytics-ktx' playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' - playImplementation 'com.google.android.play:core:1.10.0' + playImplementation 'com.google.android.play:core:1.10.1' playImplementation 'com.google.android.play:core-ktx:1.8.1' hmsImplementation 'com.huawei.hms:hianalytics:6.2.0.300' From a43acaaa07e516c637ec61a2e4ce62912a378020 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Sep 2021 18:10:05 +0000 Subject: [PATCH 09/15] Bump kotlinx-coroutines-android from 1.5.1 to 1.5.2 (#1479) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index b175a274c..17685c164 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -161,7 +161,7 @@ dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2" implementation "androidx.core:core-ktx:1.6.0" implementation "androidx.activity:activity-ktx:1.3.1" From 0008a72be127eae827a89c14a37db40dea1727c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Sep 2021 18:10:22 +0000 Subject: [PATCH 10/15] Bump kotlinx-coroutines-test from 1.5.1 to 1.5.2 (#1477) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 17685c164..57395372f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -228,7 +228,7 @@ dependencies { testImplementation "junit:junit:4.13.2" testImplementation "io.mockk:mockk:$mockk" - testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.1' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.2' testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" testImplementation 'org.robolectric:robolectric:4.6.1' From 44a9db48a67a8ac9632f12ebe90711a0b52c4983 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Sep 2021 18:17:18 +0000 Subject: [PATCH 11/15] Bump hianalytics from 6.2.0.300 to 6.2.0.301 (#1476) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 57395372f..e1d1be79b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -218,7 +218,7 @@ dependencies { playImplementation 'com.google.android.play:core:1.10.1' playImplementation 'com.google.android.play:core-ktx:1.8.1' - hmsImplementation 'com.huawei.hms:hianalytics:6.2.0.300' + hmsImplementation 'com.huawei.hms:hianalytics:6.2.0.301' hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.0.300' releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" From 49ebae6e6381cae38240e523a4c2702ae57dd435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sun, 5 Sep 2021 21:59:03 +0200 Subject: [PATCH 12/15] Fix overlaping empty and error view in grade statistics (#1475) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mikołaj Pich --- .../res/layout/fragment_grade_statistics.xml | 170 ++++++++++-------- 1 file changed, 97 insertions(+), 73 deletions(-) diff --git a/app/src/main/res/layout/fragment_grade_statistics.xml b/app/src/main/res/layout/fragment_grade_statistics.xml index 3bd57b4b1..981ee48f3 100644 --- a/app/src/main/res/layout/fragment_grade_statistics.xml +++ b/app/src/main/res/layout/fragment_grade_statistics.xml @@ -36,7 +36,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - @@ -44,91 +44,115 @@ android:id="@+id/gradeStatisticsProgress" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" android:indeterminate="true" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" tools:visibility="gone" /> + android:layout_height="wrap_content" + app:layout_constraintTop_toTopOf="parent" + tools:listitem="@layout/item_grade_statistics_pie" + tools:visibility="visible" /> - + android:layout_height="0dp" + android:layout_marginTop="12dp" + android:fillViewport="true" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/gradeStatisticsRecycler"> - - - - - - - - - - - - + android:layout_height="wrap_content"> - + android:gravity="center" + android:orientation="vertical" + android:visibility="invisible" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:ignore="UseCompoundDrawables" + tools:visibility="gone"> - + + + + + - - - + android:gravity="center" + android:orientation="vertical" + android:visibility="gone" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:ignore="UseCompoundDrawables" + tools:visibility="gone"> + + + + + + + + + + + + + + + From 2b55ec02ff5da46f8f3713f91032aa15ee827c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sun, 5 Sep 2021 23:06:44 +0200 Subject: [PATCH 13/15] New translations strings.xml (Polish) (#1474) --- app/src/main/res/values-pl/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 74627ed1f..2a0f3cb77 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -53,7 +53,7 @@ Nie znaleziono ucznia. Sprawdź poprawność symbolu i wybranej odmiany dziennika UONET+ To pole jest wymagane Wybrany uczeń jest już zalogowany - Symbol znajdziesz na stronie dziennika w Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUpewnij się, że w polu Dziennik UONET+ na poprzednim ekranie została ustawiona odpowiednia odmiana dziennika. Wulkanowy na chwilę obecną nie wykrywa uczniów przedszkolnych + Symbol znajdziesz na stronie dziennika w Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUpewnij się, że w polu Dziennik UONET+ na poprzednim ekranie została ustawiona odpowiednia odmiana dziennika.\n\nWulkanowy na chwilę obecną nie wykrywa uczniów przedszkolnych (z zerówki) Wybierz uczniów do zalogowania w aplikacji Inne opcje W tym trybie nie działa szczęśliwy numerek, uczeń na tle klasy, podsumowanie frekwencji, usprawiedliwianie nieobecności, lekcje zrealizowane, informacje o szkole i podgląd listy zarejestrowanych urządzeń From 77c5330f91f1f026fb6ebd9118b99e69f196bc39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sun, 5 Sep 2021 23:24:03 +0200 Subject: [PATCH 14/15] Dashboard fixes (#1463) --- .../data/repositories/GradeRepository.kt | 29 +- .../data/repositories/SemesterRepository.kt | 30 +- .../github/wulkanowy/ui/base/BasePresenter.kt | 2 +- .../ui/modules/account/AccountFragment.kt | 8 +- .../ui/modules/account/AccountPresenter.kt | 2 +- .../ui/modules/account/AccountView.kt | 4 +- .../accountdetails/AccountDetailsFragment.kt | 6 +- .../accountdetails/AccountDetailsPresenter.kt | 5 +- .../ui/modules/dashboard/DashboardAdapter.kt | 74 +- .../ui/modules/dashboard/DashboardFragment.kt | 6 +- .../ui/modules/dashboard/DashboardItem.kt | 3 + .../modules/dashboard/DashboardPresenter.kt | 630 +++++++++--------- .../wulkanowy/utils/TimetableExtension.kt | 2 +- .../item_dashboard_horizontal_group.xml | 14 +- app/src/main/res/values-cs/strings.xml | 3 +- app/src/main/res/values-de/strings.xml | 3 +- app/src/main/res/values-pl/strings.xml | 3 +- app/src/main/res/values-ru/strings.xml | 3 +- app/src/main/res/values-sk/strings.xml | 3 +- app/src/main/res/values-uk/strings.xml | 3 +- app/src/main/res/values/strings.xml | 3 +- .../wulkanowy/utils/TimetableExtensionTest.kt | 17 +- 22 files changed, 433 insertions(+), 420 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt index 0b8a0e710..d8417f8a9 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt @@ -33,10 +33,16 @@ class GradeRepository @Inject constructor( private val cacheKey = "grade" - fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource( + fun getGrades( + student: Student, + semester: Semester, + forceRefresh: Boolean, + notify: Boolean = false + ) = networkBoundResource( mutex = saveFetchResultMutex, shouldFetch = { (details, summaries) -> - val isShouldBeRefreshed = refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) + val isShouldBeRefreshed = + refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) details.isEmpty() || summaries.isEmpty() || forceRefresh || isShouldBeRefreshed }, query = { @@ -59,8 +65,14 @@ class GradeRepository @Inject constructor( } ) - private suspend fun refreshGradeDetails(student: Student, oldGrades: List, newDetails: List, notify: Boolean) { - val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date ?: student.registrationDate.toLocalDate() + private suspend fun refreshGradeDetails( + student: Student, + oldGrades: List, + newDetails: List, + notify: Boolean + ) { + val notifyBreakDate = + oldGrades.maxByOrNull { it.date }?.date ?: student.registrationDate.toLocalDate() gradeDb.deleteAll(oldGrades uniqueSubtract newDetails) gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach { if (it.date >= notifyBreakDate) it.apply { @@ -70,10 +82,15 @@ class GradeRepository @Inject constructor( }) } - private suspend fun refreshGradeSummaries(oldSummaries: List, newSummary: List, notify: Boolean) { + private suspend fun refreshGradeSummaries( + oldSummaries: List, + newSummary: List, + notify: Boolean + ) { gradeSummaryDb.deleteAll(oldSummaries uniqueSubtract newSummary) gradeSummaryDb.insertAll((newSummary uniqueSubtract oldSummaries).onEach { summary -> - val oldSummary = oldSummaries.find { oldSummary -> oldSummary.subject == summary.subject } + val oldSummary = + oldSummaries.find { oldSummary -> oldSummary.subject == summary.subject } summary.isPredictedGradeNotified = when { summary.predictedGrade.isEmpty() -> true notify && oldSummary?.predictedGrade != summary.predictedGrade -> false diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt index 8942391c8..4336877a5 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt @@ -22,7 +22,11 @@ class SemesterRepository @Inject constructor( private val dispatchers: DispatchersProvider ) { - suspend fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false) = withContext(dispatchers.backgroundThread) { + suspend fun getSemesters( + student: Student, + forceRefresh: Boolean = false, + refreshOnNoCurrent: Boolean = false + ) = withContext(dispatchers.backgroundThread) { val semesters = semesterDb.loadAll(student.studentId, student.classId) if (isShouldFetch(student, semesters, forceRefresh, refreshOnNoCurrent)) { @@ -31,14 +35,21 @@ class SemesterRepository @Inject constructor( } else semesters } - private fun isShouldFetch(student: Student, semesters: List, forceRefresh: Boolean, refreshOnNoCurrent: Boolean): Boolean { + private fun isShouldFetch( + student: Student, + semesters: List, + forceRefresh: Boolean, + refreshOnNoCurrent: Boolean + ): Boolean { val isNoSemesters = semesters.isEmpty() - val isRefreshOnModeChangeRequired = if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { - semesters.firstOrNull { it.isCurrent }?.diaryId == 0 - } else false + val isRefreshOnModeChangeRequired = + if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { + semesters.firstOrNull { it.isCurrent }?.diaryId == 0 + } else false - val isRefreshOnNoCurrentAppropriate = refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent } + val isRefreshOnNoCurrentAppropriate = + refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent } return forceRefresh || isNoSemesters || isRefreshOnModeChangeRequired || isRefreshOnNoCurrentAppropriate } @@ -52,7 +63,8 @@ class SemesterRepository @Inject constructor( semesterDb.insertSemesters(new.uniqueSubtract(old)) } - suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = withContext(dispatchers.backgroundThread) { - getSemesters(student, forceRefresh).getCurrentOrLast() - } + suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = + withContext(dispatchers.backgroundThread) { + getSemesters(student, forceRefresh).getCurrentOrLast() + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt index be5300499..6f363bfc4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt @@ -18,7 +18,7 @@ open class BasePresenter( protected val studentRepository: StudentRepository ) : CoroutineScope { - private var job: Job = Job() + private var job = Job() private val jobs = mutableMapOf() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt index 7a8f8585f..051c93c95 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt @@ -8,7 +8,7 @@ import androidx.core.view.get import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.databinding.FragmentAccountBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment @@ -75,9 +75,7 @@ class AccountFragment : BaseFragment(R.layout.fragment_a } } - override fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters) { - (activity as? MainActivity)?.pushView( - AccountDetailsFragment.newInstance(studentWithSemesters) - ) + override fun openAccountDetailsView(student: Student) { + (activity as? MainActivity)?.pushView(AccountDetailsFragment.newInstance(student)) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt index 8d1651395..7fe77ca7a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt @@ -28,7 +28,7 @@ class AccountPresenter @Inject constructor( } fun onItemSelected(studentWithSemesters: StudentWithSemesters) { - view?.openAccountDetailsView(studentWithSemesters) + view?.openAccountDetailsView(studentWithSemesters.student) } private fun loadData() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt index d7deefafd..56fcb0a35 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.ui.modules.account -import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.ui.base.BaseView interface AccountView : BaseView { @@ -11,5 +11,5 @@ interface AccountView : BaseView { fun openLoginView() - fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters) + fun openAccountDetailsView(student: Student) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt index f1c7f7bd1..a9890ad9a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt @@ -37,9 +37,9 @@ class AccountDetailsFragment : private const val ARGUMENT_KEY = "Data" - fun newInstance(studentWithSemesters: StudentWithSemesters) = + fun newInstance(student: Student) = AccountDetailsFragment().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, studentWithSemesters) } + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, student) } } } @@ -51,7 +51,7 @@ class AccountDetailsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentAccountDetailsBinding.bind(view) - presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as StudentWithSemesters) + presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student) } override fun initView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt index cc53c9b63..d4cba580a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.account.accountdetails import io.github.wulkanowy.data.Resource import io.github.wulkanowy.data.Status +import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.services.sync.SyncManager @@ -27,9 +28,9 @@ class AccountDetailsPresenter @Inject constructor( private var studentId: Long? = null - fun onAttachView(view: AccountDetailsView, studentWithSemesters: StudentWithSemesters) { + fun onAttachView(view: AccountDetailsView, student: Student) { super.onAttachView(view) - studentId = studentWithSemesters.student.id + studentId = student.id view.initView() errorHandler.showErrorMessage = ::showErrorViewOnError diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt index fa081ce7f..56503d027 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt @@ -14,6 +14,7 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.TimetableHeader import io.github.wulkanowy.databinding.ItemDashboardAccountBinding @@ -41,7 +42,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter Unit = {} + var onAccountTileClickListener: (Student) -> Unit = {} var onLuckyNumberTileClickListener: () -> Unit = {} @@ -152,7 +153,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter { + attendancePercentage == null || attendancePercentage == .0 -> { + context.getThemeAttrColor(R.attr.colorOnSurface) + } + attendancePercentage <= ATTENDANCE_SECOND_WARNING_THRESHOLD -> { context.getThemeAttrColor(R.attr.colorPrimary) } - attendancePercentage ?: 0.0 <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> { + attendancePercentage <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> { context.getThemeAttrColor(R.attr.colorTimetableChange) } else -> context.getThemeAttrColor(R.attr.colorOnSurface) } + val attendanceString = if (attendancePercentage == null || attendancePercentage == .0) { + context.getString(R.string.dashboard_horizontal_group_no_data) + } else { + "%.2f%%".format(attendancePercentage) + } with(binding.dashboardHorizontalGroupItemAttendanceValue) { - text = "%.2f%%".format(attendancePercentage) + text = attendanceString setTextColor(attendanceColor) } with(binding) { dashboardHorizontalGroupItemMessageValue.text = unreadMessagesCount.toString() - dashboardHorizontalGroupItemLuckyValue.text = if (luckyNumber == -1) { - context.getString(R.string.dashboard_horizontal_group_no_lukcy_number) + dashboardHorizontalGroupItemLuckyValue.text = if (luckyNumber == 0) { + context.getString(R.string.dashboard_horizontal_group_no_data) } else luckyNumber?.toString() - if (dashboardHorizontalGroupItemInfoContainer.isVisible != (error != null || isLoading)) { - dashboardHorizontalGroupItemInfoContainer.isVisible = error != null || isLoading - } - - if (dashboardHorizontalGroupItemInfoProgress.isVisible != isLoading) { - dashboardHorizontalGroupItemInfoProgress.isVisible = isLoading - } - + dashboardHorizontalGroupItemInfoContainer.isVisible = error != null || isLoading + dashboardHorizontalGroupItemInfoProgress.isVisible = + (isLoading && !item.isDataLoaded) || (isLoading && !item.isFullDataLoaded) dashboardHorizontalGroupItemInfoErrorText.isVisible = error != null with(dashboardHorizontalGroupItemLuckyContainer) { - isVisible = error == null && !isLoading && luckyNumber != null + isVisible = luckyNumber != null && luckyNumber != -1 setOnClickListener { onLuckyNumberTileClickListener() } updateLayoutParams { @@ -216,7 +220,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter { matchConstraintPercentWidth = when { luckyNumber == null && unreadMessagesCount == null -> 1.0f @@ -228,7 +232,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter { - updateLessonView(item, emptyList(), binding, currentDayHeader) - binding.dashboardLessonsItemTitleTomorrow.isVisible = false - } tomorrowTimetable.isNotEmpty() -> { updateLessonView(item, tomorrowTimetable, binding) binding.dashboardLessonsItemTitleTomorrow.isVisible = true } + currentDayHeader != null && currentDayHeader.content.isNotBlank() -> { + updateLessonView(item, emptyList(), binding, currentDayHeader) + binding.dashboardLessonsItemTitleTomorrow.isVisible = false + } tomorrowDayHeader != null && tomorrowDayHeader.content.isNotBlank() -> { updateLessonView(item, emptyList(), binding, tomorrowDayHeader) binding.dashboardLessonsItemTitleTomorrow.isVisible = true @@ -348,6 +352,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter 60) { val formattedStartTime = firstLesson.start.toFormattedString("HH:mm") val formattedEndTime = firstLesson.end.toFormattedString("HH:mm") - firstTimeRangeText = "${formattedStartTime}-${formattedEndTime}" + firstTimeRangeText = "$formattedStartTime - $formattedEndTime" firstTimeText = "" isFirstTimeRangeVisible = true @@ -421,7 +426,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter(R.layout.fragme ) dashboardAdapter.apply { - onAccountTileClickListener = { mainActivity.pushView(AccountFragment.newInstance()) } + onAccountTileClickListener = { + mainActivity.pushView(AccountDetailsFragment.newInstance(it)) + } onLuckyNumberTileClickListener = { mainActivity.pushView(LuckyNumberFragment.newInstance()) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt index 2948b42fa..cf99f0c9a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt @@ -35,6 +35,9 @@ sealed class DashboardItem(val type: Type) { override val isDataLoaded get() = unreadMessagesCount != null || attendancePercentage != null || luckyNumber != null + + val isFullDataLoaded + get() = luckyNumber != -1 && attendancePercentage != -1.0 && unreadMessagesCount != -1 } data class Grades( diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt index 0e24f0a14..027bcc053 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt @@ -2,6 +2,8 @@ package io.github.wulkanowy.ui.modules.dashboard import io.github.wulkanowy.data.Resource import io.github.wulkanowy.data.Status +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository import io.github.wulkanowy.data.repositories.ConferenceRepository @@ -18,11 +20,18 @@ import io.github.wulkanowy.data.repositories.TimetableRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.calculatePercentage -import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.nextOrSameSchoolDay +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.filterNot +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import timber.log.Timber import java.time.LocalDate import java.time.LocalDateTime @@ -48,9 +57,11 @@ class DashboardPresenter @Inject constructor( private val dashboardItemRefreshLoadedList = mutableListOf() - private lateinit var dashboardItemsToLoad: Set + private var dashboardItemsToLoad = emptySet() - private var dashboardTilesToLoad: Set = emptySet() + private var dashboardTileLoadedList = emptySet() + + private val firstLoadedItemList = mutableListOf() private lateinit var lastError: Throwable @@ -69,8 +80,10 @@ class DashboardPresenter @Inject constructor( } fun onDragAndDropEnd(list: List) { - dashboardItemLoadedList.clear() - dashboardItemLoadedList.addAll(list) + with(dashboardItemLoadedList) { + clear() + addAll(list) + } val positionList = list.mapIndexed { index, dashboardItem -> Pair(dashboardItem.type, index) }.toMap() @@ -78,87 +91,102 @@ class DashboardPresenter @Inject constructor( preferencesRepository.dashboardItemsPosition = positionList } - fun loadData(forceRefresh: Boolean = false, tilesToLoad: Set) { - val oldDashboardDataToLoad = dashboardTilesToLoad + fun loadData( + tilesToLoad: Set, + forceRefresh: Boolean = false, + ) { + val oldDashboardTileLoadedList = dashboardTileLoadedList + dashboardItemsToLoad = tilesToLoad.map { it.toDashboardItemType() }.toSet() + dashboardTileLoadedList = tilesToLoad - dashboardTilesToLoad = tilesToLoad - dashboardItemsToLoad = dashboardTilesToLoad.map { it.toDashboardItemType() }.toSet() + val itemsToLoad = generateDashboardTileListToLoad( + dashboardTilesToLoad = tilesToLoad, + dashboardLoadedTiles = oldDashboardTileLoadedList, + forceRefresh = forceRefresh + ).map { it.toDashboardItemType() } - removeUnselectedTiles() - - val newTileList = generateTileListToLoad(oldDashboardDataToLoad, forceRefresh) - loadTiles(forceRefresh, newTileList) + removeUnselectedTiles(tilesToLoad.toList()) + loadTiles(tileList = itemsToLoad, forceRefresh = forceRefresh) } - private fun removeUnselectedTiles() { - val isLuckyNumberToLoad = - dashboardTilesToLoad.any { it == DashboardItem.Tile.LUCKY_NUMBER } - val isMessagesToLoad = - dashboardTilesToLoad.any { it == DashboardItem.Tile.MESSAGES } - val isAttendanceToLoad = - dashboardTilesToLoad.any { it == DashboardItem.Tile.ATTENDANCE } + private fun generateDashboardTileListToLoad( + dashboardTilesToLoad: Set, + dashboardLoadedTiles: Set, + forceRefresh: Boolean + ) = dashboardTilesToLoad.filter { newItemToLoad -> + dashboardLoadedTiles.none { it == newItemToLoad } || forceRefresh + } + private fun removeUnselectedTiles(tilesToLoad: List) { dashboardItemLoadedList.removeAll { loadedTile -> dashboardItemsToLoad.none { it == loadedTile.type } } val horizontalGroup = dashboardItemLoadedList.find { it is DashboardItem.HorizontalGroup } as DashboardItem.HorizontalGroup? if (horizontalGroup != null) { - val horizontalIndex = dashboardItemLoadedList.indexOf(horizontalGroup) - dashboardItemLoadedList.remove(horizontalGroup) + val isLuckyNumberToLoad = DashboardItem.Tile.LUCKY_NUMBER in tilesToLoad + val isMessagesToLoad = DashboardItem.Tile.MESSAGES in tilesToLoad + val isAttendanceToLoad = DashboardItem.Tile.ATTENDANCE in tilesToLoad - var updatedHorizontalGroup = horizontalGroup + val horizontalGroupIndex = dashboardItemLoadedList.indexOf(horizontalGroup) - if (horizontalGroup.luckyNumber != null && !isLuckyNumberToLoad) { - updatedHorizontalGroup = updatedHorizontalGroup.copy(luckyNumber = null) + val newHorizontalGroup = horizontalGroup.copy( + attendancePercentage = horizontalGroup.attendancePercentage.takeIf { isAttendanceToLoad }, + unreadMessagesCount = horizontalGroup.unreadMessagesCount.takeIf { isMessagesToLoad }, + luckyNumber = horizontalGroup.luckyNumber.takeIf { isLuckyNumberToLoad } + ) + + with(dashboardItemLoadedList) { + removeAt(horizontalGroupIndex) + add(horizontalGroupIndex, newHorizontalGroup) } - - if (horizontalGroup.attendancePercentage != null && !isAttendanceToLoad) { - updatedHorizontalGroup = updatedHorizontalGroup.copy(attendancePercentage = null) - } - - if (horizontalGroup.unreadMessagesCount != null && !isMessagesToLoad) { - updatedHorizontalGroup = updatedHorizontalGroup.copy(unreadMessagesCount = null) - } - - if (horizontalGroup.error != null) { - updatedHorizontalGroup = updatedHorizontalGroup.copy(error = null, isLoading = true) - } - - dashboardItemLoadedList.add(horizontalIndex, updatedHorizontalGroup) } view?.updateData(dashboardItemLoadedList) } - private fun loadTiles(forceRefresh: Boolean, tileList: List) { - tileList.forEach { - when (it) { - DashboardItem.Tile.ACCOUNT -> loadCurrentAccount(forceRefresh) - DashboardItem.Tile.LUCKY_NUMBER -> loadLuckyNumber(forceRefresh) - DashboardItem.Tile.MESSAGES -> loadMessages(forceRefresh) - DashboardItem.Tile.ATTENDANCE -> loadAttendance(forceRefresh) - DashboardItem.Tile.LESSONS -> loadLessons(forceRefresh) - DashboardItem.Tile.GRADES -> loadGrades(forceRefresh) - DashboardItem.Tile.HOMEWORK -> loadHomework(forceRefresh) - DashboardItem.Tile.ANNOUNCEMENTS -> loadSchoolAnnouncements(forceRefresh) - DashboardItem.Tile.EXAMS -> loadExams(forceRefresh) - DashboardItem.Tile.CONFERENCES -> loadConferences(forceRefresh) - DashboardItem.Tile.ADS -> TODO() + private fun loadTiles( + tileList: List, + forceRefresh: Boolean + ) { + launch { + Timber.i("Loading dashboard account data started") + val student = runCatching { studentRepository.getCurrentStudent(true) } + .onFailure { + Timber.i("Loading dashboard account result: An exception occurred") + errorHandler.dispatch(it) + updateData(DashboardItem.Account(error = it), forceRefresh) + } + .onSuccess { Timber.i("Loading dashboard account result: Success") } + .getOrNull() ?: return@launch + + tileList.forEach { + when (it) { + DashboardItem.Type.ACCOUNT -> { + updateData(DashboardItem.Account(student), forceRefresh) + } + DashboardItem.Type.HORIZONTAL_GROUP -> { + loadHorizontalGroup(student, forceRefresh) + } + DashboardItem.Type.LESSONS -> loadLessons(student, forceRefresh) + DashboardItem.Type.GRADES -> loadGrades(student, forceRefresh) + DashboardItem.Type.HOMEWORK -> loadHomework(student, forceRefresh) + DashboardItem.Type.ANNOUNCEMENTS -> { + loadSchoolAnnouncements(student, forceRefresh) + } + DashboardItem.Type.EXAMS -> loadExams(student, forceRefresh) + DashboardItem.Type.CONFERENCES -> { + loadConferences(student, forceRefresh) + } + DashboardItem.Type.ADS -> TODO() + } } } } - private fun generateTileListToLoad( - oldDashboardTileToLoad: Set, - forceRefresh: Boolean - ) = dashboardTilesToLoad.filter { newTileToLoad -> - oldDashboardTileToLoad.none { it == newTileToLoad } || forceRefresh - } - fun onSwipeRefresh() { Timber.i("Force refreshing the dashboard") - loadData(true, preferencesRepository.selectedDashboardTiles) + loadData(preferencesRepository.selectedDashboardTiles, forceRefresh = true) } fun onRetry() { @@ -166,7 +194,7 @@ class DashboardPresenter @Inject constructor( showErrorView(false) showProgress(true) } - loadData(true, preferencesRepository.selectedDashboardTiles) + loadData(preferencesRepository.selectedDashboardTiles, forceRefresh = true) } fun onViewReselected() { @@ -192,139 +220,86 @@ class DashboardPresenter @Inject constructor( }.toSet() } - private fun loadCurrentAccount(forceRefresh: Boolean) { - flowWithResource { studentRepository.getCurrentStudent(false) } + private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) { + flow { + val semester = semesterRepository.getCurrentSemester(student) + val selectedTiles = preferencesRepository.selectedDashboardTiles + + val luckyNumberFlow = luckyNumberRepository.getLuckyNumber(student, forceRefresh) + .map { + if (it.data == null) { + it.copy(data = LuckyNumber(0, LocalDate.now(), 0)) + } else it + } + .takeIf { DashboardItem.Tile.LUCKY_NUMBER in selectedTiles } ?: flowOf(null) + + val messageFLow = messageRepository.getMessages( + student = student, + semester = semester, + folder = MessageFolder.RECEIVED, + forceRefresh = forceRefresh + ).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowOf(null) + + val attendanceFlow = attendanceSummaryRepository.getAttendanceSummary( + student = student, + semester = semester, + subjectId = -1, + forceRefresh = forceRefresh + ).takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowOf(null) + + emitAll( + combine( + luckyNumberFlow, + messageFLow, + attendanceFlow + ) { luckyNumberResource, messageResource, attendanceResource -> + val error = + luckyNumberResource?.error ?: messageResource?.error ?: attendanceResource?.error + error?.let { throw it } + + val luckyNumber = luckyNumberResource?.data?.luckyNumber + val messageCount = messageResource?.data?.count { it.unread } + val attendancePercentage = attendanceResource?.data?.calculatePercentage() + + val isLoading = + luckyNumberResource?.status == Status.LOADING || messageResource?.status == Status.LOADING || attendanceResource?.status == Status.LOADING + + DashboardItem.HorizontalGroup( + isLoading = isLoading, + attendancePercentage = if (attendancePercentage == 0.0 && isLoading) -1.0 else attendancePercentage, + unreadMessagesCount = if (messageCount == 0 && isLoading) -1 else messageCount, + luckyNumber = if (luckyNumber == 0 && isLoading) -1 else luckyNumber + ) + }) + } + .filterNot { it.isLoading && forceRefresh } + .distinctUntilChanged() .onEach { - when (it.status) { - Status.LOADING -> { - Timber.i("Loading dashboard account data started") - if (forceRefresh) return@onEach - updateData(DashboardItem.Account(it.data, isLoading = true), forceRefresh) - } - Status.SUCCESS -> { - Timber.i("Loading dashboard account result: Success") - updateData(DashboardItem.Account(it.data), forceRefresh) - } - Status.ERROR -> { - Timber.i("Loading dashboard account result: An exception occurred") - errorHandler.dispatch(it.error!!) - updateData(DashboardItem.Account(error = it.error), forceRefresh) + updateData(it, forceRefresh) + + if (it.isLoading) { + Timber.i("Loading horizontal group data started") + + if (it.isFullDataLoaded) { + firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP } + } else { + Timber.i("Loading horizontal group result: Success") } } - .launch("dashboard_account") - } - - private fun loadLuckyNumber(forceRefresh: Boolean) { - flowWithResourceIn { - val student = studentRepository.getCurrentStudent(true) - - luckyNumberRepository.getLuckyNumber(student, forceRefresh) - }.onEach { - when (it.status) { - Status.LOADING -> { - Timber.i("Loading dashboard lucky number data started") - if (forceRefresh) return@onEach - processHorizontalGroupData( - luckyNumber = it.data?.luckyNumber, - isLoading = true, - forceRefresh = forceRefresh - ) - } - Status.SUCCESS -> { - Timber.i("Loading dashboard lucky number result: Success") - processHorizontalGroupData( - luckyNumber = it.data?.luckyNumber ?: -1, - forceRefresh = forceRefresh - ) - } - Status.ERROR -> { - Timber.i("Loading dashboard lucky number result: An exception occurred") - errorHandler.dispatch(it.error!!) - processHorizontalGroupData(error = it.error, forceRefresh = forceRefresh) - } + .catch { + Timber.i("Loading horizontal group result: An exception occurred") + updateData( + DashboardItem.HorizontalGroup(error = it), + forceRefresh, + ) + errorHandler.dispatch(it) } - }.launch("dashboard_lucky_number") + .launch("horizontal_group") } - private fun loadMessages(forceRefresh: Boolean) { + private fun loadGrades(student: Student, forceRefresh: Boolean) { flowWithResourceIn { - val student = studentRepository.getCurrentStudent(true) - val semester = semesterRepository.getCurrentSemester(student) - - messageRepository.getMessages(student, semester, MessageFolder.RECEIVED, forceRefresh) - }.onEach { - when (it.status) { - Status.LOADING -> { - Timber.i("Loading dashboard messages data started") - if (forceRefresh) return@onEach - val unreadMessagesCount = it.data?.count { message -> message.unread } - - processHorizontalGroupData( - unreadMessagesCount = unreadMessagesCount, - isLoading = true, - forceRefresh = forceRefresh - ) - } - Status.SUCCESS -> { - Timber.i("Loading dashboard messages result: Success") - val unreadMessagesCount = it.data?.count { message -> message.unread } - - processHorizontalGroupData( - unreadMessagesCount = unreadMessagesCount, - forceRefresh = forceRefresh - ) - } - Status.ERROR -> { - Timber.i("Loading dashboard messages result: An exception occurred") - errorHandler.dispatch(it.error!!) - processHorizontalGroupData(error = it.error, forceRefresh = forceRefresh) - } - } - }.launch("dashboard_messages") - } - - private fun loadAttendance(forceRefresh: Boolean) { - flowWithResourceIn { - val student = studentRepository.getCurrentStudent(true) - val semester = semesterRepository.getCurrentSemester(student) - - attendanceSummaryRepository.getAttendanceSummary(student, semester, -1, forceRefresh) - }.onEach { - when (it.status) { - Status.LOADING -> { - Timber.i("Loading dashboard attendance data started") - if (forceRefresh) return@onEach - val attendancePercentage = it.data?.calculatePercentage() - - processHorizontalGroupData( - attendancePercentage = attendancePercentage, - isLoading = true, - forceRefresh = forceRefresh - ) - } - Status.SUCCESS -> { - Timber.i("Loading dashboard attendance result: Success") - val attendancePercentage = it.data?.calculatePercentage() - - processHorizontalGroupData( - attendancePercentage = attendancePercentage, - forceRefresh = forceRefresh - ) - } - Status.ERROR -> { - Timber.i("Loading dashboard attendance result: An exception occurred") - errorHandler.dispatch(it.error!!) - - processHorizontalGroupData(error = it.error, forceRefresh = forceRefresh) - } - } - }.launch("dashboard_attendance") - } - - private fun loadGrades(forceRefresh: Boolean) { - flowWithResourceIn { - val student = studentRepository.getCurrentStudent(true) val semester = semesterRepository.getCurrentSemester(student) gradeRepository.getGrades(student, semester, forceRefresh) @@ -353,6 +328,7 @@ class DashboardPresenter @Inject constructor( Status.LOADING -> { Timber.i("Loading dashboard grades data started") if (forceRefresh) return@onEach + updateData( DashboardItem.Grades( subjectWithGrades = it.data, @@ -360,6 +336,10 @@ class DashboardPresenter @Inject constructor( isLoading = true ), forceRefresh ) + + if (!it.data.isNullOrEmpty()) { + firstLoadedItemList += DashboardItem.Type.GRADES + } } Status.SUCCESS -> { Timber.i("Loading dashboard grades result: Success") @@ -367,7 +347,8 @@ class DashboardPresenter @Inject constructor( DashboardItem.Grades( subjectWithGrades = it.data, gradeTheme = preferencesRepository.gradeColorTheme - ), forceRefresh + ), + forceRefresh ) } Status.ERROR -> { @@ -379,9 +360,8 @@ class DashboardPresenter @Inject constructor( }.launch("dashboard_grades") } - private fun loadLessons(forceRefresh: Boolean) { + private fun loadLessons(student: Student, forceRefresh: Boolean) { flowWithResourceIn { - val student = studentRepository.getCurrentStudent(true) val semester = semesterRepository.getCurrentSemester(student) val date = LocalDate.now().nextOrSameSchoolDay @@ -398,24 +378,34 @@ class DashboardPresenter @Inject constructor( Status.LOADING -> { Timber.i("Loading dashboard lessons data started") if (forceRefresh) return@onEach - updateData(DashboardItem.Lessons(it.data, isLoading = true), forceRefresh) + updateData( + DashboardItem.Lessons(it.data, isLoading = true), + forceRefresh + ) + + if (!it.data?.lessons.isNullOrEmpty()) { + firstLoadedItemList += DashboardItem.Type.LESSONS + } } Status.SUCCESS -> { Timber.i("Loading dashboard lessons result: Success") - updateData(DashboardItem.Lessons(it.data), forceRefresh) + updateData( + DashboardItem.Lessons(it.data), forceRefresh + ) } Status.ERROR -> { Timber.i("Loading dashboard lessons result: An exception occurred") errorHandler.dispatch(it.error!!) - updateData(DashboardItem.Lessons(error = it.error), forceRefresh) + updateData( + DashboardItem.Lessons(error = it.error), forceRefresh + ) } } }.launch("dashboard_lessons") } - private fun loadHomework(forceRefresh: Boolean) { + private fun loadHomework(student: Student, forceRefresh: Boolean) { flowWithResourceIn { - val student = studentRepository.getCurrentStudent(true) val semester = semesterRepository.getCurrentSemester(student) val date = LocalDate.now().nextOrSameSchoolDay @@ -443,6 +433,10 @@ class DashboardPresenter @Inject constructor( DashboardItem.Homework(it.data ?: emptyList(), isLoading = true), forceRefresh ) + + if (!it.data.isNullOrEmpty()) { + firstLoadedItemList += DashboardItem.Type.HOMEWORK + } } Status.SUCCESS -> { Timber.i("Loading dashboard homework result: Success") @@ -457,10 +451,8 @@ class DashboardPresenter @Inject constructor( }.launch("dashboard_homework") } - private fun loadSchoolAnnouncements(forceRefresh: Boolean) { + private fun loadSchoolAnnouncements(student: Student, forceRefresh: Boolean) { flowWithResourceIn { - val student = studentRepository.getCurrentStudent(true) - schoolAnnouncementRepository.getSchoolAnnouncements(student, forceRefresh) }.onEach { when (it.status) { @@ -468,11 +460,13 @@ class DashboardPresenter @Inject constructor( Timber.i("Loading dashboard announcements data started") if (forceRefresh) return@onEach updateData( - DashboardItem.Announcements( - it.data ?: emptyList(), - isLoading = true - ), forceRefresh + DashboardItem.Announcements(it.data ?: emptyList(), isLoading = true), + forceRefresh ) + + if (!it.data.isNullOrEmpty()) { + firstLoadedItemList += DashboardItem.Type.ANNOUNCEMENTS + } } Status.SUCCESS -> { Timber.i("Loading dashboard announcements result: Success") @@ -487,9 +481,8 @@ class DashboardPresenter @Inject constructor( }.launch("dashboard_announcements") } - private fun loadExams(forceRefresh: Boolean) { + private fun loadExams(student: Student, forceRefresh: Boolean) { flowWithResourceIn { - val student = studentRepository.getCurrentStudent(true) val semester = semesterRepository.getCurrentSemester(student) examRepository.getExams( @@ -508,6 +501,10 @@ class DashboardPresenter @Inject constructor( DashboardItem.Exams(it.data.orEmpty(), isLoading = true), forceRefresh ) + + if (!it.data.isNullOrEmpty()) { + firstLoadedItemList += DashboardItem.Type.EXAMS + } } Status.SUCCESS -> { Timber.i("Loading dashboard exams result: Success") @@ -522,9 +519,8 @@ class DashboardPresenter @Inject constructor( }.launch("dashboard_exams") } - private fun loadConferences(forceRefresh: Boolean) { + private fun loadConferences(student: Student, forceRefresh: Boolean) { flowWithResourceIn { - val student = studentRepository.getCurrentStudent(true) val semester = semesterRepository.getCurrentSemester(student) conferenceRepository.getConferences( @@ -542,6 +538,10 @@ class DashboardPresenter @Inject constructor( DashboardItem.Conferences(it.data ?: emptyList(), isLoading = true), forceRefresh ) + + if (!it.data.isNullOrEmpty()) { + firstLoadedItemList += DashboardItem.Type.CONFERENCES + } } Status.SUCCESS -> { Timber.i("Loading dashboard conferences result: Success") @@ -556,145 +556,119 @@ class DashboardPresenter @Inject constructor( }.launch("dashboard_conferences") } - private fun processHorizontalGroupData( - luckyNumber: Int? = null, - unreadMessagesCount: Int? = null, - attendancePercentage: Double? = null, - error: Throwable? = null, - isLoading: Boolean = false, - forceRefresh: Boolean - ) { - val isLuckyNumberToLoad = - dashboardTilesToLoad.any { it == DashboardItem.Tile.LUCKY_NUMBER } - val isMessagesToLoad = - dashboardTilesToLoad.any { it == DashboardItem.Tile.MESSAGES } - val isAttendanceToLoad = - dashboardTilesToLoad.any { it == DashboardItem.Tile.ATTENDANCE } - val isPushedToList = - dashboardItemLoadedList.any { it.type == DashboardItem.Type.HORIZONTAL_GROUP } + private fun updateData(dashboardItem: DashboardItem, forceRefresh: Boolean) { + val isForceRefreshError = forceRefresh && dashboardItem.error != null + val isFirstRunDataLoadedError = + dashboardItem.type in firstLoadedItemList && dashboardItem.error != null - if (error != null) { - updateData(DashboardItem.HorizontalGroup(error = error), forceRefresh) - return + with(dashboardItemLoadedList) { + removeAll { it.type == dashboardItem.type && !isForceRefreshError && !isFirstRunDataLoadedError } + if (!isForceRefreshError && !isFirstRunDataLoadedError) add(dashboardItem) } - if (isLoading) { - val horizontalGroup = - dashboardItemLoadedList.find { it is DashboardItem.HorizontalGroup } as DashboardItem.HorizontalGroup? - val updatedHorizontalGroup = - horizontalGroup?.copy(isLoading = true) ?: DashboardItem.HorizontalGroup(isLoading = true) + sortDashboardItems() - updateData(updatedHorizontalGroup, forceRefresh) - } - - if (forceRefresh && !isPushedToList) { - updateData(DashboardItem.HorizontalGroup(), forceRefresh) - } - - val horizontalGroup = - dashboardItemLoadedList.single { it is DashboardItem.HorizontalGroup } as DashboardItem.HorizontalGroup - - when { - luckyNumber != null -> { - updateData(horizontalGroup.copy(luckyNumber = luckyNumber), forceRefresh) - } - unreadMessagesCount != null -> { - updateData( - horizontalGroup.copy(unreadMessagesCount = unreadMessagesCount), - forceRefresh - ) - } - attendancePercentage != null -> { - updateData( - horizontalGroup.copy(attendancePercentage = attendancePercentage), - forceRefresh - ) - } - } - - val isHorizontalGroupLoaded = dashboardItemLoadedList.any { - if (it !is DashboardItem.HorizontalGroup) return@any false - - val isLuckyNumberStateCorrect = (it.luckyNumber != null) == isLuckyNumberToLoad - val isMessagesStateCorrect = (it.unreadMessagesCount != null) == isMessagesToLoad - val isAttendanceStateCorrect = (it.attendancePercentage != null) == isAttendanceToLoad - - isLuckyNumberStateCorrect && isAttendanceStateCorrect && isMessagesStateCorrect - } - - if (isHorizontalGroupLoaded) { - val updatedHorizontalGroup = - dashboardItemLoadedList.single { it is DashboardItem.HorizontalGroup } as DashboardItem.HorizontalGroup - - updateData(updatedHorizontalGroup.copy(isLoading = false, error = null), forceRefresh) + if (forceRefresh) { + updateForceRefreshData(dashboardItem) + } else { + updateNormalData() } } - private fun updateData(dashboardItem: DashboardItem, forceRefresh: Boolean) { - val isForceRefreshError = forceRefresh && dashboardItem.error != null - val dashboardItemsPosition = preferencesRepository.dashboardItemsPosition - - with(dashboardItemLoadedList) { - removeAll { it.type == dashboardItem.type && !isForceRefreshError } - if (!isForceRefreshError) add(dashboardItem) - sortBy { tile -> dashboardItemsToLoad.single { it == tile.type }.ordinal } + private fun updateNormalData() { + val isItemsLoaded = + dashboardItemsToLoad.all { type -> dashboardItemLoadedList.any { it.type == type } } + val isItemsDataLoaded = isItemsLoaded && dashboardItemLoadedList.all { + it.isDataLoaded || it.error != null } - if (forceRefresh) { - with(dashboardItemRefreshLoadedList) { - removeAll { it.type == dashboardItem.type } - add(dashboardItem) + if (isItemsDataLoaded) { + view?.run { + showProgress(false) + showErrorView(false) + showContent(true) + updateData(dashboardItemLoadedList.toList()) } } + showErrorIfExists( + isItemsLoaded = isItemsLoaded, + itemsLoadedList = dashboardItemLoadedList, + forceRefresh = false + ) + } + + private fun updateForceRefreshData(dashboardItem: DashboardItem) { + with(dashboardItemRefreshLoadedList) { + removeAll { it.type == dashboardItem.type } + add(dashboardItem) + } + + val isRefreshItemLoaded = + dashboardItemsToLoad.all { type -> dashboardItemRefreshLoadedList.any { it.type == type } } + val isRefreshItemsDataLoaded = isRefreshItemLoaded && dashboardItemRefreshLoadedList.all { + it.isDataLoaded || it.error != null + } + + if (isRefreshItemsDataLoaded) { + view?.run { + showRefresh(false) + showErrorView(false) + showContent(true) + updateData(dashboardItemLoadedList.toList()) + } + } + + showErrorIfExists( + isItemsLoaded = isRefreshItemLoaded, + itemsLoadedList = dashboardItemRefreshLoadedList, + forceRefresh = true + ) + + if (isRefreshItemsDataLoaded) dashboardItemRefreshLoadedList.clear() + } + + private fun showErrorIfExists( + isItemsLoaded: Boolean, + itemsLoadedList: List, + forceRefresh: Boolean + ) { + val filteredItems = itemsLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT } + val isAccountItemError = + itemsLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null + val isGeneralError = + filteredItems.none { it.error == null } && filteredItems.isNotEmpty() || isAccountItemError + val errorMessage = itemsLoadedList.map { it.error?.stackTraceToString() }.toString() + + val filteredOriginalLoadedList = + dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT } + val wasAccountItemError = + dashboardItemLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null + val wasGeneralError = + filteredOriginalLoadedList.none { it.error == null } && filteredOriginalLoadedList.isNotEmpty() || wasAccountItemError + + if (isGeneralError && isItemsLoaded) { + lastError = Exception(errorMessage) + + view?.run { + showProgress(false) + showRefresh(false) + if ((forceRefresh && wasGeneralError) || !forceRefresh) { + showContent(false) + showErrorView(true) + } + } + } + } + + private fun sortDashboardItems() { + val dashboardItemsPosition = preferencesRepository.dashboardItemsPosition + dashboardItemLoadedList.sortBy { tile -> dashboardItemsPosition?.getOrDefault( tile.type, tile.type.ordinal + 100 ) ?: tile.type.ordinal } - - val isItemsLoaded = - dashboardItemsToLoad.all { type -> dashboardItemLoadedList.any { it.type == type } } - val isRefreshItemLoaded = - dashboardItemsToLoad.all { type -> dashboardItemRefreshLoadedList.any { it.type == type } } - val isItemsDataLoaded = isItemsLoaded && dashboardItemLoadedList.all { - it.isDataLoaded || it.error != null - } - val isRefreshItemsDataLoaded = isRefreshItemLoaded && dashboardItemRefreshLoadedList.all { - it.isDataLoaded || it.error != null - } - - if (isRefreshItemsDataLoaded) { - view?.showRefresh(false) - dashboardItemRefreshLoadedList.clear() - } - - view?.run { - if (!forceRefresh) { - showProgress(!isItemsDataLoaded) - showContent(isItemsDataLoaded) - } - updateData(dashboardItemLoadedList.toList()) - } - - if (isItemsLoaded) { - val filteredItems = - dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT } - val isAccountItemError = - dashboardItemLoadedList.single { it.type == DashboardItem.Type.ACCOUNT }.error != null - val isGeneralError = - filteredItems.all { it.error != null } && filteredItems.isNotEmpty() || isAccountItemError - - val errorMessage = filteredItems.map { it.error?.stackTraceToString() }.toString() - - lastError = Exception(errorMessage) - - view?.run { - showProgress(false) - showContent(!isGeneralError) - showErrorView(isGeneralError) - } - } } } \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt index f3591306e..9d15216c6 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt @@ -18,7 +18,7 @@ inline val Timetable.left: Duration? get() = when { canceled -> null !isStudentPlan -> null - end.isAfter(now()) && start.isBefore(now()) -> between(now(), end) + end >= now() && start <= now() -> between(now(), end) else -> null } diff --git a/app/src/main/res/layout/item_dashboard_horizontal_group.xml b/app/src/main/res/layout/item_dashboard_horizontal_group.xml index bbd8f3517..1d43d5115 100644 --- a/app/src/main/res/layout/item_dashboard_horizontal_group.xml +++ b/app/src/main/res/layout/item_dashboard_horizontal_group.xml @@ -4,14 +4,14 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingHorizontal="12dp" android:layout_marginVertical="2dp" - android:clipToPadding="false"> + android:clipToPadding="false" + android:paddingHorizontal="12dp"> Lekce (Zítra) - %1$s (%2$s) Za chvíli: Brzy: První: @@ -566,7 +565,7 @@ Ještě %1$d dalších setkání Při načítání dat došlo k chybě - Žádné + Žádné Zkontrolovat aktualizace Před hlášením chyby zkontrolujte, zda je k dispozici aktualizace s opravou chyb diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 29d5c7636..5f14a4258 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -432,7 +432,6 @@ Lektionen (Morgen) - %1$s (%2$s) Gleich: Bald: Erstens: @@ -488,7 +487,7 @@ %1$d weitere Konferenzen Fehler beim Laden der Daten - Keine + Keine Auf Updates prüfen Bevor Sie einen Fehler melden, prüfen Sie zuerst, ob ein Update mit der Fehlerbehebung verfügbar ist diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 2a0f3cb77..14f23cd29 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -496,7 +496,6 @@ Lekcje (Jutro) - %1$s (%2$s) Za chwilę: Wkrótce: Pierwsza: @@ -566,7 +565,7 @@ Jeszcze %1$d dodatkowych zebrań Wystąpił błąd podczas ładowania danych - Brak + Brak Sprawdź dostępność aktualizacji Przed zgłoszeniem błędu sprawdź wcześniej, czy dostępna jest już aktualizacja z poprawką błędu diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index db78f5952..09b8a146d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -496,7 +496,6 @@ Уроки (Завтра) - %1$s (%2$s) Сейчас: Скоро: Первый: @@ -566,7 +565,7 @@ Еще %1$d конференций Произошла ошибка при загрузке данных - Отсутствует + Отсутствует Проверить наличие обновлений Прежде чем сообщать об ошибке, проверьте наличие обновлений diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 72899b78b..ad078e33f 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -496,7 +496,6 @@ Lekcie (Zajtra) - %1$s (%2$s) Za chvíľu: Čoskoro: Prvá: @@ -566,7 +565,7 @@ Ešte %1$d ďalších stretnutí Pri načítaní dát došlo k chybe - Žiadne + Žiadne Skontrolovať aktualizácie Pred hlásením chyby skontrolujte, či je k dispozícii aktualizácia s opravou chýb diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 4850e2396..41f1c300c 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -496,7 +496,6 @@ Уроки (Завтра) - %1$s (%2$s) Через мить: Незабаром: Перше: @@ -566,7 +565,7 @@ %1$d більше конференцій Помилка при завантаженні даних - Нічого + Нічого Провірити наявність оновлень Перед тим, як повідомлювати о помілці, перевірте наявність оновлень diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 08776c1ab..cf8a07604 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -495,7 +495,6 @@ Lessons (Tomorrow) - %1$s (%2$s) In a moment: Soon: First: @@ -557,7 +556,7 @@ An error occurred while loading data - None + None diff --git a/app/src/test/java/io/github/wulkanowy/utils/TimetableExtensionTest.kt b/app/src/test/java/io/github/wulkanowy/utils/TimetableExtensionTest.kt index 84b61a904..b7fa58c6f 100644 --- a/app/src/test/java/io/github/wulkanowy/utils/TimetableExtensionTest.kt +++ b/app/src/test/java/io/github/wulkanowy/utils/TimetableExtensionTest.kt @@ -32,7 +32,22 @@ class TimetableExtensionTest { assertEquals(null, getTimetableEntity(canceled = true).left) assertEquals(null, getTimetableEntity(start = now().plusMinutes(5), end = now().plusMinutes(50)).left) assertEquals(null, getTimetableEntity(start = now().minusMinutes(1), end = now().plusMinutes(44), isStudentPlan = false).left) - assertNotEquals(null, getTimetableEntity(start = now().minusMinutes(1), end = now().plusMinutes(44), isStudentPlan = true).left) + assertNotEquals( + null, + getTimetableEntity( + start = now().minusMinutes(1), + end = now().plusMinutes(44), + isStudentPlan = true + ).left + ) + assertNotEquals( + null, + getTimetableEntity( + start = now(), + end = now().plusMinutes(45), + isStudentPlan = true + ).left + ) } @Test From b64b41c11c0830d24e7f1c7adea48cb36895029f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 5 Sep 2021 23:29:15 +0200 Subject: [PATCH 15/15] Version 1.2.1 --- app/build.gradle | 8 ++++---- app/src/main/play/release-notes/pl-PL/default.txt | 11 ++++------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e1d1be79b..695dc6399 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,8 +21,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 targetSdkVersion 30 - versionCode 93 - versionName "1.2.0" + versionCode 94 + versionName "1.2.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -133,7 +133,7 @@ play { serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") ?: "jan@fakelog.cf" serviceAccountCredentials = file('key.p12') defaultToAppBundles = false - track = 'beta' + track = 'production' updatePriority = 3 } @@ -157,7 +157,7 @@ ext { } dependencies { - implementation "io.github.wulkanowy:sdk:7c399ffaea" + implementation "io.github.wulkanowy:sdk:1.2.1" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 42fd22292..aeef2a77e 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,10 +1,7 @@ -Wersja 1.2.0 +Wersja 1.2.1 -- dodaliśmy nowy ekran startowy 🎉 -- usprawniliśmy powiadomienia -- dodaliśmy wersje robocze, filtrowanie oraz informację o odczytaniu przez odbiorcę w wiadomościach -- dodaliśmy informacje o liczeniu średniej w podsumowaniu ocen -- dodaliśmy opcję generowania wiadomości z usprawiedliwieniem dni w szkołach pozbawionych funkcji usprawiedliwiania przez zakładkę frekwencja -- oraz wiele wiele innych ulepszeń i poprawek +- dodaliśmy brakujące okienka z podglądem szczegółów ogłoszeń szkolnych +- naprawiliśmy rzucające się w oczy błędy na ekranie startowym +- naprawiliśmy też inne drobne błędy w wyglądzie i stabilności aplikacji Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases