From fb3853dc70ffea9d33c07b646a223f26227d7654 Mon Sep 17 00:00:00 2001 From: Dominik Korsa Date: Thu, 30 Jan 2020 00:13:34 +0100 Subject: [PATCH 01/41] Fix timetable_show_whole_class_entries en translation (#668) --- app/src/main/res/values/preferences_values.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/preferences_values.xml b/app/src/main/res/values/preferences_values.xml index 3b1fde65d..786f1c93b 100644 --- a/app/src/main/res/values/preferences_values.xml +++ b/app/src/main/res/values/preferences_values.xml @@ -92,7 +92,7 @@ - No show + Don\'t show Show all Show smaller From 731afbb00cffebc1927ba21a00e3ce215f3668aa Mon Sep 17 00:00:00 2001 From: Dominik Korsa Date: Sun, 2 Feb 2020 16:38:14 +0100 Subject: [PATCH 02/41] Fix login more options button in dark theme (#674) --- app/src/main/res/layout/fragment_login_form.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/res/layout/fragment_login_form.xml b/app/src/main/res/layout/fragment_login_form.xml index 13880ff70..b74f6fee0 100644 --- a/app/src/main/res/layout/fragment_login_form.xml +++ b/app/src/main/res/layout/fragment_login_form.xml @@ -208,7 +208,6 @@ android:layout_marginRight="16dp" android:text="@string/login_advanced" android:textAppearance="?android:textAppearance" - android:textColor="@color/colorPrimary" app:backgroundTint="?android:windowBackground" app:fontFamily="sans-serif-medium" app:layout_constraintBottom_toBottomOf="@id/loginFormSignIn" From c3a68420277b6b9b9110c36cedb4bf5bcfe228d0 Mon Sep 17 00:00:00 2001 From: Dominik Korsa Date: Sun, 2 Feb 2020 16:40:00 +0100 Subject: [PATCH 03/41] Add total attendance summary (#672) --- .../summary/AttendanceSummaryFragment.kt | 2 ++ .../summary/AttendanceSummaryPresenter.kt | 16 +++++++++++++++- .../attendance/summary/AttendanceSummaryView.kt | 2 ++ app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 6 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt index fc3601847..8e30ea8b7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt @@ -36,6 +36,8 @@ class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainVie fun newInstance() = AttendanceSummaryFragment() } + override val totalString get() = getString(R.string.attendance_summary_total) + override val titleStringId get() = R.string.attendance_title override val isViewEmpty get() = attendanceSummaryAdapter.isEmpty diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt index 3ce85d815..bb46e0c45 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt @@ -144,8 +144,22 @@ class AttendanceSummaryPresenter @Inject constructor( ) } + private fun createAttendanceSummaryTotalItem(attendanceSummary: List): AttendanceSummaryItem { + return AttendanceSummaryItem( + month = view?.totalString.orEmpty(), + percentage = formatPercentage(attendanceSummary.calculatePercentage()), + present = attendanceSummary.sumBy { it.presence }.toString(), + absence = attendanceSummary.sumBy { it.absence }.toString(), + excusedAbsence = attendanceSummary.sumBy { it.absenceExcused }.toString(), + schoolAbsence = attendanceSummary.sumBy { it.absenceForSchoolReasons }.toString(), + exemption = attendanceSummary.sumBy { it.exemption }.toString(), + lateness = attendanceSummary.sumBy { it.lateness }.toString(), + excusedLateness = attendanceSummary.sumBy { it.latenessExcused }.toString() + ) + } + private fun createAttendanceSummaryItems(attendanceSummary: List): List { - return attendanceSummary.sortedByDescending { it.id }.map { + return listOf(createAttendanceSummaryTotalItem(attendanceSummary)) + attendanceSummary.sortedByDescending { it.id }.map { AttendanceSummaryItem( month = it.month.getFormattedName(), percentage = formatPercentage(it.calculatePercentage()), diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt index b86f6590d..8bd5332d6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt @@ -4,6 +4,8 @@ import io.github.wulkanowy.ui.base.BaseView interface AttendanceSummaryView : BaseView { + val totalString: String + val isViewEmpty: Boolean fun initView() diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 5a1ac4704..9ebaf8313 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -151,6 +151,7 @@ Frekwencja + Razem diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 5698b35a2..736d5fd63 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -146,6 +146,7 @@ Посещаемость + вместе diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3fa133c9f..2c896624a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -145,6 +145,7 @@ Attendance + Total From 2f56f7e4a4498454b1fe01bac9ef7487475bc736 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sun, 2 Feb 2020 15:50:12 +0000 Subject: [PATCH 04/41] Bump dagger from 2.25.4 to 2.26 (#673) --- app/build.gradle | 2 +- .../io/github/wulkanowy/services/ServicesModule.kt | 5 ++--- .../github/wulkanowy/ui/modules/grade/GradeModule.kt | 5 ++--- .../github/wulkanowy/ui/modules/login/LoginModule.kt | 5 ++--- .../io/github/wulkanowy/ui/modules/main/MainModule.kt | 10 ++++------ .../wulkanowy/ui/modules/message/MessageModule.kt | 5 ++--- .../schoolandteachers/SchoolAndTeachersModule.kt | 5 ++--- 7 files changed, 15 insertions(+), 22 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 24306cd19..d621904c7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -112,7 +112,7 @@ play { ext { work_manager = "2.3.0" room = "2.2.3" - dagger = "2.25.4" + dagger = "2.26" chucker = "2.0.4" mockk = "1.9.2" } diff --git a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt index 78bdbd8c0..2ca55d2e9 100644 --- a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt +++ b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt @@ -29,11 +29,10 @@ import javax.inject.Singleton @Suppress("unused") @AssistedModule -@Module(includes = [AssistedInject_ServicesModule::class, ServicesModule.Static::class]) +@Module(includes = [AssistedInject_ServicesModule::class]) abstract class ServicesModule { - @Module - object Static { + companion object { @Provides fun provideWorkManager(context: Context) = WorkManager.getInstance(context) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeModule.kt index 6e6ca2d63..342f356cb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeModule.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeModule.kt @@ -11,11 +11,10 @@ import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment @Suppress("unused") -@Module(includes = [GradeModule.Static::class]) +@Module abstract class GradeModule { - @Module - object Static { + companion object { @PerFragment @Provides diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt index 28339a467..6b5d5aef7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt @@ -12,11 +12,10 @@ import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFrag import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment @Suppress("unused") -@Module(includes = [LoginModule.Static::class]) +@Module internal abstract class LoginModule { - @Module - object Static { + companion object { @PerActivity @Provides diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt index 50e5896fe..a16e63208 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt @@ -35,16 +35,14 @@ import io.github.wulkanowy.ui.modules.timetable.TimetableFragment import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment @Suppress("unused") -@Module(includes = [MainModule.Static::class]) +@Module abstract class MainModule { - @Module - object Static { + companion object { @Provides - fun provideFragNavController(activity: MainActivity): FragNavController { - return FragNavController(activity.supportFragmentManager, R.id.mainFragmentContainer) - } + fun provideFragNavController(activity: MainActivity) = + FragNavController(activity.supportFragmentManager, R.id.mainFragmentContainer) //In activities must be injected as Lazy @Provides diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageModule.kt index b25219025..bf7950209 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageModule.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageModule.kt @@ -9,11 +9,10 @@ import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment @Suppress("unused") -@Module(includes = [MessageModule.Static::class]) +@Module abstract class MessageModule { - @Module - object Static { + companion object { @PerFragment @Provides diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersModule.kt index 62803a28c..2087b0b42 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersModule.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersModule.kt @@ -10,11 +10,10 @@ import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolFragment import io.github.wulkanowy.ui.modules.schoolandteachers.teacher.TeacherFragment @Suppress("unused") -@Module(includes = [SchoolAndTeachersModule.Static::class]) +@Module abstract class SchoolAndTeachersModule { - @Module - object Static { + companion object { @PerFragment @Provides From 720a530a6cf0a2e1b1e70a6ceb02b43e86e9ff0a Mon Sep 17 00:00:00 2001 From: Dominik Korsa Date: Tue, 4 Feb 2020 09:43:24 +0100 Subject: [PATCH 05/41] Disable signed in students in login (#677) --- .../studentselect/LoginStudentSelectItem.kt | 21 ++++++++++-- .../LoginStudentSelectPresenter.kt | 31 ++++++++++++++--- .../res/layout/item_login_student_select.xml | 34 +++++++++++++++---- app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + .../LoginStudentSelectPresenterTest.kt | 4 +-- 7 files changed, 78 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt index fa21cff94..967e762e8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt @@ -2,6 +2,8 @@ package io.github.wulkanowy.ui.modules.login.studentselect import android.annotation.SuppressLint import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.IFlexible @@ -10,12 +12,16 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Student import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.item_login_student_select.* +import timber.log.Timber -class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem() { +class LoginStudentSelectItem(val student: Student, val alreadySaved: Boolean) : + AbstractFlexibleItem() { override fun getLayoutRes() = R.layout.item_login_student_select override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ItemViewHolder { + Timber.i("createViewHolder()") + Timber.i(alreadySaved.toString()) return ItemViewHolder(view, adapter) } @@ -24,6 +30,10 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem) : FlexibleViewHolder(view, adapter), LayoutContainer { + class ItemViewHolder(view: View, val adapter: FlexibleAdapter<*>) : + FlexibleViewHolder(view, adapter), LayoutContainer { override val containerView: View get() = itemView @@ -53,7 +65,10 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem?) { - if (item is LoginStudentSelectItem) { + if (item is LoginStudentSelectItem && !item.alreadySaved) { selectedStudents.removeAll { it == item.student } .let { if (!it) selectedStudents.add(item.student) } @@ -58,11 +59,33 @@ class LoginStudentSelectPresenter @Inject constructor( } } + private fun compareStudents(a: Student, b: Student): Boolean { + return a.email == b.email + && a.symbol == b.symbol + && a.studentId == b.studentId + && a.schoolSymbol == b.schoolSymbol + && a.classId == b.classId + } + private fun loadData(students: List) { this.students = students - view?.apply { - updateData(students.map { LoginStudentSelectItem(it) }) - } + disposable.add(studentRepository.getSavedStudents() + .map { savedStudents -> + students.map { student -> + Pair(student, savedStudents.any { compareStudents(student, it) }) + } + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + view?.updateData(it.map { studentPair -> + LoginStudentSelectItem(studentPair.first, studentPair.second) + }) + }, { + errorHandler.dispatch(it) + view?.updateData(students.map { student -> LoginStudentSelectItem(student, false) }) + }) + ) } private fun registerStudents(students: List) { diff --git a/app/src/main/res/layout/item_login_student_select.xml b/app/src/main/res/layout/item_login_student_select.xml index 254bff60f..941159eb1 100644 --- a/app/src/main/res/layout/item_login_student_select.xml +++ b/app/src/main/res/layout/item_login_student_select.xml @@ -2,8 +2,11 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/loginItemContainer" android:layout_width="match_parent" - android:layout_height="72dp" - android:background="?selectableItemBackground"> + android:layout_height="wrap_content" + android:minHeight="72dp" + android:background="?selectableItemBackground" + android:paddingBottom="8dp" + android:paddingTop="8dp"> + + + android:text="@string/login_signed_in" + android:enabled="false" + android:visibility="gone" + tools:visibility="visible" /> diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 9ebaf8313..2ba2975e4 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -52,6 +52,7 @@ Email Discord Wyślij email + Uczeń jest już zalogowany diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 736d5fd63..7777bdc6a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -53,6 +53,7 @@ Электронная почта Discord Отправить письмо + Студент уже вошел в систему diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2c896624a..237384896 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -54,6 +54,7 @@ Send email Zgłoszenie: Problemy z logowaniem Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\n\nOpis problemu: + Student is already signed in diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt index 2c174fb58..90df9317c 100644 --- a/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt +++ b/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt @@ -53,7 +53,7 @@ class LoginStudentSelectPresenterTest { fun onSelectedStudentTest() { doReturn(Single.just(listOf(1L))).`when`(studentRepository).saveStudents(listOf(testStudent)) doReturn(Completable.complete()).`when`(studentRepository).switchStudent(testStudent) - presenter.onItemSelected(LoginStudentSelectItem(testStudent)) + presenter.onItemSelected(LoginStudentSelectItem(testStudent, false)) presenter.onSignIn() verify(loginStudentSelectView).showContent(false) verify(loginStudentSelectView).showProgress(true) @@ -64,7 +64,7 @@ class LoginStudentSelectPresenterTest { fun onSelectedStudentErrorTest() { doReturn(Single.error(testException)).`when`(studentRepository).saveStudents(listOf(testStudent)) doReturn(Completable.complete()).`when`(studentRepository).logoutStudent(testStudent) - presenter.onItemSelected(LoginStudentSelectItem(testStudent)) + presenter.onItemSelected(LoginStudentSelectItem(testStudent, false)) presenter.onSignIn() verify(loginStudentSelectView).showContent(false) verify(loginStudentSelectView).showProgress(true) From bdbf1fe30456317e1962acc04838972ab04a52c9 Mon Sep 17 00:00:00 2001 From: Dominik Korsa Date: Wed, 5 Feb 2020 00:42:49 +0100 Subject: [PATCH 06/41] Add dynamic nick/email in login (#676) --- .../login/advanced/LoginAdvancedFragment.kt | 45 ++++++++++++------- .../login/advanced/LoginAdvancedPresenter.kt | 23 ++++++---- .../login/advanced/LoginAdvancedView.kt | 16 ++++--- .../modules/login/form/LoginFormFragment.kt | 38 +++++++++++----- .../modules/login/form/LoginFormPresenter.kt | 19 +++++--- .../ui/modules/login/form/LoginFormView.kt | 16 ++++--- .../res/layout/fragment_login_advanced.xml | 8 ++-- .../main/res/layout/fragment_login_form.xml | 8 ++-- app/src/main/res/values-pl/strings.xml | 3 +- app/src/main/res/values-ru/strings.xml | 3 +- app/src/main/res/values/strings.xml | 3 +- .../login/form/LoginFormPresenterTest.kt | 24 +++++----- 12 files changed, 132 insertions(+), 74 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt index 353effa9b..e04e50bb7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt @@ -35,8 +35,8 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { else -> "HYBRID" } - override val formNameValue: String - get() = loginFormName.text.toString().trim() + override val formUsernameValue: String + get() = loginFormUsername.text.toString().trim() override val formPassValue: String get() = loginFormPass.text.toString().trim() @@ -45,8 +45,8 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { private lateinit var hostValues: Array - override val formHostValue: String? - get() = hostValues.getOrNull(hostKeys.indexOf(loginFormHost.text.toString())) + override val formHostValue: String + get() = hostValues.getOrNull(hostKeys.indexOf(loginFormHost.text.toString())).orEmpty() override val formPinValue: String get() = loginFormPin.text.toString().trim() @@ -57,6 +57,12 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { override val formTokenValue: String get() = loginFormToken.text.toString().trim() + override val nicknameLabel: String + get() = getString(R.string.login_nickname_hint) + + override val emailLabel: String + get() = getString(R.string.login_email_hint) + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_login_advanced, container, false) } @@ -70,7 +76,7 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { hostKeys = resources.getStringArray(R.array.hosts_keys) hostValues = resources.getStringArray(R.array.hosts_values) - loginFormName.doOnTextChanged { _, _, _, _ -> presenter.onNameTextChanged() } + loginFormUsername.doOnTextChanged { _, _, _, _ -> presenter.onUsernameTextChanged() } loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() } loginFormPin.doOnTextChanged { _, _, _, _ -> presenter.onPinTextChanged() } loginFormSymbol.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() } @@ -103,16 +109,20 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { } } - override fun setDefaultCredentials(name: String, pass: String, symbol: String, token: String, pin: String) { - loginFormName.setText(name) + override fun setDefaultCredentials(username: String, pass: String, symbol: String, token: String, pin: String) { + loginFormUsername.setText(username) loginFormPass.setText(pass) loginFormToken.setText(token) loginFormSymbol.setText(symbol) loginFormPin.setText(pin) } - override fun setErrorNameRequired() { - with(loginFormNameLayout) { + override fun setUsernameLabel(label: String) { + loginFormUsernameLayout.hint = label + } + + override fun setErrorUsernameRequired() { + with(loginFormUsernameLayout) { requestFocus() error = getString(R.string.login_field_required) } @@ -181,8 +191,8 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { } } - override fun clearNameError() { - loginFormNameLayout.error = null + override fun clearUsernameError() { + loginFormUsernameLayout.error = null } override fun clearPassError() { @@ -202,7 +212,7 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { } override fun showOnlyHybridModeInputs() { - loginFormNameLayout.visibility = View.VISIBLE + loginFormUsernameLayout.visibility = View.VISIBLE loginFormPassLayout.visibility = View.VISIBLE loginFormHostLayout.visibility = View.VISIBLE loginFormPinLayout.visibility = View.GONE @@ -211,7 +221,7 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { } override fun showOnlyScrapperModeInputs() { - loginFormNameLayout.visibility = View.VISIBLE + loginFormUsernameLayout.visibility = View.VISIBLE loginFormPassLayout.visibility = View.VISIBLE loginFormHostLayout.visibility = View.VISIBLE loginFormPinLayout.visibility = View.GONE @@ -220,7 +230,7 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { } override fun showOnlyMobileApiModeInputs() { - loginFormNameLayout.visibility = View.GONE + loginFormUsernameLayout.visibility = View.GONE loginFormPassLayout.visibility = View.GONE loginFormHostLayout.visibility = View.GONE loginFormPinLayout.visibility = View.VISIBLE @@ -246,12 +256,17 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { override fun notifyParentAccountLogged(students: List) { (activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple( - loginFormName.text.toString(), + loginFormUsername.text.toString(), loginFormPass.text.toString(), resources.getStringArray(R.array.hosts_values)[1] )) } + override fun onResume() { + super.onResume() + presenter.updateUsernameLabel() + } + override fun onDestroyView() { super.onDestroyView() presenter.onDetachView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt index 1e49e5d8b..5c49ca607 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt @@ -65,13 +65,20 @@ class LoginAdvancedPresenter @Inject constructor( } } + fun updateUsernameLabel() { + view?.apply { + setUsernameLabel(if ("vulcan" in formHostValue || "fakelog" in formHostValue) emailLabel else nicknameLabel) + } + } + fun onHostSelected() { view?.apply { clearPassError() - clearNameError() - if (formHostValue?.contains("fakelog") == true) { + clearUsernameError() + if (formHostValue.contains("fakelog")) { setDefaultCredentials("jan@fakelog.cf", "jan123", "powiatwulkanowy", "FK100000", "999999") } + updateUsernameLabel() } } @@ -89,8 +96,8 @@ class LoginAdvancedPresenter @Inject constructor( view?.clearPassError() } - fun onNameTextChanged() { - view?.clearNameError() + fun onUsernameTextChanged() { + view?.clearUsernameError() } fun onPinTextChanged() { @@ -137,7 +144,7 @@ class LoginAdvancedPresenter @Inject constructor( } private fun getStudentsAppropriatesToLoginType(): Single> { - val email = view?.formNameValue.orEmpty() + val email = view?.formUsernameValue.orEmpty() val password = view?.formPassValue.orEmpty() val endpoint = view?.formHostValue.orEmpty() @@ -153,7 +160,7 @@ class LoginAdvancedPresenter @Inject constructor( } private fun validateCredentials(): Boolean { - val login = view?.formNameValue.orEmpty() + val login = view?.formUsernameValue.orEmpty() val password = view?.formPassValue.orEmpty() val pin = view?.formPinValue.orEmpty() @@ -181,7 +188,7 @@ class LoginAdvancedPresenter @Inject constructor( } Sdk.Mode.SCRAPPER -> { if (login.isEmpty()) { - view?.setErrorNameRequired() + view?.setErrorUsernameRequired() isCorrect = false } @@ -197,7 +204,7 @@ class LoginAdvancedPresenter @Inject constructor( } Sdk.Mode.HYBRID -> { if (login.isEmpty()) { - view?.setErrorNameRequired() + view?.setErrorUsernameRequired() isCorrect = false } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt index 85a85a331..602b1d369 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt @@ -5,11 +5,11 @@ import io.github.wulkanowy.ui.base.BaseView interface LoginAdvancedView : BaseView { - val formNameValue: String + val formUsernameValue: String val formPassValue: String - val formHostValue: String? + val formHostValue: String val formLoginType: String @@ -19,11 +19,17 @@ interface LoginAdvancedView : BaseView { val formTokenValue: String + val nicknameLabel: String + + val emailLabel: String + fun initView() - fun setDefaultCredentials(name: String, pass: String, symbol: String, token: String, pin: String) + fun setDefaultCredentials(username: String, pass: String, symbol: String, token: String, pin: String) - fun setErrorNameRequired() + fun setUsernameLabel(label: String) + + fun setErrorUsernameRequired() fun setErrorPassRequired(focus: Boolean) @@ -31,7 +37,7 @@ interface LoginAdvancedView : BaseView { fun setErrorPassIncorrect() - fun clearNameError() + fun clearUsernameError() fun clearPassError() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt index cbd405c28..e4ee66fc2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt @@ -34,11 +34,20 @@ class LoginFormFragment : BaseFragment(), LoginFormView { fun newInstance() = LoginFormFragment() } - override val formNameValue get() = loginFormName.text.toString() + override val formUsernameValue: String + get() = loginFormUsername.text.toString() - override val formPassValue get() = loginFormPass.text.toString() + override val formPassValue: String + get() = loginFormPass.text.toString() - override val formHostValue get() = hostValues.getOrNull(hostKeys.indexOf(loginFormHost.text.toString())) + override val formHostValue: String + get() = hostValues.getOrNull(hostKeys.indexOf(loginFormHost.text.toString())).orEmpty() + + override val nicknameLabel: String + get() = getString(R.string.login_nickname_hint) + + override val emailLabel: String + get() = getString(R.string.login_email_hint) private lateinit var hostKeys: Array @@ -57,7 +66,7 @@ class LoginFormFragment : BaseFragment(), LoginFormView { hostKeys = resources.getStringArray(R.array.hosts_keys) hostValues = resources.getStringArray(R.array.hosts_values) - loginFormName.doOnTextChanged { _, _, _, _ -> presenter.onNameTextChanged() } + loginFormUsername.doOnTextChanged { _, _, _, _ -> presenter.onUsernameTextChanged() } loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() } loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() } loginFormSignIn.setOnClickListener { presenter.onSignInClick() } @@ -76,13 +85,17 @@ class LoginFormFragment : BaseFragment(), LoginFormView { } } - override fun setCredentials(name: String, pass: String) { - loginFormName.setText(name) + override fun setCredentials(username: String, pass: String) { + loginFormUsername.setText(username) loginFormPass.setText(pass) } - override fun setErrorNameRequired() { - with(loginFormNameLayout) { + override fun setUsernameLabel(label: String) { + loginFormUsernameLayout.hint = label + } + + override fun setErrorUsernameRequired() { + with(loginFormUsernameLayout) { requestFocus() error = getString(R.string.login_field_required) } @@ -109,8 +122,8 @@ class LoginFormFragment : BaseFragment(), LoginFormView { } } - override fun clearNameError() { - loginFormNameLayout.error = null + override fun clearUsernameError() { + loginFormUsernameLayout.error = null } override fun clearPassError() { @@ -163,6 +176,11 @@ class LoginFormFragment : BaseFragment(), LoginFormView { context?.openInternetBrowser("https://wulkanowy.github.io/czesto-zadawane-pytania/dlaczego-nie-moge-sie-zalogowac", ::showMessage) } + override fun onResume() { + super.onResume() + presenter.updateUsernameLabel() + } + override fun openEmail() { context?.openEmailClient( requireContext().getString(R.string.login_email_intent_title), diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt index 0b45c9a53..bc8655346 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt @@ -42,10 +42,17 @@ class LoginFormPresenter @Inject constructor( fun onHostSelected() { view?.apply { clearPassError() - clearNameError() - if (formHostValue?.contains("fakelog") == true) { + clearUsernameError() + if (formHostValue.contains("fakelog")) { setCredentials("jan@fakelog.cf", "jan123") } + updateUsernameLabel() + } + } + + fun updateUsernameLabel() { + view?.apply { + setUsernameLabel(if ("vulcan" in formHostValue || "fakelog" in formHostValue) emailLabel else nicknameLabel) } } @@ -53,12 +60,12 @@ class LoginFormPresenter @Inject constructor( view?.clearPassError() } - fun onNameTextChanged() { - view?.clearNameError() + fun onUsernameTextChanged() { + view?.clearUsernameError() } fun onSignInClick() { - val email = view?.formNameValue.orEmpty().trim() + val email = view?.formUsernameValue.orEmpty().trim() val password = view?.formPassValue.orEmpty().trim() val endpoint = view?.formHostValue.orEmpty().trim() @@ -105,7 +112,7 @@ class LoginFormPresenter @Inject constructor( var isCorrect = true if (login.isEmpty()) { - view?.setErrorNameRequired() + view?.setErrorUsernameRequired() isCorrect = false } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt index 6da57c8c5..e03f00422 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt @@ -7,15 +7,21 @@ interface LoginFormView : BaseView { fun initView() - val formNameValue: String + val formUsernameValue: String val formPassValue: String - val formHostValue: String? + val formHostValue: String - fun setCredentials(name: String, pass: String) + val nicknameLabel: String - fun setErrorNameRequired() + val emailLabel: String + + fun setCredentials(username: String, pass: String) + + fun setUsernameLabel(label: String) + + fun setErrorUsernameRequired() fun setErrorPassRequired(focus: Boolean) @@ -23,7 +29,7 @@ interface LoginFormView : BaseView { fun setErrorPassIncorrect() - fun clearNameError() + fun clearUsernameError() fun clearPassError() diff --git a/app/src/main/res/layout/fragment_login_advanced.xml b/app/src/main/res/layout/fragment_login_advanced.xml index 2174716ab..dec61baea 100644 --- a/app/src/main/res/layout/fragment_login_advanced.xml +++ b/app/src/main/res/layout/fragment_login_advanced.xml @@ -52,7 +52,7 @@ android:gravity="center" android:orientation="horizontal" android:paddingTop="24dp" - app:layout_constraintBottom_toTopOf="@+id/loginFormNameLayout" + app:layout_constraintBottom_toTopOf="@+id/loginFormUsernameLayout" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/loginFormHeader"> @@ -81,7 +81,7 @@ Zaloguj się za pomocą konta ucznia lub rodzica Podaj symbol - Email lub nick + Nazwa użytkownika + Email Hasło Dziennik Symbol diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 7777bdc6a..e0b07aebc 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -25,7 +25,8 @@ Авторизируйтесь при помощи аккаунта ученика или родителя Впишите \"symbol\" - Электронная почта или имя пользователя + Имя пользователя + Электронная почта Пароль Дневник Мобильный API diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 237384896..4bc0aaa66 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -24,7 +24,8 @@ Sign in with the student or parent account Enter the symbol - Email or nick + Username + Email Password Register Mobile API diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt index be99d4d46..c21960621 100644 --- a/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt +++ b/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt @@ -4,7 +4,6 @@ import io.github.wulkanowy.TestSchedulersProvider import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.reactivex.Single import org.junit.Before @@ -34,9 +33,6 @@ class LoginFormPresenterTest { @Mock lateinit var analytics: FirebaseAnalyticsHelper - @Mock - lateinit var appInfo: AppInfo - private lateinit var presenter: LoginFormPresenter @Before @@ -54,36 +50,36 @@ class LoginFormPresenterTest { @Test fun emptyNicknameLoginTest() { - `when`(loginFormView.formNameValue).thenReturn("") + `when`(loginFormView.formUsernameValue).thenReturn("") `when`(loginFormView.formPassValue).thenReturn("test123") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") presenter.onSignInClick() - verify(loginFormView).setErrorNameRequired() + verify(loginFormView).setErrorUsernameRequired() verify(loginFormView, never()).setErrorPassRequired(false) verify(loginFormView, never()).setErrorPassInvalid(false) } @Test fun emptyPassLoginTest() { - `when`(loginFormView.formNameValue).thenReturn("@") + `when`(loginFormView.formUsernameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") presenter.onSignInClick() - verify(loginFormView, never()).setErrorNameRequired() + verify(loginFormView, never()).setErrorUsernameRequired() verify(loginFormView).setErrorPassRequired(true) verify(loginFormView, never()).setErrorPassInvalid(false) } @Test fun invalidPassLoginTest() { - `when`(loginFormView.formNameValue).thenReturn("@") + `when`(loginFormView.formUsernameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") presenter.onSignInClick() - verify(loginFormView, never()).setErrorNameRequired() + verify(loginFormView, never()).setErrorUsernameRequired() verify(loginFormView, never()).setErrorPassRequired(true) verify(loginFormView).setErrorPassInvalid(true) } @@ -93,7 +89,7 @@ class LoginFormPresenterTest { val studentTest = Student(email = "test@", password = "123", scrapperBaseUrl = "https://fakelog.cf", loginType = "AUTO", studentName = "", schoolSymbol = "", schoolName = "", studentId = 0, classId = 1, isCurrent = false, symbol = "", registrationDate = now(), className = "", mobileBaseUrl = "", privateKey = "", certificateKey = "", loginMode = "", userLoginId = 0, isParent = false) doReturn(Single.just(listOf(studentTest))).`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString()) - `when`(loginFormView.formNameValue).thenReturn("@") + `when`(loginFormView.formUsernameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") presenter.onSignInClick() @@ -109,7 +105,7 @@ class LoginFormPresenterTest { fun loginEmptyTest() { doReturn(Single.just(emptyList())) .`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString()) - `when`(loginFormView.formNameValue).thenReturn("@") + `when`(loginFormView.formUsernameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") presenter.onSignInClick() @@ -125,7 +121,7 @@ class LoginFormPresenterTest { fun loginEmptyTwiceTest() { doReturn(Single.just(emptyList())) .`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString()) - `when`(loginFormView.formNameValue).thenReturn("@") + `when`(loginFormView.formUsernameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") presenter.onSignInClick() @@ -142,7 +138,7 @@ class LoginFormPresenterTest { fun loginErrorTest() { val testException = RuntimeException("test") doReturn(Single.error>(testException)).`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString()) - `when`(loginFormView.formNameValue).thenReturn("@") + `when`(loginFormView.formUsernameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") presenter.onSignInClick() From 6925204019f974fef9bfcbd8c8898fc63de42f0d Mon Sep 17 00:00:00 2001 From: Dominik Korsa Date: Wed, 5 Feb 2020 00:53:07 +0100 Subject: [PATCH 07/41] Fix showing empty total summary and ordering of summary months (#678) --- .../attendance/summary/AttendanceSummaryPresenter.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt index bb46e0c45..18f67726a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt @@ -12,6 +12,7 @@ import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.calculatePercentage import io.github.wulkanowy.utils.getFormattedName +import org.threeten.bp.Month import timber.log.Timber import java.lang.String.format import java.util.Locale.FRANCE @@ -159,7 +160,10 @@ class AttendanceSummaryPresenter @Inject constructor( } private fun createAttendanceSummaryItems(attendanceSummary: List): List { - return listOf(createAttendanceSummaryTotalItem(attendanceSummary)) + attendanceSummary.sortedByDescending { it.id }.map { + if (attendanceSummary.isEmpty()) return emptyList() + return listOf(createAttendanceSummaryTotalItem(attendanceSummary)) + attendanceSummary.sortedByDescending { + if (it.month.value <= Month.JUNE.value) it.month.value + 12 else it.month.value + }.map { AttendanceSummaryItem( month = it.month.getFormattedName(), percentage = formatPercentage(it.calculatePercentage()), From 0826e45a251aa66a79ecc28f4a24af4bb3c251b4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2020 00:22:12 +0000 Subject: [PATCH 08/41] Bump coil from 0.9.2 to 0.9.4 (#679) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index d621904c7..aea1d5539 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -176,7 +176,7 @@ dependencies { implementation "com.mikepenz:aboutlibraries-core:7.1.0" implementation 'com.wdullaer:materialdatetimepicker:4.2.3' - implementation("io.coil-kt:coil:0.9.2") + implementation("io.coil-kt:coil:0.9.4") playImplementation "com.google.firebase:firebase-core:17.2.2" playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1" From 1cc2080cb96510351054f92c69bc2ce8775ebb8c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2020 00:22:51 +0000 Subject: [PATCH 09/41] Bump material from 1.1.0-rc02 to 1.1.0 (#680) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index aea1d5539..dc3eed774 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -139,7 +139,7 @@ dependencies { implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03" implementation "androidx.constraintlayout:constraintlayout:1.1.3" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" - implementation "com.google.android.material:material:1.1.0-rc02" + implementation "com.google.android.material:material:1.1.0" implementation "com.github.wulkanowy:material-chips-input:2.0.1" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation "me.zhanghai.android.materialprogressbar:library:1.6.1" From 34738a4839f221bcf7e0901ae217ddb36b3fae47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Fri, 14 Feb 2020 09:58:58 +0100 Subject: [PATCH 10/41] Show semester in appbar subtitle in grades view (#684) --- .../wulkanowy/ui/modules/grade/GradeFragment.kt | 7 +++++++ .../wulkanowy/ui/modules/grade/GradePresenter.kt | 16 ++++++++++------ .../wulkanowy/ui/modules/grade/GradeView.kt | 2 ++ .../studentselect/LoginStudentSelectItem.kt | 3 --- .../wulkanowy/ui/modules/main/MainActivity.kt | 6 ++++++ .../wulkanowy/ui/modules/main/MainPresenter.kt | 1 + .../github/wulkanowy/ui/modules/main/MainView.kt | 8 ++++++++ app/src/main/res/values-pl/strings.xml | 3 +++ app/src/main/res/values-ru/strings.xml | 3 +++ app/src/main/res/values/strings.xml | 3 +++ 10 files changed, 43 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt index e8d970010..b1faf18f1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt @@ -40,6 +40,8 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainVie override val titleStringId get() = R.string.grade_title + override var subtitleString = "" + override val currentPageIndex get() = gradeViewPager.currentItem override fun onCreate(savedInstanceState: Bundle?) { @@ -133,6 +135,11 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainVie .show() } + override fun setCurrentSemesterName(semester: Int, schoolYear: Int) { + subtitleString = getString(R.string.grade_subtitle, semester, schoolYear, schoolYear + 1) + (activity as MainView).setViewSubTitle(subtitleString) + } + fun onChildRefresh() { presenter.onChildViewRefresh() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt index d8c202381..948d5a949 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt @@ -21,6 +21,8 @@ class GradePresenter @Inject constructor( var selectedIndex = 0 private set + private var schoolYear = 0 + private var semesters = emptyList() private val loadedSemesterId = mutableMapOf() @@ -56,6 +58,7 @@ class GradePresenter @Inject constructor( selectedIndex = index + 1 loadedSemesterId.clear() view?.let { + it.setCurrentSemesterName(index + 1, schoolYear) notifyChildrenSemesterChange() loadChild(it.currentPageIndex) } @@ -96,16 +99,17 @@ class GradePresenter @Inject constructor( Timber.i("Loading grade data started") disposable.add(studentRepository.getCurrentStudent() .flatMap { semesterRepository.getSemesters(it) } - .doOnSuccess { - it.first { item -> item.isCurrent }.also { current -> - selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex - semesters = it.filter { semester -> semester.diaryId == current.diaryId } - } - } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doFinally { view?.showProgress(false) } .subscribe({ + it.first { item -> item.isCurrent }.also { current -> + selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex + schoolYear = current.schoolYear + semesters = it.filter { semester -> semester.diaryId == current.diaryId } + view?.setCurrentSemesterName(current.semesterName, schoolYear) + } + view?.run { Timber.i("Loading grade result: Attempt load index $currentPageIndex") loadChild(currentPageIndex) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt index bbdbc32ea..7b52daa88 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt @@ -20,6 +20,8 @@ interface GradeView : BaseView { fun showSemesterDialog(selectedIndex: Int) + fun setCurrentSemesterName(semester: Int, schoolYear: Int) + fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) fun notifyChildParentReselected(index: Int) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt index 967e762e8..06be61387 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt @@ -12,7 +12,6 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Student import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.item_login_student_select.* -import timber.log.Timber class LoginStudentSelectItem(val student: Student, val alreadySaved: Boolean) : AbstractFlexibleItem() { @@ -20,8 +19,6 @@ class LoginStudentSelectItem(val student: Student, val alreadySaved: Boolean) : override fun getLayoutRes() = R.layout.item_login_student_select override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ItemViewHolder { - Timber.i("createViewHolder()") - Timber.i(alreadySaved.toString()) return ItemViewHolder(view, adapter) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt index ae4be9530..864ad4239 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt @@ -68,6 +68,8 @@ class MainActivity : BaseActivity(), MainView { override val currentViewTitle get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId?.let { getString(it) } + override val currentViewSubtitle get() = (navController.currentFrag as? MainView.TitledView)?.subtitleString + override var startMenuIndex = 0 override var startMenuMoreIndex = -1 @@ -152,6 +154,10 @@ class MainActivity : BaseActivity(), MainView { supportActionBar?.title = title } + override fun setViewSubTitle(subtitle: String?) { + supportActionBar?.subtitle = subtitle + } + override fun showHomeArrow(show: Boolean) { supportActionBar?.setDisplayHomeAsUpEnabled(show) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt index 8eccbc67e..e6c299171 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt @@ -41,6 +41,7 @@ class MainPresenter @Inject constructor( view?.apply { showActionBarElevation(section != GRADE && section != MESSAGE && section != SCHOOL) currentViewTitle?.let { setViewTitle(it) } + currentViewSubtitle?.let { setViewSubTitle(it.ifBlank { null }) } currentStackSize?.let { if (it > 1) showHomeArrow(true) else showHomeArrow(false) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt index 0d8b14169..97b556e3e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt @@ -12,6 +12,8 @@ interface MainView : BaseView { val currentViewTitle: String? + val currentViewSubtitle: String? + val currentStackSize: Int? fun initView() @@ -30,6 +32,8 @@ interface MainView : BaseView { fun setViewTitle(title: String) + fun setViewSubTitle(subtitle: String?) + fun popView(depth: Int = 1) interface MainChildView { @@ -42,6 +46,10 @@ interface MainView : BaseView { interface TitledView { val titleStringId: Int + + var subtitleString: String + get() = "" + set(_) {} } enum class Section(val id: Int) { diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 4eb3b0069..d6ef19d71 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -20,6 +20,9 @@ Zadania domowe Wybierz konto + + Semestr %d, %d/%d + Zaloguj się za pomocą konta ucznia lub rodzica diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index e0b07aebc..6a994f6d6 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -21,6 +21,9 @@ Домашние задания Выберите аккаунт + + Семестр %d, %d/%d + Авторизируйтесь при помощи аккаунта ученика или родителя diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4bc0aaa66..e2afb061a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,6 +20,9 @@ Homework Choose account + + Semester %d, %d/%d + Sign in with the student or parent account From 30e43501ac6f471b3f377702ca4aefc5a4fedc0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Fri, 14 Feb 2020 10:04:53 +0100 Subject: [PATCH 11/41] Refactor the resizing of the lucky number app widget (#682) --- .../LuckyNumberWidgetProvider.kt | 154 ++++++++---------- 1 file changed, 66 insertions(+), 88 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt index 12753b35d..bd42914fe 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt @@ -1,21 +1,14 @@ package io.github.wulkanowy.ui.modules.luckynumberwidget -import android.annotation.TargetApi import android.app.PendingIntent import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.appwidget.AppWidgetManager -import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_DELETED -import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED -import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE -import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID -import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS -import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_OPTIONS import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT -import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH -import android.content.BroadcastReceiver +import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH +import android.appwidget.AppWidgetProvider import android.content.Context import android.content.Intent -import android.os.Build +import android.os.Bundle import android.view.View.GONE import android.view.View.VISIBLE import android.widget.RemoteViews @@ -34,7 +27,7 @@ import io.reactivex.Maybe import timber.log.Timber import javax.inject.Inject -class LuckyNumberWidgetProvider : BroadcastReceiver() { +class LuckyNumberWidgetProvider : AppWidgetProvider() { @Inject lateinit var studentRepository: StudentRepository @@ -59,20 +52,20 @@ class LuckyNumberWidgetProvider : BroadcastReceiver() { fun getStudentWidgetKey(appWidgetId: Int) = "lucky_number_widget_student_$appWidgetId" fun getThemeWidgetKey(appWidgetId: Int) = "lucky_number_widget_theme_$appWidgetId" + + fun getHeightWidgetKey(appWidgetId: Int) = "lucky_number_widget_height_$appWidgetId" + + fun getWidthWidgetKey(appWidgetId: Int) = "lucky_number_widget_width_$appWidgetId" } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) override fun onReceive(context: Context, intent: Intent) { AndroidInjection.inject(this, context) - when (intent.action) { - ACTION_APPWIDGET_UPDATE -> onUpdate(context, intent) - ACTION_APPWIDGET_DELETED -> onDelete(intent) - ACTION_APPWIDGET_OPTIONS_CHANGED -> onOptionsChange(context, intent) - } + super.onReceive(context, intent) } - private fun onUpdate(context: Context, intent: Intent) { - intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId -> + override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray?) { + super.onUpdate(context, appWidgetManager, appWidgetIds) + appWidgetIds?.forEach { appWidgetId -> val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0) val layoutId = if (savedTheme == 0L) R.layout.widget_luckynumber else R.layout.widget_luckynumber_dark @@ -85,14 +78,64 @@ class LuckyNumberWidgetProvider : BroadcastReceiver() { setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent) } - setStyles(remoteView, intent) + setStyles(remoteView, appWidgetId) appWidgetManager.updateAppWidget(appWidgetId, remoteView) } } - private fun onDelete(intent: Intent) { - val appWidgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID, 0) - if (appWidgetId != 0) sharedPref.delete(getStudentWidgetKey(appWidgetId)) + override fun onDeleted(context: Context?, appWidgetIds: IntArray?) { + super.onDeleted(context, appWidgetIds) + appWidgetIds?.forEach { appWidgetId -> + if (appWidgetId != 0) sharedPref.delete(getStudentWidgetKey(appWidgetId)) + } + } + + override fun onAppWidgetOptionsChanged(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int, newOptions: Bundle?) { + super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions) + + val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0) + val layoutId = if (savedTheme == 0L) R.layout.widget_luckynumber else R.layout.widget_luckynumber_dark + + val remoteView = RemoteViews(context.packageName, layoutId) + + setStyles(remoteView, appWidgetId, newOptions) + appWidgetManager.updateAppWidget(appWidgetId, remoteView) + } + + private fun setStyles(views: RemoteViews, appWidgetId: Int, options: Bundle? = null) { + val width = options?.getInt(OPTION_APPWIDGET_MIN_WIDTH) ?: sharedPref.getLong(getWidthWidgetKey(appWidgetId), 74).toInt() + val height = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: sharedPref.getLong(getHeightWidgetKey(appWidgetId), 74).toInt() + + sharedPref.putLong(getWidthWidgetKey(appWidgetId), width.toLong()) + sharedPref.putLong(getHeightWidgetKey(appWidgetId), height.toLong()) + + val rows = getCellsForSize(height) + val cols = getCellsForSize(width) + + Timber.d("New lucky number widget measurement: %dx%d", width, height) + Timber.d("Widget size: $cols x $rows") + + when { + 1 == cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = false) + 1 == cols && 1 < rows -> views.setVisibility(imageTop = true, imageLeft = false) + 1 < cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = true) + 1 == cols && 1 == rows -> views.setVisibility(imageTop = true, imageLeft = false) + 2 == cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = true) + else -> views.setVisibility(imageTop = false, imageLeft = false, title = true) + } + } + + private fun RemoteViews.setVisibility(imageTop: Boolean, imageLeft: Boolean, title: Boolean = false) { + setViewVisibility(R.id.luckyNumberWidgetImageTop, if (imageTop) VISIBLE else GONE) + setViewVisibility(R.id.luckyNumberWidgetImageLeft, if (imageLeft) VISIBLE else GONE) + setViewVisibility(R.id.luckyNumberWidgetTitle, if (title) VISIBLE else GONE) + setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE) + } + + private fun getCellsForSize(size: Int): Int { + var n = 2 + while (74 * n - 30 < size) ++n + return n - 1 } private fun getLuckyNumber(studentId: Long, appWidgetId: Int): LuckyNumber? { @@ -124,69 +167,4 @@ class LuckyNumberWidgetProvider : BroadcastReceiver() { null } } - - private fun onOptionsChange(context: Context, intent: Intent) { - intent.extras?.getInt(EXTRA_APPWIDGET_ID)?.let { appWidgetId -> - val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0) - val layoutId = if (savedTheme == 0L) R.layout.widget_luckynumber else R.layout.widget_luckynumber_dark - - val remoteView = RemoteViews(context.packageName, layoutId) - - setStyles(remoteView, intent) - appWidgetManager.updateAppWidget(appWidgetId, remoteView) - } - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - private fun setStyles(views: RemoteViews, intent: Intent) { - val options = intent.extras?.getBundle(EXTRA_APPWIDGET_OPTIONS) - - val maxWidth = options?.getInt(OPTION_APPWIDGET_MAX_WIDTH) ?: 150 - val maxHeight = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: 40 - - Timber.d("New lucky number widget measurement: %dx%d", maxWidth, maxHeight) - - when { - // 1x1 - maxWidth < 150 && maxHeight < 110 -> { - Timber.d("Lucky number widget size: 1x1") - with(views) { - setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE) - setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE) - setViewVisibility(R.id.luckyNumberWidgetTitle, GONE) - setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE) - } - } - // 1x2 - maxWidth < 150 && maxHeight > 110 -> { - Timber.d("Lucky number widget size: 1x2") - with(views) { - setViewVisibility(R.id.luckyNumberWidgetImageTop, VISIBLE) - setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE) - setViewVisibility(R.id.luckyNumberWidgetTitle, GONE) - setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE) - } - } - // 2x1 - maxWidth >= 150 && maxHeight <= 110 -> { - Timber.d("Lucky number widget size: 2x1") - with(views) { - setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE) - setViewVisibility(R.id.luckyNumberWidgetImageLeft, VISIBLE) - setViewVisibility(R.id.luckyNumberWidgetTitle, GONE) - setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE) - } - } - // 2x2 and bigger - else -> { - Timber.d("Lucky number widget size: 2x2 and bigger") - with(views) { - setViewVisibility(R.id.luckyNumberWidgetImageTop, GONE) - setViewVisibility(R.id.luckyNumberWidgetImageLeft, GONE) - setViewVisibility(R.id.luckyNumberWidgetTitle, VISIBLE) - setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE) - } - } - } - } } From 9a87df731539f47eefe57487f48a004089f2a633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 22 Feb 2020 20:42:02 +0100 Subject: [PATCH 12/41] Respect user settings in timetable app widget (#687) --- .../timetable/TestTimetableEntityCreator.kt | 2 +- .../wulkanowy/data/db/entities/Timetable.kt | 2 +- .../repositories/timetable/TimetableRemote.kt | 2 +- .../widgets/TimetableWidgetService.kt | 6 +- .../ui/modules/timetable/TimetableItem.kt | 2 +- .../modules/timetable/TimetablePresenter.kt | 2 +- .../timetablewidget/TimetableWidgetFactory.kt | 25 ++++- .../layout/item_widget_timetable_small.xml | 94 ++++++++++++++++++ .../item_widget_timetable_small_dark.xml | 95 +++++++++++++++++++ 9 files changed, 219 insertions(+), 11 deletions(-) create mode 100644 app/src/main/res/layout/item_widget_timetable_small.xml create mode 100644 app/src/main/res/layout/item_widget_timetable_small_dark.xml diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt index 77c061821..aa35fe790 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt @@ -21,7 +21,7 @@ fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", s teacher = teacher, teacherOld = "", info = "", - studentPlan = true, + isStudentPlan = true, changes = changes, canceled = false ) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt index 313feb62c..cad3b7c69 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt @@ -41,7 +41,7 @@ data class Timetable( val info: String, @ColumnInfo(name = "student_plan") - val studentPlan: Boolean, + val isStudentPlan: Boolean, val changes: Boolean, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt index 26e7836ad..c0aa16ebb 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt @@ -30,7 +30,7 @@ class TimetableRemote @Inject constructor(private val sdk: Sdk) { teacher = it.teacher, teacherOld = it.teacherOld, info = it.info, - studentPlan = it.studentPlan, + isStudentPlan = it.studentPlan, changes = it.changes, canceled = it.canceled ) diff --git a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt index 7c7e67101..e5ccf963c 100644 --- a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt +++ b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt @@ -4,6 +4,7 @@ import android.content.Intent import android.widget.RemoteViewsService import dagger.android.AndroidInjection import io.github.wulkanowy.data.db.SharedPrefProvider +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository @@ -22,6 +23,9 @@ class TimetableWidgetService : RemoteViewsService() { @Inject lateinit var semesterRepo: SemesterRepository + @Inject + lateinit var prefRepository: PreferencesRepository + @Inject lateinit var sharedPref: SharedPrefProvider @@ -30,6 +34,6 @@ class TimetableWidgetService : RemoteViewsService() { override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory { AndroidInjection.inject(this) - return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, sharedPref, schedulers, applicationContext, intent) + return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, prefRepository, sharedPref, schedulers, applicationContext, intent) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt index 99aca2a23..8dd48abd7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt @@ -21,7 +21,7 @@ class TimetableItem(val lesson: Timetable, private val showWholeClassPlan: Strin AbstractFlexibleItem() { override fun getLayoutRes() = when { - showWholeClassPlan == "small" && !lesson.studentPlan -> R.layout.item_timetable_small + showWholeClassPlan == "small" && !lesson.isStudentPlan -> R.layout.item_timetable_small else -> R.layout.item_timetable } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt index d9eb44785..7916a4b87 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -177,7 +177,7 @@ class TimetablePresenter @Inject constructor( private fun createTimetableItems(items: List): List { return items - .filter { if (prefRepository.showWholeClassPlan == "no") it.studentPlan else true } + .filter { if (prefRepository.showWholeClassPlan == "no") it.isStudentPlan else true } .map { TimetableItem(it, prefRepository.showWholeClassPlan) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt index fa989749a..0141e4d32 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt @@ -14,6 +14,7 @@ import android.widget.RemoteViewsService import io.github.wulkanowy.R import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository @@ -31,6 +32,7 @@ class TimetableWidgetFactory( private val timetableRepository: TimetableRepository, private val studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, + private val prefRepository: PreferencesRepository, private val sharedPref: SharedPrefProvider, private val schedulers: SchedulersProvider, private val context: Context, @@ -39,6 +41,8 @@ class TimetableWidgetFactory( private var lessons = emptyList() + private var savedTheme: Long? = null + private var layoutId: Int? = null private var primaryColor: Int? = null @@ -53,7 +57,7 @@ class TimetableWidgetFactory( override fun getCount() = lessons.size - override fun getViewTypeCount() = 1 + override fun getViewTypeCount() = 2 override fun getItemId(position: Int) = position.toLong() @@ -73,7 +77,7 @@ class TimetableWidgetFactory( } private fun updateTheme(appWidgetId: Int) { - val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0) + savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0) layoutId = if (savedTheme == 0L) R.layout.item_widget_timetable else R.layout.item_widget_timetable_dark primaryColor = if (savedTheme == 0L) R.color.colorPrimary else R.color.colorPrimaryLight @@ -81,6 +85,17 @@ class TimetableWidgetFactory( timetableChangeColor = if (savedTheme == 0L) R.color.timetable_change_dark else R.color.timetable_change_light } + private fun getItemLayout(lesson: Timetable): Int { + return when { + prefRepository.showWholeClassPlan == "small" && !lesson.isStudentPlan -> { + if (savedTheme == 0L) R.layout.item_widget_timetable_small + else R.layout.item_widget_timetable_small_dark + } + savedTheme == 0L -> R.layout.item_widget_timetable + else -> R.layout.item_widget_timetable_dark + } + } + private fun updateLessons(date: LocalDate, studentId: Long) { lessons = try { studentRepository.isStudentSaved() @@ -95,6 +110,7 @@ class TimetableWidgetFactory( .flatMap { semesterRepository.getCurrentSemester(it).toMaybe() } .flatMap { timetableRepository.getTimetable(it, date, date).toMaybe() } .map { item -> item.sortedBy { it.number } } + .map { lessons -> lessons.filter { if (prefRepository.showWholeClassPlan == "no") it.isStudentPlan else true } } .subscribeOn(schedulers.backgroundThread) .blockingGet(emptyList()) } catch (e: Exception) { @@ -107,9 +123,8 @@ class TimetableWidgetFactory( override fun getViewAt(position: Int): RemoteViews? { if (position == INVALID_POSITION || lessons.getOrNull(position) == null) return null - return RemoteViews(context.packageName, layoutId!!).apply { - val lesson = lessons[position] - + val lesson = lessons[position] + return RemoteViews(context.packageName, getItemLayout(lesson)).apply { setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject) setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString()) setTextViewText(R.id.timetableWidgetItemTimeStart, lesson.start.toFormattedString("HH:mm")) diff --git a/app/src/main/res/layout/item_widget_timetable_small.xml b/app/src/main/res/layout/item_widget_timetable_small.xml new file mode 100644 index 000000000..fb75a7517 --- /dev/null +++ b/app/src/main/res/layout/item_widget_timetable_small.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_widget_timetable_small_dark.xml b/app/src/main/res/layout/item_widget_timetable_small_dark.xml new file mode 100644 index 000000000..43ff6b1ad --- /dev/null +++ b/app/src/main/res/layout/item_widget_timetable_small_dark.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + From 00f5b9431ea57af62d8112ad026af7c027476175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 22 Feb 2020 21:24:06 +0100 Subject: [PATCH 13/41] Add log viewer (#686) --- app/build.gradle | 3 +- .../wulkanowy/utils/CrashlyticsUtils.kt | 13 ++- app/src/main/AndroidManifest.xml | 14 ++- .../java/io/github/wulkanowy/WulkanowyApp.kt | 13 ++- .../repositories/logger/LoggerRepository.kt | 39 ++++++++ .../ui/modules/about/AboutFragment.kt | 5 + .../ui/modules/about/AboutPresenter.kt | 5 + .../wulkanowy/ui/modules/about/AboutView.kt | 2 + .../about/logviewer/LogViewerAdapter.kt | 22 +++++ .../about/logviewer/LogViewerFragment.kt | 98 +++++++++++++++++++ .../about/logviewer/LogViewerPresenter.kt | 54 ++++++++++ .../modules/about/logviewer/LogViewerView.kt | 13 +++ .../wulkanowy/ui/modules/main/MainModule.kt | 5 + .../io/github/wulkanowy/utils/LoggerUtils.kt | 12 +-- app/src/main/res/drawable/ic_refresh.xml | 5 + .../main/res/layout/fragment_logviewer.xml | 34 +++++++ .../main/res/menu/action_menu_logviewer.xml | 11 +++ app/src/main/res/values-pl/strings.xml | 5 + app/src/main/res/values-ru/strings.xml | 10 ++ app/src/main/res/values/strings.xml | 5 + app/src/main/res/xml/provider_paths.xml | 5 + .../wulkanowy/utils/CrashlyticsUtils.kt | 15 +-- 22 files changed, 361 insertions(+), 27 deletions(-) create mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/logger/LoggerRepository.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerAdapter.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerFragment.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerPresenter.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerView.kt create mode 100644 app/src/main/res/drawable/ic_refresh.xml create mode 100644 app/src/main/res/layout/fragment_logviewer.xml create mode 100644 app/src/main/res/menu/action_menu_logviewer.xml create mode 100644 app/src/main/res/xml/provider_paths.xml diff --git a/app/build.gradle b/app/build.gradle index dc3eed774..95522dd32 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,7 +15,7 @@ android { defaultConfig { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" - minSdkVersion 16 + minSdkVersion 17 targetSdkVersion 29 versionCode 52 versionName "0.15.0" @@ -173,6 +173,7 @@ dependencies { implementation "com.jakewharton.threetenabp:threetenabp:1.2.2" implementation "com.jakewharton.timber:timber:4.7.1" implementation "at.favre.lib:slf4j-timber:1.0.1" + implementation "fr.bipi.treessence:treessence:0.3.0" implementation "com.mikepenz:aboutlibraries-core:7.1.0" implementation 'com.wdullaer:materialdatetimepicker:4.2.3' diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt index be4c0ebeb..60dc6b56a 100644 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt @@ -5,13 +5,12 @@ package io.github.wulkanowy.utils import android.content.Context import timber.log.Timber -fun initCrashlytics(context: Context, appInfo: AppInfo) { - // do nothing +fun initCrashlytics(context: Context, appInfo: AppInfo) {} + +open class TimberTreeNoOp : Timber.Tree() { + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {} } -class CrashlyticsTree : Timber.Tree() { +class CrashlyticsTree : TimberTreeNoOp() - override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - // do nothing - } -} +class CrashlyticsExceptionTree : TimberTreeNoOp() diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c644c8633..694c8053a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -43,8 +43,8 @@ android:name=".ui.modules.message.send.SendMessageActivity" android:configChanges="orientation|screenSize" android:label="@string/send_message_title" - android:windowSoftInputMode="adjustResize" - android:theme="@style/WulkanowyTheme.NoActionBar" /> + android:theme="@style/WulkanowyTheme.NoActionBar" + android:windowSoftInputMode="adjustResize" /> + + + + diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt index 90b3581c8..96ec7cb84 100644 --- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt +++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt @@ -1,6 +1,7 @@ package io.github.wulkanowy import android.content.Context +import android.util.Log.DEBUG import android.util.Log.INFO import android.util.Log.VERBOSE import androidx.multidex.MultiDex @@ -11,11 +12,13 @@ import dagger.android.AndroidInjector import dagger.android.support.DaggerApplication import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.utils.Log +import fr.bipi.tressence.file.FileLoggerTree import io.github.wulkanowy.di.DaggerAppComponent import io.github.wulkanowy.services.sync.SyncWorkerFactory import io.github.wulkanowy.ui.base.ThemeManager import io.github.wulkanowy.utils.ActivityLifecycleLogger import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.CrashlyticsExceptionTree import io.github.wulkanowy.utils.CrashlyticsTree import io.github.wulkanowy.utils.DebugLogTree import io.github.wulkanowy.utils.initCrashlytics @@ -54,9 +57,17 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider { private fun initLogging() { if (appInfo.isDebug) { - Timber.plant(DebugLogTree()) FlexibleAdapter.enableLogs(Log.Level.DEBUG) + Timber.plant(DebugLogTree()) + Timber.plant(FileLoggerTree.Builder() + .withFileName("wulkanowy.%g.log") + .withDirName(applicationContext.filesDir.absolutePath) + .withFileLimit(10) + .withMinPriority(DEBUG) + .build() + ) } else { + Timber.plant(CrashlyticsExceptionTree()) Timber.plant(CrashlyticsTree()) } registerActivityLifecycleCallbacks(ActivityLifecycleLogger()) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/logger/LoggerRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/logger/LoggerRepository.kt new file mode 100644 index 000000000..be6ad2f48 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/logger/LoggerRepository.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.data.repositories.logger + +import android.content.Context +import io.reactivex.Single +import java.io.File +import java.io.FileNotFoundException +import javax.inject.Inject + +class LoggerRepository @Inject constructor(private val context: Context) { + + fun getLastLogLines(): Single> { + return getLastModified() + .map { it.readText() } + .map { it.split("\n") } + } + + fun getLogFiles(): Single> { + return Single.fromCallable { + File(context.filesDir.absolutePath).listFiles(File::isFile)?.filter { + it.name.endsWith(".log") + } + } + } + + private fun getLastModified(): Single { + return Single.fromCallable { + var lastModifiedTime = Long.MIN_VALUE + var chosenFile: File? = null + File(context.filesDir.absolutePath).listFiles(File::isFile)?.forEach { file -> + if (file.lastModified() > lastModifiedTime) { + lastModifiedTime = file.lastModified() + chosenFile = file + } + } + if (chosenFile == null) throw FileNotFoundException("Log file not found") + chosenFile + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt index d9430a47a..d92c3cd6f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt @@ -18,6 +18,7 @@ import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment import io.github.wulkanowy.ui.modules.about.license.LicenseFragment +import io.github.wulkanowy.ui.modules.about.logviewer.LogViewerFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.AppInfo @@ -110,6 +111,10 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { } } + override fun openLogViewer() { + if (appInfo.isDebug) (activity as? MainActivity)?.pushView(LogViewerFragment.newInstance()) + } + override fun openDiscordInvite() { context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt index 662939832..7e740b32b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt @@ -27,6 +27,11 @@ class AboutPresenter @Inject constructor( if (item !is AboutItem) return view?.run { when (item.title) { + versionRes?.first -> { + Timber.i("Opening log viewer") + openLogViewer() + analytics.logEvent("about_open", "name" to "log_viewer") + } feedbackRes?.first -> { Timber.i("Opening email client") openEmailClient() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt index 228225ec6..4bc0c3fe0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt @@ -25,6 +25,8 @@ interface AboutView : BaseView { fun updateData(header: AboutScrollableHeader, items: List) + fun openLogViewer() + fun openDiscordInvite() fun openEmailClient() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerAdapter.kt new file mode 100644 index 000000000..45c304aae --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerAdapter.kt @@ -0,0 +1,22 @@ +package io.github.wulkanowy.ui.modules.about.logviewer + +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView + +class LogViewerAdapter : RecyclerView.Adapter() { + + var lines = emptyList() + + class ViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder(TextView(parent.context)) + } + + override fun getItemCount() = lines.size + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.textView.text = lines[position] + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerFragment.kt new file mode 100644 index 000000000..0b7b05c78 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerFragment.kt @@ -0,0 +1,98 @@ +package io.github.wulkanowy.ui.modules.about.logviewer + +import android.content.Intent +import android.content.Intent.EXTRA_EMAIL +import android.content.Intent.EXTRA_STREAM +import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION +import android.net.Uri +import android.os.Build.VERSION.SDK_INT +import android.os.Build.VERSION_CODES.LOLLIPOP +import android.os.Bundle +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.core.content.FileProvider +import androidx.recyclerview.widget.LinearLayoutManager +import io.github.wulkanowy.BuildConfig.APPLICATION_ID +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import kotlinx.android.synthetic.main.fragment_logviewer.* +import java.io.File +import javax.inject.Inject + +class LogViewerFragment : BaseFragment(), LogViewerView, MainView.TitledView { + + @Inject + lateinit var presenter: LogViewerPresenter + + private val logAdapter = LogViewerAdapter() + + override val titleStringId: Int + get() = R.string.logviewer_title + + companion object { + fun newInstance() = LogViewerFragment() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_logviewer, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + messageContainer = logViewerRecycler + presenter.onAttachView(this) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.action_menu_logviewer, menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.logViewerMenuShare) presenter.onShareLogsSelected() + else false + } + + override fun initView() { + with(logViewerRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = logAdapter + } + + logViewRefreshButton.setOnClickListener { presenter.onRefreshClick() } + } + + override fun setLines(lines: List) { + logAdapter.lines = lines + logAdapter.notifyDataSetChanged() + logViewerRecycler.scrollToPosition(lines.size - 1) + } + + override fun shareLogs(files: List) { + val intent = Intent(Intent.ACTION_SEND_MULTIPLE).apply { + type = "text/plain" + putExtra(EXTRA_EMAIL, arrayOf("wulkanowyinc@gmail.com")) + addFlags(FLAG_GRANT_READ_URI_PERMISSION) + putParcelableArrayListExtra(EXTRA_STREAM, ArrayList(files.map { + if (SDK_INT < LOLLIPOP) Uri.fromFile(it) + else FileProvider.getUriForFile(requireContext(), "$APPLICATION_ID.fileprovider", it) + })) + } + + startActivity(Intent.createChooser(intent, getString(R.string.logviewer_share))) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerPresenter.kt new file mode 100644 index 000000000..4485cb3eb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerPresenter.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.ui.modules.about.logviewer + +import io.github.wulkanowy.data.repositories.logger.LoggerRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject + +class LogViewerPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val loggerRepository: LoggerRepository +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: LogViewerView) { + super.onAttachView(view) + view.initView() + loadLogFile() + } + + fun onShareLogsSelected(): Boolean { + disposable.add(loggerRepository.getLogFiles() + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + Timber.i("Loading logs files result: ${it.joinToString { it.name }}") + view?.shareLogs(it) + }, { + Timber.i("Loading logs files result: An exception occurred") + errorHandler.dispatch(it) + })) + return true + } + + fun onRefreshClick() { + loadLogFile() + } + + private fun loadLogFile() { + disposable.add(loggerRepository.getLastLogLines() + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + Timber.i("Loading last log file result: load ${it.size} lines") + view?.setLines(it) + }, { + Timber.i("Loading last log file result: An exception occurred") + errorHandler.dispatch(it) + })) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerView.kt new file mode 100644 index 000000000..bd98c24fd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerView.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.ui.modules.about.logviewer + +import io.github.wulkanowy.ui.base.BaseView +import java.io.File + +interface LogViewerView : BaseView { + + fun initView() + + fun setLines(lines: List) + + fun shareLogs(files: List) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt index a16e63208..b3a9e4a0a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt @@ -11,6 +11,7 @@ import io.github.wulkanowy.ui.modules.about.AboutFragment import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment import io.github.wulkanowy.ui.modules.about.license.LicenseFragment import io.github.wulkanowy.ui.modules.about.license.LicenseModule +import io.github.wulkanowy.ui.modules.about.logviewer.LogViewerFragment import io.github.wulkanowy.ui.modules.account.AccountDialog import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment import io.github.wulkanowy.ui.modules.attendance.AttendanceModule @@ -121,6 +122,10 @@ abstract class MainModule { @ContributesAndroidInjector(modules = [LicenseModule::class]) abstract fun bindLicenseFragment(): LicenseFragment + @PerFragment + @ContributesAndroidInjector + abstract fun bindLogViewerFragment(): LogViewerFragment + @PerFragment @ContributesAndroidInjector() abstract fun bindCreatorsFragment(): CreatorFragment diff --git a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt index f9db04653..15de40d37 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt @@ -18,6 +18,8 @@ class DebugLogTree : Timber.DebugTree() { } } +private fun Bundle?.checkSavedState() = if (this == null) "(STATE IS NULL)" else "" + class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks { override fun onActivityPaused(activity: Activity?) { @@ -45,7 +47,7 @@ class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks { } override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { - activity?.let { Timber.d("${it::class.java.simpleName} CREATED ${checkSavedState(savedInstanceState)}") } + activity?.let { Timber.d("${it::class.java.simpleName} CREATED ${savedInstanceState.checkSavedState()}") } } } @@ -53,7 +55,7 @@ class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks { class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLifecycleCallbacks() { override fun onFragmentViewCreated(fm: FragmentManager, f: Fragment, v: View, savedInstanceState: Bundle?) { - Timber.d("${f::class.java.simpleName} VIEW CREATED ${checkSavedState(savedInstanceState)}") + Timber.d("${f::class.java.simpleName} VIEW CREATED ${savedInstanceState.checkSavedState()}") } override fun onFragmentStopped(fm: FragmentManager, f: Fragment) { @@ -61,7 +63,7 @@ class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLi } override fun onFragmentCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) { - Timber.d("${f::class.java.simpleName} CREATED ${checkSavedState(savedInstanceState)}") + Timber.d("${f::class.java.simpleName} CREATED ${savedInstanceState.checkSavedState()}") } override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { @@ -89,7 +91,7 @@ class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLi } override fun onFragmentActivityCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) { - Timber.d("${f::class.java.simpleName} ACTIVITY CREATED ${checkSavedState(savedInstanceState)}") + Timber.d("${f::class.java.simpleName} ACTIVITY CREATED ${savedInstanceState.checkSavedState()}") } override fun onFragmentPaused(fm: FragmentManager, f: Fragment) { @@ -100,5 +102,3 @@ class FragmentLifecycleLogger @Inject constructor() : FragmentManager.FragmentLi Timber.d("${f::class.java.simpleName} DETACHED") } } - -private fun checkSavedState(savedInstanceState: Bundle?) = if (savedInstanceState == null) "(STATE IS NULL)" else "" diff --git a/app/src/main/res/drawable/ic_refresh.xml b/app/src/main/res/drawable/ic_refresh.xml new file mode 100644 index 000000000..cc2d1e04f --- /dev/null +++ b/app/src/main/res/drawable/ic_refresh.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/fragment_logviewer.xml b/app/src/main/res/layout/fragment_logviewer.xml new file mode 100644 index 000000000..64f9125fb --- /dev/null +++ b/app/src/main/res/layout/fragment_logviewer.xml @@ -0,0 +1,34 @@ + + + + + + + + + + diff --git a/app/src/main/res/menu/action_menu_logviewer.xml b/app/src/main/res/menu/action_menu_logviewer.xml new file mode 100644 index 000000000..cb2b31a0f --- /dev/null +++ b/app/src/main/res/menu/action_menu_logviewer.xml @@ -0,0 +1,11 @@ + + + + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index d6ef19d71..e45c6d62d 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -306,6 +306,11 @@ Awatar Zobacz więcej na GitHub + + Przeglądarka logów + Share logs + Odśwież + Treść diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 6a994f6d6..30e4ef80e 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -295,6 +295,16 @@ Лицензия + + аватар + Смотрите больше на GitHub + + + Просмотр журнала + Share logs + Обновление + + Содержание Снова diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e2afb061a..9090fc6c8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,6 +12,7 @@ Settings More About + Log viewer Creators Licenses Messages @@ -287,6 +288,10 @@ Avatar See more on GitHub + + Share logs + Refresh + Content diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml new file mode 100644 index 000000000..f0da4709a --- /dev/null +++ b/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/play/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt b/app/src/play/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt index 4f76ef837..5c661f1ae 100644 --- a/app/src/play/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt +++ b/app/src/play/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt @@ -1,10 +1,12 @@ package io.github.wulkanowy.utils import android.content.Context +import android.util.Log import com.crashlytics.android.Crashlytics import com.crashlytics.android.core.CrashlyticsCore +import fr.bipi.tressence.crash.CrashlyticsLogExceptionTree +import fr.bipi.tressence.crash.CrashlyticsLogTree import io.fabric.sdk.android.Fabric -import timber.log.Timber fun initCrashlytics(context: Context, appInfo: AppInfo) { Fabric.with(Fabric.Builder(context) @@ -19,13 +21,6 @@ fun initCrashlytics(context: Context, appInfo: AppInfo) { .build()) } -class CrashlyticsTree : Timber.Tree() { +class CrashlyticsTree : CrashlyticsLogTree(Log.VERBOSE) - override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - Crashlytics.setInt("priority", priority) - Crashlytics.setString("tag", tag) - - if (t == null) Crashlytics.log(message) - else Crashlytics.logException(t) - } -} +class CrashlyticsExceptionTree : CrashlyticsLogExceptionTree() From 96c1bb4c69f175aacd65b3109183270ae8fff1fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Mon, 24 Feb 2020 00:24:40 +0100 Subject: [PATCH 14/41] Automatically switch semesters without sync (#681) --- app/build.gradle | 4 +- .../data/db/migrations/Migration13Test.kt | 54 ++++----- .../attendance/AttendanceLocalTest.kt | 22 ++-- .../CompletedLessonsLocalTest.kt | 18 +-- .../data/repositories/exam/ExamLocalTest.kt | 23 ++-- .../data/repositories/grade/GradeLocalTest.kt | 2 +- .../GradeStatisticsLocalTest.kt | 31 ++--- .../luckynumber/LuckyNumberLocalTest.kt | 2 +- .../timetable/TimetableLocalTest.kt | 2 +- .../wulkanowy/data/db/entities/Semester.kt | 7 +- .../repositories/semester/SemesterLocal.kt | 2 +- .../repositories/semester/SemesterRemote.kt | 1 - .../semester/SemesterRepository.kt | 33 ++--- .../ui/modules/grade/GradePresenter.kt | 18 +-- .../wulkanowy/utils/SemesterExtension.kt | 19 +++ .../github/wulkanowy/utils/TimeExtension.kt | 17 --- .../res/layout/fragment_grade_statistics.xml | 1 - .../semester/SemesterRepositoryTest.kt | 113 ++++++++++++++++-- .../semester/TestSemesterEnityCreator.kt | 21 ++-- .../modules/grade/GradeAverageProviderTest.kt | 9 +- 20 files changed, 246 insertions(+), 153 deletions(-) create mode 100644 app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt diff --git a/app/build.gradle b/app/build.gradle index 95522dd32..0bd259edd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -122,10 +122,10 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:sdk:0.15.0" + implementation "io.github.wulkanowy:sdk:7e89883" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation "androidx.core:core-ktx:1.2.0-rc01" + implementation "androidx.core:core-ktx:1.2.0" implementation "androidx.activity:activity-ktx:1.1.0" implementation "androidx.appcompat:appcompat:1.1.0" implementation "androidx.appcompat:appcompat-resources:1.1.0" diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt index 8311f94b5..6f9406f6d 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt @@ -8,7 +8,6 @@ import io.github.wulkanowy.data.db.entities.Semester import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test -import org.threeten.bp.LocalDate.now import org.threeten.bp.LocalDate.of import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -104,45 +103,45 @@ class Migration13Test : AbstractMigrationTest() { val db = helper.runMigrationsAndValidate(dbName, 13, true, Migration13()) val semesters1 = getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 1 AND class_id = 5") - assertTrue { semesters1.single { it.isCurrent }.isCurrent } + assertTrue { semesters1.single { it.second }.second } semesters1[0].run { - assertFalse(isCurrent) - assertEquals(1, semesterId) - assertEquals(1, diaryId) + assertFalse(second) + assertEquals(1, first.semesterId) + assertEquals(1, first.diaryId) } semesters1[2].run { - assertFalse(isCurrent) - assertEquals(3, semesterId) - assertEquals(2, diaryId) + assertFalse(second) + assertEquals(3, first.semesterId) + assertEquals(2, first.diaryId) } semesters1[3].run { - assertTrue(isCurrent) - assertEquals(4, semesterId) - assertEquals(2, diaryId) + assertTrue(second) + assertEquals(4, first.semesterId) + assertEquals(2, first.diaryId) } getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let { - assertTrue { it.single { it.isCurrent }.isCurrent } - assertEquals(1970, it[0].schoolYear) - assertEquals(of(1970, 1, 1), it[0].end) - assertEquals(of(1970, 1, 1), it[0].start) - assertFalse(it[0].isCurrent) - assertFalse(it[1].isCurrent) - assertFalse(it[2].isCurrent) - assertTrue(it[3].isCurrent) + assertTrue { it.single { it.second }.second } + assertEquals(1970, it[0].first.schoolYear) + assertEquals(of(1970, 1, 1), it[0].first.end) + assertEquals(of(1970, 1, 1), it[0].first.start) + assertFalse(it[0].second) + assertFalse(it[1].second) + assertFalse(it[2].second) + assertTrue(it[3].second) } getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let { - assertTrue { it.single { it.isCurrent }.isCurrent } - assertFalse(it[0].isCurrent) - assertFalse(it[1].isCurrent) - assertFalse(it[2].isCurrent) - assertTrue(it[3].isCurrent) + assertTrue { it.single { it.second }.second } + assertFalse(it[0].second) + assertFalse(it[1].second) + assertFalse(it[2].second) + assertTrue(it[3].second) } } - private fun getSemesters(db: SupportSQLiteDatabase, query: String): List { - val semesters = mutableListOf() + private fun getSemesters(db: SupportSQLiteDatabase, query: String): List> { + val semesters = mutableListOf>() val cursor = db.query(query) if (cursor.moveToFirst()) { @@ -153,13 +152,12 @@ class Migration13Test : AbstractMigrationTest() { diaryName = cursor.getString(3), semesterId = cursor.getInt(4), semesterName = cursor.getInt(5), - isCurrent = cursor.getInt(6) == 1, classId = cursor.getInt(7), unitId = cursor.getInt(8), schoolYear = cursor.getInt(9), start = Converters().timestampToDate(cursor.getLong(10))!!, end = Converters().timestampToDate(cursor.getLong(11))!! - )) + ) to (cursor.getInt(6) == 1)) } while (cursor.moveToNext()) } return semesters.toList() diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt index 5fd5b193a..c7ede6ae5 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt @@ -10,8 +10,8 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate.now +import org.threeten.bp.LocalDate.of import kotlin.test.assertEquals @RunWith(AndroidJUnit4::class) @@ -35,19 +35,19 @@ class AttendanceLocalTest { @Test fun saveAndReadTest() { attendanceLocal.saveAttendance(listOf( - Attendance(1, 2, 3, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name), - Attendance(1, 2, 3, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.WAITING.name), - Attendance(1, 2, 3, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name) + Attendance(1, 2, 3, of(2018, 9, 10), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name), + Attendance(1, 2, 3, of(2018, 9, 14), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.WAITING.name), + Attendance(1, 2, 3, of(2018, 9, 17), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name) )) val attendance = attendanceLocal - .getAttendance(Semester(1, 2, "", 1, 3, 2019, true, now(), now(), 1, 1), - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 14) - ) - .blockingGet() + .getAttendance(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1), + of(2018, 9, 10), + of(2018, 9, 14) + ) + .blockingGet() assertEquals(2, attendance.size) - assertEquals(attendance[0].date, LocalDate.of(2018, 9, 10)) - assertEquals(attendance[1].date, LocalDate.of(2018, 9, 14)) + assertEquals(attendance[0].date, of(2018, 9, 10)) + assertEquals(attendance[1].date, of(2018, 9, 14)) } } diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt index 356073e8e..50badc468 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt @@ -11,6 +11,8 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDate.now +import org.threeten.bp.LocalDate.of import kotlin.test.assertEquals @RunWith(AndroidJUnit4::class) @@ -35,20 +37,20 @@ class CompletedLessonsLocalTest { @Test fun saveAndReadTest() { completedLessonsLocal.saveCompletedLessons(listOf( - getCompletedLesson(LocalDate.of(2018, 9, 10), 1), - getCompletedLesson(LocalDate.of(2018, 9, 14), 2), - getCompletedLesson(LocalDate.of(2018, 9, 17), 3) + getCompletedLesson(of(2018, 9, 10), 1), + getCompletedLesson(of(2018, 9, 14), 2), + getCompletedLesson(of(2018, 9, 17), 3) )) val completed = completedLessonsLocal - .getCompletedLessons(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1), - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 14) + .getCompletedLessons(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1), + of(2018, 9, 10), + of(2018, 9, 14) ) .blockingGet() assertEquals(2, completed.size) - assertEquals(completed[0].date, LocalDate.of(2018, 9, 10)) - assertEquals(completed[1].date, LocalDate.of(2018, 9, 14)) + assertEquals(completed[0].date, of(2018, 9, 10)) + assertEquals(completed[1].date, of(2018, 9, 14)) } private fun getCompletedLesson(date: LocalDate, number: Int): CompletedLesson { diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt index fb76306d9..98dfc88eb 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt @@ -10,7 +10,8 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDate.now +import org.threeten.bp.LocalDate.of import kotlin.test.assertEquals @RunWith(AndroidJUnit4::class) @@ -34,19 +35,19 @@ class ExamLocalTest { @Test fun saveAndReadTest() { examLocal.saveExams(listOf( - Exam(1, 2, LocalDate.of(2018, 9, 10), LocalDate.now(), "", "", "", "", "", ""), - Exam(1, 2, LocalDate.of(2018, 9, 14), LocalDate.now(), "", "", "", "", "", ""), - Exam(1, 2, LocalDate.of(2018, 9, 17), LocalDate.now(), "", "", "", "", "", "") + Exam(1, 2, of(2018, 9, 10), now(), "", "", "", "", "", ""), + Exam(1, 2, of(2018, 9, 14), now(), "", "", "", "", "", ""), + Exam(1, 2, of(2018, 9, 17), now(), "", "", "", "", "", "") )) val exams = examLocal - .getExams(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1), - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 14) - ) - .blockingGet() + .getExams(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1), + of(2018, 9, 10), + of(2018, 9, 14) + ) + .blockingGet() assertEquals(2, exams.size) - assertEquals(exams[0].date, LocalDate.of(2018, 9, 10)) - assertEquals(exams[1].date, LocalDate.of(2018, 9, 14)) + assertEquals(exams[0].date, of(2018, 9, 10)) + assertEquals(exams[1].date, of(2018, 9, 14)) } } diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt index 954d0eea2..7233c306a 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt @@ -40,7 +40,7 @@ class GradeLocalTest { createGradeLocal(3, 5.0, LocalDate.of(2019, 2, 28), "", 2) )) - val semester = Semester(1, 2, "", 2019, 2, 1, true, now(), now(), 1, 1) + val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1) val grades = gradeLocal .getGrades(semester) diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt index f8aa58158..0d27b9bb7 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt @@ -11,7 +11,7 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDate.now import kotlin.test.assertEquals @RunWith(AndroidJUnit4::class) @@ -40,10 +40,7 @@ class GradeStatisticsLocalTest { getGradeStatistics("Fizyka", 1, 2) )) - val stats = gradeStatisticsLocal.getGradesStatistics( - Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false, - "Matematyka" - ).blockingGet() + val stats = gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Matematyka").blockingGet() assertEquals(1, stats.size) assertEquals(stats[0].subject, "Matematyka") } @@ -56,10 +53,7 @@ class GradeStatisticsLocalTest { getGradeStatistics("Fizyka", 1, 2) )) - val stats = gradeStatisticsLocal.getGradesStatistics( - Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false, - "Wszystkie" - ).blockingGet() + val stats = gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Wszystkie").blockingGet() assertEquals(1, stats.size) assertEquals(stats[0].subject, "Wszystkie") } @@ -72,10 +66,7 @@ class GradeStatisticsLocalTest { getGradePointsStatistics("Fizyka", 1, 2) )) - val stats = gradeStatisticsLocal.getGradesPointsStatistics( - Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), - "Matematyka" - ).blockingGet() + val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka").blockingGet() with(stats) { assertEquals(subject, "Matematyka") assertEquals(others, 5.0) @@ -87,10 +78,7 @@ class GradeStatisticsLocalTest { fun saveAndRead_subjectEmpty() { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) - val stats = gradeStatisticsLocal.getGradesPointsStatistics( - Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), - "Matematyka" - ).blockingGet() + val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka").blockingGet() assertEquals(null, stats) } @@ -98,13 +86,14 @@ class GradeStatisticsLocalTest { fun saveAndRead_allEmpty() { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) - val stats = gradeStatisticsLocal.getGradesPointsStatistics( - Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), - "Wszystkie" - ).blockingGet() + val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Wszystkie").blockingGet() assertEquals(null, stats) } + private fun getSemester(): Semester { + return Semester(2, 2, "", 2019, 1, 2, now(), now(), 1, 1) + } + private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradeStatistics { return GradeStatistics(studentId, semesterId, subject, 5, 5, false) } diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt index 77ddafb9c..9091a45de 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt @@ -36,7 +36,7 @@ class LuckyNumberLocalTest { fun saveAndReadTest() { luckyNumberLocal.saveLuckyNumber(LuckyNumber(1, LocalDate.of(2019, 1, 20), 14)) - val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1), + val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, 2019, LocalDate.now(), LocalDate.now(), 1, 1), LocalDate.of(2019, 1, 20) ).blockingGet() diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt index 6406d7435..a66e5843c 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt @@ -41,7 +41,7 @@ class TimetableLocalTest { )) val exams = timetableDb.getTimetable( - Semester(1, 2, "", 1, 1, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1), + Semester(1, 2, "", 1, 1, 2019, LocalDate.now(), LocalDate.now(), 1, 1), LocalDate.of(2018, 9, 10), LocalDate.of(2018, 9, 14) ).blockingGet() diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt index 6c06be111..0641e0b6d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt @@ -27,9 +27,6 @@ data class Semester( @ColumnInfo(name = "semester_name") val semesterName: Int, - @ColumnInfo(name = "is_current") - val isCurrent: Boolean, - val start: LocalDate, val end: LocalDate, @@ -43,4 +40,8 @@ data class Semester( @PrimaryKey(autoGenerate = true) var id: Long = 0 + + + @ColumnInfo(name = "is_current") + var current: Boolean = false } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt index b9750e7d5..3d98785cd 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt @@ -19,6 +19,6 @@ class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) { } fun getSemesters(student: Student): Maybe> { - return semesterDb.loadAll(student.studentId, student.classId).filter { !it.isEmpty() } + return semesterDb.loadAll(student.studentId, student.classId).filter { it.isNotEmpty() } } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt index 55d13a765..f7b98a4b7 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt @@ -20,7 +20,6 @@ class SemesterRemote @Inject constructor(private val sdk: Sdk) { schoolYear = it.schoolYear, semesterId = it.semesterId, semesterName = it.semesterNumber, - isCurrent = it.current, start = it.start, end = it.end, classId = it.classId, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt index 6377cb78e..a13658209 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt @@ -5,10 +5,11 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter import io.github.wulkanowy.data.SdkHelper import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.utils.getCurrentOrLast +import io.github.wulkanowy.utils.isCurrent import io.github.wulkanowy.utils.uniqueSubtract import io.reactivex.Maybe import io.reactivex.Single -import timber.log.Timber import java.net.UnknownHostException import javax.inject.Inject import javax.inject.Singleton @@ -21,28 +22,30 @@ class SemesterRepository @Inject constructor( private val sdkHelper: SdkHelper ) { - fun getSemesters(student: Student, forceRefresh: Boolean = false): Single> { + fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false): Single> { return Maybe.just(sdkHelper.init(student)) - .flatMap { local.getSemesters(student).filter { !forceRefresh } } + .flatMap { + local.getSemesters(student).filter { !forceRefresh }.filter { + if (refreshOnNoCurrent) { + it.any { semester -> semester.isCurrent } + } else true + } + } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getSemesters(student) else Single.error(UnknownHostException()) + if (it) remote.getSemesters(student) + else Single.error(UnknownHostException()) }.flatMap { new -> - val currentSemesters = new.filter { it.isCurrent } - if (currentSemesters.size == 1) { - local.getSemesters(student).toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteSemesters(old.uniqueSubtract(new)) - local.saveSemesters(new.uniqueSubtract(old)) - } - } else { - Timber.i("Current semesters list:\n${new.joinToString(separator = "\n")}") - throw IllegalArgumentException("Current semester can be only one.") + if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!") + + local.getSemesters(student).toSingle(emptyList()).doOnSuccess { old -> + local.deleteSemesters(old.uniqueSubtract(new)) + local.saveSemesters(new.uniqueSubtract(old)) } }.flatMap { local.getSemesters(student).toSingle(emptyList()) }) } fun getCurrentSemester(student: Student, forceRefresh: Boolean = false): Single { - return getSemesters(student, forceRefresh).map { item -> item.single { it.isCurrent } } + return getSemesters(student, forceRefresh).map { it.getCurrentOrLast() } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt index 948d5a949..78885ebd5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt @@ -7,7 +7,9 @@ import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.getCurrentOrLast import timber.log.Timber +import java.util.concurrent.TimeUnit.MILLISECONDS import javax.inject.Inject class GradePresenter @Inject constructor( @@ -98,17 +100,16 @@ class GradePresenter @Inject constructor( private fun loadData() { Timber.i("Loading grade data started") disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it) } + .flatMap { semesterRepository.getSemesters(it, refreshOnNoCurrent = true) } + .delay(200, MILLISECONDS) .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) - .doFinally { view?.showProgress(false) } .subscribe({ - it.first { item -> item.isCurrent }.also { current -> - selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex - schoolYear = current.schoolYear - semesters = it.filter { semester -> semester.diaryId == current.diaryId } - view?.setCurrentSemesterName(current.semesterName, schoolYear) - } + val current = it.getCurrentOrLast() + selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex + schoolYear = current.schoolYear + semesters = it.filter { semester -> semester.diaryId == current.diaryId } + view?.setCurrentSemesterName(current.semesterName, schoolYear) view?.run { Timber.i("Loading grade result: Attempt load index $currentPageIndex") @@ -125,6 +126,7 @@ class GradePresenter @Inject constructor( private fun showErrorViewOnError(message: String, error: Throwable) { lastError = error view?.run { + showProgress(false) showErrorView(true) setErrorDetails(message) } diff --git a/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt new file mode 100644 index 000000000..b3c479c38 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.utils + +import io.github.wulkanowy.data.db.entities.Semester +import org.threeten.bp.LocalDate.now + +inline val Semester.isCurrent: Boolean + get() = now() in start..end + +fun List.getCurrentOrLast(): Semester { + if (isEmpty()) throw RuntimeException("Empty semester list") + + // when there is only one current semester + singleOrNull { it.isCurrent }?.let { return it } + + // when there is more than one current semester - find one with higher id + singleOrNull { semester -> semester.semesterId == maxBy { it.semesterId }?.semesterId }?.let { return it } + + throw IllegalArgumentException("Duplicated last semester! Semesters: ${joinToString(separator = "\n")}") +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt index a2672f865..a91f823fa 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt @@ -1,42 +1,30 @@ package io.github.wulkanowy.utils -import org.threeten.bp.DateTimeUtils import org.threeten.bp.DayOfWeek.FRIDAY import org.threeten.bp.DayOfWeek.MONDAY import org.threeten.bp.DayOfWeek.SATURDAY import org.threeten.bp.DayOfWeek.SUNDAY -import org.threeten.bp.Instant -import org.threeten.bp.Instant.ofEpochMilli import org.threeten.bp.LocalDate import org.threeten.bp.LocalDateTime import org.threeten.bp.Month -import org.threeten.bp.ZoneId import org.threeten.bp.format.DateTimeFormatter.ofPattern import org.threeten.bp.format.TextStyle.FULL_STANDALONE import org.threeten.bp.temporal.TemporalAdjusters.firstInMonth import org.threeten.bp.temporal.TemporalAdjusters.next import org.threeten.bp.temporal.TemporalAdjusters.previous -import java.util.Date import java.util.Locale private const val DATE_PATTERN = "dd.MM.yyyy" -fun Date.toLocalDate(): LocalDate = Instant.ofEpochMilli(time).atZone(ZoneId.systemDefault()).toLocalDate() - -fun Date.toLocalDateTime(): LocalDateTime = ofEpochMilli(time).atZone(ZoneId.systemDefault()).toLocalDateTime() - fun String.toLocalDate(format: String = DATE_PATTERN): LocalDate = LocalDate.parse(this, ofPattern(format)) fun LocalDate.toFormattedString(format: String = DATE_PATTERN): String = format(ofPattern(format)) fun LocalDateTime.toFormattedString(format: String = DATE_PATTERN): String = format(ofPattern(format)) -fun LocalDateTime.toDate(): Date = DateTimeUtils.toDate(atZone(ZoneId.systemDefault()).toInstant()) - /** * https://github.com/ThreeTen/threetenbp/issues/55 */ - fun Month.getFormattedName(): String { return getDisplayName(FULL_STANDALONE, Locale.getDefault()) .let { @@ -93,9 +81,6 @@ inline val LocalDate.previousOrSameSchoolDay: LocalDate inline val LocalDate.weekDayName: String get() = format(ofPattern("EEEE", Locale.getDefault())) -inline val LocalDate.shortcutWeekDayName: String - get() = format(ofPattern("EEE", Locale.getDefault())) - inline val LocalDate.monday: LocalDate get() = with(MONDAY) @@ -105,7 +90,6 @@ inline val LocalDate.friday: LocalDate /** * [Dz.U. 2016 poz. 1335](http://prawo.sejm.gov.pl/isap.nsf/DocDetails.xsp?id=WDU20160001335) */ - inline val LocalDate.isHolidays: Boolean get() = isBefore(firstSchoolDay) && isAfter(lastSchoolDay) @@ -121,7 +105,6 @@ inline val LocalDate.lastSchoolDay: LocalDate get() = LocalDate.of(year, 6, 20) .with(next(FRIDAY)) - private fun Int.getSchoolYearByMonth(monthValue: Int): Int { return when (monthValue) { in 9..12 -> this diff --git a/app/src/main/res/layout/fragment_grade_statistics.xml b/app/src/main/res/layout/fragment_grade_statistics.xml index 10c8bb195..3ed80d106 100644 --- a/app/src/main/res/layout/fragment_grade_statistics.xml +++ b/app/src/main/res/layout/fragment_grade_statistics.xml @@ -31,7 +31,6 @@ android:spinnerMode="dialog" /> - ()).`when`(semesterLocal).getSemesters(student) doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student) - semesterRepository.getSemesters(student).blockingGet() + semesterRepository.getSemesters(student, refreshOnNoCurrent = true).blockingGet() + + verify(semesterLocal).deleteSemesters(emptyList()) + verify(semesterLocal).saveSemesters(semesters) + } + + @Test + fun getSemesters_noCurrent_refreshOnNoCurrent() { + val semesters = listOf( + createSemesterEntity(0, 0, now().minusMonths(12), now().minusMonths(6)), + createSemesterEntity(0, 0, now().minusMonths( 6), now().minusMonths(1)) + ) + + doNothing().`when`(sdkHelper).init(student) + doReturn(Maybe.just(semesters)).`when`(semesterLocal).getSemesters(student) + doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student) + + val items = semesterRepository.getSemesters(student, refreshOnNoCurrent = true).blockingGet() + assertEquals(2, items.size) + } + + @Test + fun getSemesters_doubleCurrent_refreshOnNoCurrent() { + val semesters = listOf( + createSemesterEntity(0, 0, now(), now()), + createSemesterEntity(0, 0, now(), now()) + ) + + doNothing().`when`(sdkHelper).init(student) + doReturn(Maybe.just(semesters)).`when`(semesterLocal).getSemesters(student) + + val items = semesterRepository.getSemesters(student, refreshOnNoCurrent = true).blockingGet() + assertEquals(2, items.size) + } + + @Test(expected = IllegalArgumentException::class) + fun getCurrentSemester_doubleCurrent() { + val semesters = listOf( + createSemesterEntity(0, 0, now(), now()), + createSemesterEntity(0, 0, now(), now()) + ) + + doNothing().`when`(sdkHelper).init(student) + doReturn(Maybe.just(semesters)).`when`(semesterLocal).getSemesters(student) + + semesterRepository.getCurrentSemester(student).blockingGet() + } + + @Test(expected = RuntimeException::class) + fun getCurrentSemester_emptyList() { + doNothing().`when`(sdkHelper).init(student) + doReturn(Maybe.just(emptyList())).`when`(semesterLocal).getSemesters(student) + + semesterRepository.getCurrentSemester(student).blockingGet() } } diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/semester/TestSemesterEnityCreator.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/semester/TestSemesterEnityCreator.kt index f628710fc..fc90135f9 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/semester/TestSemesterEnityCreator.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/semester/TestSemesterEnityCreator.kt @@ -1,20 +1,19 @@ package io.github.wulkanowy.data.repositories.semester import io.github.wulkanowy.data.db.entities.Semester -import org.threeten.bp.LocalDate.now +import org.threeten.bp.LocalDate -fun createSemesterEntity(current: Boolean): Semester { +fun createSemesterEntity(diaryId: Int, semesterId: Int, start: LocalDate, end: LocalDate, semesterName: Int = 1): Semester { return Semester( - studentId = 0, - diaryId = 0, - semesterId = 0, - diaryName = "", + studentId = 1, + diaryId = diaryId, + semesterId = semesterId, + diaryName = "$semesterId", schoolYear = 1970, classId = 0, - isCurrent = current, - semesterName = 0, - unitId = 0, - start = now(), - end = now() + semesterName = semesterName, + unitId = 1, + start = start, + end = end ) } diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt index 432ca6d99..e1fee990b 100644 --- a/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt +++ b/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt @@ -2,11 +2,11 @@ package io.github.wulkanowy.ui.modules.grade import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.grade.GradeRepository import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.semester.createSemesterEntity import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import org.junit.Assert.assertEquals @@ -16,6 +16,7 @@ import org.mockito.Mock import org.mockito.Mockito.doReturn import org.mockito.MockitoAnnotations import org.threeten.bp.LocalDate.now +import org.threeten.bp.LocalDate.of import org.threeten.bp.LocalDateTime class GradeAverageProviderTest { @@ -34,9 +35,9 @@ class GradeAverageProviderTest { private val student = Student("", "", "", "SCRAPPER", "", "", false, "", "", "", 101, 0, "", "", "", "", 1, true, LocalDateTime.now()) private val semesters = mutableListOf( - Semester(101, 10, "", 1, 21, 1, false, now(), now(), 1, 1), - Semester(101, 11, "", 1, 22, 1, false, now(), now(), 1, 1), - Semester(101, 11, "", 1, 23, 2, true, now(), now(), 1, 1) + createSemesterEntity(10, 21, of(2019, 1, 31), of(2019, 6, 23)), + createSemesterEntity(11, 22, of(2019, 9, 1), of(2020, 1, 31)), + createSemesterEntity(11, 23, of(2020, 2, 1), now(), semesterName = 2) ) private val firstGrades = listOf( From 18d6ec6961aba1450daa36261531930f29a8b375 Mon Sep 17 00:00:00 2001 From: doteq Date: Thu, 27 Feb 2020 00:10:11 +0100 Subject: [PATCH 15/41] Add account recover (#635) --- .idea/codeStyles/Project.xml | 3 - app/build.gradle | 4 +- .../repositories/recover/RecoverRemote.kt | 19 ++ .../repositories/recover/RecoverRepository.kt | 26 ++ .../ui/modules/login/LoginActivity.kt | 8 +- .../ui/modules/login/LoginErrorHandler.kt | 3 + .../wulkanowy/ui/modules/login/LoginModule.kt | 5 + .../ui/modules/login/LoginPresenter.kt | 8 +- .../login/advanced/LoginAdvancedFragment.kt | 45 +-- .../modules/login/form/LoginFormFragment.kt | 9 +- .../modules/login/form/LoginFormPresenter.kt | 4 + .../ui/modules/login/form/LoginFormView.kt | 2 + .../login/recover/LoginRecoverFragment.kt | 209 ++++++++++++++ .../login/recover/LoginRecoverPresenter.kt | 157 ++++++++++ .../modules/login/recover/LoginRecoverView.kt | 54 ++++ .../login/recover/RecoverErrorHandler.kt | 32 +++ .../main/res/layout/fragment_login_form.xml | 22 +- .../res/layout/fragment_login_recover.xml | 272 ++++++++++++++++++ app/src/main/res/values-pl/strings.xml | 8 +- app/src/main/res/values-ru/strings.xml | 6 + app/src/main/res/values/strings.xml | 6 + 21 files changed, 867 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/recover/RecoverRemote.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/recover/RecoverRepository.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverView.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt create mode 100644 app/src/main/res/layout/fragment_login_recover.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 15a4246be..d66c3361f 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,9 +1,6 @@ + + + app:layout_constraintTop_toBottomOf="@+id/loginFormRecoverLink"> + + + + + + + + + + + + + + + + + + + + + + + + + + +