From 040857ba20ce34aef3aa5cbda7f59db172f5a385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 19 Mar 2019 13:23:52 +0100 Subject: [PATCH 001/322] Change grade weightValue type to double (#285) --- .travis.yml | 6 ++-- app/build.gradle | 2 +- .../data/repositories/grade/GradeLocalTest.kt | 6 ++-- .../repositories/grade/GradeRepositoryTest.kt | 22 ++++++------ .../grade/TestGradeEntityCreator.kt | 4 +-- .../github/wulkanowy/data/db/AppDatabase.kt | 6 ++-- .../wulkanowy/data/db/entities/Grade.kt | 2 +- .../data/db/migrations/Migration11.kt | 34 +++++++++++++++++++ .../wulkanowy/utils/GradeExtensionTest.kt | 24 ++++++------- 9 files changed, 71 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt diff --git a/.travis.yml b/.travis.yml index e1d86ee3..8920018e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,9 +11,9 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ -#branches: -# only: -# - master +branches: + only: + - master android: licenses: diff --git a/app/build.gradle b/app/build.gradle index b18a22cc..f7fd82c0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('io.github.wulkanowy:api:0.7.0') { exclude module: "threetenbp" } + implementation('com.github.wulkanowy:api:0aea843800') { exclude module: "threetenbp" } implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" 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 e50a52ff..36238f1b 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 @@ -34,9 +34,9 @@ class GradeLocalTest { @Test fun saveAndReadTest() { gradeLocal.saveGrades(listOf( - createGradeLocal(5, 3, LocalDate.of(2018, 9, 10), "", 1), - createGradeLocal(4, 4, LocalDate.of(2019, 2, 27), "", 2), - createGradeLocal(3, 5, LocalDate.of(2019, 2, 28), "", 2) + createGradeLocal(5, 3.0, LocalDate.of(2018, 9, 10), "", 1), + createGradeLocal(4, 4.0, LocalDate.of(2019, 2, 27), "", 2), + createGradeLocal(3, 5.0, LocalDate.of(2019, 2, 28), "", 2) )) val grades = gradeLocal diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt index 7da2d455..17e788fc 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt @@ -71,10 +71,10 @@ class GradeRepositoryTest { @Test fun markOlderThanRegisterDateAsRead() { every { mockApi.getGrades(1) } returns Single.just(listOf( - createGradeApi(5, 4, of(2019, 2, 25), "Ocena pojawiła się"), - createGradeApi(5, 4, of(2019, 2, 26), "przed zalogowanie w aplikacji"), - createGradeApi(5, 4, of(2019, 2, 27), "Ocena z dnia logowania"), - createGradeApi(5, 4, of(2019, 2, 28), "Ocena jeszcze nowsza") + createGradeApi(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"), + createGradeApi(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"), + createGradeApi(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"), + createGradeApi(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza") )) val grades = GradeRepository(settings, gradeLocal, gradeRemote) @@ -89,16 +89,16 @@ class GradeRepositoryTest { @Test fun mitigateOldGradesNotifications() { gradeLocal.saveGrades(listOf( - createGradeLocal(5, 3, of(2019, 2, 25), "Jedna ocena"), - createGradeLocal(4, 4, of(2019, 2, 26), "Druga"), - createGradeLocal(3, 5, of(2019, 2, 27), "Trzecia") + createGradeLocal(5, 3.0, of(2019, 2, 25), "Jedna ocena"), + createGradeLocal(4, 4.0, of(2019, 2, 26), "Druga"), + createGradeLocal(3, 5.0, of(2019, 2, 27), "Trzecia") )) every { mockApi.getGrades(1) } returns Single.just(listOf( - createGradeApi(5, 2, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"), - createGradeApi(4, 3, of(2019, 2, 26), "starszą niż ostatnia lokalnie"), - createGradeApi(3, 4, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"), - createGradeApi(2, 5, of(2019, 2, 28), "Ta jest już w ogóle nowa") + createGradeApi(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"), + createGradeApi(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"), + createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"), + createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa") )) val grades = GradeRepository(settings, gradeLocal, gradeRemote) diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt index 485e5692..e0fd05a8 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt @@ -5,7 +5,7 @@ import org.threeten.bp.LocalDate import io.github.wulkanowy.api.grades.Grade as GradeRemote import io.github.wulkanowy.data.db.entities.Grade as GradeLocal -fun createGradeLocal(value: Int, weight: Int, date: LocalDate, desc: String, semesterId: Int = 1): GradeLocal { +fun createGradeLocal(value: Int, weight: Double, date: LocalDate, desc: String, semesterId: Int = 1): GradeLocal { return GradeLocal( semesterId = semesterId, studentId = 1, @@ -24,7 +24,7 @@ fun createGradeLocal(value: Int, weight: Int, date: LocalDate, desc: String, sem ) } -fun createGradeApi(value: Int, weight: Int, date: LocalDate, desc: String): GradeRemote { +fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String): GradeRemote { return GradeRemote().apply { this.value = value this.weightValue = weight diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index 1601c91a..4fefd9c9 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -41,6 +41,7 @@ import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Subject import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.migrations.Migration10 +import io.github.wulkanowy.data.db.migrations.Migration11 import io.github.wulkanowy.data.db.migrations.Migration2 import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration4 @@ -79,7 +80,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 10 + const val VERSION_SCHEMA = 11 fun newInstance(context: Context): AppDatabase { return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database") @@ -95,7 +96,8 @@ abstract class AppDatabase : RoomDatabase() { Migration7(), Migration8(), Migration9(), - Migration10() + Migration10(), + Migration11() ) .build() } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt index fdf25c9e..1221a7aa 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt @@ -34,7 +34,7 @@ data class Grade( val weight: String, - val weightValue: Int, + val weightValue: Double, val date: LocalDate, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt new file mode 100644 index 00000000..cb437c0e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt @@ -0,0 +1,34 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration11 : Migration(10, 11) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Grades_temp ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + is_read INTEGER NOT NULL, + is_notified INTEGER NOT NULL, + semester_id INTEGER NOT NULL, + student_id INTEGER NOT NULL, + subject TEXT NOT NULL, + entry TEXT NOT NULL, + value INTEGER NOT NULL, + modifier REAL NOT NULL, + comment TEXT NOT NULL, + color TEXT NOT NULL, + grade_symbol TEXT NOT NULL, + description TEXT NOT NULL, + weight TEXT NOT NULL, + weightValue REAL NOT NULL, + date INTEGER NOT NULL, + teacher TEXT NOT NULL + ) + """) + database.execSQL("INSERT INTO Grades_temp SELECT * FROM Grades") + database.execSQL("DROP TABLE Grades") + database.execSQL("ALTER TABLE Grades_temp RENAME TO Grades") + } +} diff --git a/app/src/test/java/io/github/wulkanowy/utils/GradeExtensionTest.kt b/app/src/test/java/io/github/wulkanowy/utils/GradeExtensionTest.kt index 12a086ed..3b1ab648 100644 --- a/app/src/test/java/io/github/wulkanowy/utils/GradeExtensionTest.kt +++ b/app/src/test/java/io/github/wulkanowy/utils/GradeExtensionTest.kt @@ -22,11 +22,11 @@ class GradeExtensionTest { @Test fun calcWeightedAverage() { assertEquals(3.47, listOf( - createGrade(5, 6, 0.33), - createGrade(5, 5, -0.33), - createGrade(4, 1, 0.0), - createGrade(1, 9, 0.5), - createGrade(0, 0, 0.0) + createGrade(5, 6.0, 0.33), + createGrade(5, 5.0, -0.33), + createGrade(4, 1.0, 0.0), + createGrade(1, 9.0, 0.5), + createGrade(0, .0, 0.0) ).calcAverage(), 0.005) } @@ -42,23 +42,23 @@ class GradeExtensionTest { @Test fun changeModifier_default() { - assertEquals(.33, createGrade(5, 0, .33).changeModifier(.0, .0).modifier, .0) - assertEquals(-.33, createGrade(5, 0, -.33).changeModifier(.0, .0).modifier, .0) + assertEquals(.33, createGrade(5, .0, .33).changeModifier(.0, .0).modifier, .0) + assertEquals(-.33, createGrade(5, .0, -.33).changeModifier(.0, .0).modifier, .0) } @Test fun changeModifier_plus() { - assertEquals(.33, createGrade(5, 0, .25).changeModifier(.33, .50).modifier, .0) - assertEquals(.25, createGrade(5, 0, .33).changeModifier(.25, .0).modifier, .0) + assertEquals(.33, createGrade(5, .0, .25).changeModifier(.33, .50).modifier, .0) + assertEquals(.25, createGrade(5, .0, .33).changeModifier(.25, .0).modifier, .0) } @Test fun changeModifier_minus() { - assertEquals(-.33, createGrade(5, 0, -.25).changeModifier(.25, .33).modifier, .0) - assertEquals(-.25, createGrade(5, 0, -.33).changeModifier(.0, .25).modifier, .0) + assertEquals(-.33, createGrade(5, .0, -.25).changeModifier(.25, .33).modifier, .0) + assertEquals(-.25, createGrade(5, .0, -.33).changeModifier(.0, .25).modifier, .0) } - private fun createGrade(value: Int, weightValue: Int = 0, modifier: Double = 0.25): Grade { + private fun createGrade(value: Int, weightValue: Double = .0, modifier: Double = 0.25): Grade { return Grade( semesterId = 1, studentId = 1, From 8db73e94596a1a269b088cfc1c0844bdb6bc688f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Tue, 19 Mar 2019 18:14:55 +0100 Subject: [PATCH 002/322] Fix the application finish after selecting an account (#286) --- app/src/main/AndroidManifest.xml | 8 +------- .../ui/modules/message/send/SendMessageActivity.kt | 4 ++++ .../ui/modules/message/send/SendMessagePresenter.kt | 5 +++++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 43b94805..352b6d2b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -39,18 +39,12 @@ android:name=".ui.modules.main.MainActivity" android:configChanges="orientation|screenSize" android:label="@string/main_title" - android:launchMode="singleTop" android:theme="@style/WulkanowyTheme.NoActionBar" /> - - + android:theme="@style/WulkanowyTheme.NoActionBar" /> = emptyList() From 575e244b3a82e2cccc60d2caa737df787b6cc7bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Wed, 20 Mar 2019 20:45:26 +0100 Subject: [PATCH 003/322] Add swipe refresh to grade fragment (#287) --- .travis.yml | 1 + app/build.gradle | 2 +- .../ui/modules/grade/GradeFragment.kt | 21 +++++++++++++++++-- .../ui/modules/grade/GradePresenter.kt | 20 ++++++++++++++++-- .../wulkanowy/ui/modules/grade/GradeView.kt | 8 ++++++- app/src/main/res/layout/fragment_grade.xml | 16 +++++++++----- 6 files changed, 57 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8920018e..b3261931 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ cache: branches: only: - master + - 0.7.x android: licenses: diff --git a/app/build.gradle b/app/build.gradle index f7fd82c0..f581ba51 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('com.github.wulkanowy:api:0aea843800') { exclude module: "threetenbp" } + implementation('com.github.wulkanowy:api:bc60169383') { exclude module: "threetenbp" } implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" 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 8fdecda2..20d3fad7 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 @@ -29,6 +29,8 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView, @Inject lateinit var pagerAdapter: BaseFragmentPagerAdapter + private var semesterSwitchMenu: MenuItem? = null + companion object { private const val SAVED_SEMESTER_KEY = "CURRENT_SEMESTER" @@ -57,6 +59,8 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView, override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { inflater?.inflate(R.menu.action_menu_grade, menu) + semesterSwitchMenu = menu?.findItem(R.id.gradeMenuSemester) + presenter.onCreateMenu() } override fun initView() { @@ -75,6 +79,7 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView, setOnSelectPageListener { presenter.onPageSelected(it) } } gradeTabLayout.setupWithViewPager(gradeViewPager) + gradeSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } } override fun onOptionsItemSelected(item: MenuItem?): Boolean { @@ -95,8 +100,20 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView, gradeProgress.visibility = if (show) VISIBLE else INVISIBLE } - override fun showEmpty() { - gradeEmpty.visibility = VISIBLE + override fun showEmpty(show: Boolean) { + gradeEmpty.visibility = if (show) VISIBLE else INVISIBLE + } + + override fun showRefresh(show: Boolean) { + gradeSwipe.isRefreshing = show + } + + override fun showSemesterSwitch(show: Boolean) { + semesterSwitchMenu?.isVisible = show + } + + override fun enableSwipe(enable: Boolean) { + gradeSwipe.isEnabled = enable } override fun showSemesterDialog(selectedIndex: Int) { 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 cc127a90..98eb2a9f 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 @@ -33,11 +33,18 @@ class GradePresenter @Inject constructor( disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread) .subscribe { selectedIndex = savedIndex ?: 0 - view.initView() + view.run { + initView() + enableSwipe(false) + } loadData() }) } + fun onCreateMenu() { + if (semesters.isEmpty()) view?.showSemesterSwitch(false) + } + fun onViewReselected() { Timber.i("Grade view is reselected") view?.run { notifyChildParentReselected(currentPageIndex) } @@ -69,6 +76,7 @@ class GradePresenter @Inject constructor( view?.apply { showContent(true) showProgress(false) + showEmpty(false) loadedSemesterId[currentPageIndex] = semesterId } } @@ -77,6 +85,10 @@ class GradePresenter @Inject constructor( loadChild(index) } + fun onSwipeRefresh() { + loadData() + } + private fun loadData() { Timber.i("Loading grade data started") disposable.add(studentRepository.getCurrentStudent() @@ -89,17 +101,21 @@ class GradePresenter @Inject constructor( } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) + .doFinally { view?.showRefresh(false) } .subscribe({ view?.run { Timber.i("Loading grade result: Attempt load index $currentPageIndex") loadChild(currentPageIndex) + enableSwipe(false) + showSemesterSwitch(true) } }) { Timber.i("Loading grade result: An exception occurred") errorHandler.dispatch(it) view?.run { showProgress(false) - showEmpty() + showEmpty(true) + enableSwipe(true) } }) } 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 3f1a197c..9fdd46b1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeView.kt @@ -12,10 +12,16 @@ interface GradeView : BaseSessionView { fun showProgress(show: Boolean) - fun showEmpty() + fun showEmpty(show: Boolean) + + fun showRefresh(show: Boolean) + + fun showSemesterSwitch(show: Boolean) fun showSemesterDialog(selectedIndex: Int) + fun enableSwipe(enable: Boolean) + fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) fun notifyChildParentReselected(index: Int) diff --git a/app/src/main/res/layout/fragment_grade.xml b/app/src/main/res/layout/fragment_grade.xml index 6cd22636..bf805175 100644 --- a/app/src/main/res/layout/fragment_grade.xml +++ b/app/src/main/res/layout/fragment_grade.xml @@ -17,13 +17,19 @@ app:tabTextColor="@android:color/white" tools:visibility="visible" /> - + android:layout_marginTop="48dp"> + + + Date: Wed, 20 Mar 2019 20:52:51 +0100 Subject: [PATCH 004/322] Version 0.7.1 --- .travis.yml | 8 ++++---- app/build.gradle | 6 +++--- app/src/main/play/release-notes/pl-PL/default.txt | 2 -- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index b3261931..56c64858 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,10 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ -branches: - only: - - master - - 0.7.x +#branches: +# only: +# - master +# - 0.7.x android: licenses: diff --git a/app/build.gradle b/app/build.gradle index f581ba51..4b471bdc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 targetSdkVersion 28 - versionCode 26 - versionName "0.7.0" + versionCode 27 + versionName "0.7.1" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -77,7 +77,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('com.github.wulkanowy:api:bc60169383') { exclude module: "threetenbp" } + implementation('io.github.wulkanowy:api:0.7.1') { exclude module: "threetenbp" } implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 2d079ff9..6f8a6b0a 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,5 +1,3 @@ -Wersja 0.7.0 - Dodaliśmy: - szczęśliwy numerek - lekcje zrealizowane From 20d0abba29d4b7644574e818d9dd568c1d97f789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Thu, 21 Mar 2019 22:34:41 +0100 Subject: [PATCH 005/322] Fix empty container id in grade fragemnt adapter (#289) --- .../ui/modules/grade/GradePresenter.kt | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) 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 98eb2a9f..1251ee2d 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,9 +7,7 @@ import io.github.wulkanowy.ui.base.session.BaseSessionPresenter import io.github.wulkanowy.ui.base.session.SessionErrorHandler import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider -import io.reactivex.Completable import timber.log.Timber -import java.util.concurrent.TimeUnit.MILLISECONDS import javax.inject.Inject class GradePresenter @Inject constructor( @@ -30,15 +28,12 @@ class GradePresenter @Inject constructor( fun onAttachView(view: GradeView, savedIndex: Int?) { super.onAttachView(view) Timber.i("Grade view is attached") - disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread) - .subscribe { - selectedIndex = savedIndex ?: 0 - view.run { - initView() - enableSwipe(false) - } - loadData() - }) + selectedIndex = savedIndex ?: 0 + view.run { + initView() + enableSwipe(false) + } + loadData() } fun onCreateMenu() { From cd1ceea8606427311a1bc50abff4dd9d463ff155 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Thu, 21 Mar 2019 22:55:47 +0100 Subject: [PATCH 006/322] Add messages forwarding (#288) --- .../data/db/migrations/Migration11.kt | 2 +- .../message/preview/MessagePreviewFragment.kt | 16 ++++++++--- .../preview/MessagePreviewPresenter.kt | 19 ++++++++----- .../message/preview/MessagePreviewView.kt | 4 ++- .../message/send/SendMessageActivity.kt | 9 ++++--- .../message/send/SendMessagePresenter.kt | 27 ++++++++++++------- .../res/drawable/ic_message_forward_24dp.xml | 10 +++++++ .../res/menu/action_menu_message_preview.xml | 6 +++++ app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 10 files changed, 71 insertions(+), 24 deletions(-) create mode 100644 app/src/main/res/drawable/ic_message_forward_24dp.xml diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt index cb437c0e..6d129bca 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt @@ -8,7 +8,7 @@ class Migration11 : Migration(10, 11) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL(""" CREATE TABLE IF NOT EXISTS Grades_temp ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + id INTEGER PRIMARY KEY NOT NULL, is_read INTEGER NOT NULL, is_notified INTEGER NOT NULL, semester_id INTEGER NOT NULL, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt index 102567ed..9afb744f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -25,6 +25,7 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi lateinit var presenter: MessagePreviewPresenter private var menuReplyButton: MenuItem? = null + private var menuForwardButton: MenuItem? = null override val titleStringId: Int get() = R.string.message_title @@ -60,12 +61,16 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { inflater?.inflate(R.menu.action_menu_message_preview, menu) menuReplyButton = menu?.findItem(R.id.messagePreviewMenuReply) + menuForwardButton = menu?.findItem(R.id.messagePreviewMenuForward) presenter.onCreateOptionsMenu() } override fun onOptionsItemSelected(item: MenuItem?): Boolean { - return if (item?.itemId == R.id.messagePreviewMenuReply) presenter.onReply() - else false + return when (item?.itemId) { + R.id.messagePreviewMenuReply -> presenter.onReply() + R.id.messagePreviewMenuForward -> presenter.onForward() + else -> false + } } override fun setSubject(subject: String) { @@ -92,8 +97,9 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi messagePreviewProgress.visibility = if (show) VISIBLE else GONE } - override fun showReplyButton(show: Boolean) { + override fun showOptions(show: Boolean) { menuReplyButton?.isVisible = show + menuForwardButton?.isVisible = show } override fun showMessageError() { @@ -101,6 +107,10 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi } override fun openMessageReply(message: Message?) { + context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message, true)) } + } + + override fun openMessageForward(message: Message?) { context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message)) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt index a5ed9f28..534c7ad3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -22,7 +22,7 @@ class MessagePreviewPresenter @Inject constructor( var messageId: Int = 0 - private var replyMessage: Message? = null + private var message: Message? = null fun onAttachView(view: MessagePreviewView, id: Int) { super.onAttachView(view) @@ -41,13 +41,13 @@ class MessagePreviewPresenter @Inject constructor( .doFinally { view?.showProgress(false) } .subscribe({ message -> Timber.i("Loading message $id preview result: Success ") - replyMessage = message + this@MessagePreviewPresenter.message = message view?.run { message.let { setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString) setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) setContent(it.content.orEmpty()) - showReplyButton(true) + showOptions(true) if (it.recipient.isNotBlank()) setRecipient(it.recipient) else setSender(it.sender) @@ -63,13 +63,20 @@ class MessagePreviewPresenter @Inject constructor( } fun onReply(): Boolean { - return if (replyMessage != null) { - view?.openMessageReply(replyMessage) + return if (message != null) { + view?.openMessageReply(message) + true + } else false + } + + fun onForward(): Boolean { + return if (message != null) { + view?.openMessageForward(message) true } else false } fun onCreateOptionsMenu() { - view?.showReplyButton(replyMessage != null) + view?.showOptions(message != null) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt index 464ea168..3ff73396 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt @@ -19,9 +19,11 @@ interface MessagePreviewView : BaseSessionView { fun showProgress(show: Boolean) - fun showReplyButton(show: Boolean) + fun showOptions(show: Boolean) fun showMessageError() fun openMessageReply(message: Message?) + + fun openMessageForward(message: Message?) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt index edf14dd6..6ac7e226 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt @@ -26,11 +26,14 @@ class SendMessageActivity : BaseActivity(), SendMessageView { companion object { private const val EXTRA_MESSAGE = "EXTRA_MESSAGE" + private const val EXTRA_REPLY = "EXTRA_REPLY" fun getStartIntent(context: Context) = Intent(context, SendMessageActivity::class.java) - fun getStartIntent(context: Context, message: Message?): Intent { - return getStartIntent(context).putExtra(EXTRA_MESSAGE, message) + fun getStartIntent(context: Context, message: Message?, reply: Boolean = false): Intent { + return getStartIntent(context) + .putExtra(EXTRA_MESSAGE, message) + .putExtra(EXTRA_REPLY, reply) } } @@ -59,7 +62,7 @@ class SendMessageActivity : BaseActivity(), SendMessageView { supportActionBar?.setDisplayHomeAsUpEnabled(true) messageContainer = sendMessageContainer - presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message) + presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, intent.getSerializableExtra(EXTRA_REPLY) as Boolean) } override fun onCreateOptionsMenu(menu: Menu?): Boolean { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt index a3715aaa..f57848d6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt @@ -30,18 +30,25 @@ class SendMessagePresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(errorHandler) { - fun onAttachView(view: SendMessageView, message: Message?) { + fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean) { super.onAttachView(view) Timber.i("Send message view is attached") - loadData(message) + loadData(message, reply) view.apply { message?.let { - setSubject("RE: ${message.subject}") - if (preferencesRepository.fillMessageContent) { - setContent(when (message.sender.isNotEmpty()) { - true -> "\n\nOd: ${message.sender}\n" - false -> "\n\nDo: ${message.recipient}\n" - } + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}") + setSubject(when (reply) { + true -> "RE: " + false -> "FE: " + } + message.subject) + if (preferencesRepository.fillMessageContent || !reply) { + setContent( + when (reply) { + true -> "\n\n" + false -> "" + } + when (message.sender.isNotEmpty()) { + true -> "Od: ${message.sender}\n" + false -> "Do: ${message.recipient}\n" + } + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}") } } } @@ -52,7 +59,7 @@ class SendMessagePresenter @Inject constructor( return true } - private fun loadData(message: Message?) { + private fun loadData(message: Message?, reply: Boolean) { var reportingUnit: ReportingUnit? = null var recipients: List = emptyList() var selectedRecipient: List = emptyList() @@ -69,7 +76,7 @@ class SendMessagePresenter @Inject constructor( recipients = it } .flatMapCompletable { - if (message == null) Completable.complete() + if (message == null || !reply) Completable.complete() else recipientRepository.getMessageRecipients(student, message) .doOnSuccess { Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", it.size) diff --git a/app/src/main/res/drawable/ic_message_forward_24dp.xml b/app/src/main/res/drawable/ic_message_forward_24dp.xml new file mode 100644 index 00000000..2b7c32c7 --- /dev/null +++ b/app/src/main/res/drawable/ic_message_forward_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/menu/action_menu_message_preview.xml b/app/src/main/res/menu/action_menu_message_preview.xml index 4c2f5920..6ba2c828 100644 --- a/app/src/main/res/menu/action_menu_message_preview.xml +++ b/app/src/main/res/menu/action_menu_message_preview.xml @@ -7,4 +7,10 @@ android:orderInCategory="1" android:title="@string/message_reply" app:showAsAction="ifRoom" /> + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index dcc73526..16ec15ff 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -140,6 +140,7 @@ Do: Data: %s Odpowiedz + Prześlij dalej Temat Treść Wiadomość wysłana pomyślnie diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1debed7b..98bef391 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -134,6 +134,7 @@ To: Date: %s Reply + Forward Subject Content Message sent successfully From 4b78862486c960a16a60250cc3232d6c0cd4cccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Fri, 22 Mar 2019 23:41:41 +0100 Subject: [PATCH 007/322] Remove retry sync work when completed lessons is disabled (#294) --- .../main/java/io/github/wulkanowy/services/sync/SyncWorker.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt index ec753b7e..86a3dd61 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt @@ -11,6 +11,7 @@ import androidx.work.WorkerParameters import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import io.github.wulkanowy.R +import io.github.wulkanowy.api.interceptor.FeatureDisabledException import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository @@ -43,7 +44,8 @@ class SyncWorker @AssistedInject constructor( .toSingleDefault(Result.success()) .onErrorReturn { Timber.e(it, "There was an error during synchronization") - Result.retry() + if (it is FeatureDisabledException) Result.success() + else Result.retry() } .doOnSuccess { if (preferencesRepository.isDebugNotificationEnable) notify(it) } } From 36785f019a4a04c1907aef1713f3ba6fca63d056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Fri, 22 Mar 2019 23:54:58 +0100 Subject: [PATCH 008/322] Fix restoring the grade fragment (#293) --- .../io/github/wulkanowy/ui/modules/grade/GradePresenter.kt | 2 +- .../java/io/github/wulkanowy/ui/modules/main/MainActivity.kt | 4 +++- .../java/io/github/wulkanowy/ui/modules/main/MainModule.kt | 2 -- 3 files changed, 4 insertions(+), 4 deletions(-) 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 1251ee2d..2357e063 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 @@ -77,7 +77,7 @@ class GradePresenter @Inject constructor( } fun onPageSelected(index: Int) { - loadChild(index) + if (semesters.isNotEmpty()) loadChild(index) } fun onSwipeRefresh() { 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 5e0ed2b2..0d9a6351 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 @@ -2,6 +2,8 @@ package io.github.wulkanowy.ui.modules.main import android.content.Context import android.content.Intent +import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.os.Bundle import android.view.Menu import android.view.MenuItem @@ -165,7 +167,7 @@ class MainActivity : BaseActivity(), MainView { override fun openLoginView() { startActivity(LoginActivity.getStartIntent(this) - .apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) }) + .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) }) } override fun onSaveInstanceState(outState: Bundle?) { 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 d1afd9c0..de4405b7 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 @@ -5,7 +5,6 @@ import dagger.Module import dagger.Provides import dagger.android.ContributesAndroidInjector import io.github.wulkanowy.R -import io.github.wulkanowy.di.scopes.PerActivity import io.github.wulkanowy.di.scopes.PerFragment import io.github.wulkanowy.ui.modules.about.AboutFragment import io.github.wulkanowy.ui.modules.about.AboutModule @@ -33,7 +32,6 @@ abstract class MainModule { companion object { @JvmStatic - @PerActivity @Provides fun provideFragNavController(activity: MainActivity): FragNavController { return FragNavController(activity.supportFragmentManager, R.id.mainFragmentContainer) From 316cd2f7f9b9f74973d2b02fb669eb295b78cb8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sat, 23 Mar 2019 00:37:13 +0100 Subject: [PATCH 009/322] Add checking current student in background services (#295) --- .travis.yml | 8 ++++---- .../wulkanowy/services/sync/SyncWorker.kt | 17 +++++++++++------ .../widgets/timetable/TimetableWidgetFactory.kt | 11 ++++++++--- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 56c64858..b3261931 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,10 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ -#branches: -# only: -# - master -# - 0.7.x +branches: + only: + - master + - 0.7.x android: licenses: diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt index 86a3dd61..a6e44b42 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt @@ -34,12 +34,17 @@ class SyncWorker @AssistedInject constructor( ) : RxWorker(appContext, workerParameters) { override fun createWork(): Single { - return studentRepository.getCurrentStudent() - .flatMapCompletable { student -> - semesterRepository.getCurrentSemester(student, true) - .flatMapCompletable { semester -> - Completable.mergeDelayError(works.map { it.create(student, semester) }) - } + return studentRepository.isStudentSaved() + .flatMapCompletable { isSaved -> + if (isSaved) { + studentRepository.getCurrentStudent() + .flatMapCompletable { student -> + semesterRepository.getCurrentSemester(student) + .flatMapCompletable { semester -> + Completable.mergeDelayError(works.map { it.create(student, semester) }) + } + } + } else Completable.complete() } .toSingleDefault(Result.success()) .onErrorReturn { diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt index 63560807..f2d40ba7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt @@ -17,6 +17,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.toFormattedString +import io.reactivex.Single import org.threeten.bp.LocalDate import timber.log.Timber @@ -51,9 +52,13 @@ class TimetableWidgetFactory( ?.let { date -> try { lessons = studentRepository.isStudentSaved() - .flatMap { studentRepository.getCurrentStudent() } - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { timetableRepository.getTimetable(it, date, date) } + .flatMap { isSaved -> + if (isSaved) { + studentRepository.getCurrentStudent() + .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { timetableRepository.getTimetable(it, date, date) } + } else Single.just(emptyList()) + } .map { item -> item.sortedBy { it.number } } .subscribeOn(schedulers.backgroundThread) .blockingGet() From 2d6610e05c807f0c9aa52e5cd0ab4da310b766be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sat, 23 Mar 2019 01:12:17 +0100 Subject: [PATCH 010/322] Set max concurrency in sync worker (#296) --- .../main/java/io/github/wulkanowy/services/sync/SyncWorker.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt index a6e44b42..5d0162f1 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt @@ -19,6 +19,7 @@ import io.github.wulkanowy.services.sync.channels.DebugChannel import io.github.wulkanowy.services.sync.works.Work import io.github.wulkanowy.utils.getCompatColor import io.reactivex.Completable +import io.reactivex.Flowable import io.reactivex.Single import timber.log.Timber import kotlin.random.Random @@ -41,7 +42,7 @@ class SyncWorker @AssistedInject constructor( .flatMapCompletable { student -> semesterRepository.getCurrentSemester(student) .flatMapCompletable { semester -> - Completable.mergeDelayError(works.map { it.create(student, semester) }) + Completable.mergeDelayError(Flowable.fromIterable(works.map { it.create(student, semester) }), 3) } } } else Completable.complete() From fc9981aa5d0ce863362491841068cead74aaf367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 23 Mar 2019 13:01:01 +0100 Subject: [PATCH 011/322] Fix issues when loading lucky number (#297) --- .../ui/modules/luckynumber/LuckyNumberPresenter.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt index 92ee4e25..74ab0d1f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt @@ -22,7 +22,11 @@ class LuckyNumberPresenter @Inject constructor( override fun onAttachView(view: LuckyNumberView) { super.onAttachView(view) Timber.i("Lucky number view is attached") - view.initView() + view.run { + initView() + showContent(false) + enableSwipe(false) + } loadData() } @@ -35,12 +39,6 @@ class LuckyNumberPresenter @Inject constructor( .flatMapMaybe { luckyNumberRepository.getLuckyNumber(it, forceRefresh) } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) - .doOnSubscribe { - view?.run { - showContent(false) - enableSwipe(false) - } - } .doFinally { view?.run { hideRefresh() From e1a83927c481c7465374358de0697ad1b8d8849a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sat, 23 Mar 2019 14:44:52 +0100 Subject: [PATCH 012/322] Fix reset button in timetable widget (#298) --- app/src/main/AndroidManifest.xml | 1 + .../wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 352b6d2b..0c6edab4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -52,6 +52,7 @@ diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt index 290007e4..402366ec 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt @@ -62,7 +62,7 @@ class TimetableWidgetProvider : BroadcastReceiver() { private fun onUpdate(context: Context, intent: Intent) { if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) { - intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS).forEach { appWidgetId -> + intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId -> updateWidget(context, appWidgetId, now().nextOrSameSchoolDay) } } else { @@ -95,7 +95,7 @@ class TimetableWidgetProvider : BroadcastReceiver() { .apply { action = createWidgetKey(appWidgetId) }) setOnClickPendingIntent(R.id.timetableWidgetNext, createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT)) setOnClickPendingIntent(R.id.timetableWidgetPrev, createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV)) - createNavIntent(context, Int.MAX_VALUE, appWidgetId, BUTTON_RESET).also { + createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET).also { setOnClickPendingIntent(R.id.timetableWidgetDate, it) setOnClickPendingIntent(R.id.timetableWidgetDay, it) } From 68b98479273e3c61ac2d46d76399d557b09ad1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sat, 23 Mar 2019 16:35:33 +0100 Subject: [PATCH 013/322] Fix reselecting root fragments (#299) --- .../java/io/github/wulkanowy/ui/modules/main/MainActivity.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 0d9a6351..85aa94a1 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 @@ -5,6 +5,7 @@ import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.os.Bundle +import android.os.Handler import android.view.Menu import android.view.MenuItem import androidx.appcompat.app.AlertDialog @@ -146,7 +147,9 @@ class MainActivity : BaseActivity(), MainView { } override fun notifyMenuViewReselected() { - (navController.currentStack?.get(0) as? MainView.MainChildView)?.onFragmentReselected() + Handler().postDelayed({ + (navController.currentStack?.get(0) as? MainView.MainChildView)?.onFragmentReselected() + }, 250) } fun showDialogFragment(dialog: DialogFragment) { From 6bb03b3be8487ce16209e178719df490e7639814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 23 Mar 2019 17:29:34 +0100 Subject: [PATCH 014/322] Fix day navigation unevenition (#301) --- app/build.gradle | 2 +- .../wulkanowy/ui/modules/attendance/AttendancePresenter.kt | 2 +- .../github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt | 2 +- .../ui/modules/timetable/completed/CompletedLessonsPresenter.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 4b471bdc..c0f7e085 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,7 +77,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('io.github.wulkanowy:api:0.7.1') { exclude module: "threetenbp" } + implementation('com.github.wulkanowy:api:f60d2b8') { exclude module: "threetenbp" } implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt index 988681b1..ec471c16 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -135,7 +135,7 @@ class AttendancePresenter @Inject constructor( clearData() showNextButton(!currentDate.plusDays(1).isHolidays) showPreButton(!currentDate.minusDays(1).isHolidays) - updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize()) + updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize()) } } } 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 3786a362..ebda3931 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 @@ -128,7 +128,7 @@ class TimetablePresenter @Inject constructor( clearData() showNextButton(!currentDate.plusDays(1).isHolidays) showPreButton(!currentDate.minusDays(1).isHolidays) - updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize()) + updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize()) } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt index ddc81818..6a14acda 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt @@ -112,7 +112,7 @@ class CompletedLessonsPresenter @Inject constructor( clearData() showNextButton(!currentDate.plusDays(1).isHolidays) showPreButton(!currentDate.minusDays(1).isHolidays) - updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize()) + updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize()) } } } From 1f65b8465e76232a76c25e62e09f5359f6fd0d17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sat, 23 Mar 2019 18:35:56 +0100 Subject: [PATCH 015/322] Add logging to sync worker (#300) --- .../io/github/wulkanowy/di/BuilderModule.kt | 4 --- .../wulkanowy/services/ServicesModule.kt | 5 ++++ .../wulkanowy/services/sync/SyncWorker.kt | 28 +++++++++++-------- .../services/sync/works/RecipientWork.kt | 4 +-- .../ui/modules/about/AboutFragment.kt | 5 ++-- .../statistics/GradeStatisticsPresenter.kt | 4 +-- .../ui/modules/main/MainPresenter.kt | 2 +- 7 files changed, 30 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt index 2616acc3..7f477630 100644 --- a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt +++ b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt @@ -3,7 +3,6 @@ package io.github.wulkanowy.di import dagger.Module import dagger.android.ContributesAndroidInjector import io.github.wulkanowy.di.scopes.PerActivity -import io.github.wulkanowy.services.widgets.TimetableWidgetService import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.login.LoginModule import io.github.wulkanowy.ui.modules.main.MainActivity @@ -30,9 +29,6 @@ internal abstract class BuilderModule { @ContributesAndroidInjector abstract fun bindMessageSendActivity(): SendMessageActivity - @ContributesAndroidInjector - abstract fun bindTimetableWidgetService(): TimetableWidgetService - @ContributesAndroidInjector abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider } 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 70cbf84c..84409a84 100644 --- a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt +++ b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt @@ -9,6 +9,7 @@ import com.squareup.inject.assisted.dagger2.AssistedModule import dagger.Binds import dagger.Module import dagger.Provides +import dagger.android.ContributesAndroidInjector import dagger.multibindings.IntoSet import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork import io.github.wulkanowy.services.sync.works.AttendanceWork @@ -24,6 +25,7 @@ import io.github.wulkanowy.services.sync.works.NoteWork import io.github.wulkanowy.services.sync.works.RecipientWork import io.github.wulkanowy.services.sync.works.TimetableWork import io.github.wulkanowy.services.sync.works.Work +import io.github.wulkanowy.services.widgets.TimetableWidgetService import javax.inject.Singleton @AssistedModule @@ -48,6 +50,9 @@ abstract class ServicesModule { fun provideNotificationManager(context: Context) = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager } + @ContributesAndroidInjector + abstract fun bindTimetableWidgetService(): TimetableWidgetService + @Binds @IntoSet abstract fun provideGradeWork(work: GradeWork): Work diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt index 5d0162f1..36593966 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt @@ -35,17 +35,20 @@ class SyncWorker @AssistedInject constructor( ) : RxWorker(appContext, workerParameters) { override fun createWork(): Single { + Timber.i("SyncWorker is starting") return studentRepository.isStudentSaved() - .flatMapCompletable { isSaved -> - if (isSaved) { - studentRepository.getCurrentStudent() - .flatMapCompletable { student -> - semesterRepository.getCurrentSemester(student) - .flatMapCompletable { semester -> - Completable.mergeDelayError(Flowable.fromIterable(works.map { it.create(student, semester) }), 3) - } - } - } else Completable.complete() + .filter { true } + .flatMap { studentRepository.getCurrentStudent().toMaybe() } + .flatMapCompletable { student -> + semesterRepository.getCurrentSemester(student, true) + .flatMapCompletable { semester -> + Completable.mergeDelayError(Flowable.fromIterable(works.map { work -> + work.create(student, semester) + .doOnSubscribe { Timber.i("${work::class.java.simpleName} is starting") } + .doOnError { Timber.i("${work::class.java.simpleName} result: An exception occurred") } + .doOnComplete { Timber.i("${work::class.java.simpleName} result: Success") } + }), 3) + } } .toSingleDefault(Result.success()) .onErrorReturn { @@ -53,7 +56,10 @@ class SyncWorker @AssistedInject constructor( if (it is FeatureDisabledException) Result.success() else Result.retry() } - .doOnSuccess { if (preferencesRepository.isDebugNotificationEnable) notify(it) } + .doOnSuccess { + if (preferencesRepository.isDebugNotificationEnable) notify(it) + Timber.i("SyncWorker result: $it") + } } private fun notify(result: Result) { diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt index af8de434..fa610dee 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt @@ -13,10 +13,10 @@ class RecipientWork @Inject constructor( ) : Work { override fun create(student: Student, semester: Semester): Completable { - return reportingUnitRepository.getReportingUnits(student) + return reportingUnitRepository.getReportingUnits(student, true) .flatMapCompletable { units -> Completable.mergeDelayError(units.map { - recipientRepository.getRecipients(student, 2, it).ignoreElement() + recipientRepository.getRecipients(student, 2, it, true).ignoreElement() }) } } 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 1002f914..52caf731 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 @@ -43,7 +43,8 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { .withAboutSpecial3(getString(R.string.about_feedback)) .withFields(R.string::class.java.fields) .withCheckCachedDetection(false) - .withExcludedLibraries("fastadapter", "AndroidIconics", "gson", "Jsoup", "Retrofit", "okio", "OkHttp") + .withExcludedLibraries("fastadapter", "AndroidIconics", "Jsoup", "Retrofit", "okio", + "OkHttp", "Butterknife", "CircleImageView") .withOnExtraListener { presenter.onExtraSelect(it) }) }.let { fragmentCompat.onCreateView(inflater.context, inflater, container, savedInstanceState, it) @@ -68,7 +69,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { data = Uri.parse("mailto:") putExtra(Intent.EXTRA_EMAIL, Array(1) { "wulkanowyinc@gmail.com" }) putExtra(Intent.EXTRA_SUBJECT, "Zgłoszenie błędu") - putExtra(Intent.EXTRA_TEXT,"Tu umieść treść zgłoszenia\n\n" + "-".repeat(40) + "\n" + """ + putExtra(Intent.EXTRA_TEXT, "Tu umieść treść zgłoszenia\n\n" + "-".repeat(40) + "\n" + """ Build: ${BuildConfig.VERSION_CODE} SDK: ${android.os.Build.VERSION.SDK_INT} Device: ${android.os.Build.MANUFACTURER} ${android.os.Build.MODEL} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt index 5111420d..5dd485cd 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt @@ -63,7 +63,7 @@ class GradeStatisticsPresenter @Inject constructor( } fun onSubjectSelected(name: String) { - Timber.i("Select attendance stats subject $name") + Timber.i("Select grade stats subject $name") view?.run { showContent(false) showProgress(true) @@ -77,7 +77,7 @@ class GradeStatisticsPresenter @Inject constructor( } fun onTypeChange(isSemester: Boolean) { - Timber.i("Select attendance stats semester: $isSemester") + Timber.i("Select grade stats semester: $isSemester") disposable.clear() view?.run { showContent(false) 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 a2a973c6..30f3b920 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 @@ -24,9 +24,9 @@ class MainPresenter @Inject constructor( fun onAttachView(view: MainView, initMenuIndex: Int) { super.onAttachView(view) - Timber.i("Main view is attached with $initMenuIndex menu index") view.run { startMenuIndex = if (initMenuIndex != -1) initMenuIndex else prefRepository.startMenuIndex + Timber.i("Main view is attached with $startMenuIndex menu index") initView() } From d178c15d2f71491b6dfbbdb9e76ab8ad8d6130e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sun, 24 Mar 2019 16:03:51 +0100 Subject: [PATCH 016/322] Update dependencies (#302) --- app/build.gradle | 26 +++++++++---------- .../wulkanowy/ui/modules/main/MainActivity.kt | 5 ---- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c0f7e085..453765fd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -86,10 +86,14 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.multidex:multidex:2.0.1' - implementation 'com.takisoft.preferencex:preferencex:1.0.0' + implementation "androidx.work:work-runtime:2.0.0" + implementation "androidx.work:work-rxjava2:2.0.0" - implementation "android.arch.work:work-rxjava2:1.0.0" - implementation "android.arch.work:work-runtime:1.0.0" + implementation "androidx.room:room-runtime:2.1.0-alpha06" + implementation "androidx.room:room-rxjava2:2.1.0-alpha06" + kapt "androidx.room:room-compiler:2.1.0-alpha06" + + implementation 'com.takisoft.preferencex:preferencex:1.0.0' implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.3.3' kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.3.3' @@ -98,17 +102,13 @@ dependencies { kapt "com.google.dagger:dagger-compiler:2.21" kapt "com.google.dagger:dagger-android-processor:2.21" - implementation "androidx.room:room-runtime:2.1.0-alpha05" - implementation "androidx.room:room-rxjava2:2.1.0-alpha05" - kapt "androidx.room:room-compiler:2.1.0-alpha05" - implementation "eu.davidea:flexible-adapter:5.1.0" implementation "eu.davidea:flexible-adapter-ui:1.0.0" implementation "com.aurelhubert:ahbottomnavigation:2.3.4" - implementation 'com.ncapdevi:frag-nav:3.1.0' + implementation 'com.ncapdevi:frag-nav:3.2.0' implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f' - implementation 'com.github.PhilJay:MPAndroidChart:971640b29d' + implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.2' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' @@ -128,15 +128,15 @@ dependencies { debugImplementation "com.amitshekhar.android:debug-db:1.0.6" testImplementation "junit:junit:4.12" - testImplementation "io.mockk:mockk:1.9.1" - testImplementation "org.mockito:mockito-inline:2.25.0" + testImplementation "io.mockk:mockk:1.9.2" + testImplementation "org.mockito:mockito-inline:2.25.1" testImplementation 'org.threeten:threetenbp:1.3.8' - androidTestImplementation "io.mockk:mockk-android:1.9.1" androidTestImplementation 'androidx.test:core:1.1.0' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.ext:junit:1.1.0' - androidTestImplementation 'org.mockito:mockito-android:2.25.0' + androidTestImplementation "io.mockk:mockk-android:1.9.2" + androidTestImplementation 'org.mockito:mockito-android:2.25.1' androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" } 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 85aa94a1..4448e556 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 @@ -71,11 +71,6 @@ class MainActivity : BaseActivity(), MainView { return true } - override fun onStart() { - super.onStart() - presenter.onViewChange() - } - override fun initView() { mainBottomNav.run { addItems( From 3656d3161f1d5ae3c160f37526ad4e33cfd423fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 24 Mar 2019 17:31:39 +0100 Subject: [PATCH 017/322] Fix crash on duplicate items (#303) --- .../ui/modules/account/AccountItem.kt | 17 ++++++------- .../ui/modules/attendance/AttendanceItem.kt | 16 ++++++------ .../summary/AttendanceSummaryItem.kt | 12 ++++----- .../wulkanowy/ui/modules/exam/ExamItem.kt | 11 ++++---- .../modules/grade/details/GradeDetailsItem.kt | 9 ++----- .../modules/grade/summary/GradeSummaryItem.kt | 15 ++++------- .../ui/modules/homework/HomeworkItem.kt | 18 ++++++------- .../studentselect/LoginStudentSelectItem.kt | 25 +++++++------------ .../ui/modules/message/MessageItem.kt | 11 +++----- .../wulkanowy/ui/modules/more/MoreItem.kt | 11 ++++---- .../wulkanowy/ui/modules/note/NoteItem.kt | 10 +++----- .../ui/modules/timetable/TimetableItem.kt | 21 ++++++++-------- 12 files changed, 70 insertions(+), 106 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt index a564306d..cb7aabda 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt @@ -14,12 +14,12 @@ class AccountItem(val student: Student) : AbstractFlexibleItem>?): ViewHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { return ViewHolder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder?, position: Int, payloads: MutableList?) { - holder?.apply { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { + holder.apply { accountItemName.text = student.studentName accountItemSchool.text = student.schoolName accountItemImage.setBackgroundResource(if (student.isCurrent) R.drawable.ic_account_circular_border else 0) @@ -38,14 +38,13 @@ class AccountItem(val student: Student) : AbstractFlexibleItem>?) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View get() = contentView } } - diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt index c2698ebb..16a140cb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt @@ -14,14 +14,13 @@ import kotlinx.android.synthetic.main.item_attendance.* class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem() { + override fun getLayoutRes() = R.layout.item_attendance + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { return ViewHolder(view, adapter) } - override fun getLayoutRes(): Int = R.layout.item_attendance - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, - position: Int, payloads: MutableList?) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { holder.apply { attendanceItemNumber.text = attendance.number.toString() attendanceItemSubject.text = attendance.subject @@ -37,16 +36,17 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem) : FlexibleViewHolder(view, adapter), - LayoutContainer { - + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { override val containerView: View get() = contentView } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt index 3102ce11..265d6ce4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryItem.kt @@ -23,12 +23,12 @@ class AttendanceSummaryItem( override fun getLayoutRes() = R.layout.item_attendance_summary - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { return ViewHolder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder?, position: Int, payloads: MutableList?) { - holder?.apply { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { + holder.apply { attendanceSummaryMonth.text = month attendanceSummaryPercentage.text = percentage attendanceSummaryPresent.text = present @@ -73,10 +73,8 @@ class AttendanceSummaryItem( return result } - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View get() = contentView } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt index 318685e9..8971b4df 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamItem.kt @@ -18,8 +18,7 @@ class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem>, holder: ViewHolder, - position: Int, payloads: MutableList?) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { holder.run { examItemSubject.text = exam.subject examItemTeacher.text = exam.teacher @@ -39,12 +38,12 @@ class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem) : FlexibleViewHolder(view, adapter), - LayoutContainer { - + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { override val containerView: View get() = contentView } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt index de1d88f7..1e47eca5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsItem.kt @@ -28,10 +28,7 @@ class GradeDetailsItem( } @SuppressLint("SetTextI18n") - override fun bindViewHolder( - adapter: FlexibleAdapter>, holder: ViewHolder, - position: Int, payloads: MutableList? - ) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { holder.run { gradeItemValue.run { text = grade.entry @@ -70,9 +67,7 @@ class GradeDetailsItem( return result } - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), - LayoutContainer { - + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { override val containerView: View get() = contentView } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt index 0daf0665..5737a832 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryItem.kt @@ -18,15 +18,12 @@ class GradeSummaryItem( override fun getLayoutRes() = R.layout.item_grade_summary - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { return ViewHolder(view, adapter) } - override fun bindViewHolder( - adapter: FlexibleAdapter>?, holder: ViewHolder?, - position: Int, payloads: MutableList? - ) { - holder?.run { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { + holder.run { gradeSummaryItemTitle.text = title gradeSummaryItemAverage.text = average gradeSummaryItemPredicted.text = predictedGrade @@ -56,10 +53,8 @@ class GradeSummaryItem( return result } - class ViewHolder(view: View?, adapter: FlexibleAdapter>?) : FlexibleViewHolder(view, adapter), - LayoutContainer { - - override val containerView: View? + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View get() = contentView } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt index a2b8cc0a..2de9233f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt @@ -10,9 +10,8 @@ import io.github.wulkanowy.data.db.entities.Homework import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.item_homework.* -class HomeworkItem( - header: HomeworkHeader, val homework: Homework -) : AbstractSectionableItem(header) { +class HomeworkItem(header: HomeworkHeader, val homework: Homework) : + AbstractSectionableItem(header) { override fun getLayoutRes() = R.layout.item_homework @@ -20,10 +19,7 @@ class HomeworkItem( return ViewHolder(view, adapter) } - override fun bindViewHolder( - adapter: FlexibleAdapter>, holder: ViewHolder, - position: Int, payloads: MutableList? - ) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { holder.apply { homeworkItemSubject.text = homework.subject homeworkItemTeacher.text = homework.teacher @@ -42,12 +38,12 @@ class HomeworkItem( } override fun hashCode(): Int { - return homework.hashCode() + var result = homework.hashCode() + result = 31 * result + homework.id.toInt() + return result } - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), - LayoutContainer { - + class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { override val containerView: View get() = contentView } 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 c206a499..71a7d681 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 @@ -8,19 +8,21 @@ import eu.davidea.viewholders.FlexibleViewHolder 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.view.* +import kotlinx.android.synthetic.main.item_login_student_select.* class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem() { override fun getLayoutRes(): Int = R.layout.item_login_student_select - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ItemViewHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ItemViewHolder { return ItemViewHolder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ItemViewHolder?, - position: Int, payloads: MutableList?) { - holder?.bind(student) + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ItemViewHolder, position: Int, payloads: MutableList?) { + holder.run { + loginItemName.text = student.studentName + loginItemSchool.text = student.schoolName + } } override fun equals(other: Any?): Boolean { @@ -38,17 +40,8 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem?) - : FlexibleViewHolder(view, adapter), LayoutContainer { - - override val containerView: View? + class ItemViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View get() = itemView - - fun bind(item: Student) { - itemView.run { - loginItemName.text = item.studentName - loginItemSchool.text = item.schoolName - } - } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt index ec725a38..26568e22 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt @@ -20,12 +20,9 @@ class MessageItem(val message: Message, private val noSubjectString: String) : return ViewHolder(view, adapter) } - override fun getLayoutRes(): Int = R.layout.item_message + override fun getLayoutRes() = R.layout.item_message - override fun bindViewHolder( - adapter: FlexibleAdapter>, holder: ViewHolder, - position: Int, payloads: MutableList? - ) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { holder.apply { val style = if (message.unread) BOLD else NORMAL @@ -58,9 +55,7 @@ class MessageItem(val message: Message, private val noSubjectString: String) : return message.hashCode() } - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), - LayoutContainer { - + class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { override val containerView: View get() = contentView } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt index e8571a8c..85b604e7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt @@ -14,12 +14,12 @@ class MoreItem(val title: String, private val drawable: Drawable?) : AbstractFle override fun getLayoutRes() = R.layout.item_more - override fun createViewHolder(view: View?, adapter: FlexibleAdapter>?): ViewHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { return ViewHolder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>?, holder: ViewHolder?, position: Int, payloads: MutableList?) { - holder?.apply { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { + holder.apply { moreItemTitle.text = title moreItemImage.setImageDrawable(drawable) } @@ -40,9 +40,8 @@ class MoreItem(val title: String, private val drawable: Drawable?) : AbstractFle return title.hashCode() } - class ViewHolder(view: View?, adapter: FlexibleAdapter<*>?) : FlexibleViewHolder(view, adapter), LayoutContainer { - - override val containerView: View? + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View get() = contentView } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt index 71562fc4..dabeef74 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt @@ -15,16 +15,13 @@ import kotlinx.android.synthetic.main.item_note.* class NoteItem(val note: Note) : AbstractFlexibleItem() { + override fun getLayoutRes() = R.layout.item_note + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): NoteItem.ViewHolder { return NoteItem.ViewHolder(view, adapter) } - override fun getLayoutRes(): Int = R.layout.item_note - - override fun bindViewHolder( - adapter: FlexibleAdapter>, - holder: NoteItem.ViewHolder, position: Int, payloads: MutableList? - ) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: NoteItem.ViewHolder, position: Int, payloads: MutableList?) { holder.apply { noteItemDate.apply { text = note.date.toFormattedString() @@ -57,7 +54,6 @@ class NoteItem(val note: Note) : AbstractFlexibleItem() { } class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { - override val containerView: View get() = contentView } 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 f37c0f51..c721401f 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 @@ -15,18 +15,17 @@ import io.github.wulkanowy.utils.toFormattedString import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.item_timetable.* -class TimetableItem(val lesson: Timetable, private val roomText: String) - : AbstractFlexibleItem() { +class TimetableItem(val lesson: Timetable, private val roomText: String) : + AbstractFlexibleItem() { - override fun getLayoutRes(): Int = R.layout.item_timetable + override fun getLayoutRes() = R.layout.item_timetable override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { return ViewHolder(view, adapter) } @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, - position: Int, payloads: MutableList?) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { holder.apply { timetableItemNumber.text = lesson.number.toString() timetableItemSubject.text = lesson.subject @@ -34,8 +33,8 @@ class TimetableItem(val lesson: Timetable, private val roomText: String) timetableItemTime.text = "${lesson.start.toFormattedString("HH:mm")} - ${lesson.end.toFormattedString("HH:mm")}" timetableItemAlert.visibility = if (lesson.changes || lesson.canceled) VISIBLE else GONE timetableItemSubject.paintFlags = - if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG - else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() + if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG + else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() } } @@ -50,12 +49,12 @@ class TimetableItem(val lesson: Timetable, private val roomText: String) } override fun hashCode(): Int { - return lesson.hashCode() + var result = lesson.hashCode() + result = 31 * result + lesson.id.toInt() + return result } - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), - LayoutContainer { - + class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { override val containerView: View get() = contentView } From ed9458d9a5ef34a09147dc02528c03e9248d624a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sun, 24 Mar 2019 20:21:05 +0100 Subject: [PATCH 018/322] Fix more than one current semester in database (#304) --- .../wulkanowy/data/db/dao/SemesterDao.kt | 13 ++-- .../repositories/semester/SemesterLocal.kt | 13 ++-- .../semester/SemesterRepository.kt | 16 +++- .../UnitTestInternetObservingStrategy.kt | 19 +++++ .../AttendanceRemoteTest.kt | 3 +- .../CompletedLessonsRemoteTest.kt | 3 +- .../{remote => exam}/ExamRemoteTest.kt | 3 +- .../GradeStatisticsRemoteTest.kt | 3 +- .../LuckyNumberRemoteTest.kt | 2 +- .../semester/SemesterRepositoryTest.kt | 74 +++++++++++++++++++ .../semester/TestSemesterEnityCreator.kt | 16 ++++ .../{remote => student}/StudentRemoteTest.kt | 3 +- .../TimetableRemoteTest.kt | 2 +- 13 files changed, 138 insertions(+), 32 deletions(-) create mode 100644 app/src/test/java/io/github/wulkanowy/data/repositories/UnitTestInternetObservingStrategy.kt rename app/src/test/java/io/github/wulkanowy/data/repositories/{remote => attendance}/AttendanceRemoteTest.kt (92%) rename app/src/test/java/io/github/wulkanowy/data/repositories/{remote => completedlessons}/CompletedLessonsRemoteTest.kt (91%) rename app/src/test/java/io/github/wulkanowy/data/repositories/{remote => exam}/ExamRemoteTest.kt (93%) rename app/src/test/java/io/github/wulkanowy/data/repositories/{remote => gradestatistics}/GradeStatisticsRemoteTest.kt (91%) rename app/src/test/java/io/github/wulkanowy/data/repositories/{remote => luckynumber}/LuckyNumberRemoteTest.kt (95%) create mode 100644 app/src/test/java/io/github/wulkanowy/data/repositories/semester/SemesterRepositoryTest.kt create mode 100644 app/src/test/java/io/github/wulkanowy/data/repositories/semester/TestSemesterEnityCreator.kt rename app/src/test/java/io/github/wulkanowy/data/repositories/{remote => student}/StudentRemoteTest.kt (87%) rename app/src/test/java/io/github/wulkanowy/data/repositories/{remote => timetable}/TimetableRemoteTest.kt (96%) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt index 44de31d8..608a6663 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt @@ -1,8 +1,8 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao +import androidx.room.Delete import androidx.room.Insert -import androidx.room.OnConflictStrategy.IGNORE import androidx.room.Query import io.github.wulkanowy.data.db.entities.Semester import io.reactivex.Maybe @@ -12,15 +12,12 @@ import javax.inject.Singleton @Dao interface SemesterDao { - @Insert(onConflict = IGNORE) + @Insert fun insertAll(semester: List) + @Delete + fun deleteAll(semester: List) + @Query("SELECT * FROM Semesters WHERE student_id = :studentId") fun loadAll(studentId: Int): Maybe> - - @Query("UPDATE Semesters SET is_current = 1 WHERE semester_id = :semesterId AND diary_id = :diaryId") - fun updateCurrent(semesterId: Int, diaryId: Int) - - @Query("UPDATE Semesters SET is_current = 0 WHERE student_id = :studentId") - fun resetCurrent(studentId: Int) } 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 5be82d7f..37d4a268 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 @@ -11,17 +11,14 @@ import javax.inject.Singleton class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) { fun saveSemesters(semesters: List) { - return semesterDb.insertAll(semesters) + semesterDb.insertAll(semesters) + } + + fun deleteSemesters(semesters: List) { + semesterDb.deleteAll(semesters) } fun getSemesters(student: Student): Maybe> { return semesterDb.loadAll(student.studentId).filter { !it.isEmpty() } } - - fun setCurrentSemester(semester: Semester) { - semesterDb.run { - resetCurrent(semester.studentId) - updateCurrent(semester.semesterId, semester.diaryId) - } - } } 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 975868a2..9735f029 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 @@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.reactivex.Maybe import io.reactivex.Single +import timber.log.Timber import java.net.UnknownHostException import javax.inject.Inject import javax.inject.Singleton @@ -25,10 +26,17 @@ class SemesterRepository @Inject constructor( .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { if (it) remote.getSemesters(student) else Single.error(UnknownHostException()) - }.map { newSemesters -> - local.apply { - saveSemesters(newSemesters) - setCurrentSemester(newSemesters.single { it.isCurrent }) + }.flatMap { new -> + val currentSemesters = new.filter { it.isCurrent } + if (currentSemesters.size == 1) { + local.getSemesters(student).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteSemesters(old - new) + local.saveSemesters(new - old) + } + } else { + Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}") + throw IllegalArgumentException("Current semester can be only one.") } }.flatMap { local.getSemesters(student).toSingle(emptyList()) }) } diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/UnitTestInternetObservingStrategy.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/UnitTestInternetObservingStrategy.kt new file mode 100644 index 00000000..28abbb99 --- /dev/null +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/UnitTestInternetObservingStrategy.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.repositories + +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingStrategy +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler +import io.reactivex.Observable +import io.reactivex.Single + +class UnitTestInternetObservingStrategy : InternetObservingStrategy { + + override fun checkInternetConnectivity(host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Single { + return Single.just(true) + } + + override fun observeInternetConnectivity(initialIntervalInMs: Int, intervalInMs: Int, host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Observable { + return Observable.just(true) + } + + override fun getDefaultPingHost() = "localhost" +} diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemoteTest.kt similarity index 92% rename from app/src/test/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemoteTest.kt rename to app/src/test/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemoteTest.kt index 4842d305..8adfa83b 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/AttendanceRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemoteTest.kt @@ -1,9 +1,8 @@ -package io.github.wulkanowy.data.repositories.remote +package io.github.wulkanowy.data.repositories.attendance import io.github.wulkanowy.api.Api import io.github.wulkanowy.api.attendance.Attendance import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.repositories.attendance.AttendanceRemote import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/CompletedLessonsRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemoteTest.kt similarity index 91% rename from app/src/test/java/io/github/wulkanowy/data/repositories/remote/CompletedLessonsRemoteTest.kt rename to app/src/test/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemoteTest.kt index f4c161bc..bb9f5294 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/CompletedLessonsRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemoteTest.kt @@ -1,9 +1,8 @@ -package io.github.wulkanowy.data.repositories.remote +package io.github.wulkanowy.data.repositories.completedlessons import io.github.wulkanowy.api.Api import io.github.wulkanowy.api.timetable.CompletedLesson import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRemote import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/ExamRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/exam/ExamRemoteTest.kt similarity index 93% rename from app/src/test/java/io/github/wulkanowy/data/repositories/remote/ExamRemoteTest.kt rename to app/src/test/java/io/github/wulkanowy/data/repositories/exam/ExamRemoteTest.kt index 0cc78548..0c83bed0 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/ExamRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/exam/ExamRemoteTest.kt @@ -1,9 +1,8 @@ -package io.github.wulkanowy.data.repositories.remote +package io.github.wulkanowy.data.repositories.exam import io.github.wulkanowy.api.Api import io.github.wulkanowy.api.exams.Exam import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.repositories.exam.ExamRemote import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/GradeStatisticsRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemoteTest.kt similarity index 91% rename from app/src/test/java/io/github/wulkanowy/data/repositories/remote/GradeStatisticsRemoteTest.kt rename to app/src/test/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemoteTest.kt index 14381791..37d3f2ad 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/GradeStatisticsRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemoteTest.kt @@ -1,9 +1,8 @@ -package io.github.wulkanowy.data.repositories.remote +package io.github.wulkanowy.data.repositories.gradestatistics import io.github.wulkanowy.api.Api import io.github.wulkanowy.api.grades.GradeStatistics import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.repositories.gradestatistics.GradeStatisticsRemote import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/LuckyNumberRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemoteTest.kt similarity index 95% rename from app/src/test/java/io/github/wulkanowy/data/repositories/remote/LuckyNumberRemoteTest.kt rename to app/src/test/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemoteTest.kt index 054f8890..67e6c2ef 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/LuckyNumberRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemoteTest.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.data.repositories.remote +package io.github.wulkanowy.data.repositories.luckynumber import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.Semester diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/semester/SemesterRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/semester/SemesterRepositoryTest.kt new file mode 100644 index 00000000..7856b937 --- /dev/null +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/semester/SemesterRepositoryTest.kt @@ -0,0 +1,74 @@ +package io.github.wulkanowy.data.repositories.semester + +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings +import io.github.wulkanowy.data.ApiHelper +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.UnitTestInternetObservingStrategy +import io.reactivex.Maybe +import io.reactivex.Single +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.doNothing +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +class SemesterRepositoryTest { + + @Mock + private lateinit var semesterRemote: SemesterRemote + + @Mock + private lateinit var semesterLocal: SemesterLocal + + @Mock + private lateinit var apiHelper: ApiHelper + + @Mock + private lateinit var student: Student + + private lateinit var semesterRepository: SemesterRepository + + private val settings = InternetObservingSettings.builder() + .strategy(UnitTestInternetObservingStrategy()) + .build() + + @Before + fun initTest() { + MockitoAnnotations.initMocks(this) + semesterRepository = SemesterRepository(semesterRemote, semesterLocal, settings, apiHelper) + } + + @Test + fun singleCurrentSemesterTest() { + val semesters = listOf( + createSemesterEntity(false), + createSemesterEntity(true) + ) + + doNothing().`when`(apiHelper).initApi(student) + doReturn(Maybe.empty()).`when`(semesterLocal).getSemesters(student) + doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student) + + semesterRepository.getSemesters(student).blockingGet() + + verify(semesterLocal).deleteSemesters(emptyList()) + verify(semesterLocal).saveSemesters(semesters) + } + + @Test(expected = IllegalArgumentException::class) + fun twoCurrentSemesterTest() { + val semesters = listOf( + createSemesterEntity(true), + createSemesterEntity(true) + ) + + doNothing().`when`(apiHelper).initApi(student) + doReturn(Maybe.empty()).`when`(semesterLocal).getSemesters(student) + doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student) + + semesterRepository.getSemesters(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 new file mode 100644 index 00000000..c3377ac5 --- /dev/null +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/semester/TestSemesterEnityCreator.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.data.repositories.semester + +import io.github.wulkanowy.data.db.entities.Semester + +fun createSemesterEntity(current: Boolean): Semester { + return Semester( + studentId = 0, + diaryId = 0, + semesterId = 0, + diaryName = "", + classId = 0, + isCurrent = current, + semesterName = 0, + unitId = 0 + ) +} diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/StudentRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt similarity index 87% rename from app/src/test/java/io/github/wulkanowy/data/repositories/remote/StudentRemoteTest.kt rename to app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt index 3a26836d..1244239e 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/StudentRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt @@ -1,8 +1,7 @@ -package io.github.wulkanowy.data.repositories.remote +package io.github.wulkanowy.data.repositories.student import io.github.wulkanowy.api.Api import io.github.wulkanowy.api.register.Student -import io.github.wulkanowy.data.repositories.student.StudentRemote import io.reactivex.Single import org.junit.Assert.assertEquals import org.junit.Before diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/TimetableRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemoteTest.kt similarity index 96% rename from app/src/test/java/io/github/wulkanowy/data/repositories/remote/TimetableRemoteTest.kt rename to app/src/test/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemoteTest.kt index 942b28ed..4bb4091f 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/remote/TimetableRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemoteTest.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.data.repositories.remote +package io.github.wulkanowy.data.repositories.timetable import io.github.wulkanowy.api.Api import io.github.wulkanowy.api.timetable.Timetable From 5a997dacb7a2fdd0f7de7a88b3f8d8e14f75af14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 24 Mar 2019 22:42:09 +0100 Subject: [PATCH 019/322] Version 0.7.2 --- .travis.yml | 8 ++++---- app/build.gradle | 6 +++--- .../main/play/release-notes/pl-PL/default.txt | 20 ++++--------------- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/.travis.yml b/.travis.yml index b3261931..56c64858 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,10 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ -branches: - only: - - master - - 0.7.x +#branches: +# only: +# - master +# - 0.7.x android: licenses: diff --git a/app/build.gradle b/app/build.gradle index 453765fd..ce21cf44 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 targetSdkVersion 28 - versionCode 27 - versionName "0.7.1" + versionCode 28 + versionName "0.7.2" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -77,7 +77,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('com.github.wulkanowy:api:f60d2b8') { exclude module: "threetenbp" } + implementation('io.github.wulkanowy:api:0.7.2') { exclude module: "threetenbp" } implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 6f8a6b0a..bdf045f5 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,18 +1,6 @@ -Dodaliśmy: -- szczęśliwy numerek -- lekcje zrealizowane -- wysyłanie wiadomości -- ucznia na tle klasy -- opcję zmiany kolorów ocen - -Zmieniliśmy: -- wygląd ekranu logowania -- sposób wyświetlania zastępstw -- widok zadań domowych na tygodniowy - Naprawiliśmy: -- automatyczne przełączanie widżetu na nowy dzień -- wyświetlanie powiadomień o starych ocenach -- znikające numery sal w planie lekcji +- problemy ze stabilnością podczas przełączania zakładek +- nierówność w nawigacji między dniami w planie lekcji i frekwencji +- zły wygląd odświeżania w szczęśliwym numerku -Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.0 +Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.2 From b3cd7e8ac131f0c838e39d5e565954ef65b3aba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Tue, 26 Mar 2019 14:32:23 +0100 Subject: [PATCH 020/322] Fix undeliverable network exceptions (#306) * Remove unnecessary this --- app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt index 68fc7c64..e427647f 100644 --- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt +++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt @@ -19,7 +19,11 @@ import io.github.wulkanowy.di.DaggerAppComponent import io.github.wulkanowy.services.sync.SyncWorkerFactory import io.github.wulkanowy.utils.CrashlyticsTree import io.github.wulkanowy.utils.DebugLogTree +import io.reactivex.exceptions.UndeliverableException +import io.reactivex.plugins.RxJavaPlugins import timber.log.Timber +import java.io.IOException +import java.lang.Exception import javax.inject.Inject class WulkanowyApp : DaggerApplication() { @@ -42,6 +46,7 @@ class WulkanowyApp : DaggerApplication() { if (DEBUG) enableDebugLog() AppCompatDelegate.setDefaultNightMode(prefRepository.currentTheme) WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(workerFactory).build()) + RxJavaPlugins.setErrorHandler(::onError) } private fun enableDebugLog() { @@ -56,6 +61,12 @@ class WulkanowyApp : DaggerApplication() { Timber.plant(CrashlyticsTree()) } + private fun onError(t: Throwable) { + if (t is UndeliverableException && t.cause is IOException || t.cause is InterruptedException) { + Timber.e(t.cause, "An undeliverable error occurred") + } else throw t + } + override fun applicationInjector(): AndroidInjector { return DaggerAppComponent.builder().create(this) } From 87268b3ef68f3c734a28562287cc2ed73479a336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Tue, 26 Mar 2019 16:47:14 +0100 Subject: [PATCH 021/322] Fix rejected execution in sync worker (#305) --- .../java/io/github/wulkanowy/services/sync/SyncWorker.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt index 36593966..134fb034 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt @@ -19,7 +19,6 @@ import io.github.wulkanowy.services.sync.channels.DebugChannel import io.github.wulkanowy.services.sync.works.Work import io.github.wulkanowy.utils.getCompatColor import io.reactivex.Completable -import io.reactivex.Flowable import io.reactivex.Single import timber.log.Timber import kotlin.random.Random @@ -42,12 +41,12 @@ class SyncWorker @AssistedInject constructor( .flatMapCompletable { student -> semesterRepository.getCurrentSemester(student, true) .flatMapCompletable { semester -> - Completable.mergeDelayError(Flowable.fromIterable(works.map { work -> + Completable.mergeDelayError(works.map { work -> work.create(student, semester) .doOnSubscribe { Timber.i("${work::class.java.simpleName} is starting") } .doOnError { Timber.i("${work::class.java.simpleName} result: An exception occurred") } .doOnComplete { Timber.i("${work::class.java.simpleName} result: Success") } - }), 3) + }) } } .toSingleDefault(Result.success()) From c2ab53cfeb8ff1b2a222951a3f271d1bc2061063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 26 Mar 2019 19:46:59 +0100 Subject: [PATCH 022/322] Version 0.7.3 --- app/build.gradle | 6 +++--- app/src/main/play/release-notes/pl-PL/default.txt | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ce21cf44..509e146f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 targetSdkVersion 28 - versionCode 28 - versionName "0.7.2" + versionCode 29 + versionName "0.7.3" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -77,7 +77,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('io.github.wulkanowy:api:0.7.2') { exclude module: "threetenbp" } + implementation('io.github.wulkanowy:api:0.7.3') { exclude module: "threetenbp" } implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index bdf045f5..6cf59d4b 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,6 +1,6 @@ -Naprawiliśmy: -- problemy ze stabilnością podczas przełączania zakładek -- nierówność w nawigacji między dniami w planie lekcji i frekwencji -- zły wygląd odświeżania w szczęśliwym numerku +Wersja 0.7.3 -Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.2 +Naprawiliśmy: +- naprawiono problemy ze stabilnością podczas odświeżania danych + +Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.3 From c6c7357623a1040afaddc8a7631e7a6fca15e0a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Thu, 28 Mar 2019 18:27:17 +0100 Subject: [PATCH 023/322] Fix black spinner in login form (#308) --- app/src/main/res/drawable/ic_login_outlined_border.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/drawable/ic_login_outlined_border.xml b/app/src/main/res/drawable/ic_login_outlined_border.xml index ec62251e..359bea5e 100644 --- a/app/src/main/res/drawable/ic_login_outlined_border.xml +++ b/app/src/main/res/drawable/ic_login_outlined_border.xml @@ -1,4 +1,5 @@ + From b97b94ae29aac599214cb449582cfa2878bff36a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Thu, 28 Mar 2019 23:07:59 +0100 Subject: [PATCH 024/322] Fix more than one current student (#311) Fix #309 --- .travis.yml | 8 ++++---- .../java/io/github/wulkanowy/data/db/dao/StudentDao.kt | 4 ++-- .../wulkanowy/data/repositories/student/StudentLocal.kt | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 56c64858..b3261931 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,10 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ -#branches: -# only: -# - master -# - 0.7.x +branches: + only: + - master + - 0.7.x android: licenses: diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt index c0c054c8..8110bb69 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt @@ -25,8 +25,8 @@ interface StudentDao { @Query("SELECT * FROM Students") fun loadAll(): Maybe> - @Query("UPDATE Students SET is_current = 1 WHERE student_id = :studentId") - fun updateCurrent(studentId: Int) + @Query("UPDATE Students SET is_current = 1 WHERE id = :id") + fun updateCurrent(id: Long) @Query("UPDATE Students SET is_current = 0") fun resetCurrent() diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt index e05e7223..7bbd283f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt @@ -35,7 +35,7 @@ class StudentLocal @Inject constructor( return Completable.fromCallable { studentDb.run { resetCurrent() - updateCurrent(student.studentId) + updateCurrent(student.id) } } } From 8601093725c6912189ae3038247686c6abd6bc5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Thu, 28 Mar 2019 23:30:30 +0100 Subject: [PATCH 025/322] Again fix rejected execution in sync worker (#310) --- app/build.gradle | 4 ++-- .../java/io/github/wulkanowy/services/sync/SyncManager.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 509e146f..935710c5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -86,8 +86,8 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.multidex:multidex:2.0.1' - implementation "androidx.work:work-runtime:2.0.0" - implementation "androidx.work:work-rxjava2:2.0.0" + implementation "android.arch.work:work-runtime:1.0.0" + implementation "android.arch.work:work-rxjava2:1.0.0" implementation "androidx.room:room-runtime:2.1.0-alpha06" implementation "androidx.room:room-rxjava2:2.1.0-alpha06" diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt index 7a9a9956..c1820afb 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt @@ -40,7 +40,7 @@ class SyncManager @Inject constructor( fun startSyncWorker(restart: Boolean = false) { if (preferencesRepository.isServiceEnabled && !now().isHolidays) { workManager.enqueueUniquePeriodicWork(SyncWorker::class.java.simpleName, if (restart) REPLACE else KEEP, - PeriodicWorkRequest.Builder(SyncWorker::class.java, preferencesRepository.servicesInterval, MINUTES) + PeriodicWorkRequest.Builder(SyncWorker::class.java, preferencesRepository.servicesInterval, MINUTES, 10, MINUTES) .setBackoffCriteria(EXPONENTIAL, 30, MINUTES) .setConstraints(Constraints.Builder() .setRequiredNetworkType(if (preferencesRepository.isServicesOnlyWifi) METERED else UNMETERED) From 4e3864f26ff01cc6d2f3289d737869a58f2f02f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sat, 30 Mar 2019 09:31:30 +0100 Subject: [PATCH 026/322] Add opening login view on no current student (#312) --- .../wulkanowy/data/exceptions/NoCurrentStudent.kt | 3 +++ .../data/repositories/student/StudentRepository.kt | 3 ++- .../wulkanowy/ui/base/session/BaseSessionFragment.kt | 10 ++++++++++ .../wulkanowy/ui/base/session/BaseSessionPresenter.kt | 5 ++++- .../wulkanowy/ui/base/session/BaseSessionView.kt | 2 ++ .../wulkanowy/ui/base/session/SessionErrorHandler.kt | 5 +++++ 6 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt diff --git a/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt b/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt new file mode 100644 index 00000000..58a2396e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/exceptions/NoCurrentStudent.kt @@ -0,0 +1,3 @@ +package io.github.wulkanowy.data.exceptions + +class NoCurrentStudentException : Exception("There no set current student in database") diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt index 7ddff0aa..b4b7c828 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt @@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import io.github.wulkanowy.data.ApiHelper import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.reactivex.Completable import io.reactivex.Maybe import io.reactivex.Single @@ -36,7 +37,7 @@ class StudentRepository @Inject constructor( fun getCurrentStudent(decryptPass: Boolean = true): Single { return local.getCurrentStudent(decryptPass) - .switchIfEmpty(Maybe.error(NoSuchElementException("No current student"))) + .switchIfEmpty(Maybe.error(NoCurrentStudentException())) .toSingle() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt index 42622b50..110729d4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionFragment.kt @@ -1,6 +1,9 @@ package io.github.wulkanowy.ui.base.session +import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.main.MainActivity open class BaseSessionFragment : BaseFragment(), BaseSessionView { @@ -8,4 +11,11 @@ open class BaseSessionFragment : BaseFragment(), BaseSessionView { override fun showExpiredDialog() { (activity as? MainActivity)?.showExpiredDialog() } + + override fun openLoginView() { + activity?.also { + startActivity(LoginActivity.getStartIntent(it) + .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) }) + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt index 17ed6537..2c9f50ea 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionPresenter.kt @@ -7,6 +7,9 @@ open class BaseSessionPresenter(private val errorHandler: S override fun onAttachView(view: T) { super.onAttachView(view) - errorHandler.onDecryptionFail = { view.showExpiredDialog() } + errorHandler.apply { + onDecryptionFail = { view.showExpiredDialog() } + onNoCurrentStudent = { view.openLoginView() } + } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt index 81ce4d8f..619c2863 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/session/BaseSessionView.kt @@ -5,4 +5,6 @@ import io.github.wulkanowy.ui.base.BaseView interface BaseSessionView : BaseView { fun showExpiredDialog() + + fun openLoginView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt index 1d81e932..017eabba 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/session/SessionErrorHandler.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.base.session import android.content.res.Resources import com.readystatesoftware.chuck.api.ChuckCollector +import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.security.ScramblerException import javax.inject.Inject @@ -13,9 +14,12 @@ open class SessionErrorHandler @Inject constructor( var onDecryptionFail: () -> Unit = {} + var onNoCurrentStudent: () -> Unit = {} + override fun proceed(error: Throwable) { when (error) { is ScramblerException -> onDecryptionFail() + is NoCurrentStudentException -> onNoCurrentStudent() else -> super.proceed(error) } } @@ -23,5 +27,6 @@ open class SessionErrorHandler @Inject constructor( override fun clear() { super.clear() onDecryptionFail = {} + onNoCurrentStudent = {} } } From e71dd5506624b4de66e2847fb2ac459ed5753419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 30 Mar 2019 18:28:37 +0100 Subject: [PATCH 027/322] Fix more than one current semester (#307) --- app/build.gradle | 12 +- .../11.json | 1325 ++++++++++++++++ .../12.json | 1332 +++++++++++++++++ .../data/db/migrations/Migration12Test.kt | 159 ++ .../recipient/RecipientLocalTest.kt | 2 +- .../repositories/student/StudentLocalTest.kt | 2 +- .../io/github/wulkanowy/data/ApiHelper.kt | 2 + .../github/wulkanowy/data/db/AppDatabase.kt | 8 +- .../wulkanowy/data/db/dao/SemesterDao.kt | 4 +- .../wulkanowy/data/db/entities/Student.kt | 5 +- .../data/db/migrations/Migration12.kt | 69 + .../repositories/semester/SemesterLocal.kt | 2 +- .../repositories/student/StudentRemote.kt | 1 + .../repositories/student/StudentRemoteTest.kt | 2 +- .../login/form/LoginFormPresenterTest.kt | 2 +- .../LoginStudentSelectPresenterTest.kt | 2 +- 16 files changed, 2916 insertions(+), 13 deletions(-) create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json create mode 100644 app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt diff --git a/app/build.gradle b/app/build.gradle index 935710c5..84f4fd87 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,6 +25,15 @@ android { fabric_api_key: System.getenv("FABRIC_API_KEY") ?: "null", crashlytics_enabled: project.hasProperty("enableCrashlytics") ] + javaCompileOptions { + annotationProcessorOptions { + arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] + } + } + } + + sourceSets { + androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } signingConfigs { @@ -77,7 +86,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('io.github.wulkanowy:api:0.7.3') { exclude module: "threetenbp" } + implementation('com.github.wulkanowy:api:f1152ae') { exclude module: "threetenbp" } implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" @@ -137,6 +146,7 @@ dependencies { androidTestImplementation 'androidx.test.ext:junit:1.1.0' androidTestImplementation "io.mockk:mockk-android:1.9.2" androidTestImplementation 'org.mockito:mockito-android:2.25.1' + androidTestImplementation "androidx.room:room-testing:2.1.0-alpha06" androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" } diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json new file mode 100644 index 00000000..e42a4a71 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/11.json @@ -0,0 +1,1325 @@ +{ + "formatVersion": 1, + "database": { + "version": 11, + "identityHash": "478af7daed6ac4563e71826fb70cc8c8", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predictedGrade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "finalGrade", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"478af7daed6ac4563e71826fb70cc8c8\")" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json new file mode 100644 index 00000000..32f94355 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/12.json @@ -0,0 +1,1332 @@ +{ + "formatVersion": 1, + "database": { + "version": 12, + "identityHash": "972ad26e6488d9a8239f6bd8597af61d", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predictedGrade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "finalGrade", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"972ad26e6488d9a8239f6bd8597af61d\")" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt new file mode 100644 index 00000000..c61e9305 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt @@ -0,0 +1,159 @@ +package io.github.wulkanowy.data.db.migrations + +import android.content.ContentValues +import android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL +import androidx.room.Room +import androidx.room.testing.MigrationTestHelper +import androidx.sqlite.db.SupportSQLiteDatabase +import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import io.github.wulkanowy.data.db.AppDatabase +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertEquals + +@RunWith(AndroidJUnit4::class) +class Migration12Test { + + private val dbName = "migration-test" + + @get:Rule + val helper: MigrationTestHelper = MigrationTestHelper( + InstrumentationRegistry.getInstrumentation(), + AppDatabase::class.java.canonicalName, + FrameworkSQLiteOpenHelperFactory() + ) + + @Test + fun migrate11To12_twoNotRelatedStudents() { + helper.createDatabase(dbName, 11).apply { + // user 1 + createStudent(this, 1, true) + createSemester(this, 1, false, 5, 1) + createSemester(this, 1, true, 5, 2) + + // user 2 + createStudent(this, 2, true) + createSemester(this, 2, false, 6, 1) + createSemester(this, 2, true, 6, 2) + close() + } + + helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) + + val db = getMigratedRoomDatabase() + val students = db.studentDao.loadAll().blockingGet() + + assertEquals(2, students.size) + + students[0].run { + assertEquals(1, studentId) + assertEquals(5, classId) + } + + students[1].run { + assertEquals(2, studentId) + assertEquals(6, classId) + } + } + + @Test + fun migrate11To12_removeStudentsWithoutClassId() { + helper.createDatabase(dbName, 11).apply { + // user 1 + createStudent(this, 1, true) + createSemester(this, 1, false, 0, 2) + createStudent(this, 2, true) + createSemester(this, 2, true, 1, 2) + close() + } + + helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) + + val db = getMigratedRoomDatabase() + val students = db.studentDao.loadAll().blockingGet() + + assertEquals(1, students.size) + + students[0].run { + assertEquals(2, studentId) + assertEquals(1, classId) + } + } + + @Test + fun migrate11To12_ensureThereIsOnlyOneCurrentStudent() { + helper.createDatabase(dbName, 11).apply { + // user 1 + createStudent(this, 1, true) + createSemester(this, 1, true, 5, 2) + createStudent(this, 2, true) + createSemester(this, 2, true, 6, 2) + createStudent(this, 3, true) + createSemester(this, 3, false, 7, 2) + close() + } + + helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) + + val db = getMigratedRoomDatabase() + val students = db.studentDao.loadAll().blockingGet() + + assertEquals(3, students.size) + + students[0].run { + assertEquals(studentId, 1) + assertEquals(false, isCurrent) + } + students[1].run { + assertEquals(studentId, 2) + assertEquals(false, isCurrent) + } + students[2].run { + assertEquals(studentId, 3) + assertEquals(true, isCurrent) + } + } + + private fun createStudent(db: SupportSQLiteDatabase, studentId: Int, isCurrent: Boolean) { + db.insert("Students", CONFLICT_FAIL, ContentValues().apply { + put("endpoint", "https://fakelog.cf") + put("loginType", "STANDARD") + put("email", "jan@fakelog.cf") + put("password", "******") + put("symbol", "Default") + put("student_id", studentId) + put("student_name", "Jan Kowalski") + put("school_id", "000123") + put("school_name", "") + put("is_current", isCurrent) + put("registration_date", "0") + }) + } + + private fun createSemester(db: SupportSQLiteDatabase, studentId: Int, isCurrent: Boolean, classId: Int, diaryId: Int) { + db.insert("Semesters", CONFLICT_FAIL, ContentValues().apply { + put("student_id", studentId) + put("diary_id", diaryId) + put("diary_name", "IA") + put("semester_id", diaryId * 5) + put("semester_name", "1") + put("is_current", isCurrent) + put("class_id", classId) + put("unit_id", "99") + }) + } + + private fun getMigratedRoomDatabase(): AppDatabase { + val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(), + AppDatabase::class.java, dbName) + .addMigrations(Migration12()) + .build() + // close the database and release any stream resources when the test finishes + helper.closeWhenFinished(database) + return database + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt index 1f6562a2..cc4dd9f3 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt @@ -42,7 +42,7 @@ class RecipientLocalTest { )) val recipients = recipientLocal.getRecipients( - Student("fakelog.cf", "AUTO", "", "", "", 1, "", "", "", true, LocalDateTime.now()), + Student("fakelog.cf", "AUTO", "", "", "", 1, "", "", "", 1, true, LocalDateTime.now()), 2, ReportingUnit(1, 4, "", 0, "", emptyList()) ).blockingGet() diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt index c31bc806..f61c6374 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt @@ -39,7 +39,7 @@ class StudentLocalTest { @Test fun saveAndReadTest() { - studentLocal.saveStudent(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, symbol = "", registrationDate = now())) + studentLocal.saveStudent(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now())) .blockingGet() val student = studentLocal.getCurrentStudent(true).blockingGet() diff --git a/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt b/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt index 5ef40ce8..b6eee316 100644 --- a/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt @@ -14,6 +14,7 @@ class ApiHelper @Inject constructor(private val api: Api) { symbol = student.symbol schoolSymbol = student.schoolSymbol studentId = student.studentId + classId = student.classId host = URL(student.endpoint).run { host + ":$port".removeSuffix(":-1") } ssl = student.endpoint.startsWith("https") loginType = Api.LoginType.valueOf(student.loginType) @@ -28,6 +29,7 @@ class ApiHelper @Inject constructor(private val api: Api) { this.symbol = symbol host = URL(endpoint).run { host + ":$port".removeSuffix(":-1") } ssl = endpoint.startsWith("https") + useNewStudent = true } } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index 4fefd9c9..9ce62b18 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -42,6 +42,7 @@ import io.github.wulkanowy.data.db.entities.Subject import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.migrations.Migration10 import io.github.wulkanowy.data.db.migrations.Migration11 +import io.github.wulkanowy.data.db.migrations.Migration12 import io.github.wulkanowy.data.db.migrations.Migration2 import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration4 @@ -74,13 +75,13 @@ import javax.inject.Singleton Recipient::class ], version = AppDatabase.VERSION_SCHEMA, - exportSchema = false + exportSchema = true ) @TypeConverters(Converters::class) abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 11 + const val VERSION_SCHEMA = 12 fun newInstance(context: Context): AppDatabase { return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database") @@ -97,7 +98,8 @@ abstract class AppDatabase : RoomDatabase() { Migration8(), Migration9(), Migration10(), - Migration11() + Migration11(), + Migration12() ) .build() } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt index 608a6663..01841fb6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SemesterDao.kt @@ -18,6 +18,6 @@ interface SemesterDao { @Delete fun deleteAll(semester: List) - @Query("SELECT * FROM Semesters WHERE student_id = :studentId") - fun loadAll(studentId: Int): Maybe> + @Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId") + fun loadAll(studentId: Int, classId: Int): Maybe> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt index 261b35fb..9cacf835 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt @@ -7,7 +7,7 @@ import androidx.room.PrimaryKey import org.threeten.bp.LocalDateTime import java.io.Serializable -@Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id"], unique = true)]) +@Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id", "class_id"], unique = true)]) data class Student( val endpoint: String, @@ -32,6 +32,9 @@ data class Student( @ColumnInfo(name = "school_name") val schoolName: String, + @ColumnInfo(name = "class_id") + val classId: Int, + @ColumnInfo(name = "is_current") val isCurrent: Boolean, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt new file mode 100644 index 00000000..1dc38e14 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt @@ -0,0 +1,69 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration12 : Migration(11, 12) { + + override fun migrate(database: SupportSQLiteDatabase) { + createTempStudentsTable(database) + replaceStudentTable(database) + updateStudentsWithClassId(database, getStudentsIds(database)) + removeStudentsWithNoClassId(database) + ensureThereIsOnlyOneCurrentStudent(database) + } + + private fun createTempStudentsTable(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Students_tmp ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + endpoint TEXT NOT NULL, + loginType TEXT NOT NULL, + email TEXT NOT NULL, + password TEXT NOT NULL, + symbol TEXT NOT NULL, + student_id INTEGER NOT NULL, + student_name TEXT NOT NULL, + school_id TEXT NOT NULL, + school_name TEXT NOT NULL, + is_current INTEGER NOT NULL, + registration_date INTEGER NOT NULL, + class_id INTEGER NOT NULL + ) + """) + database.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students_tmp (email, symbol, student_id, school_id, class_id)") + } + + private fun replaceStudentTable(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") + database.execSQL("INSERT INTO Students_tmp SELECT * FROM Students") + database.execSQL("DROP TABLE Students") + database.execSQL("ALTER TABLE Students_tmp RENAME TO Students") + } + + private fun getStudentsIds(database: SupportSQLiteDatabase): List { + val students = mutableListOf() + val studentsCursor = database.query("SELECT student_id FROM Students") + if (studentsCursor.moveToFirst()) { + do { + students.add(studentsCursor.getInt(0)) + } while (studentsCursor.moveToNext()) + } + return students + } + + private fun updateStudentsWithClassId(database: SupportSQLiteDatabase, students: List) { + students.forEach { + database.execSQL("UPDATE Students SET class_id = IFNULL((SELECT class_id FROM Semesters WHERE student_id = $it), 0) WHERE student_id = $it") + } + } + + private fun removeStudentsWithNoClassId(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Students WHERE class_id = 0") + } + + private fun ensureThereIsOnlyOneCurrentStudent(database: SupportSQLiteDatabase) { + database.execSQL("UPDATE Students SET is_current = 0") + database.execSQL("UPDATE Students SET is_current = 1 WHERE id = (SELECT MAX(id) FROM Students)") + } +} 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 37d4a268..b9750e7d 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).filter { !it.isEmpty() } + return semesterDb.loadAll(student.studentId, student.classId).filter { !it.isEmpty() } } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt index 7a504cca..f0692a53 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt @@ -21,6 +21,7 @@ class StudentRemote @Inject constructor(private val api: Api) { studentName = student.studentName, schoolSymbol = student.schoolSymbol, schoolName = student.schoolName, + classId = student.classId, endpoint = endpoint, loginType = student.loginType.name, isCurrent = false, diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt index 1244239e..8bd819e2 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt @@ -22,7 +22,7 @@ class StudentRemoteTest { @Test fun testRemoteAll() { - doReturn(Single.just(listOf(Student("", "", 1, "test", "", "", Api.LoginType.AUTO)))) + doReturn(Single.just(listOf(Student("", "", 1, "test", "", "", 1, Api.LoginType.AUTO)))) .`when`(mockApi).getStudents() val students = StudentRemote(mockApi).getStudents("", "", "").blockingGet() 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 e2d910a9..f1113f7b 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 @@ -86,7 +86,7 @@ class LoginFormPresenterTest { @Test fun loginTest() { - val studentTest = Student(email = "test@", password = "123", endpoint = "https://fakelog.cf", loginType = "AUTO", studentName = "", schoolSymbol = "", schoolName = "", studentId = 0, isCurrent = false, symbol = "", registrationDate = now()) + val studentTest = Student(email = "test@", password = "123", endpoint = "https://fakelog.cf", loginType = "AUTO", studentName = "", schoolSymbol = "", schoolName = "", studentId = 0, classId = 1, isCurrent = false, symbol = "", registrationDate = now()) doReturn(Single.just(listOf(studentTest))) .`when`(repository).getStudents(anyString(), anyString(), anyString(), anyString()) 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 1059e0ac..2de4e43a 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 @@ -32,7 +32,7 @@ class LoginStudentSelectPresenterTest { private lateinit var presenter: LoginStudentSelectPresenter - private val testStudent by lazy { Student(email = "test", password = "test123", endpoint = "https://fakelog.cf", loginType = "AUTO", symbol = "", isCurrent = false, studentId = 0, schoolName = "", schoolSymbol = "", studentName = "", registrationDate = now()) } + private val testStudent by lazy { Student(email = "test", password = "test123", endpoint = "https://fakelog.cf", loginType = "AUTO", symbol = "", isCurrent = false, studentId = 0, schoolName = "", schoolSymbol = "", classId = 1, studentName = "", registrationDate = now()) } private val testException by lazy { RuntimeException("Problem") } From 935bec3f5bd8b225bee1976bd92659fb25b64420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sat, 30 Mar 2019 19:26:19 +0100 Subject: [PATCH 028/322] Add 0,75 grade modifier (#313) --- app/src/main/res/values-pl/value_prefernces.xml | 1 + app/src/main/res/values/value_prefernces.xml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/src/main/res/values-pl/value_prefernces.xml b/app/src/main/res/values-pl/value_prefernces.xml index 05f2ed50..2231cc0b 100644 --- a/app/src/main/res/values-pl/value_prefernces.xml +++ b/app/src/main/res/values-pl/value_prefernces.xml @@ -22,6 +22,7 @@ 0,25 0,33 0,5 + 0,75 diff --git a/app/src/main/res/values/value_prefernces.xml b/app/src/main/res/values/value_prefernces.xml index 1a1dceca..661615e1 100644 --- a/app/src/main/res/values/value_prefernces.xml +++ b/app/src/main/res/values/value_prefernces.xml @@ -50,12 +50,14 @@ 0,25 0,33 0,5 + 0,75 0.0 0.25 0.33 0.5 + 0.75 From 297a2909ba13a2a92fa77e9cfdd7226ac476c00a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 30 Mar 2019 19:44:27 +0100 Subject: [PATCH 029/322] Version 0.7.4 --- .travis.yml | 8 ++++---- app/build.gradle | 6 +++--- app/src/main/play/release-notes/pl-PL/default.txt | 11 ++++++++--- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index b3261931..56c64858 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,10 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ -branches: - only: - - master - - 0.7.x +#branches: +# only: +# - master +# - 0.7.x android: licenses: diff --git a/app/build.gradle b/app/build.gradle index 84f4fd87..22af6e6b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 targetSdkVersion 28 - versionCode 29 - versionName "0.7.3" + versionCode 30 + versionName "0.7.4" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -86,7 +86,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('com.github.wulkanowy:api:f1152ae') { exclude module: "threetenbp" } + implementation('io.github.wulkanowy:api:0.7.4') { exclude module: "threetenbp" } implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 6cf59d4b..c85a155b 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,6 +1,11 @@ -Wersja 0.7.3 +Wersja 0.7.4 Naprawiliśmy: -- naprawiono problemy ze stabilnością podczas odświeżania danych +- problem ze stabilnością na androidach 4 i 5 +- problem z przełączaniem kont, jeśli zalogowany był jednocześnie uczeń i rodzic +- problem z odświeżaniem danych, jeśli uczeń przeniósł się z klasy do klasy w bieżącym roku szkolnym -Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.3 +Dodaliśmy: +- nową opcję zmiany wartości plusa i minusa na 0.75 + +Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.4 From 2bff468e56aa253ac308e2e50b6d10bdc52b5e26 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sun, 31 Mar 2019 22:01:04 +0200 Subject: [PATCH 030/322] Add deleting messages (#290) --- .../repositories/message/MessageRemote.kt | 4 ++ .../repositories/message/MessageRepository.kt | 17 ++++++ .../ui/modules/message/MessageFragment.kt | 11 +++- .../ui/modules/message/MessagePresenter.kt | 10 ++++ .../ui/modules/message/MessageView.kt | 7 +-- .../message/preview/MessagePreviewFragment.kt | 29 ++++++++++ .../preview/MessagePreviewPresenter.kt | 53 ++++++++++++++++++- .../message/preview/MessagePreviewView.kt | 12 +++++ .../message/send/SendMessageActivity.kt | 2 +- .../message/send/SendMessagePresenter.kt | 12 ++--- .../modules/message/tab/MessageTabFragment.kt | 9 ++-- .../message/tab/MessageTabPresenter.kt | 36 ++++++++----- .../res/drawable/ic_message_delete_24dp.xml | 10 ++++ .../res/layout/fragment_message_preview.xml | 1 + .../res/menu/action_menu_message_preview.xml | 6 +++ app/src/main/res/values-pl/strings.xml | 4 ++ app/src/main/res/values/strings.xml | 4 ++ 17 files changed, 195 insertions(+), 32 deletions(-) create mode 100644 app/src/main/res/drawable/ic_message_delete_24dp.xml diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt index 3fb01d30..e9cff4ab 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt @@ -58,4 +58,8 @@ class MessageRemote @Inject constructor(private val api: Api) { } ) } + + fun deleteMessage(message: Message): Single { + return api.deleteMessages(listOf(Pair(message.realId, message.folderId))) + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt index d319689e..09f19815 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt @@ -9,6 +9,7 @@ import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED import io.reactivex.Completable +import io.reactivex.Maybe import io.reactivex.Single import java.net.UnknownHostException import javax.inject.Inject @@ -89,4 +90,20 @@ class MessageRepository @Inject constructor( else Single.error(UnknownHostException()) } } + + fun deleteMessage(message: Message): Maybe { + return ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.deleteMessage(message) + else Single.error(UnknownHostException()) + } + .filter { it } + .doOnSuccess { + if (!message.removed) local.updateMessages(listOf(message.copy(removed = true).apply { + id = message.id + content = message.content + })) + else local.deleteMessages(listOf(message)) + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt index d8a6bc84..7bd35f2d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt @@ -7,6 +7,7 @@ import android.view.View.INVISIBLE import android.view.View.VISIBLE import android.view.ViewGroup import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED import io.github.wulkanowy.data.repositories.message.MessageFolder.SENT import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED @@ -75,12 +76,20 @@ class MessageFragment : BaseFragment(), MessageView, MainView.TitledView { messageProgress.visibility = if (show) VISIBLE else INVISIBLE } + fun onDeleteMessage(message: Message) { + presenter.onDeleteMessage(message) + } + fun onChildFragmentLoaded() { presenter.onChildViewLoaded() } + override fun notifyChildMessageDeleted(tabId: Int) { + (pagerAdapter.getFragmentInstance(tabId) as? MessageTabFragment)?.onParentDeleteMessage() + } + override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) { - (pagerAdapter.getFragmentInstance(index) as? MessageView.MessageChildView)?.onParentLoadData(forceRefresh) + (pagerAdapter.getFragmentInstance(index) as? MessageTabFragment)?.onParentLoadData(forceRefresh) } override fun openSendMessage() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt index 332d5b74..8e7af512 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.message +import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.SchedulersProvider @@ -43,6 +44,15 @@ class MessagePresenter @Inject constructor( } } + fun onDeleteMessage(message: Message) { + view?.notifyChildMessageDeleted( + when (message.removed) { + true -> 2 + else -> message.folderId - 1 + } + ) + } + fun onSendMessageButtonClicked() { view?.openSendMessage() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt index 41257ecc..2aa4d78e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt @@ -14,10 +14,7 @@ interface MessageView : BaseView { fun notifyChildLoadData(index: Int, forceRefresh: Boolean) + fun notifyChildMessageDeleted(tabId: Int) + fun openSendMessage() - - interface MessageChildView { - - fun onParentLoadData(forceRefresh: Boolean) - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt index 9afb744f..4a752401 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -13,7 +13,9 @@ import android.view.ViewGroup import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.ui.base.session.BaseSessionFragment +import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity import kotlinx.android.synthetic.main.fragment_message_preview.* import javax.inject.Inject @@ -26,6 +28,7 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi private var menuReplyButton: MenuItem? = null private var menuForwardButton: MenuItem? = null + private var menuDeleteButton: MenuItem? = null override val titleStringId: Int get() = R.string.message_title @@ -33,6 +36,9 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi override val noSubjectString: String get() = getString(R.string.message_no_subject) + override val deleteMessageSuccessString: String + get() = getString(R.string.message_delete_success) + companion object { const val MESSAGE_ID_KEY = "message_id" @@ -62,6 +68,7 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi inflater?.inflate(R.menu.action_menu_message_preview, menu) menuReplyButton = menu?.findItem(R.id.messagePreviewMenuReply) menuForwardButton = menu?.findItem(R.id.messagePreviewMenuForward) + menuDeleteButton = menu?.findItem(R.id.messagePreviewMenuDelete) presenter.onCreateOptionsMenu() } @@ -69,6 +76,7 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi return when (item?.itemId) { R.id.messagePreviewMenuReply -> presenter.onReply() R.id.messagePreviewMenuForward -> presenter.onForward() + R.id.messagePreviewMenuDelete -> presenter.onMessageDelete() else -> false } } @@ -97,9 +105,22 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi messagePreviewProgress.visibility = if (show) VISIBLE else GONE } + override fun showContent(show: Boolean) { + messagePreviewContentContainer.visibility = if (show) VISIBLE else GONE + } + override fun showOptions(show: Boolean) { menuReplyButton?.isVisible = show menuForwardButton?.isVisible = show + menuDeleteButton?.isVisible = show + } + + override fun setDeletedOptionsLabels() { + menuDeleteButton?.setTitle(R.string.message_delete_forever) + } + + override fun setNotDeletedOptionsLabels() { + menuDeleteButton?.setTitle(R.string.message_move_to_bin) } override fun showMessageError() { @@ -114,6 +135,14 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message)) } } + override fun popView() { + (activity as MainActivity).popView() + } + + override fun notifyParentMessageDeleted(message: Message) { + fragmentManager?.fragments?.forEach { if (it is MessageFragment) it.onDeleteMessage(message) } + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putInt(MESSAGE_ID_KEY, presenter.messageId) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt index 534c7ad3..397e103b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -47,7 +47,7 @@ class MessagePreviewPresenter @Inject constructor( setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString) setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) setContent(it.content.orEmpty()) - showOptions(true) + initOptions() if (it.recipient.isNotBlank()) setRecipient(it.recipient) else setSender(it.sender) @@ -76,7 +76,56 @@ class MessagePreviewPresenter @Inject constructor( } else false } + private fun deleteMessage() { + message?.let { message -> + disposable.add(messageRepository.deleteMessage(message) + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doOnSubscribe { + view?.run { + showContent(false) + showProgress(true) + showOptions(false) + } + } + .doFinally { + view?.showProgress(false) + } + .subscribe({ + view?.run { + notifyParentMessageDeleted(message) + showMessage(deleteMessageSuccessString) + popView() + } + }, { error -> + view?.showMessageError() + errorHandler.dispatch(error) + }, { + view?.showMessageError() + }) + ) + } + } + + fun onMessageDelete(): Boolean { + deleteMessage() + return true + } + + private fun initOptions() { + view?.apply { + showOptions(message != null) + message?.let { + when (it.removed) { + true -> setDeletedOptionsLabels() + false -> setNotDeletedOptionsLabels() + } + } + + } + } + fun onCreateOptionsMenu() { - view?.showOptions(message != null) + initOptions() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt index 3ff73396..8bc528a3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt @@ -7,6 +7,8 @@ interface MessagePreviewView : BaseSessionView { val noSubjectString: String + val deleteMessageSuccessString: String + fun setSubject(subject: String) fun setRecipient(recipient: String) @@ -19,11 +21,21 @@ interface MessagePreviewView : BaseSessionView { fun showProgress(show: Boolean) + fun showContent(show: Boolean) + fun showOptions(show: Boolean) + fun setDeletedOptionsLabels() + + fun setNotDeletedOptionsLabels() + fun showMessageError() fun openMessageReply(message: Message?) fun openMessageForward(message: Message?) + + fun popView() + + fun notifyParentMessageDeleted(message: Message) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt index 6ac7e226..3a44ea86 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt @@ -62,7 +62,7 @@ class SendMessageActivity : BaseActivity(), SendMessageView { supportActionBar?.setDisplayHomeAsUpEnabled(true) messageContainer = sendMessageContainer - presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, intent.getSerializableExtra(EXTRA_REPLY) as Boolean) + presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, intent.getSerializableExtra(EXTRA_REPLY) as? Boolean) } override fun onCreateOptionsMenu(menu: Menu?): Boolean { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt index f57848d6..32e3d5b2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt @@ -30,7 +30,7 @@ class SendMessagePresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(errorHandler) { - fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean) { + fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean?) { super.onAttachView(view) Timber.i("Send message view is attached") loadData(message, reply) @@ -38,13 +38,13 @@ class SendMessagePresenter @Inject constructor( message?.let { setSubject(when (reply) { true -> "RE: " - false -> "FE: " + else -> "FW: " } + message.subject) - if (preferencesRepository.fillMessageContent || !reply) { + if (preferencesRepository.fillMessageContent || reply != true) { setContent( when (reply) { true -> "\n\n" - false -> "" + else -> "" } + when (message.sender.isNotEmpty()) { true -> "Od: ${message.sender}\n" false -> "Do: ${message.recipient}\n" @@ -59,7 +59,7 @@ class SendMessagePresenter @Inject constructor( return true } - private fun loadData(message: Message?, reply: Boolean) { + private fun loadData(message: Message?, reply: Boolean?) { var reportingUnit: ReportingUnit? = null var recipients: List = emptyList() var selectedRecipient: List = emptyList() @@ -76,7 +76,7 @@ class SendMessagePresenter @Inject constructor( recipients = it } .flatMapCompletable { - if (message == null || !reply) Completable.complete() + if (message == null || reply != true) Completable.complete() else recipientRepository.getMessageRecipients(student, message) .doOnSuccess { Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", it.size) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt index 047f2a34..1f4595ae 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt @@ -17,13 +17,12 @@ import io.github.wulkanowy.ui.base.session.BaseSessionFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.MessageItem -import io.github.wulkanowy.ui.modules.message.MessageView import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.fragment_message_tab.* import javax.inject.Inject -class MessageTabFragment : BaseSessionFragment(), MessageTabView, MessageView.MessageChildView { +class MessageTabFragment : BaseSessionFragment(), MessageTabView { @Inject lateinit var presenter: MessageTabPresenter @@ -115,10 +114,14 @@ class MessageTabFragment : BaseSessionFragment(), MessageTabView, MessageView.Me (parentFragment as? MessageFragment)?.onChildFragmentLoaded() } - override fun onParentLoadData(forceRefresh: Boolean) { + fun onParentLoadData(forceRefresh: Boolean) { presenter.onParentViewLoadData(forceRefresh) } + fun onParentDeleteMessage() { + presenter.onDeleteMessage() + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putString(MessageTabFragment.MESSAGE_TAB_FOLDER_ID, presenter.folder.name) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index 7d9fda0d..4a8415e8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -34,7 +34,29 @@ class MessageTabPresenter @Inject constructor( onParentViewLoadData(true) } + fun onDeleteMessage() { + loadData(false) + } + fun onParentViewLoadData(forceRefresh: Boolean) { + loadData(forceRefresh) + } + + fun onMessageItemSelected(item: AbstractFlexibleItem<*>) { + if (item is MessageItem) { + Timber.i("Select message ${item.message.realId} item") + view?.run { + openMessage(item.message.realId) + if (item.message.unread) { + item.message.unread = false + updateItem(item) + updateMessage(item.message) + } + } + } + } + + private fun loadData(forceRefresh: Boolean) { Timber.i("Loading $folder message data started") disposable.apply { clear() @@ -67,20 +89,6 @@ class MessageTabPresenter @Inject constructor( } } - fun onMessageItemSelected(item: AbstractFlexibleItem<*>) { - if (item is MessageItem) { - Timber.i("Select message ${item.message.realId} item") - view?.run { - openMessage(item.message.realId) - if (item.message.unread) { - item.message.unread = false - updateItem(item) - updateMessage(item.message) - } - } - } - } - private fun updateMessage(message: Message) { Timber.i("Attempt to update message ${message.realId}") disposable.add(messageRepository.updateMessage(message) diff --git a/app/src/main/res/drawable/ic_message_delete_24dp.xml b/app/src/main/res/drawable/ic_message_delete_24dp.xml new file mode 100644 index 00000000..3760de23 --- /dev/null +++ b/app/src/main/res/drawable/ic_message_delete_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/fragment_message_preview.xml b/app/src/main/res/layout/fragment_message_preview.xml index 7150c03a..962d94ac 100644 --- a/app/src/main/res/layout/fragment_message_preview.xml +++ b/app/src/main/res/layout/fragment_message_preview.xml @@ -10,6 +10,7 @@ android:layout_height="match_parent"> + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 16ec15ff..3caa356e 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -141,6 +141,10 @@ Data: %s Odpowiedz Prześlij dalej + Usuń + Przenieś do kosza + Usuń trwale + Wiadomość usunięta pomyślnie Temat Treść Wiadomość wysłana pomyślnie diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 98bef391..6a56b441 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -135,6 +135,10 @@ Date: %s Reply Forward + Delete + Move to trash + Delete permanently + Message deleted successfully Subject Content Message sent successfully From 8daea5c90074772c2dc74372e3cb7773c6406717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Thu, 4 Apr 2019 13:00:07 +0200 Subject: [PATCH 031/322] Add class name to student (#315) --- .travis.yml | 8 +- app/build.gradle | 2 +- .../13.json | 1356 +++++++++++++++++ .../db/migrations/AbstractMigrationTest.kt | 31 + .../data/db/migrations/Migration12Test.kt | 34 +- .../data/db/migrations/Migration13Test.kt | 171 +++ .../attendance/AttendanceLocalTest.kt | 3 +- .../CompletedLessonsLocalTest.kt | 2 +- .../data/repositories/exam/ExamLocalTest.kt | 2 +- .../data/repositories/grade/GradeLocalTest.kt | 5 +- .../GradeStatisticsLocalTest.kt | 5 +- .../luckynumber/LuckyNumberLocalTest.kt | 2 +- .../recipient/RecipientLocalTest.kt | 2 +- .../repositories/student/StudentLocalTest.kt | 2 +- .../timetable/TimetableLocalTest.kt | 2 +- .../github/wulkanowy/data/db/AppDatabase.kt | 6 +- .../wulkanowy/data/db/entities/Semester.kt | 8 + .../wulkanowy/data/db/entities/Student.kt | 3 + .../data/db/migrations/Migration13.kt | 59 + .../repositories/semester/SemesterRemote.kt | 3 + .../repositories/student/StudentRemote.kt | 1 + .../ui/modules/account/AccountItem.kt | 4 +- .../studentselect/LoginStudentSelectItem.kt | 4 +- app/src/main/res/layout/item_account.xml | 10 +- .../semester/TestSemesterEnityCreator.kt | 6 +- .../repositories/student/StudentRemoteTest.kt | 2 +- .../login/form/LoginFormPresenterTest.kt | 2 +- .../LoginStudentSelectPresenterTest.kt | 2 +- 28 files changed, 1680 insertions(+), 57 deletions(-) create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json create mode 100644 app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt create mode 100644 app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt diff --git a/.travis.yml b/.travis.yml index 56c64858..b3261931 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,10 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ -#branches: -# only: -# - master -# - 0.7.x +branches: + only: + - master + - 0.7.x android: licenses: diff --git a/app/build.gradle b/app/build.gradle index 22af6e6b..6b6a1301 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -86,7 +86,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('io.github.wulkanowy:api:0.7.4') { exclude module: "threetenbp" } + implementation('com.github.wulkanowy:api:383ff93') { exclude module: "threetenbp" } implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json new file mode 100644 index 00000000..ab554665 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/13.json @@ -0,0 +1,1356 @@ +{ + "formatVersion": 1, + "database": { + "version": 13, + "identityHash": "18643bb64804b8268ec9395e3dd55ecb", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `endpoint` TEXT NOT NULL, `loginType` TEXT NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "endpoint", + "columnName": "endpoint", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "loginType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` INTEGER NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predictedGrade` TEXT NOT NULL, `finalGrade` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predictedGrade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "finalGrade", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"18643bb64804b8268ec9395e3dd55ecb\")" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt new file mode 100644 index 00000000..507105f6 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.Room +import androidx.room.testing.MigrationTestHelper +import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory +import androidx.test.core.app.ApplicationProvider +import androidx.test.platform.app.InstrumentationRegistry +import io.github.wulkanowy.data.db.AppDatabase +import org.junit.Rule + +abstract class AbstractMigrationTest { + + val dbName = "migration-test" + + @get:Rule + val helper: MigrationTestHelper = MigrationTestHelper( + InstrumentationRegistry.getInstrumentation(), + AppDatabase::class.java.canonicalName, + FrameworkSQLiteOpenHelperFactory() + ) + + fun getMigratedRoomDatabase(): AppDatabase { + val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(), + AppDatabase::class.java, dbName) + .addMigrations(Migration12(), Migration13()) + .build() + // close the database and release any stream resources when the test finishes + helper.closeWhenFinished(database) + return database + } +} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt index c61e9305..0bbcc427 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration12Test.kt @@ -2,33 +2,17 @@ package io.github.wulkanowy.data.db.migrations import android.content.ContentValues import android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL -import androidx.room.Room -import androidx.room.testing.MigrationTestHelper import androidx.sqlite.db.SupportSQLiteDatabase -import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory -import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import io.github.wulkanowy.data.db.AppDatabase -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import kotlin.test.assertEquals @RunWith(AndroidJUnit4::class) -class Migration12Test { - - private val dbName = "migration-test" - - @get:Rule - val helper: MigrationTestHelper = MigrationTestHelper( - InstrumentationRegistry.getInstrumentation(), - AppDatabase::class.java.canonicalName, - FrameworkSQLiteOpenHelperFactory() - ) +class Migration12Test : AbstractMigrationTest() { @Test - fun migrate11To12_twoNotRelatedStudents() { + fun twoNotRelatedStudents() { helper.createDatabase(dbName, 11).apply { // user 1 createStudent(this, 1, true) @@ -61,7 +45,7 @@ class Migration12Test { } @Test - fun migrate11To12_removeStudentsWithoutClassId() { + fun removeStudentsWithoutClassId() { helper.createDatabase(dbName, 11).apply { // user 1 createStudent(this, 1, true) @@ -85,7 +69,7 @@ class Migration12Test { } @Test - fun migrate11To12_ensureThereIsOnlyOneCurrentStudent() { + fun ensureThereIsOnlyOneCurrentStudent() { helper.createDatabase(dbName, 11).apply { // user 1 createStudent(this, 1, true) @@ -146,14 +130,4 @@ class Migration12Test { put("unit_id", "99") }) } - - private fun getMigratedRoomDatabase(): AppDatabase { - val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(), - AppDatabase::class.java, dbName) - .addMigrations(Migration12()) - .build() - // close the database and release any stream resources when the test finishes - helper.closeWhenFinished(database) - return database - } } 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 new file mode 100644 index 00000000..eb9d02a5 --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt @@ -0,0 +1,171 @@ +package io.github.wulkanowy.data.db.migrations + +import android.content.ContentValues +import android.database.sqlite.SQLiteDatabase +import androidx.sqlite.db.SupportSQLiteDatabase +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Test +import org.threeten.bp.LocalDate.of +import kotlin.test.assertTrue + +class Migration13Test : AbstractMigrationTest() { + + @Test + fun studentsWithSchoolNameWithClassName() { + helper.createDatabase(dbName, 12).apply { + createStudent(this, 1, "Klasa A - Publiczna szkoła Wulkanowego nr 1 w fakelog.cf", 1, 1) + createStudent(this, 2, "Klasa B - Publiczna szkoła Wulkanowego-fejka nr 1 w fakelog.cf", 2, 1) + createStudent(this, 2, "Klasa C - Publiczna szkoła Wulkanowego-fejka nr 2 w fakelog.cf", 1, 2) + close() + } + + helper.runMigrationsAndValidate(dbName, 13, true, Migration13()) + + val db = getMigratedRoomDatabase() + val students = db.studentDao.loadAll().blockingGet() + + assertEquals(3, students.size) + + students[0].run { + assertEquals(1, studentId) + assertEquals("A", className) + assertEquals("Publiczna szkoła Wulkanowego nr 1 w fakelog.cf", schoolName) + } + + students[1].run { + assertEquals(2, studentId) + assertEquals("B", className) + assertEquals("Publiczna szkoła Wulkanowego-fejka nr 1 w fakelog.cf", schoolName) + } + + students[2].run { + assertEquals(2, studentId) + assertEquals("C", className) + assertEquals("Publiczna szkoła Wulkanowego-fejka nr 2 w fakelog.cf", schoolName) + } + } + + @Test + fun studentsWithSchoolNameWithoutClassName() { + helper.createDatabase(dbName, 12).apply { + createStudent(this, 1, "Publiczna szkoła Wulkanowego nr 1 w fakelog.cf", 1) + createStudent(this, 2, "Publiczna szkoła Wulkanowego-fejka nr 1 w fakelog.cf", 1) + close() + } + + helper.runMigrationsAndValidate(dbName, 13, true, Migration13()) + + val db = getMigratedRoomDatabase() + val students = db.studentDao.loadAll().blockingGet() + + assertEquals(2, students.size) + + students[0].run { + assertEquals(1, studentId) + assertEquals("", className) + assertEquals("Publiczna szkoła Wulkanowego nr 1 w fakelog.cf", schoolName) + } + + students[1].run { + assertEquals(2, studentId) + assertEquals("", className) + assertEquals("Publiczna szkoła Wulkanowego-fejka nr 1 w fakelog.cf", schoolName) + } + } + + @Test + fun markAtLeastAndOnlyOneSemesterAtCurrent() { + helper.createDatabase(dbName, 12).apply { + createStudent(this, 1, "", 5) + createSemester(this, 1, 5, 1, 1, false) + createSemester(this, 1, 5, 2, 1, false) + createSemester(this, 1, 5, 3, 2, false) + createSemester(this, 1, 5, 4, 2, false) + + createStudent(this, 2, "", 5) + createSemester(this, 2, 5, 5, 5, true) + createSemester(this, 2, 5, 6, 5, true) + createSemester(this, 2, 5, 7, 55, true) + createSemester(this, 2, 5, 8, 55, true) + + createStudent(this, 3, "", 5) + createSemester(this, 3, 5, 11, 99, false) + createSemester(this, 3, 5, 12, 99, false) + createSemester(this, 3, 5, 13, 100, false) + createSemester(this, 3, 5, 14, 100, true) + close() + } + + helper.runMigrationsAndValidate(dbName, 13, true, Migration13()) + + val db = getMigratedRoomDatabase() + + val semesters1 = db.semesterDao.loadAll(1, 5).blockingGet() + assertTrue { semesters1.single { it.isCurrent }.isCurrent } + semesters1[0].run { + assertFalse(isCurrent) + assertEquals(1, semesterId) + assertEquals(1, diaryId) + } + semesters1[2].run { + assertFalse(isCurrent) + assertEquals(3, semesterId) + assertEquals(2, diaryId) + } + semesters1[3].run { + assertTrue(isCurrent) + assertEquals(4, semesterId) + assertEquals(2, diaryId) + } + + db.semesterDao.loadAll(2, 5).blockingGet().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) + } + + db.semesterDao.loadAll(2, 5).blockingGet().let { + assertTrue { it.single { it.isCurrent }.isCurrent } + assertFalse(it[0].isCurrent) + assertFalse(it[1].isCurrent) + assertFalse(it[2].isCurrent) + assertTrue(it[3].isCurrent) + } + } + + private fun createStudent(db: SupportSQLiteDatabase, studentId: Int, schoolName: String = "", classId: Int = -1, schoolId: Int = 123) { + db.insert("Students", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { + put("endpoint", "https://fakelog.cf") + put("loginType", "STANDARD") + put("email", "jan@fakelog.cf") + put("password", "******") + put("symbol", "Default") + put("student_id", studentId) + put("class_id", classId) + put("student_name", "Jan Kowalski") + put("school_id", schoolId) + put("school_name", schoolName) + put("is_current", false) + put("registration_date", "0") + }) + } + + private fun createSemester(db: SupportSQLiteDatabase, studentId: Int, classId: Int, semesterId: Int, diaryId: Int, isCurrent: Boolean = false) { + db.insert("Semesters", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply { + put("student_id", studentId) + put("diary_id", diaryId) + put("diary_name", "IA") + put("semester_id", semesterId) + put("semester_name", "1") + put("is_current", isCurrent) + put("class_id", classId) + put("unit_id", "99") + }) + } +} 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 ee65cf84..69502e74 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 @@ -11,6 +11,7 @@ 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,7 +41,7 @@ class AttendanceLocalTest { )) val attendance = attendanceLocal - .getAttendance(Semester(1, 2, "", 1, 3, true, 1, 1), + .getAttendance(Semester(1, 2, "", 1, 3, 2019, true, now(), now(), 1, 1), LocalDate.of(2018, 9, 10), LocalDate.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 014f0b8b..356073e8 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 @@ -41,7 +41,7 @@ class CompletedLessonsLocalTest { )) val completed = completedLessonsLocal - .getCompletedLessons(Semester(1, 2, "", 1, 3, true, 1, 1), + .getCompletedLessons(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1), LocalDate.of(2018, 9, 10), LocalDate.of(2018, 9, 14) ) 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 dc66fa42..fb76306d 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 @@ -40,7 +40,7 @@ class ExamLocalTest { )) val exams = examLocal - .getExams(Semester(1, 2, "", 1, 3, true, 1, 1), + .getExams(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1), LocalDate.of(2018, 9, 10), LocalDate.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 36238f1b..954d0eea 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 @@ -10,6 +10,7 @@ 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) @@ -39,8 +40,10 @@ 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 grades = gradeLocal - .getGrades(Semester(1, 2, "", 2, 3, true, 1, 1)) + .getGrades(semester) .blockingGet() assertEquals(2, grades.size) 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 5c0590e7..0057a26e 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 @@ -10,6 +10,7 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.threeten.bp.LocalDate import kotlin.test.assertEquals @RunWith(AndroidJUnit4::class) @@ -39,7 +40,7 @@ class GradeStatisticsLocalTest { )) val stats = gradeStatisticsLocal.getGradesStatistics( - Semester(2, 2, "", 1, 2, true, 1, 1), false, + Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false, "Matematyka" ).blockingGet() assertEquals(1, stats.size) @@ -55,7 +56,7 @@ class GradeStatisticsLocalTest { )) val stats = gradeStatisticsLocal.getGradesStatistics( - Semester(2, 2, "", 1, 2, true, 1, 1), false, + Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false, "Wszystkie" ).blockingGet() assertEquals(1, stats.size) 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 a656ac05..77ddafb9 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, true, 1, 1), + val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1), LocalDate.of(2019, 1, 20) ).blockingGet() diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt index cc4dd9f3..6edaccdb 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt @@ -42,7 +42,7 @@ class RecipientLocalTest { )) val recipients = recipientLocal.getRecipients( - Student("fakelog.cf", "AUTO", "", "", "", 1, "", "", "", 1, true, LocalDateTime.now()), + Student("fakelog.cf", "AUTO", "", "", "", 1, "", "", "", "", 1, true, LocalDateTime.now()), 2, ReportingUnit(1, 4, "", 0, "", emptyList()) ).blockingGet() diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt index f61c6374..b27446fa 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt @@ -39,7 +39,7 @@ class StudentLocalTest { @Test fun saveAndReadTest() { - studentLocal.saveStudent(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now())) + studentLocal.saveStudent(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "")) .blockingGet() val student = studentLocal.getCurrentStudent(true).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 0ecbcf92..fe25e4e9 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, true, 1, 1), + Semester(1, 2, "", 1, 1, 2019, true, 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/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index 9ce62b18..d867583e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -43,6 +43,7 @@ import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.migrations.Migration10 import io.github.wulkanowy.data.db.migrations.Migration11 import io.github.wulkanowy.data.db.migrations.Migration12 +import io.github.wulkanowy.data.db.migrations.Migration13 import io.github.wulkanowy.data.db.migrations.Migration2 import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration4 @@ -81,7 +82,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 12 + const val VERSION_SCHEMA = 13 fun newInstance(context: Context): AppDatabase { return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database") @@ -99,7 +100,8 @@ abstract class AppDatabase : RoomDatabase() { Migration9(), Migration10(), Migration11(), - Migration12() + Migration12(), + Migration13() ) .build() } 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 0f44fa2d..6c06be11 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 @@ -4,6 +4,7 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey +import org.threeten.bp.LocalDate @Entity(tableName = "Semesters", indices = [Index(value = ["student_id", "diary_id", "semester_id"], unique = true)]) data class Semester( @@ -17,6 +18,9 @@ data class Semester( @ColumnInfo(name = "diary_name") val diaryName: String, + @ColumnInfo(name = "school_year") + val schoolYear: Int, + @ColumnInfo(name = "semester_id") val semesterId: Int, @@ -26,6 +30,10 @@ data class Semester( @ColumnInfo(name = "is_current") val isCurrent: Boolean, + val start: LocalDate, + + val end: LocalDate, + @ColumnInfo(name = "class_id") val classId: Int, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt index 9cacf835..13c5ee08 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt @@ -32,6 +32,9 @@ data class Student( @ColumnInfo(name = "school_name") val schoolName: String, + @ColumnInfo(name = "class_name") + val className: String, + @ColumnInfo(name = "class_id") val classId: Int, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt new file mode 100644 index 00000000..83ab8609 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt @@ -0,0 +1,59 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration13 : Migration(12, 13) { + + override fun migrate(database: SupportSQLiteDatabase) { + addClassNameToStudents(database, getStudentsIds(database)) + updateSemestersTable(database) + markAtLeastAndOnlyOneSemesterAtCurrent(database, getStudentsAndClassIds(database)) + } + + private fun addClassNameToStudents(database: SupportSQLiteDatabase, students: List>) { + database.execSQL("ALTER TABLE Students ADD COLUMN class_name TEXT DEFAULT \"\" NOT NULL") + + students.forEach { (id, name) -> + val schoolName = name.substringAfter(" - ") + val className = name.substringBefore(" - ", "").replace("Klasa ", "") + database.execSQL("UPDATE Students SET class_name = '$className' WHERE id = '$id'") + database.execSQL("UPDATE Students SET school_name = '$schoolName' WHERE id = '$id'") + } + } + + private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList> { + val students = mutableListOf>() + val studentsCursor = database.query("SELECT id, school_name FROM Students") + if (studentsCursor.moveToFirst()) { + do { + students.add(studentsCursor.getInt(0) to studentsCursor.getString(1)) + } while (studentsCursor.moveToNext()) + } + return students + } + + private fun updateSemestersTable(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Semesters ADD COLUMN school_year INTEGER DEFAULT 1970 NOT NULL") + database.execSQL("ALTER TABLE Semesters ADD COLUMN start INTEGER DEFAULT 0 NOT NULL") + database.execSQL("ALTER TABLE Semesters ADD COLUMN `end` INTEGER DEFAULT 0 NOT NULL") + } + + private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List> { + val students = mutableListOf>() + val studentsCursor = database.query("SELECT student_id, class_id FROM Students") + if (studentsCursor.moveToFirst()) { + do { + students.add(studentsCursor.getInt(0) to studentsCursor.getInt(1)) + } while (studentsCursor.moveToNext()) + } + return students + } + + private fun markAtLeastAndOnlyOneSemesterAtCurrent(database: SupportSQLiteDatabase, students: List>) { + students.forEach { (studentId, classId) -> + database.execSQL("UPDATE Semesters SET is_current = 0 WHERE student_id = '$studentId' AND class_id = '$classId'") + database.execSQL("UPDATE Semesters SET is_current = 1 WHERE id = (SELECT id FROM Semesters WHERE student_id = '$studentId' AND class_id = '$classId' ORDER BY semester_id DESC)") + } + } +} 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 b25c8881..c199c16c 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 @@ -17,9 +17,12 @@ class SemesterRemote @Inject constructor(private val api: Api) { studentId = student.studentId, diaryId = semester.diaryId, diaryName = semester.diaryName, + schoolYear = semester.schoolYear, semesterId = semester.semesterId, semesterName = semester.semesterNumber, isCurrent = semester.current, + start = semester.start, + end = semester.end, classId = semester.classId, unitId = semester.unitId ) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt index f0692a53..251d3834 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt @@ -21,6 +21,7 @@ class StudentRemote @Inject constructor(private val api: Api) { studentName = student.studentName, schoolSymbol = student.schoolSymbol, schoolName = student.schoolName, + className = student.className, classId = student.classId, endpoint = endpoint, loginType = student.loginType.name, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt index cb7aabda..972d10a2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountItem.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.account +import android.annotation.SuppressLint import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -18,9 +19,10 @@ class AccountItem(val student: Student) : AbstractFlexibleItem>, holder: ViewHolder, position: Int, payloads: MutableList?) { holder.apply { - accountItemName.text = student.studentName + accountItemName.text = "${student.studentName} ${student.className}" accountItemSchool.text = student.schoolName accountItemImage.setBackgroundResource(if (student.isCurrent) R.drawable.ic_account_circular_border else 0) } 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 71a7d681..65940a98 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 @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.login.studentselect +import android.annotation.SuppressLint import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -18,9 +19,10 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem>, holder: ItemViewHolder, position: Int, payloads: MutableList?) { holder.run { - loginItemName.text = student.studentName + loginItemName.text = "${student.studentName} ${student.className}" loginItemSchool.text = student.schoolName } } diff --git a/app/src/main/res/layout/item_account.xml b/app/src/main/res/layout/item_account.xml index 844dd2c9..56f553e5 100644 --- a/app/src/main/res/layout/item_account.xml +++ b/app/src/main/res/layout/item_account.xml @@ -30,8 +30,10 @@ android:layout_marginLeft="20dp" android:layout_toEndOf="@id/accountItemImage" android:layout_toRightOf="@id/accountItemImage" - android:text="@string/app_name" - android:textSize="16sp" /> + android:ellipsize="end" + android:maxLines="1" + android:textSize="16sp" + tools:text="@tools:sample/lorem/random" /> + android:textSize="12sp" + tools:text="@tools:sample/lorem/random" /> 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 c3377ac5..f628710f 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,6 +1,7 @@ package io.github.wulkanowy.data.repositories.semester import io.github.wulkanowy.data.db.entities.Semester +import org.threeten.bp.LocalDate.now fun createSemesterEntity(current: Boolean): Semester { return Semester( @@ -8,9 +9,12 @@ fun createSemesterEntity(current: Boolean): Semester { diaryId = 0, semesterId = 0, diaryName = "", + schoolYear = 1970, classId = 0, isCurrent = current, semesterName = 0, - unitId = 0 + unitId = 0, + start = now(), + end = now() ) } diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt index 8bd819e2..4d7b23eb 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt @@ -22,7 +22,7 @@ class StudentRemoteTest { @Test fun testRemoteAll() { - doReturn(Single.just(listOf(Student("", "", 1, "test", "", "", 1, Api.LoginType.AUTO)))) + doReturn(Single.just(listOf(Student("", "", 1, "test", "", "", "", 1, Api.LoginType.AUTO)))) .`when`(mockApi).getStudents() val students = StudentRemote(mockApi).getStudents("", "", "").blockingGet() 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 f1113f7b..83047e02 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 @@ -86,7 +86,7 @@ class LoginFormPresenterTest { @Test fun loginTest() { - val studentTest = Student(email = "test@", password = "123", endpoint = "https://fakelog.cf", loginType = "AUTO", studentName = "", schoolSymbol = "", schoolName = "", studentId = 0, classId = 1, isCurrent = false, symbol = "", registrationDate = now()) + val studentTest = Student(email = "test@", password = "123", endpoint = "https://fakelog.cf", loginType = "AUTO", studentName = "", schoolSymbol = "", schoolName = "", studentId = 0, classId = 1, isCurrent = false, symbol = "", registrationDate = now(), className = "") doReturn(Single.just(listOf(studentTest))) .`when`(repository).getStudents(anyString(), anyString(), anyString(), anyString()) 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 2de4e43a..192602b7 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 @@ -32,7 +32,7 @@ class LoginStudentSelectPresenterTest { private lateinit var presenter: LoginStudentSelectPresenter - private val testStudent by lazy { Student(email = "test", password = "test123", endpoint = "https://fakelog.cf", loginType = "AUTO", symbol = "", isCurrent = false, studentId = 0, schoolName = "", schoolSymbol = "", classId = 1, studentName = "", registrationDate = now()) } + private val testStudent by lazy { Student(email = "test", password = "test123", endpoint = "https://fakelog.cf", loginType = "AUTO", symbol = "", isCurrent = false, studentId = 0, schoolName = "", schoolSymbol = "", classId = 1, studentName = "", registrationDate = now(), className = "") } private val testException by lazy { RuntimeException("Problem") } From 7d97d710668c4e68cfbcddbd936f83c7ff267cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Fri, 5 Apr 2019 19:35:21 +0200 Subject: [PATCH 032/322] Fix message loading if student and parent are logged in (#319) Fixes #316 --- .../io/github/wulkanowy/data/db/migrations/Migration13.kt | 5 +++++ .../wulkanowy/data/repositories/message/MessageLocal.kt | 6 +++--- .../wulkanowy/data/repositories/message/MessageRemote.kt | 5 +++-- .../data/repositories/message/MessageRepository.kt | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt index 83ab8609..0cf8cd9b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt @@ -9,6 +9,7 @@ class Migration13 : Migration(12, 13) { addClassNameToStudents(database, getStudentsIds(database)) updateSemestersTable(database) markAtLeastAndOnlyOneSemesterAtCurrent(database, getStudentsAndClassIds(database)) + clearMessagesTable(database) } private fun addClassNameToStudents(database: SupportSQLiteDatabase, students: List>) { @@ -56,4 +57,8 @@ class Migration13 : Migration(12, 13) { database.execSQL("UPDATE Semesters SET is_current = 1 WHERE id = (SELECT id FROM Semesters WHERE student_id = '$studentId' AND class_id = '$classId' ORDER BY semester_id DESC)") } } + + private fun clearMessagesTable(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Messages") + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt index e9ab7297..411cca39 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt @@ -24,13 +24,13 @@ class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) { } fun getMessage(student: Student, id: Int): Maybe { - return messagesDb.load(student.studentId, id) + return messagesDb.load(student.id.toInt(), id) } fun getMessages(student: Student, folder: MessageFolder): Maybe> { return when (folder) { - TRASHED -> messagesDb.loadDeleted(student.studentId) - else -> messagesDb.loadAll(student.studentId, folder.id) + TRASHED -> messagesDb.loadDeleted(student.id.toInt()) + else -> messagesDb.loadAll(student.id.toInt(), folder.id) }.filter { it.isNotEmpty() } } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt index 3fb01d30..d50c40f3 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt @@ -5,6 +5,7 @@ import io.github.wulkanowy.api.messages.Folder import io.github.wulkanowy.api.messages.SentMessage import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.utils.toLocalDateTime import io.reactivex.Single import org.threeten.bp.LocalDateTime.now @@ -16,11 +17,11 @@ import io.github.wulkanowy.api.messages.Recipient as ApiRecipient @Singleton class MessageRemote @Inject constructor(private val api: Api) { - fun getMessages(studentId: Int, folder: MessageFolder): Single> { + fun getMessages(student: Student, folder: MessageFolder): Single> { return api.getMessages(Folder.valueOf(folder.name)).map { messages -> messages.map { Message( - studentId = studentId, + studentId = student.id.toInt(), realId = it.id ?: 0, messageId = it.messageId ?: 0, sender = it.sender.orEmpty(), diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt index d319689e..3b4f9582 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt @@ -28,7 +28,7 @@ class MessageRepository @Inject constructor( local.getMessages(student, folder).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getMessages(student.studentId, folder) + if (it) remote.getMessages(student, folder) else Single.error(UnknownHostException()) }.flatMap { new -> local.getMessages(student, folder).toSingle(emptyList()) From f2fa04105d343c3076a72b69b602f19f53418331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 6 Apr 2019 00:21:51 +0200 Subject: [PATCH 033/322] Version 0.7.5 --- .travis.yml | 8 ++++---- app/build.gradle | 6 +++--- app/src/main/play/release-notes/pl-PL/default.txt | 14 ++++++-------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index b3261931..56c64858 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,10 @@ cache: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ -branches: - only: - - master - - 0.7.x +#branches: +# only: +# - master +# - 0.7.x android: licenses: diff --git a/app/build.gradle b/app/build.gradle index 6b6a1301..c58c1e33 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 targetSdkVersion 28 - versionCode 30 - versionName "0.7.4" + versionCode 31 + versionName "0.7.5" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -86,7 +86,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('com.github.wulkanowy:api:383ff93') { exclude module: "threetenbp" } + implementation('io.github.wulkanowy:api:0.7.5') { exclude module: "threetenbp" } implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index c85a155b..78576903 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,11 +1,9 @@ -Wersja 0.7.4 +Wersja 0.7.5 Naprawiliśmy: -- problem ze stabilnością na androidach 4 i 5 -- problem z przełączaniem kont, jeśli zalogowany był jednocześnie uczeń i rodzic -- problem z odświeżaniem danych, jeśli uczeń przeniósł się z klasy do klasy w bieżącym roku szkolnym +- problem z brakiem aktywnego semestru (jeśli doświadczysz jakichś problemów - wyloguj i zaloguj się ponownie) +- logowanie w niestandardowych dziennikach na vulcan.net.pl +- oznaczanie lekcji w planie jako odwołanej, jeśli brak opisu tej zmiany +- ładowanie wiadomości, jeśli byli zalogowani jednocześnie uczeń i rodzic -Dodaliśmy: -- nową opcję zmiany wartości plusa i minusa na 0.75 - -Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.4 +Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.5 From 333f7bfa162f9f6b34c3a09dbd10bb4f3ee8ab1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 7 Apr 2019 10:59:27 +0200 Subject: [PATCH 034/322] Add privacy policy link (#320) --- .../ui/modules/login/form/LoginFormFragment.kt | 2 ++ app/src/main/res/layout/fragment_login_form.xml | 12 +++++++++++- app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 4 files changed, 15 insertions(+), 1 deletion(-) 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 54fd0704..5c0a6ed8 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 @@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.login.form import android.annotation.SuppressLint import android.os.Bundle +import android.text.method.LinkMovementMethod import android.view.LayoutInflater import android.view.View import android.view.View.GONE @@ -53,6 +54,7 @@ class LoginFormFragment : BaseFragment(), LoginFormView { loginFormName.setOnTextChangedListener { presenter.onNameTextChanged() } loginFormPass.setOnTextChangedListener { presenter.onPassTextChanged() } loginFormHost.setOnItemSelectedListener { presenter.onHostSelected() } + loginFormPrivacyPolicyLink.movementMethod = LinkMovementMethod.getInstance() loginFormSignIn.setOnClickListener { presenter.attemptLogin() } loginFormPass.setOnEditorActionListener { _, id, _ -> diff --git a/app/src/main/res/layout/fragment_login_form.xml b/app/src/main/res/layout/fragment_login_form.xml index 81baf4af..7a82eaf7 100644 --- a/app/src/main/res/layout/fragment_login_form.xml +++ b/app/src/main/res/layout/fragment_login_form.xml @@ -124,7 +124,8 @@ + android:layout_height="56dp" + android:entries="@array/endpoints_keys" /> + + Hasło Dziennik Symbol + Polityka prywatności Zaloguj To hasło jest za krótkie Dane logowania są niepoprawne diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1debed7b..e12d94fd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,6 +26,7 @@ Password Register Symbol + Privacy policy Sign in This password is too short Login details are incorrect From 6cd6cae1e0fa7f6f4b08c50ba212a82862efe2b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 7 Apr 2019 11:08:46 +0200 Subject: [PATCH 035/322] Version 0.7.6 --- app/build.gradle | 6 +++--- app/src/main/play/release-notes/pl-PL/default.txt | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c58c1e33..48f0e65c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 15 targetSdkVersion 28 - versionCode 31 - versionName "0.7.5" + versionCode 32 + versionName "0.7.6" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -86,7 +86,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('io.github.wulkanowy:api:0.7.5') { exclude module: "threetenbp" } + implementation('io.github.wulkanowy:api:0.7.6') { exclude module: "threetenbp" } implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 78576903..d0f12e55 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,7 +1,9 @@ Wersja 0.7.5 +Uwaga! Jeżeli w aplikacji przestały wyświetlać się oceny, prosimy o wylogowanie i zalogowanie się ponownie! + Naprawiliśmy: -- problem z brakiem aktywnego semestru (jeśli doświadczysz jakichś problemów - wyloguj i zaloguj się ponownie) +- problem z brakiem aktywnego semestru - logowanie w niestandardowych dziennikach na vulcan.net.pl - oznaczanie lekcji w planie jako odwołanej, jeśli brak opisu tej zmiany - ładowanie wiadomości, jeśli byli zalogowani jednocześnie uczeń i rodzic From c18877466ffea8848ef47025c2edd945bb48d1c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Mon, 8 Apr 2019 00:18:45 +0200 Subject: [PATCH 036/322] Add account picker for timetable widget (#314) Close #281 --- app/src/main/AndroidManifest.xml | 11 +- .../wulkanowy/data/db/SharedPrefHelper.kt | 6 +- .../io/github/wulkanowy/di/BuilderModule.kt | 6 +- .../widgets/TimetableWidgetService.kt | 2 +- .../TimetableWidgetConfigureActivity.kt | 79 ++++++++++++++ .../TimetableWidgetConfigureItem.kt | 53 ++++++++++ .../TimetableWidgetConfigurePresenter.kt | 65 ++++++++++++ .../TimetableWidgetConfigureView.kt | 18 ++++ .../TimetableWidgetFactory.kt | 52 ++++++---- .../TimetableWidgetProvider.kt | 83 ++++++++++++--- .../github/wulkanowy/utils/TimeExtension.kt | 3 + .../res/drawable-hdpi/ic_widget_account.png | Bin 0 -> 461 bytes .../res/drawable-mdpi/ic_widget_account.png | Bin 0 -> 319 bytes .../res/drawable-xhdpi/ic_widget_account.png | Bin 0 -> 596 bytes .../res/drawable-xxhdpi/ic_widget_account.png | Bin 0 -> 922 bytes .../drawable/img_timetable_widget_preview.png | Bin 3425 -> 5284 bytes .../activity_timetable_widget_configure.xml | 31 ++++++ app/src/main/res/layout/item_account.xml | 4 +- .../main/res/layout/item_widget_timetable.xml | 2 +- app/src/main/res/layout/widget_timetable.xml | 97 ++++++++++-------- app/src/main/res/values-night/styles.xml | 5 - app/src/main/res/values-pl/strings.xml | 2 - app/src/main/res/values/dimens.xml | 3 - app/src/main/res/values/strings.xml | 2 - app/src/main/res/values/styles.xml | 8 ++ .../res/xml/provider_widget_timetable.xml | 7 +- 26 files changed, 437 insertions(+), 102 deletions(-) create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt rename app/src/main/java/io/github/wulkanowy/ui/{widgets/timetable => modules/timetablewidget}/TimetableWidgetFactory.kt (67%) rename app/src/main/java/io/github/wulkanowy/ui/{widgets/timetable => modules/timetablewidget}/TimetableWidgetProvider.kt (57%) create mode 100644 app/src/main/res/drawable-hdpi/ic_widget_account.png create mode 100644 app/src/main/res/drawable-mdpi/ic_widget_account.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_widget_account.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_widget_account.png create mode 100644 app/src/main/res/layout/activity_timetable_widget_configure.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0c6edab4..f0dd9cb4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -45,13 +45,22 @@ android:configChanges="orientation|screenSize" android:label="@string/send_message_title" android:theme="@style/WulkanowyTheme.NoActionBar" /> + + + + + diff --git a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt index b3b6f5e3..74f9fa65 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefHelper.kt @@ -6,18 +6,16 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton +@SuppressLint("ApplySharedPref") class SharedPrefHelper @Inject constructor(private val sharedPref: SharedPreferences) { - @SuppressLint("ApplySharedPref") fun putLong(key: String, value: Long, sync: Boolean = false) { sharedPref.edit().putLong(key, value).apply { if (sync) commit() else apply() } } - fun getLong(key: String, defaultValue: Long): Long { - return sharedPref.getLong(key, defaultValue) - } + fun getLong(key: String, defaultValue: Long) = sharedPref.getLong(key, defaultValue) fun delete(key: String) { sharedPref.edit().remove(key).apply() diff --git a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt index 7f477630..2b48029e 100644 --- a/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt +++ b/app/src/main/java/io/github/wulkanowy/di/BuilderModule.kt @@ -9,7 +9,8 @@ import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainModule import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity import io.github.wulkanowy.ui.modules.splash.SplashActivity -import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetProvider +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider @Module internal abstract class BuilderModule { @@ -29,6 +30,9 @@ internal abstract class BuilderModule { @ContributesAndroidInjector abstract fun bindMessageSendActivity(): SendMessageActivity + @ContributesAndroidInjector + abstract fun bindTimetableWidgetAccountActivity(): TimetableWidgetConfigureActivity + @ContributesAndroidInjector abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider } 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 0432ee14..f3429457 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 @@ -7,7 +7,7 @@ import io.github.wulkanowy.data.db.SharedPrefHelper import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository -import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetFactory +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetFactory import io.github.wulkanowy.utils.SchedulersProvider import javax.inject.Inject diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt new file mode 100644 index 00000000..37d0571a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt @@ -0,0 +1,79 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +import android.widget.Toast.LENGTH_LONG +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_PROVIDER +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.activity_timetable_widget_configure.* +import javax.inject.Inject + +class TimetableWidgetConfigureActivity : BaseActivity(), TimetableWidgetConfigureView { + + @Inject + lateinit var configureAdapter: FlexibleAdapter> + + @Inject + lateinit var presenter: TimetableWidgetConfigurePresenter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setResult(RESULT_CANCELED) + setContentView(R.layout.activity_timetable_widget_configure) + + intent.extras.let { + presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID), it?.getBoolean(EXTRA_FROM_PROVIDER)) + } + } + + override fun initView() { + timetableWidgetConfigureRecycler.apply { + adapter = configureAdapter + layoutManager = SmoothScrollLinearLayoutManager(context) + } + configureAdapter.setOnItemClickListener { presenter.onItemSelect(it) } + } + + override fun updateData(data: List) { + configureAdapter.updateDataSet(data) + } + + override fun updateTimetableWidget(widgetId: Int) { + sendBroadcast(Intent(this, TimetableWidgetProvider::class.java) + .apply { + action = ACTION_APPWIDGET_UPDATE + putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId)) + }) + } + + override fun setSuccessResult(widgetId: Int) { + setResult(RESULT_OK, Intent().apply { putExtra(EXTRA_APPWIDGET_ID, widgetId) }) + } + + override fun showError(text: String, error: Throwable) { + Toast.makeText(this, text, LENGTH_LONG).show() + } + + override fun finishView() { + finish() + } + + override fun openLoginView() { + startActivity(LoginActivity.getStartIntent(this)) + } + + override fun onDestroy() { + super.onDestroy() + presenter.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt new file mode 100644 index 00000000..f6ec1519 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureItem.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import android.annotation.SuppressLint +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_account.* + +class TimetableWidgetConfigureItem(val student: Student, private val isCurrent: Boolean) : + AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_account + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { + return ViewHolder(view, adapter) + } + + @SuppressLint("SetTextI18n") + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList) { + holder.apply { + accountItemName.text = "${student.studentName} ${student.className}" + accountItemSchool.text = student.schoolName + accountItemImage.setBackgroundResource(if (isCurrent) R.drawable.ic_account_circular_border else 0) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as TimetableWidgetConfigureItem + + if (student != other.student) return false + + return true + } + + override fun hashCode(): Int { + var result = student.hashCode() + result = 31 * result + student.id.toInt() + return result + } + + class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View + get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt new file mode 100644 index 00000000..5a7bdd7c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt @@ -0,0 +1,65 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.SharedPrefHelper +import io.github.wulkanowy.data.db.entities.Student +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.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey +import io.github.wulkanowy.utils.SchedulersProvider +import javax.inject.Inject + +class TimetableWidgetConfigurePresenter @Inject constructor( + private val errorHandler: ErrorHandler, + private val schedulers: SchedulersProvider, + private val studentRepository: StudentRepository, + private val sharedPref: SharedPrefHelper +) : BasePresenter(errorHandler) { + + private var appWidgetId: Int? = null + + private var isFromProvider = false + + fun onAttachView(view: TimetableWidgetConfigureView, appWidgetId: Int?, isFromProvider: Boolean?) { + super.onAttachView(view) + this.appWidgetId = appWidgetId + this.isFromProvider = isFromProvider ?: false + view.initView() + loadData() + } + + fun onItemSelect(item: AbstractFlexibleItem<*>) { + if (item is TimetableWidgetConfigureItem) { + registerStudent(item.student) + } + } + + private fun loadData() { + disposable.add(studentRepository.getSavedStudents(false) + .map { it to appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } } + .map { (students, currentStudentId) -> + students.map { student -> TimetableWidgetConfigureItem(student, student.id == currentStudentId) } + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + when { + it.isEmpty() -> view?.openLoginView() + it.size == 1 && !isFromProvider -> registerStudent(it.single().student) + else -> view?.updateData(it) + } + }, { errorHandler.dispatch(it) })) + } + + private fun registerStudent(student: Student) { + appWidgetId?.also { + sharedPref.putLong(getStudentWidgetKey(it), student.id) + view?.apply { + updateTimetableWidget(it) + setSuccessResult(it) + } + } + view?.finishView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt new file mode 100644 index 00000000..98c800d4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.ui.modules.timetablewidget + +import io.github.wulkanowy.ui.base.BaseView + +interface TimetableWidgetConfigureView : BaseView { + + fun initView() + + fun updateData(data: List) + + fun updateTimetableWidget(widgetId: Int) + + fun setSuccessResult(widgetId: Int) + + fun finishView() + + fun openLoginView() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt similarity index 67% rename from app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt index f2d40ba7..b288f7b7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetFactory.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt @@ -1,5 +1,6 @@ -package io.github.wulkanowy.ui.widgets.timetable +package io.github.wulkanowy.ui.modules.timetablewidget +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID import android.content.Context import android.content.Intent import android.graphics.Paint.ANTI_ALIAS_FLAG @@ -15,9 +16,11 @@ import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getDateWidgetKey +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.toFormattedString -import io.reactivex.Single +import io.reactivex.Maybe import org.threeten.bp.LocalDate import timber.log.Timber @@ -48,28 +51,37 @@ class TimetableWidgetFactory( override fun onDestroy() {} override fun onDataSetChanged() { - intent?.action?.let { LocalDate.ofEpochDay(sharedPref.getLong(it, 0)) } - ?.let { date -> - try { - lessons = studentRepository.isStudentSaved() - .flatMap { isSaved -> - if (isSaved) { - studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { timetableRepository.getTimetable(it, date, date) } - } else Single.just(emptyList()) - } - .map { item -> item.sortedBy { it.number } } - .subscribeOn(schedulers.backgroundThread) - .blockingGet() - } catch (e: Exception) { - Timber.e(e, "An error has occurred while downloading data for the widget") - } + intent?.extras?.getInt(EXTRA_APPWIDGET_ID)?.let { appWidgetId -> + val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0)) + val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0) + + lessons = try { + studentRepository.isStudentSaved() + .filter { true } + .flatMap { studentRepository.getSavedStudents().toMaybe() } + .flatMap { + if (studentId == 0L) throw IllegalArgumentException("Student id is 0") + + it.singleOrNull { student -> student.id == studentId } + .let { student -> + if (student != null) Maybe.just(student) + else Maybe.empty() + } + } + .flatMap { semesterRepository.getCurrentSemester(it).toMaybe() } + .flatMap { timetableRepository.getTimetable(it, date, date).toMaybe() } + .map { item -> item.sortedBy { it.number } } + .subscribeOn(schedulers.backgroundThread) + .blockingGet(emptyList()) + } catch (e: Exception) { + Timber.e(e, "An error has occurred in timetable widget factory") + emptyList() } + } } override fun getViewAt(position: Int): RemoteViews? { - if (position == INVALID_POSITION || lessons.getOrNull(position) === null) return null + if (position == INVALID_POSITION || lessons.getOrNull(position) == null) return null return RemoteViews(context.packageName, R.layout.item_widget_timetable).apply { lessons[position].let { diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt similarity index 57% rename from app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt index 402366ec..83334c78 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/timetable/TimetableWidgetProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.widgets.timetable +package io.github.wulkanowy.ui.modules.timetablewidget import android.app.PendingIntent import android.app.PendingIntent.FLAG_UPDATE_CURRENT @@ -10,21 +10,28 @@ import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.widget.RemoteViews import dagger.android.AndroidInjection import io.github.wulkanowy.R import io.github.wulkanowy.data.db.SharedPrefHelper +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.services.widgets.TimetableWidgetService import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainActivity.Companion.EXTRA_START_MENU_INDEX import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.nextOrSameSchoolDay import io.github.wulkanowy.utils.nextSchoolDay import io.github.wulkanowy.utils.previousSchoolDay +import io.github.wulkanowy.utils.shortcutWeekDayName import io.github.wulkanowy.utils.toFormattedString -import io.github.wulkanowy.utils.weekDayName +import io.reactivex.Maybe import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate.now +import timber.log.Timber import javax.inject.Inject class TimetableWidgetProvider : BroadcastReceiver() { @@ -32,13 +39,21 @@ class TimetableWidgetProvider : BroadcastReceiver() { @Inject lateinit var appWidgetManager: AppWidgetManager + @Inject + lateinit var studentRepository: StudentRepository + @Inject lateinit var sharedPref: SharedPrefHelper + @Inject + lateinit var schedulers: SchedulersProvider + @Inject lateinit var analytics: FirebaseAnalyticsHelper companion object { + const val EXTRA_FROM_PROVIDER = "extraFromProvider" + const val EXTRA_TOGGLED_WIDGET_ID = "extraToggledWidget" const val EXTRA_BUTTON_TYPE = "extraButtonType" @@ -49,7 +64,9 @@ class TimetableWidgetProvider : BroadcastReceiver() { const val BUTTON_RESET = "buttonReset" - fun createWidgetKey(appWidgetId: Int) = "timetable_widget_$appWidgetId" + fun getDateWidgetKey(appWidgetId: Int) = "timetable_widget_date_$appWidgetId" + + fun getStudentWidgetKey(appWidgetId: Int) = "timetable_widget_student_$appWidgetId" } override fun onReceive(context: Context, intent: Intent) { @@ -63,12 +80,14 @@ class TimetableWidgetProvider : BroadcastReceiver() { private fun onUpdate(context: Context, intent: Intent) { if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) { intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId -> - updateWidget(context, appWidgetId, now().nextOrSameSchoolDay) + val student = getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId) + updateWidget(context, appWidgetId, now().nextOrSameSchoolDay, student) } } else { val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE) val toggledWidgetId = intent.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0) - val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(createWidgetKey(toggledWidgetId), 0)) + val student = getStudent(sharedPref.getLong(getStudentWidgetKey(toggledWidgetId), 0), toggledWidgetId) + val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0)) val date = when (buttonType) { BUTTON_RESET -> now().nextOrSameSchoolDay BUTTON_NEXT -> savedDate.nextSchoolDay @@ -76,35 +95,46 @@ class TimetableWidgetProvider : BroadcastReceiver() { else -> now().nextOrSameSchoolDay } if (!buttonType.isNullOrBlank()) analytics.logEvent("changed_timetable_widget_day", "button" to buttonType) - updateWidget(context, toggledWidgetId, date) + updateWidget(context, toggledWidgetId, date, student) } } private fun onDelete(intent: Intent) { intent.getIntExtra(EXTRA_APPWIDGET_ID, 0).let { - if (it != 0) sharedPref.delete(createWidgetKey(it)) + if (it != 0) { + sharedPref.apply { + delete(getStudentWidgetKey(it)) + delete(getDateWidgetKey(it)) + } + } } } - private fun updateWidget(context: Context, appWidgetId: Int, date: LocalDate) { + private fun updateWidget(context: Context, appWidgetId: Int, date: LocalDate, student: Student?) { RemoteViews(context.packageName, R.layout.widget_timetable).apply { setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty) - setTextViewText(R.id.timetableWidgetDay, date.weekDayName.capitalize()) - setTextViewText(R.id.timetableWidgetDate, date.toFormattedString()) + setTextViewText(R.id.timetableWidgetDate, "${date.shortcutWeekDayName.capitalize()} ${date.toFormattedString()}") + setTextViewText(R.id.timetableWidgetName, student?.studentName ?: context.getString(R.string.all_no_data)) setRemoteAdapter(R.id.timetableWidgetList, Intent(context, TimetableWidgetService::class.java) - .apply { action = createWidgetKey(appWidgetId) }) + .apply { putExtra(EXTRA_APPWIDGET_ID, appWidgetId) }) setOnClickPendingIntent(R.id.timetableWidgetNext, createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT)) setOnClickPendingIntent(R.id.timetableWidgetPrev, createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV)) - createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET).also { + createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET).let { setOnClickPendingIntent(R.id.timetableWidgetDate, it) - setOnClickPendingIntent(R.id.timetableWidgetDay, it) + setOnClickPendingIntent(R.id.timetableWidgetName, it) } + setOnClickPendingIntent(R.id.timetableWidgetAccount, PendingIntent.getActivity(context, -Int.MAX_VALUE + appWidgetId, + Intent(context, TimetableWidgetConfigureActivity::class.java).apply { + addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK) + putExtra(EXTRA_APPWIDGET_ID, appWidgetId) + putExtra(EXTRA_FROM_PROVIDER, true) + }, FLAG_UPDATE_CURRENT)) setPendingIntentTemplate(R.id.timetableWidgetList, PendingIntent.getActivity(context, 1, MainActivity.getStartIntent(context).apply { putExtra(EXTRA_START_MENU_INDEX, 3) }, FLAG_UPDATE_CURRENT)) }.also { - sharedPref.putLong(createWidgetKey(appWidgetId), date.toEpochDay(), true) + sharedPref.putLong(getDateWidgetKey(appWidgetId), date.toEpochDay(), true) appWidgetManager.apply { notifyAppWidgetViewDataChanged(appWidgetId, R.id.timetableWidgetList) updateAppWidget(appWidgetId, it) @@ -120,4 +150,29 @@ class TimetableWidgetProvider : BroadcastReceiver() { putExtra(EXTRA_TOGGLED_WIDGET_ID, appWidgetId) }, FLAG_UPDATE_CURRENT) } + + private fun getStudent(id: Long, appWidgetId: Int): Student? { + return try { + studentRepository.isStudentSaved() + .filter { true } + .flatMap { studentRepository.getSavedStudents(false).toMaybe() } + .flatMap { students -> + students.singleOrNull { student -> student.id == id } + .let { student -> + if (student != null) { + Maybe.just(student) + } else { + studentRepository.getCurrentStudent(false) + .toMaybe() + .doOnSuccess { sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) } + } + } + } + .subscribeOn(schedulers.backgroundThread) + .blockingGet() + } catch (e: Exception) { + Timber.e(e, "An error has occurred in timetable widget provider") + null + } + } } 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 886b3d4d..426816f4 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt @@ -99,6 +99,9 @@ inline val LocalDate.previousOrSameSchoolDay: LocalDate inline val LocalDate.weekDayName: String get() = this.format(ofPattern("EEEE", Locale.getDefault())) +inline val LocalDate.shortcutWeekDayName: String + get() = this.format(ofPattern("EEE", Locale.getDefault())) + inline val LocalDate.monday: LocalDate get() = this.with(MONDAY) diff --git a/app/src/main/res/drawable-hdpi/ic_widget_account.png b/app/src/main/res/drawable-hdpi/ic_widget_account.png new file mode 100644 index 0000000000000000000000000000000000000000..4cb5ac89ee607739a25ef940414c0078935095ee GIT binary patch literal 461 zcmeAS@N?(olHy`uVBq!ia0y~yU{C>J4mJh`hKCF@W-u@?PVsbc45_&Fc80wlQ=q`H zMmDW=4o^kbY*3iT@M7CS#g7aumJJfZI~oEq5;?XX&~|V)GZRw_2v{_;ygu zr-eta9Nb*E{m6DXCl9fh#@>(Hdyky@)gdWtZ^FF&P|uILH!56ycWoQv9ltcktGqXP zeXDDw;BgVne}aWIreQvf9uG7H)4f!x8U?SYeM)dRA}LUK%jaUWOuz*v-FX#}YU>L+ zlz(s9B(@|!FuZ0`)$L!i7IeBQ%+KDRE9`sW&O)Z!V#nT_U1wQ)_F?Yd9>)HEP8}Koo33Hx) zXvgZ(DNGz%k=yx}?%c-2krinkp8DqpLnyytXlJ5*od)wQ>lZiHm`!4m{a7k}p+nH@ z=5DQ)de?nlF3iMHY} zolW|9UC60Ifvx&NllH|RpSsUE=KTje&v-v`QIob#P(P!3-NK5Eb@LC#q77_% zrnCkhdy*!+bKEKAn>B_3(sWtpR6n`k1R-5taGo>H0dm`hS_xSXIogMr| z>x`aHj~BT9MRo)88YSx=J)6?Sbn2USF#)0@gJxE^&e~n zzB4kMF3b}sS<1^R%f9SL?{lFov5UTiJa1Vl7i8L0=lgg=c94ujc8lF?K98Qzqud9| zdpUFT6dTP%b2@L|IkKg{<>k7(pfu&4e@8ryGTv8UePDN@?r?aaLV`!XWl(1=M~F;B z_miZxj~r$supg>_$Z%a~QsHmbojt{zth1!UpS{0+KsX__KaaJdD_PFuf5`z^xA?~C z2b6nLK1x)u);#80ti^s)tb$p`P{c>|%B>>Vly!%SEH_^;v0$$&Tp|>B^3H{%=gAp! z&S);VtlQ1ErRQ>HYg1an#YRSNN8!MrI;P!XJ?|H7tMptMa3$YjuES&VHLiP|qFvp3 z?3zFR?RfEcrk}vo8&xdZOiOP6Shx3>*v73I$74?X`QH#GdVIwz(Vf>cw(k7h^Z5P5 z;u^8~Vsp{AUqfOFmOSgy6rEnPa%bAv35u*Y7@ur98?bm~su}|W1B0ilpUXO@geCxx C#0n4q literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_widget_account.png b/app/src/main/res/drawable-xxhdpi/ic_widget_account.png new file mode 100644 index 0000000000000000000000000000000000000000..0f8933b1777630ae0d5574c12408d0e358422a4a GIT binary patch literal 922 zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84mJh`hS0a0-5D5|6FprVLn>~)jWP5O4is@q zwi1Yc;dI%P%_(7pG_%znKb z4>K@lH!ite{Max|`Rl#k=WM@Uny3GB(zA>Eet-PB_xCx=?|1roI9Qk(9TW^&g0e&` z_+uEaHU2(e_QB=vF;2PL$G@+6knYBQbqdo4wj3twQ?jx)?RMdb0zICMybqQxI<7j; zdT-~kDUH@${8ei{`5G+axD)bl!EwD~S$?b)>kc_*uQ-z8!?$DMqvmTp4ssiQXSc#Zz7$S`?5o0RLBD*Qe1I|`z752lLA zbIi^9!~I#0ZEe+>i!EMZ>kqH$b}T*Ls`IYM-Tq1}PnKcm9}BLu|IVk6F4!g|w%a z@s3%ZIlj85J!&EIQRW9yALJZ2Suxgd+3?6}1TB$z-@tF9=WNTekAG@bif7pVA12SF z7WeI#9D2Rzxc7s(3};26-b-&QYEM4+rjq5amwp6S8lUR**avGlcy{$hMR=_FFxGZJy7jC6aWx zY5DU;)%hDI+L`wy2H#oz>oQG@7eS|`qqOTb7F5btS{ix7VL>XRD82+ z?uRXm-@m-rVK(V?e(lXPrQ@6xkvq$GeKei)y<^#pt^%$a?ihBv!wl=Y)?fdazg+V8 zgZo_{u1{5&9m(@dM8 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/img_timetable_widget_preview.png b/app/src/main/res/drawable/img_timetable_widget_preview.png index 60a35ab6ac5a6e0f5a7b30055844efacf36e4a7c..550260258a82ae1fcb8c54530efff52985580153 100644 GIT binary patch literal 5284 zcmeAS@N?(olHy`uVBq!ia0y~yV02?(U^v0S%)r2~_{ffX3=9k`>5jgR3=A9lx&I`x zGB7YO76-XIF|0c$^OAvqAu7Np#P$FG|1X*+Uh(z4yJ+#J_aARou;lhPiuU<__N$KnBTP`Mkva@?%PTnRr_syF(-%U>Q_V!*e zVZuQbm8+(vJUl$t92_ogo1eqLApFA9#WAGf)|*+qRj*bFwJDxSnC$RR_)|{VyZ>hA z&0SXK=C01|e|KxcG_jKtj+o!)F<7zZ-l^~1mv+b6{LY^q7rk={%fTH}-%raqykliy z@Z)92*5+j0xdLXNpQhSf@6h|dWd8K*od1=jJ73h+)R_MJT;jR&(6;K@oyV8_@anbi z`}lIHd-n6QpB8ym%U`p%vfI99`<5lj0U!3M8sA$oZ9!q5L|^Q`X;O+YhV91#sc*1E}xUb$C#_R9*2AA$E)kl1CTnHd zg5`Ht9oxWhY=!Qbpvf|RTYEO$z7%DY>tkVjbH`Gt#cIoq6Bm6iFFLYfFRy#gfyHk( z9iJhxGJLbL+xu^y_QiJI+UTSx|IRun{obKoQ{D67_6a6=yd~^Ck}K?zTkapfF2O9F zdsX-9j*7$+PLtyK^X9MK_*(DCjasW9cgZ!H*6oGc+p7z*(zkBQtIvq%nek@O7SGPgm-yfY)yvJ_dWwJXjVSKIO$>Ivn2Re7xc((Di=FUoW zQ)V!nw;=pe&EEbs7k@}6nRG6?vq-e>p_XacUSsj3lDa za39<);oG)~@3nyLrEayeZCl)YwyG^*-*{Yt@8K7>V5z#c=?%NGuiW_hO?S@cZRx)8 z$Bvyb+^w*f-KO&WdLOq#d>f-b%qg5bFGJtxhshS{iw%nu-Q#w$PFJ1rZ`#B{-kyg$ zR~>qqWz+raW!01848;Y{CE~ZOsei;h`}wrz_dYIpyG?sWhOo8G0f|cmYtJ0bvhn}; z@-KtK`m?*+BTa&nmfzU9bZh5TiBZKtJRn0l%506V7nF zjSM)ro+ z>QxsrSgeIDk4(?%*_6FCJh`CkambV_-x51ycoG;VkTHVAwxwoXeCOhUP zJ)4^&Bh}(MIj8s9C(Q-2wE~iVW1shiuMjM-(~sX&E4@12e9!chKcY|1=%0F&{$GFF zU+dK0wU7UM_eS4@lsLJ0#c@o<< zCH`@im}%B?T}eAoE6@MrQ{MkZJ>~PwgC;Lt@kb=a?!Q@2O??mNO&8XXo6iDL?_D!4 zcIDFjy6l*@p^)HeqvgBuq}g_?I-8!Q9cof}(WW40$s&cNJLY|ha%|;a>wbJr*A)v{ zDNeVT1$(}Zo~p(Ke)QDPX3f!a*7M2Uu!1%D?jCXJ({oODEj-6&``J>T`>NlMFFQ7F)VREQ z?aqvfRV~bMKkj`x%AdOKsVnbE#s^P@82RsZHQhhRF@0LI@Rix;=a`3>x_*srd!xUi zFv{?nujU#BtBV4*$!ChK5_CeYPDxd|7&gZsEca*Dstw0ZHD&Mk>f*)XBfNUiF=Z>a zYsU{)1SB&q*pswG*=<&Oh0=P7REfNn;5ei8j+18pIm~&ZbCG5@a?jn#~r5o->pafh+ExrZbk*?B;T{9SuGb^y2C_`IWMH2 z&)+$3vg2*7q@^Ni9>$N#=pZe|iZf~8xNA%c7onyt_2UZ*IoAa}- z?c&Cut~>gn$%jrH`@+(*P5mA3l8*n&-cJt7&D!w8WK*Ks-Dtz211F|v?S0R_Iwqej zXzzs=y(Z4RmwFRBf_I)f!8!NJ&i9Je&3S%nmrq!IA!Z+ zTWo##H{;BUrTaqzHq1GF%Jpx;hn32%-^?~noV_yRoA<8CYXh{nPl|?3dJq^|?NZC= z_G-`fzFl%QZ6b5svf0z!EA^I3I127EWMX(EeDLeFuhl&h=2kWb`qgw@^P6qsM!nNrA@fd`#T}mV{Bn(l*eY8$mt&iDy>pB6EBi3DZ}PmYpFVB; zFw^tw%`0+qWg=gHS$6+q=!>&c_lZk)f0v!SQfud`u9l5@%d_sOU-;1wxU}W_b#d0( z@4M@ceQteIZrYjb-Dj5QVx#lSG;LkK<;sZ*Z|2{b_B++S?H7|I5gNS;@^yyLF?hy|@k< zJ}J<--Bl9w-}C>Q=Lb7}a2zu;3N{guWm0;wU!-SE)k!U@x;2Jd1HaAl-MvYrzh>*~ zt#=-tb6m2jJh%F1;ulBfFHa3^cInBzz2@>_+5Gu(nQu!rbM4>ka_{YnJ+WyMPXF6~ z{Mh-{k5|q$eUq4N*shZOy+uCaTcj%24!?xO?+$RUb(|LRO|02Q>(su(p5ltPB(nUO z7dlmcesqFy?QO?dn_ss}$T)wwWpF9eCec?Z_^4xAy}Hkf4gTUf%MZ$wF@NjzTK`a` zwe7Ns_QAqQtn)T5p7_|mWX1)aTL-_H$fYiAe!ly1ho;ECWY(3@mFbf*m+dot=V@0` zQ)#v+e{r__hO&(xd}JQxZg7u`l(xK`d7$Lig~+>+K`t*|W5?1r1w+bR1eAF5!uadur zD&>}(=l*}d)6&Q9;ST5iDYd-E{&L>y_`5~E@34aJ%IzCB7H+!4*Jpq8aEoGEYarhS z>))FUxPGct%r2j7@LXkXSx#7%z2LXEw@oKaJj2j_Xo>OU=CVMAF6JdxYVMcLIcobB zxF%>=`KT)z@i5NQ+g4cVzD>DLH8XO9^w$}4DZ?=KigS{ykl- zv(#bII-mEoH-2o&`NPq;-oMP;wm>#(z2Ek`zpwv2mifcc;GWMUdGA+QiMj9n*W1sL zyH?C*zx2zSfBK(4e7ThPXMcjsj_J!!Cw$vBeYv-M(yrV$2Hz@^_2>4=9^*^i-ur*| zcD3x7hSTMD%)?8k|36x4cCW5++N>F`b}hcs>(ZTF5UL@2EIwh)gn$*If)`Fa{UWf= zp=}wjp19VDt5>cYUKdy_FrD>S#EEG!D-Q3dVeCJ0U1Zfp&10u}N+RNulEkhAl^P0O zJ*0WdHSidBPt4+zjsFeWK2_gzW3iWPIr;gMQIm?!ZSLnAuL@KZeUv%hnf}MSWs2_` zPlr`)Ga@f&9^7$i&cb8K3dHf{h z|2flKrd2;`#Ci@Iu4@a)OH{ewonUgWoh^A~P{?EEkGlgbye#;(2__kdNYm{MCC zD@&$t?MV?GCf{hIFm0%fB6=b?LY5e&%E!%ghoBt8g)k zJ>h}Ez0J{6{0|(vwwjqGB`0WUSGJj$@Ba;srM4ewFOC+`kg+d4xW+9vipoG@+@^Pl4U=eCB<;XIX`!|1hM6#rC%wg%dvu^tl21dyuvvM=Lpb!VPqW!9oq7yEz)~lNN zPe*5$MEhHm@sjA zU}DcpUx#IzKYrgPs9d^0#7TI<$D^woe7LmUU%UB3;|pKG%|}YNGfs%ccyI3GozwQQLA>CEsR|1w5Y(+D4^u|Dz^9Az7(yk z`pUZM`#j!HujXt&mO5MK`jVBguUS9TPwqW=beibAX)X!R=j0^sIkBo|>#yQa-iSo` z>8qmWMkTl$lhIN#Jb5}!o&Epe9G7{a7j`e@&U-V>qV~)FjsTk%QXX$l|E^izxvpZ%4)?^x z-v7+Br-ey)U$t-umsH;Bd+};ayy~;>=W_Rbwh8}vdr#5+lnqtK9S{4)^eJBCQr`KL zty$MuxH0&bk;>MZ6}wEYrfs?qrC<>A-B7yh%(tmGXQcPcuUz;2X2y->o1C09=OyYa zy|XEB^$oS(PA~7S;PTV2yu8@g?30bQ`?kwlX61&&ZPPVsdfc0v^WmUbmTKDvZ-@8) zczb@D2G81-yubFe>)F{0?=!K8cxf#1=e9ez+55b4h=% zSxSBSl6Oz}pY^V)xm4UUFGbmKx5@H(=0Eo2s@U)DyI{O}#<4?2zxys6zxg67D#1r~ z;qoxUvokNG3e8JYUb@rmbltkzj>=PJRcD#j-PZ1l=vsVrvcvS<6U7d{`g-lTech5} z5=8~KFS~s+eD;C6C#-C_hh=iarRTNxe#AANKgjk-NG3r=e8Y}wrd!@@JaK8!k4^h# zou6-hZ?>P>-my3duyek`|j#GN#FK`L7NhD+tzS zn=H-a@YG|f@60D+o26F0+8wj*@aLMZYenM!eHMH? zd$PejHnsIFv+i%GbzEewp01P8>vt|7Ui6CFzu1SdLXzyPHZPx?UL3xj-R0N)nZn9gk<@~t{pV>c;*ngVSV=w>p&CU8Y`|W+l zZ4ckylQaA0Lr;m%uXZ;r?3CLuTf3)H{6zAf6m#>umUfjM`F2ZPsZw$8oyD#4u4g1N zT9YizWhDI1SG4ray`v$|SIic*PN7XmY1z}BJ4@yWFW#bJkd+i6R#*M<4qy2>%Xb># z0#;d_2{{~Y%8Pm>dt=Vbc>Z*5%P)-*ohilER?kjv?{O=7m8>VFd+*hWhZRXzx39x>J8Mp&MSFZNlmV&rbuhO z*(AgKW1=!Gs#2n_6?%C6uJJA{UILE%Wj&gYg^m4$_8U651Wx!ggMXg$p7$)}-`vyI z8+iO_3pzH%Fx5s>S*by!E~JB_ExjUyli9}OQHqM)_ls>WFB@0MU9{z@$XmQqe%{Io zpLx-bgfcBJTQr;YcQx#)Uf*%z%}L3?38lH34(UGA|E+A9^}}MtL`$X`*QoN7+Zys( zF6{fEz|y+vwTr=`^BOfqZkbxXxiVj}mP{+%oVdw!O{vlJl{JoMqHkW{ox1+Od~ZJE zTAT8!VuQVpu3nltyXW{@^>h<`i7N}j5+lu%?xg%>XpB@|Ep2`-!ESAA!fLnLJ?Ere z=S&E_9=YtXLiF2+$X9dcZ1ngpX?wm`B30Z{FY55JN4Fc+8V4PDZNYrnDEdN7VEnHK zHydrvUV3%v>cwMI)@!_qojYselMS`U7+4if&AK~vE2r9t&sR+yV!AS{Hb+<7U9@k{ z%N0!<_hs&~nqgdddQ*3|u(8?PSht+iyQW+Ly)QqRWEmHqk+f~G7JsX{>uTYuXx+@H zWIMisK!a6#Hogqtesc6z&pN*|8G2`8L+5I5irQ)SZ-ZmBx=nEO&dBt+OAEGrelzph z9kE;YHgK13?JC)`vnc!W8tu*9r@lQq(o=dPS?3z#>IDs6t_D79KFv*FieP!nqoL*+ z@H4aPbw}GptD_;yIucxU=j}N=Bfj#D5Ccp3LI2o&r?0E{UlE_B{(A36CZY2`N|L*# z7^eD2&Y%3TfamDr`$l5R*^etre7ttkJGL<9X5r802b(|0RDJA_{$FCSD>+~(Lk7p) z-Nkc%Xw>@{_4NPEPuQ}7l||uAG)F`K^>&=hKH&b02-x=I3v^`$VaV7A<;aN3DEE4zfy!Jcs z`}CuH;Y)j6CKo>YE8&0aQi_Ui8?REyk;#F{MtkOLP_@5(WdD@YTdFw?W^Oo{VU#hU zF!|E6@ZB~0-}wK_^FQX^dw6HgJE2JLlx{&Y<_WR#X)j(qV6%}aIu>|LVp?zfsbjB; zCQmiqGI8Nkt=B0T{H>{f^>4YRnjJfwIseB6JWrT6K#3Gq^%e+6Il z7s&7G*lXKY_*y_ZM|zqaAIoA+yE6`(`6hkcy|?FM_4`Xx-RwP27jE}fbe58w6sUhY z-!tUmuKKQ1Ov-<%ZZ;GxyZ9z$OX2i`mcLSYCTZ)gJ6~y(d855f{_979(tp)5i$fPH zF5b%{soEX9E;K^kXqvw9v7JG$irp$LLr#gf_I!QxRNjTLK`?y@m)x-)-j|zpoyc&m z(@e<`_>%lvW=cn4$FKUJ%pcb$_Dp)3J#A&=#J*io>oXcx%3eEe_s{n5u~}AmEB2ec zs$6UGs>m(#;##Tq#>aNJJlJb>PUp{l9xaK}$AtdRs+{{!lk?AwH~oQes?4{pu4@na zA0+(SMYs2#bKkGJvkPKBbv_XmjrA6p^^fUy$fuX?Crvu1O>>EUa8788>KC5QDFN$c z->valaOC~Vt+VHrNgGa`-)fd7^iWfAzY1GinTL1<8;|k7$fyl|?O%7f8`>Q&lXLE| z7g{lShULn@k`p`RV?F!&UjBJpr(3idi;2*0Ryc>#x1lU!KE6_Dkp0Ivw@xb-+_t5^6^SBw( zDpXG$_&23|83)7pM{`$hoGHS<$8hc9nO*J7v60Om&Yk)AR;v2NTtJ9zPWHLVC+Dmm+jp0{P0Y7jFgtff8L#xi z~1IW>@}YC;atv{6V}0>TFTw(h2HnPv9R`z-hNNAf$OPY0LPld-rkuX z&gs09T>foQtJ%Wxw9J-y{rtc7mOs0)Pb7a~#dmG#jpb}9QNhbMWt=r|&pyC-Xztam zwb#>@J)2w3cDE}1MsQ{N1ZRhPD<4lRU)FAKaU!zy?XG7#f3&sTo7p{8dTG$It(n~U z>u{gQ8e)&-pM7cP!7#y(&(E<HJ()Z6*P=De9>=>@ zsrb1+IaGRed5yW#d!O_14#soe|Ll{zyz|){{b|ru0uI=2{{`Jt8pEI_-4mloj zT;J?q`n8R_8dmW()X2~JIJ-hiVLsD`jH~7E)A=4*#?EwlKlAv*&D>Vzk`MUp?z!Zf zyoz2?a;+gZ=eAg_UvTT#+1Bb)G6CRfzd9C*1fv48wyVq)s z9`yKf`L=O>36R!ze0#Mv=uKeflv&c3Qi6hO1Vmp|c>1keY%+BX1M93UTQ`5Sew5I} zxy)yxpMIXY$&oFq;^&@ya&wQ?qZX=3|JDZ|Tu$?GRMht$Oy7eW6vY_aCu~*-K7I`fhC5eDSE4U-yhT8!c=X zujR5{9GSc;Xt~*q{>vL*SQ>9y{WxSkJ43;;Vz(zd7z~v|?iTo3+?qA*evyoA4FB#M z1#b66daN=-S(48dYOQfgy*{tuQ ziP|~EvI|*V7cM?0nZB`4d|{s{SLC@FrO)4V-RRG`eq!N<&EFJnm7Y|dl~cX_?JVO@ zWx6Yk=T=7T5o=G6Io`GRj9yKkeCp|JHo?UkPwK7fD!ckFb!N``6I~VwW$WaZE?)8X z(mDHEF%BzvwZ4dzpLgfo;IDdU@sD@5vzGaXi0so<4h%?C{OQ8X9J{@W=cm*c5B-|o z&!kKO%JZrdYC{;FBy&Gr%^h*kNxZVD?pey~7zV4#Np) zdCxt4oUyJ}FV=sQQ8;O(q`a_acN}YbgNgT!K0fB!<@$GI6FPoO<^Az%>Fea9ALq(O zUjJ3H-coYeCa$<$v5V*Bzlqs6A@r_LrFWIzXXXYzp}^+p`PX;1YG332J1KP<)B2qk zHGeE%s4A4@UzZ~*wN}yfweh|Z=Rl^CJIPz3j=_ss7(8A5T-G@yGywqd|CmAm diff --git a/app/src/main/res/layout/activity_timetable_widget_configure.xml b/app/src/main/res/layout/activity_timetable_widget_configure.xml new file mode 100644 index 00000000..2a95e01d --- /dev/null +++ b/app/src/main/res/layout/activity_timetable_widget_configure.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/app/src/main/res/layout/item_account.xml b/app/src/main/res/layout/item_account.xml index 56f553e5..5f76dded 100644 --- a/app/src/main/res/layout/item_account.xml +++ b/app/src/main/res/layout/item_account.xml @@ -33,7 +33,8 @@ android:ellipsize="end" android:maxLines="1" android:textSize="16sp" - tools:text="@tools:sample/lorem/random" /> + tools:text="@tools:sample/lorem/random" + android:textColor="?android:textColorSecondary"/> diff --git a/app/src/main/res/layout/item_widget_timetable.xml b/app/src/main/res/layout/item_widget_timetable.xml index af055280..aa808864 100644 --- a/app/src/main/res/layout/item_widget_timetable.xml +++ b/app/src/main/res/layout/item_widget_timetable.xml @@ -7,7 +7,7 @@ android:background="@drawable/ic_all_divider" android:minHeight="45dp" android:orientation="horizontal" - tools:context=".ui.widgets.timetable.TimetableWidgetFactory"> + tools:context=".ui.modules.timetablewidget.TimetableWidgetFactory"> + tools:context=".ui.modules.timetablewidget.TimetableWidgetProvider"> + + + + + + + + + - - - - - @color/about_libraries_dividerDark_openSource_dark @color/about_libraries_dividerLight_openSource_dark - - diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 3caa356e..144772a5 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -233,8 +233,6 @@ Brak lekcji - Dziś - Jutro diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index e0708095..a4bf80ed 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -2,7 +2,4 @@ 8dp 8dp - - - 60dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6a56b441..a00514be 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -218,8 +218,6 @@ No lessons - Today - Tomorrow diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 4bbe3bc1..58cd99eb 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -40,4 +40,12 @@ + + diff --git a/app/src/main/res/xml/provider_widget_timetable.xml b/app/src/main/res/xml/provider_widget_timetable.xml index 48c6ba39..1e0eedcb 100644 --- a/app/src/main/res/xml/provider_widget_timetable.xml +++ b/app/src/main/res/xml/provider_widget_timetable.xml @@ -1,8 +1,11 @@ Date: Tue, 9 Apr 2019 23:33:53 +0200 Subject: [PATCH 037/322] Change style of privacy policy link (#321) --- .../modules/login/form/LoginFormFragment.kt | 14 +++++-- .../modules/login/form/LoginFormPresenter.kt | 9 ++++- .../ui/modules/login/form/LoginFormView.kt | 4 ++ .../main/res/layout/fragment_login_form.xml | 37 +++++++++++-------- app/src/main/res/values-pl/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- .../login/form/LoginFormPresenterTest.kt | 16 ++++---- 7 files changed, 53 insertions(+), 31 deletions(-) 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 5c0a6ed8..2a0467d9 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 @@ -1,8 +1,8 @@ package io.github.wulkanowy.ui.modules.login.form import android.annotation.SuppressLint +import android.content.Intent import android.os.Bundle -import android.text.method.LinkMovementMethod import android.view.LayoutInflater import android.view.View import android.view.View.GONE @@ -54,8 +54,8 @@ class LoginFormFragment : BaseFragment(), LoginFormView { loginFormName.setOnTextChangedListener { presenter.onNameTextChanged() } loginFormPass.setOnTextChangedListener { presenter.onPassTextChanged() } loginFormHost.setOnItemSelectedListener { presenter.onHostSelected() } - loginFormPrivacyPolicyLink.movementMethod = LinkMovementMethod.getInstance() - loginFormSignIn.setOnClickListener { presenter.attemptLogin() } + loginFormSignIn.setOnClickListener { presenter.onSignInClick() } + loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() } loginFormPass.setOnEditorActionListener { _, id, _ -> if (id == IME_ACTION_DONE || id == IME_NULL) loginFormSignIn.callOnClick() else false @@ -132,6 +132,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView { } } + override fun showPrivacyPolicy() { + loginFormPrivacyLink.visibility = VISIBLE + } + override fun notifyParentAccountLogged(students: List) { (activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple( loginFormName.text.toString(), @@ -140,6 +144,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView { )) } + override fun openPrivacyPolicyPage() { + startActivity(Intent.parseUri("https://wulkanowy.github.io/polityka-prywatnosci.html", 0)) + } + override fun onDestroyView() { super.onDestroyView() presenter.onDetachView() 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 a0717649..3ab47c29 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 @@ -22,7 +22,8 @@ class LoginFormPresenter @Inject constructor( super.onAttachView(view) view.run { initView() - if (isDebug) showVersion() + if (isDebug) showVersion() else showPrivacyPolicy() + errorHandler.onBadCredentials = { setErrorPassIncorrect() showSoftKeyboard() @@ -31,6 +32,10 @@ class LoginFormPresenter @Inject constructor( } } + fun onPrivacyLinkClick() { + view?.openPrivacyPolicyPage() + } + fun onHostSelected() { view?.apply { clearPassError() @@ -47,7 +52,7 @@ class LoginFormPresenter @Inject constructor( view?.clearNameError() } - fun attemptLogin() { + fun onSignInClick() { val email = view?.formNameValue.orEmpty() val password = view?.formPassValue.orEmpty() val endpoint = view?.formHostValue.orEmpty() 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 69672780..80a7b5e9 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 @@ -37,5 +37,9 @@ interface LoginFormView : BaseView { fun showVersion() + fun showPrivacyPolicy() + fun notifyParentAccountLogged(students: List) + + fun openPrivacyPolicyPage() } diff --git a/app/src/main/res/layout/fragment_login_form.xml b/app/src/main/res/layout/fragment_login_form.xml index 7a82eaf7..197db570 100644 --- a/app/src/main/res/layout/fragment_login_form.xml +++ b/app/src/main/res/layout/fragment_login_form.xml @@ -39,7 +39,6 @@ app:fontFamily="sans-serif-light" app:layout_constraintBottom_toTopOf="@+id/loginFormNameLayout" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_chainStyle="packed" /> @@ -59,7 +58,6 @@ app:errorEnabled="true" app:layout_constraintBottom_toTopOf="@+id/loginFormPassLayout" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/loginFormHeader"> @@ -87,7 +85,6 @@ app:errorEnabled="true" app:layout_constraintBottom_toTopOf="@+id/loginFormHostLayout" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/loginFormNameLayout" app:passwordToggleEnabled="true"> @@ -117,7 +114,6 @@ android:orientation="vertical" app:layout_constraintBottom_toTopOf="@+id/loginFormSignIn" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/loginFormPassLayout"> @@ -145,15 +141,6 @@ app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="@+id/loginFormHostLayout" /> - - + app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout" /> + + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index a5b8b66a..04a4a2ea 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -26,7 +26,6 @@ Hasło Dziennik Symbol - Polityka prywatności Zaloguj To hasło jest za krótkie Dane logowania są niepoprawne @@ -34,6 +33,7 @@ To pole jest wymagane Ten student jest już zalogowany Symbol znajduje się na stronie dziennika w zakładce Dostęp Mobilny + Polityka prywatności diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a4089a46..69481ecf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,7 +26,6 @@ Password Register Symbol - Privacy policy Sign in This password is too short Login details are incorrect @@ -34,6 +33,7 @@ This field is required This student has already been logged in The symbol is located on the register page in the Mobile Access tab + Privacy policy 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 83047e02..b643a550 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 @@ -53,7 +53,7 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("") `when`(loginFormView.formPassValue).thenReturn("test123") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() + presenter.onSignInClick() verify(loginFormView).setErrorNameRequired() verify(loginFormView, never()).setErrorPassRequired(false) @@ -65,7 +65,7 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() + presenter.onSignInClick() verify(loginFormView, never()).setErrorNameRequired() verify(loginFormView).setErrorPassRequired(true) @@ -77,7 +77,7 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() + presenter.onSignInClick() verify(loginFormView, never()).setErrorNameRequired() verify(loginFormView, never()).setErrorPassRequired(true) @@ -93,7 +93,7 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() + presenter.onSignInClick() verify(loginFormView).hideSoftKeyboard() verify(loginFormView).showProgress(true) @@ -109,7 +109,7 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() + presenter.onSignInClick() verify(loginFormView).hideSoftKeyboard() verify(loginFormView).showProgress(true) @@ -125,8 +125,8 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() - presenter.attemptLogin() + presenter.onSignInClick() + presenter.onSignInClick() verify(loginFormView, times(2)).hideSoftKeyboard() verify(loginFormView, times(2)).showProgress(true) @@ -143,7 +143,7 @@ class LoginFormPresenterTest { `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") - presenter.attemptLogin() + presenter.onSignInClick() verify(loginFormView).hideSoftKeyboard() verify(loginFormView).showProgress(true) From 034b99c7ab990ae81d781c692e3eacb41bc58a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Thu, 18 Apr 2019 00:32:43 +0200 Subject: [PATCH 038/322] Add counting of the full-year average to the summary of grades (#322) --- .../preferences/PreferencesRepository.kt | 6 +- .../ui/modules/grade/GradeAverageProvider.kt | 54 ++++++++ .../grade/details/GradeDetailsPresenter.kt | 61 +++++---- .../grade/summary/GradeSummaryPresenter.kt | 68 +++++----- app/src/main/res/values-pl/strings.xml | 4 +- .../main/res/values-pl/value_prefernces.xml | 5 + app/src/main/res/values/preferences_keys.xml | 1 + app/src/main/res/values/strings.xml | 4 +- app/src/main/res/values/value_prefernces.xml | 9 ++ app/src/main/res/xml/scheme_preferences.xml | 10 +- .../modules/grade/GradeAverageProviderTest.kt | 124 ++++++++++++++++++ 11 files changed, 275 insertions(+), 71 deletions(-) create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt create mode 100644 app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt index d51fc495..b29e5143 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt @@ -17,6 +17,9 @@ class PreferencesRepository @Inject constructor( val isShowPresent: Boolean get() = sharedPref.getBoolean(context.getString(R.string.pref_key_attendance_present), true) + val gradeAverageMode: String + get() = sharedPref.getString(context.getString(R.string.pref_key_grade_average_mode), "only_one_semester") ?: "only_one_semester" + val isGradeExpandable: Boolean get() = !sharedPref.getBoolean(context.getString(R.string.pref_key_expand_grade), false) @@ -50,8 +53,7 @@ class PreferencesRepository @Inject constructor( get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0 val gradeMinusModifier: Double - get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble() - ?: 0.0 + get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble() ?: 0.0 val fillMessageContent: Boolean get() = sharedPref.getBoolean(context.getString(R.string.pref_key_fill_message_content), true) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt new file mode 100644 index 00000000..a76f3d5a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt @@ -0,0 +1,54 @@ +package io.github.wulkanowy.ui.modules.grade + +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.preferences.PreferencesRepository +import io.github.wulkanowy.utils.calcAverage +import io.github.wulkanowy.utils.changeModifier +import io.reactivex.Single +import javax.inject.Inject + +class GradeAverageProvider @Inject constructor( + private val preferencesRepository: PreferencesRepository, + private val gradeRepository: GradeRepository +) { + fun getGradeAverage(student: Student, semesters: List, selectedSemesterId: Int, forceRefresh: Boolean): Single> { + return when (preferencesRepository.gradeAverageMode) { + "all_year" -> getAllYearAverage(student, semesters, selectedSemesterId, forceRefresh) + "only_one_semester" -> getOnlyOneSemesterAverage(student, semesters, selectedSemesterId, forceRefresh) + else -> throw IllegalArgumentException("Incorrect grade average mode: ${preferencesRepository.gradeAverageMode} ") + } + } + + private fun getAllYearAverage(student: Student, semesters: List, semesterId: Int, forceRefresh: Boolean): Single> { + val selectedSemester = semesters.single { it.semesterId == semesterId } + val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 } + val plusModifier = preferencesRepository.gradePlusModifier + val minusModifier = preferencesRepository.gradeMinusModifier + + return gradeRepository.getGrades(student, selectedSemester, forceRefresh) + .flatMap { firstGrades -> + if (selectedSemester == firstSemester) Single.just(firstGrades) + else gradeRepository.getGrades(student, firstSemester) + .map { secondGrades -> secondGrades + firstGrades } + }.map { grades -> + grades.map { it.changeModifier(plusModifier, minusModifier) } + .groupBy { it.subject } + .mapValues { it.value.calcAverage() } + } + } + + private fun getOnlyOneSemesterAverage(student: Student, semesters: List, semesterId: Int, forceRefresh: Boolean): Single> { + val selectedSemester = semesters.single { it.semesterId == semesterId } + val plusModifier = preferencesRepository.gradePlusModifier + val minusModifier = preferencesRepository.gradeMinusModifier + + return gradeRepository.getGrades(student, selectedSemester, forceRefresh) + .map { grades -> + grades.map { it.changeModifier(plusModifier, minusModifier) } + .groupBy { it.subject } + .mapValues { it.value.calcAverage() } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt index 1be0b36f..12e60687 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -8,10 +8,9 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.base.session.BaseSessionPresenter import io.github.wulkanowy.ui.base.session.SessionErrorHandler +import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider -import io.github.wulkanowy.utils.calcAverage -import io.github.wulkanowy.utils.changeModifier import io.github.wulkanowy.utils.getBackgroundColor import timber.log.Timber import javax.inject.Inject @@ -23,6 +22,7 @@ class GradeDetailsPresenter @Inject constructor( private val studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, private val preferencesRepository: PreferencesRepository, + private val averageProvider: GradeAverageProvider, private val analytics: FirebaseAnalyticsHelper ) : BaseSessionPresenter(errorHandler) { @@ -109,11 +109,16 @@ class GradeDetailsPresenter @Inject constructor( private fun loadData(semesterId: Int, forceRefresh: Boolean) { Timber.i("Loading grade details data started") disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } } - .flatMap { gradeRepository.getGrades(it.second, it.first.first { item -> item.semesterId == semesterId }, forceRefresh) } - .map { it.sortedByDescending { grade -> grade.date } } - .map { it.map { item -> item.changeModifier(preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier) } } - .map { createGradeItems(it.groupBy { grade -> grade.subject }.toSortedMap()) } + .flatMap { semesterRepository.getSemesters(it).map { semester -> it to semester } } + .flatMap { (student, semesters) -> + averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh) + .flatMap { averages -> + gradeRepository.getGrades(student, semesters.first { semester -> semester.semesterId == semesterId }) + .map { it.sortedByDescending { grade -> grade.date } } + .map { it.groupBy { grade -> grade.subject }.toSortedMap() } + .map { createGradeItems(it, averages) } + } + } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doFinally { @@ -139,32 +144,36 @@ class GradeDetailsPresenter @Inject constructor( }) } - private fun createGradeItems(items: Map>): List { + private fun createGradeItems(items: Map>, averages: Map): List { + val isGradeExpandable = preferencesRepository.isGradeExpandable + val gradeColorTheme = preferencesRepository.gradeColorTheme + + val noDescriptionString = view?.noDescriptionString.orEmpty() + val weightString = view?.weightString.orEmpty() + return items.map { - it.value.calcAverage().let { average -> - GradeDetailsHeader( - subject = it.key, - average = formatAverage(average), - number = view?.getGradeNumberString(it.value.size).orEmpty(), - newGrades = it.value.filter { grade -> !grade.isRead }.size, - isExpandable = preferencesRepository.isGradeExpandable - ).apply { - subItems = it.value.map { item -> - GradeDetailsItem( - grade = item, - valueBgColor = item.getBackgroundColor(preferencesRepository.gradeColorTheme), - weightString = view?.weightString.orEmpty(), - noDescriptionString = view?.noDescriptionString.orEmpty() - ) - } + GradeDetailsHeader( + subject = it.key, + average = formatAverage(averages[it.key]), + number = view?.getGradeNumberString(it.value.size).orEmpty(), + newGrades = it.value.filter { grade -> !grade.isRead }.size, + isExpandable = isGradeExpandable + ).apply { + subItems = it.value.map { item -> + GradeDetailsItem( + grade = item, + valueBgColor = item.getBackgroundColor(gradeColorTheme), + weightString = weightString, + noDescriptionString = noDescriptionString + ) } } } } - private fun formatAverage(average: Double): String { + private fun formatAverage(average: Double?): String { return view?.run { - if (average == 0.0) emptyAverageString + if (average == null || average == .0) emptyAverageString else averageString.format(average) }.orEmpty() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt index 5dbf7513..cbd4169d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt @@ -1,17 +1,15 @@ package io.github.wulkanowy.ui.modules.grade.summary import io.github.wulkanowy.data.db.entities.GradeSummary -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.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.base.session.BaseSessionPresenter import io.github.wulkanowy.ui.base.session.SessionErrorHandler +import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.calcAverage -import io.github.wulkanowy.utils.changeModifier import timber.log.Timber import java.lang.String.format import java.util.Locale.FRANCE @@ -20,10 +18,9 @@ import javax.inject.Inject class GradeSummaryPresenter @Inject constructor( private val errorHandler: SessionErrorHandler, private val gradeSummaryRepository: GradeSummaryRepository, - private val gradeRepository: GradeRepository, private val studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, - private val preferencesRepository: PreferencesRepository, + private val averageProvider: GradeAverageProvider, private val schedulers: SchedulersProvider, private val analytics: FirebaseAnalyticsHelper ) : BaseSessionPresenter(errorHandler) { @@ -36,25 +33,12 @@ class GradeSummaryPresenter @Inject constructor( fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { Timber.i("Loading grade summary data started") disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it).map { semester -> semester to it } } - .map { pair -> pair.first.first { it.semesterId == semesterId } to pair.second } - .flatMap { - gradeSummaryRepository.getGradesSummary(it.first, forceRefresh) + .flatMap { semesterRepository.getSemesters(it).map { semesters -> it to semesters } } + .flatMap { (student, semesters) -> + gradeSummaryRepository.getGradesSummary(semesters.first { it.semesterId == semesterId }, forceRefresh) .flatMap { gradesSummary -> - gradeRepository.getGrades(it.second, it.first, forceRefresh) - .map { grades -> - grades.map { item -> item.changeModifier(preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier) } - .groupBy { grade -> grade.subject } - .mapValues { entry -> entry.value.calcAverage() } - .filterValues { value -> value != 0.0 } - .let { averages -> - createGradeSummaryItems(gradesSummary, averages) to - GradeSummaryScrollableHeader( - formatAverage(gradesSummary.calcAverage()), - formatAverage(averages.values.average()) - ) - } - } + averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh) + .map { averages -> createGradeSummaryItemsAndHeader(gradesSummary, averages) } } } .subscribeOn(schedulers.backgroundThread) @@ -66,14 +50,14 @@ class GradeSummaryPresenter @Inject constructor( enableSwipe(true) notifyParentDataLoaded(semesterId) } - }.subscribe({ + }.subscribe({ (gradeSummaryItems, gradeSummaryHeader) -> Timber.i("Loading grade summary result: Success") view?.run { - showEmpty(it.first.isEmpty()) - showContent(it.first.isNotEmpty()) - updateData(it.first, it.second) + showEmpty(gradeSummaryItems.isEmpty()) + showContent(gradeSummaryItems.isNotEmpty()) + updateData(gradeSummaryItems, gradeSummaryHeader) } - analytics.logEvent("load_grade_summary", "items" to it.first.size, "force_refresh" to forceRefresh) + analytics.logEvent("load_grade_summary", "items" to gradeSummaryItems.size, "force_refresh" to forceRefresh) }) { Timber.i("Loading grade summary result: An exception occurred") view?.run { showEmpty(isViewEmpty) } @@ -104,16 +88,24 @@ class GradeSummaryPresenter @Inject constructor( disposable.clear() } - private fun createGradeSummaryItems(gradesSummary: List, averages: Map) - : List { - return gradesSummary.filter { !checkEmpty(it, averages) }.map { it -> - GradeSummaryItem( - title = it.subject, - average = formatAverage(averages.getOrElse(it.subject) { 0.0 }, ""), - predictedGrade = it.predictedGrade, - finalGrade = it.finalGrade - ) - } + private fun createGradeSummaryItemsAndHeader(gradesSummary: List, averages: Map) + : Pair, GradeSummaryScrollableHeader> { + return averages.filterValues { value -> value != 0.0 } + .let { filteredAverages -> + gradesSummary.filter { !checkEmpty(it, filteredAverages) } + .map { + GradeSummaryItem( + title = it.subject, + average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, ""), + predictedGrade = it.predictedGrade, + finalGrade = it.finalGrade + ) + }.let { + it to GradeSummaryScrollableHeader( + formatAverage(gradesSummary.calcAverage()), + formatAverage(filteredAverages.values.average())) + } + } } private fun checkEmpty(gradeSummary: GradeSummary, averages: Map): Boolean { diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 04a4a2ea..aee503b8 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -239,11 +239,11 @@ Wygląd Domyślny widok - Pokazuj podsumowanie w ocenach + Obliczanie średniej końcoworocznej Pokazuj obecność we frekwencji Ciemny motyw (Beta) Rozwiń oceny - Schemat kolorów ocen + Schemat kolorów ocen Powiadomienia Pokazuj powiadomienia diff --git a/app/src/main/res/values-pl/value_prefernces.xml b/app/src/main/res/values-pl/value_prefernces.xml index 2231cc0b..34a10909 100644 --- a/app/src/main/res/values-pl/value_prefernces.xml +++ b/app/src/main/res/values-pl/value_prefernces.xml @@ -30,4 +30,9 @@ Wulkanowy Kolory ocen w dzienniku + + + Średnia ocen z 2 semestru + Średnia ocen z całego roku + diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml index 427cca56..d03b4fbb 100644 --- a/app/src/main/res/values/preferences_keys.xml +++ b/app/src/main/res/values/preferences_keys.xml @@ -5,6 +5,7 @@ theme grade_color_scheme expand_grade + grade_average_mode services_enable services_interval services_disable_wifi_only diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 69481ecf..8dc6a25c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -224,11 +224,11 @@ Appearance Default view - Show the summary in the grades + Calculation of the end-of-year average Show presence in attendance Dark theme (Beta) Expand grades - Grades color scheme + Grades color scheme Notifications Show notifications diff --git a/app/src/main/res/values/value_prefernces.xml b/app/src/main/res/values/value_prefernces.xml index 661615e1..3921b4e2 100644 --- a/app/src/main/res/values/value_prefernces.xml +++ b/app/src/main/res/values/value_prefernces.xml @@ -70,4 +70,13 @@ material grade_color + + + Average grades from the 2nd semester + Average of grades from the whole year + + + only_one_semester + all_year + diff --git a/app/src/main/res/xml/scheme_preferences.xml b/app/src/main/res/xml/scheme_preferences.xml index 08c24124..5c2a8dda 100644 --- a/app/src/main/res/xml/scheme_preferences.xml +++ b/app/src/main/res/xml/scheme_preferences.xml @@ -36,7 +36,7 @@ android:entryValues="@array/grade_color_scheme_values" android:key="@string/pref_key_grade_color_scheme" android:summary="%s" - android:title="@string/pref_grade_color_scheme" + android:title="@string/pref_view_grade_color_scheme" app:iconSpaceReserved="false" /> + Date: Thu, 18 Apr 2019 12:18:58 +0200 Subject: [PATCH 039/322] Add a selection of multiple students to login (#318) --- app/build.gradle | 2 +- .../repositories/student/StudentLocalTest.kt | 2 +- app/src/main/AndroidManifest.xml | 1 + .../wulkanowy/data/db/dao/StudentDao.kt | 2 +- .../data/repositories/student/StudentLocal.kt | 4 +- .../repositories/student/StudentRepository.kt | 4 +- .../LoginStudentSelectFragment.kt | 10 +-- .../studentselect/LoginStudentSelectItem.kt | 16 ++++- .../LoginStudentSelectPresenter.kt | 33 ++++++---- .../studentselect/LoginStudentSelectView.kt | 2 +- .../main/res/layout/fragment_login_form.xml | 4 +- .../layout/fragment_login_student_select.xml | 61 +++++++++++++++++-- .../main/res/layout/fragment_login_symbol.xml | 8 +-- .../res/layout/item_login_student_select.xml | 50 +++++++-------- app/src/main/res/values-pl/strings.xml | 5 +- app/src/main/res/values/strings.xml | 5 +- .../LoginStudentSelectPresenterTest.kt | 6 +- 17 files changed, 141 insertions(+), 74 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 59037033..5780d02b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -86,7 +86,7 @@ play { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation('com.github.wulkanowy:api:fe4ffeb') { exclude module: "threetenbp" } + implementation 'com.github.wulkanowy:api:3335bd6' implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.appcompat:appcompat:1.0.2" diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt index b27446fa..cecd8099 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt @@ -39,7 +39,7 @@ class StudentLocalTest { @Test fun saveAndReadTest() { - studentLocal.saveStudent(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "")) + studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = ""))) .blockingGet() val student = studentLocal.getCurrentStudent(true).blockingGet() diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f0dd9cb4..5b281926 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -34,6 +34,7 @@ android:name=".ui.modules.login.LoginActivity" android:configChanges="orientation|screenSize" android:label="@string/login_title" + android:theme="@style/WulkanowyTheme.NoActionBar" android:windowSoftInputMode="adjustResize" /> ): List @Delete fun delete(student: Student) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt index 7bbd283f..e6d74421 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt @@ -17,8 +17,8 @@ class StudentLocal @Inject constructor( private val context: Context ) { - fun saveStudent(student: Student): Single { - return Single.fromCallable { studentDb.insert(student.copy(password = encrypt(student.password, context))) } + fun saveStudents(students: List): Single> { + return Single.fromCallable { studentDb.insertAll(students.map { it.copy(password = encrypt(it.password, context)) }) } } fun getStudents(decryptPass: Boolean): Maybe> { diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt index b4b7c828..e0ab6bf6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt @@ -41,8 +41,8 @@ class StudentRepository @Inject constructor( .toSingle() } - fun saveStudent(student: Student): Single { - return local.saveStudent(student) + fun saveStudents(students: List): Single> { + return local.saveStudents(students) } fun switchStudent(student: Student): Completable { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt index 2bcb6b4e..9f321775 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt @@ -8,7 +8,6 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -45,6 +44,7 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView { } override fun initView() { + loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() } loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } } loginStudentSelectRecycler.apply { @@ -54,7 +54,7 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView { } override fun updateData(data: List) { - loginAdapter.updateDataSet(data, true) + loginAdapter.updateDataSet(data) } override fun openMainView() { @@ -69,11 +69,11 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView { } override fun showContent(show: Boolean) { - loginStudentSelectRecycler.visibility = if (show) VISIBLE else GONE + loginStudentSelectContent.visibility = if (show) VISIBLE else GONE } - override fun showActionBar(show: Boolean) { - (activity as? AppCompatActivity)?.supportActionBar?.run { if (show) show() else hide() } + override fun enableSignIn(enable: Boolean) { + loginStudentSelectSignIn.isEnabled = enable } fun onParentInitStudentSelectFragment(students: List) { 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 65940a98..27723c53 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 @@ -13,15 +13,15 @@ import kotlinx.android.synthetic.main.item_login_student_select.* class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem() { - override fun getLayoutRes(): Int = R.layout.item_login_student_select + override fun getLayoutRes() = R.layout.item_login_student_select override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ItemViewHolder { return ItemViewHolder(view, adapter) } @SuppressLint("SetTextI18n") - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ItemViewHolder, position: Int, payloads: MutableList?) { - holder.run { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ItemViewHolder, position: Int, payloads: MutableList) { + holder.apply { loginItemName.text = "${student.studentName} ${student.className}" loginItemSchool.text = student.schoolName } @@ -43,7 +43,17 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem) : FlexibleViewHolder(view, adapter), LayoutContainer { + override val containerView: View get() = itemView + + init { + loginItemCheck.setOnClickListener { super.onClick(loginItemContainer) } + } + + override fun onClick(view: View?) { + super.onClick(view) + loginItemCheck.apply { isChecked = !isChecked } + } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt index 6c2acb3b..21dde7b8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt @@ -8,7 +8,6 @@ import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginErrorHandler import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider -import io.reactivex.Single import timber.log.Timber import java.io.Serializable import javax.inject.Inject @@ -22,10 +21,13 @@ class LoginStudentSelectPresenter @Inject constructor( var students = emptyList() + var selectedStudents = mutableListOf() + fun onAttachView(view: LoginStudentSelectView, students: Serializable?) { super.onAttachView(view) view.run { initView() + enableSignIn(false) errorHandler.onStudentDuplicate = { showMessage(it) Timber.i("The student already registered in the app was selected") @@ -37,13 +39,21 @@ class LoginStudentSelectPresenter @Inject constructor( } } + fun onSignIn() { + registerStudents(selectedStudents) + } + fun onParentInitStudentSelectView(students: List) { loadData(students) + if (students.size == 1) registerStudents(students) } fun onItemSelected(item: AbstractFlexibleItem<*>?) { if (item is LoginStudentSelectItem) { - registerStudent(item.student) + selectedStudents.removeAll { it == item.student } + .let { if (!it) selectedStudents.add(item.student) } + + view?.enableSignIn(selectedStudents.isNotEmpty()) } } @@ -54,33 +64,30 @@ class LoginStudentSelectPresenter @Inject constructor( } } - private fun registerStudent(student: Student) { - disposable.add(studentRepository.saveStudent(student) - .map { student.apply { id = it } } - .onErrorResumeNext { studentRepository.logoutStudent(student).andThen(Single.error(it)) } - .flatMapCompletable { studentRepository.switchStudent(student) } + private fun registerStudents(students: List) { + disposable.add(studentRepository.saveStudents(students) + .map { students.first().apply { id = it.first() } } + .flatMapCompletable { studentRepository.switchStudent(it) } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doOnSubscribe { view?.apply { showProgress(true) showContent(false) - showActionBar(false) } Timber.i("Registration started") } .subscribe({ - analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to "No error") + students.forEach { analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to "No error") } Timber.i("Registration result: Success") view?.openMainView() - }, { - analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to student.endpoint, "symbol" to student.symbol, "error" to it.localizedMessage) + }, { error -> + students.forEach { analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to error.localizedMessage) } Timber.i("Registration result: An exception occurred ") - errorHandler.dispatch(it) + errorHandler.dispatch(error) view?.apply { showProgress(false) showContent(true) - showActionBar(true) } })) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt index 824f421f..3967313c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt @@ -14,5 +14,5 @@ interface LoginStudentSelectView : BaseView { fun showContent(show: Boolean) - fun showActionBar(show: Boolean) + fun enableSignIn(enable: Boolean) } diff --git a/app/src/main/res/layout/fragment_login_form.xml b/app/src/main/res/layout/fragment_login_form.xml index 197db570..34f1f995 100644 --- a/app/src/main/res/layout/fragment_login_form.xml +++ b/app/src/main/res/layout/fragment_login_form.xml @@ -30,7 +30,7 @@ android:layout_height="wrap_content" android:layout_marginStart="32dp" android:layout_marginLeft="32dp" - android:layout_marginTop="48dp" + android:layout_marginTop="32dp" android:layout_marginEnd="32dp" android:layout_marginRight="32dp" android:gravity="center_horizontal" @@ -150,7 +150,7 @@ android:layout_marginTop="48dp" android:layout_marginEnd="24dp" android:layout_marginRight="24dp" - android:layout_marginBottom="48dp" + android:layout_marginBottom="16dp" android:text="@string/login_sign_in" android:textAppearance="?android:textAppearanceSmall" android:textColor="@android:color/white" diff --git a/app/src/main/res/layout/fragment_login_student_select.xml b/app/src/main/res/layout/fragment_login_student_select.xml index 72af7293..a94978bb 100644 --- a/app/src/main/res/layout/fragment_login_student_select.xml +++ b/app/src/main/res/layout/fragment_login_student_select.xml @@ -1,4 +1,5 @@ - + android:layout_height="match_parent"> + + + + + + + diff --git a/app/src/main/res/layout/fragment_login_symbol.xml b/app/src/main/res/layout/fragment_login_symbol.xml index 6cdf7ef0..6c820889 100644 --- a/app/src/main/res/layout/fragment_login_symbol.xml +++ b/app/src/main/res/layout/fragment_login_symbol.xml @@ -29,7 +29,7 @@ android:layout_height="wrap_content" android:layout_marginStart="32dp" android:layout_marginLeft="32dp" - android:layout_marginTop="48dp" + android:layout_marginTop="32dp" android:layout_marginEnd="32dp" android:layout_marginRight="32dp" android:gravity="center_horizontal" @@ -79,11 +79,10 @@ style="@style/Widget.MaterialComponents.Button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" android:layout_marginTop="48dp" android:layout_marginEnd="24dp" android:layout_marginRight="24dp" - android:layout_marginBottom="48dp" + android:layout_marginBottom="16dp" android:text="@string/login_sign_in" android:textAppearance="?android:textAppearanceSmall" android:textColor="@android:color/white" @@ -91,8 +90,7 @@ app:backgroundTint="@color/colorPrimary" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@+id/loginSymbolNameLayout" - app:layout_constraintVertical_bias="1" /> + app:layout_constraintTop_toBottomOf="@+id/loginSymbolNameLayout" /> 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 f13eb601..eb74cebb 100644 --- a/app/src/main/res/layout/item_login_student_select.xml +++ b/app/src/main/res/layout/item_login_student_select.xml @@ -1,46 +1,42 @@ - + android:background="?selectableItemBackground" + android:orientation="horizontal"> - + + app:firstBaselineToTopHeight="32dp" + tools:text="Jan Kowalski" /> - + app:firstBaselineToTopHeight="20dp" + tools:text="Szkoła Wulkanowego nr 1" /> diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index aee503b8..b2689133 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -3,7 +3,7 @@ - Wybierz ucznia + Logowanie Wulkanowy Oceny Frekwencja @@ -31,8 +31,9 @@ Dane logowania są niepoprawne Nie znaleziono ucznia. Sprawdź symbol To pole jest wymagane - Ten student jest już zalogowany + Wybrany uczeń jest już zalogowany Symbol znajduje się na stronie dziennika w zakładce Dostęp Mobilny + Wybierz uczniów do zalogowania w aplikacji Polityka prywatności diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8dc6a25c..6195c36c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,7 +3,7 @@ - Select student + Login Wulkanowy Grades Attendance @@ -31,8 +31,9 @@ Login details are incorrect Student not found. Check the symbol This field is required - This student has already been logged in + The selected student is already logged in The symbol is located on the register page in the Mobile Access tab + Select students to log in to the application Privacy policy 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 192602b7..17c16f41 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 @@ -51,9 +51,10 @@ class LoginStudentSelectPresenterTest { @Test fun onSelectedStudentTest() { - doReturn(Single.just(1L)).`when`(studentRepository).saveStudent(testStudent) + doReturn(Single.just(listOf(1L))).`when`(studentRepository).saveStudents(listOf(testStudent)) doReturn(Completable.complete()).`when`(studentRepository).switchStudent(testStudent) presenter.onItemSelected(LoginStudentSelectItem(testStudent)) + presenter.onSignIn() verify(loginStudentSelectView).showContent(false) verify(loginStudentSelectView).showProgress(true) verify(loginStudentSelectView).openMainView() @@ -61,9 +62,10 @@ class LoginStudentSelectPresenterTest { @Test fun onSelectedStudentErrorTest() { - doReturn(Single.error(testException)).`when`(studentRepository).saveStudent(testStudent) + doReturn(Single.error(testException)).`when`(studentRepository).saveStudents(listOf(testStudent)) doReturn(Completable.complete()).`when`(studentRepository).logoutStudent(testStudent) presenter.onItemSelected(LoginStudentSelectItem(testStudent)) + presenter.onSignIn() verify(loginStudentSelectView).showContent(false) verify(loginStudentSelectView).showProgress(true) verify(errorHandler).dispatch(testException) From 269af4b7ba47b6e1541cd6762b6732b064a1012c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Thu, 18 Apr 2019 16:38:49 +0200 Subject: [PATCH 040/322] Update dependencies (#323) --- .idea/codeStyles/Project.xml | 25 ----------- app/build.gradle | 43 ++++++++++--------- .../java/io/github/wulkanowy/WulkanowyApp.kt | 5 +-- .../io/github/wulkanowy/di/AppComponent.kt | 4 +- build.gradle | 8 ++-- gradle/wrapper/gradle-wrapper.properties | 4 +- 6 files changed, 32 insertions(+), 57 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index a8407c84..43e9b544 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -21,31 +21,6 @@