1
0
Fork 1

Compare commits

..

56 commits

Author SHA1 Message Date
Mikołaj Pich
4ab47fef46 Merge branch 'release/0.15.0' 2020-01-26 18:15:12 +01:00
Mikołaj Pich
8e17b1d72a
Version 0.15.0 2020-01-26 16:52:41 +01:00
Mikołaj Pich
ae9b616896 Show points sum in grades details (#664) 2020-01-26 02:16:05 +01:00
Mikołaj Pich
1999cd6eaf Visual enhancement of displaying grades point stats (#665) 2020-01-26 01:26:06 +01:00
Mikołaj Pich
1e5269c22c Don't hide password toggle button on error (#666) 2020-01-26 01:24:24 +01:00
Kacper Ziubryniewicz
19f495cba6 Add excusing for absence (#533) 2020-01-25 18:07:25 +01:00
dependabot-preview[bot]
0c5d45717c
Bump fragment-ktx from 1.2.0-rc05 to 1.2.0 (#662) 2020-01-24 09:18:54 +00:00
dependabot-preview[bot]
1dbe470391
Bump activity-ktx from 1.1.0-rc03 to 1.1.0 (#661) 2020-01-24 08:59:20 +00:00
dependabot-preview[bot]
7142e05561
Bump work_manager from 2.3.0-rc01 to 2.3.0 (#660) 2020-01-24 08:58:31 +00:00
Dominik Korsa
4640d114f6 Add creators list (#636) 2020-01-22 10:59:13 +01:00
Mikołaj Pich
ad3bb3a522 Add option to show only student plan lessons (#654) 2020-01-20 22:18:26 +01:00
dependabot-preview[bot]
e9fa95f113 Bump rxjava from 2.2.16 to 2.2.17 (#656)
Bumps [rxjava](https://github.com/ReactiveX/RxJava) from 2.2.16 to 2.2.17.
- [Release notes](https://github.com/ReactiveX/RxJava/releases)
- [Changelog](https://github.com/ReactiveX/RxJava/blob/v2.2.17/CHANGES.md)
- [Commits](https://github.com/ReactiveX/RxJava/compare/v2.2.16...v2.2.17)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-20 21:51:13 +01:00
Dominik Korsa
76a4bacb34 Fix login info autofill (#652) 2020-01-20 21:29:42 +01:00
Dominik Korsa
c5cadbd004 Improve grades dialog layout (#631) 2020-01-20 21:29:06 +01:00
dependabot-preview[bot]
77e7e4e6e9
Bump material from 1.1.0-rc01 to 1.1.0-rc02 (#655) 2020-01-20 20:27:52 +00:00
dependabot-preview[bot]
cc242e1e87
Bump threetenabp from 1.2.1 to 1.2.2 (#657) 2020-01-20 20:27:29 +00:00
dependabot-preview[bot]
e0ec2f8160
Bump firebase-core from 17.2.1 to 17.2.2 (#658) 2020-01-20 20:27:10 +00:00
dependabot-preview[bot]
eb1ce251a0
Bump threetenbp from 1.4.0 to 1.4.1 (#659) 2020-01-20 20:26:53 +00:00
Dominik Korsa
4d1de2d8ce Add debug notification icons (#637) 2020-01-12 14:21:33 +01:00
Mikołaj Pich
0af7c00d12 Merge tag '0.14.2' into develop
Version 0.14.2
2020-01-11 20:10:39 +01:00
Mikołaj Pich
afaf6c0e56 Merge branch 'release/0.14.2' 2020-01-11 20:10:34 +01:00
Mikołaj Pich
e8b4e16382
Version 0.14.2 2020-01-11 20:10:23 +01:00
Mikołaj Pich
83a8c857e5
Change color attr in backtround_header_note drawable to color re… (#644) 2020-01-11 20:09:33 +01:00
Mikołaj Pich
57e943fb3b Update error_service_unavailable string (#645) 2020-01-11 17:25:13 +01:00
Mikołaj Pich
ed310e7764 Merge tag '0.14.1' into develop
Version 0.14.1
2020-01-09 00:53:55 +01:00
Mikołaj Pich
16e3a877af Merge branch 'release/0.14.1' 2020-01-09 00:53:51 +01:00
Mikołaj Pich
2b5b87fe84
Version 0.14.1 2020-01-09 00:53:32 +01:00
Rafał Borcz
d706c000b9 Add missing translations (#642) 2020-01-09 00:42:16 +01:00
dependabot-preview[bot]
15184550f4
Bump junit from 4.12 to 4.13 (#640) 2020-01-08 23:25:02 +00:00
dependabot-preview[bot]
69a8e35150
Bump logging-interceptor from 3.12.6 to 3.12.7 (#641) 2020-01-08 23:05:59 +00:00
dependabot-preview[bot]
70166d0245
Bump fragment-ktx from 1.2.0-rc04 to 1.2.0-rc05 (#639) 2020-01-08 22:50:35 +00:00
dependabot-preview[bot]
e0657eb5b2
Bump work_manager from 2.3.0-beta02 to 2.3.0-rc01 (#638) 2020-01-08 22:48:37 +00:00
Dominik Korsa
f204264d2d Change grade header note to unread count (#634) 2020-01-04 01:46:42 +01:00
pcktm
b9378c24b5 Minor translation changes (#630) 2020-01-03 21:12:05 +01:00
dependabot-preview[bot]
8a6ceeb2e4
Bump aboutlibraries from 7.0.4 to 7.1.0 (#633) 2020-01-02 19:42:15 +00:00
dependabot-preview[bot]
a45c7bd3e3
Bump dagger from 2.25.3 to 2.25.4 (#632) 2019-12-31 22:04:35 +00:00
Mikołaj Pich
4df245755a Add fields validation in mobile api login (#629) 2019-12-31 17:31:26 +01:00
Mikołaj Pich
6be801d4a8 Fix error view which overlaps grade statistics content (#627) 2019-12-30 22:57:48 +01:00
Mikołaj Pich
54f3733b56 Merge tag '0.14.0' into develop
Version 0.14.0
2019-12-25 16:52:34 +01:00
Mikołaj Pich
4f60673e4e Merge branch 'release/0.14.0' 2019-12-25 16:52:28 +01:00
Mikołaj Pich
7bd4fd7cbd
Version 0.14.0 2019-12-25 16:52:17 +01:00
Nikolas Ostrowski
65995cdc6c Change Wulkanowy API link to SDK in README (#626) 2019-12-24 16:51:35 +01:00
Mikołaj Pich
9d27723f30 Don't copy teacher from previous lesson with changes if new one… (#622) 2019-12-22 20:19:31 +01:00
Mikołaj Pich
2e7d2b66f8 Fix lucky number empty screen (#623) 2019-12-22 20:18:48 +01:00
Mikołaj Pich
304c49d61e Migration to Wulkanowy SDK (#336) 2019-12-22 00:14:46 +01:00
dependabot-preview[bot]
826ea32fc0
Bump room from 2.2.2 to 2.2.3 (#618) 2019-12-20 10:18:00 +00:00
dependabot-preview[bot]
d70c4fa9fe
Bump work_manager from 2.3.0-beta01 to 2.3.0-beta02 (#620) 2019-12-20 09:46:28 +00:00
dependabot-preview[bot]
bc43359467
Bump mockito-inline from 3.2.0 to 3.2.4 (#621) 2019-12-20 09:44:18 +00:00
dependabot-preview[bot]
cf286f3c23
Bump mockito-android from 3.2.0 to 3.2.4 (#619) 2019-12-20 09:27:14 +00:00
dependabot-preview[bot]
57abd43214
Bump fragment-ktx from 1.2.0-rc03 to 1.2.0-rc04 (#617) 2019-12-20 09:24:51 +00:00
dependabot-preview[bot]
90bdc9d157
Bump material from 1.1.0-beta02 to 1.1.0-rc01 (#616) 2019-12-20 09:13:34 +00:00
dependabot-preview[bot]
93bce685bd
Bump dagger from 2.25.2 to 2.25.3 (#614) 2019-12-20 09:07:39 +00:00
dependabot-preview[bot]
4639a075b0
Bump rxjava from 2.2.15 to 2.2.16 (#615) 2019-12-20 09:06:52 +00:00
stha
91f63da6d0 Fixed capitalization in the about tab (en) (#613) 2019-12-09 17:35:18 +01:00
stha
3894c9d48e Fixed letter capitalization in the About tab (pl) (#612) 2019-12-09 17:34:58 +01:00
Mikołaj Pich
fb40701962 Merge tag '0.13.0' into develop
Version 0.13.0
2019-12-07 23:04:39 +01:00
189 changed files with 8083 additions and 837 deletions

View file

@ -14,7 +14,7 @@ cache:
branches:
only:
- develop
- 0.13.0
- 0.15.0
android:
licenses:

View file

@ -7,11 +7,11 @@
[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/)
[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases)
Unofficial android VULCAN UONET+ register client for student and parent
Unofficial android VULCAN UONET+ register client for both students and their parents
## Features
* logging in using the email and password
* logging in using the email and password OR using token and pin
* functions from the register website:
* grades
* grade statistics
@ -24,7 +24,7 @@ Unofficial android VULCAN UONET+ register client for student and parent
* homework
* notes
* lucky number
* calculation of the average
* calculation of the average independently of school's preferences
* notifications, e.g. about a new grade
* dark and black (AMOLED) theme
* offline mode
@ -32,21 +32,21 @@ Unofficial android VULCAN UONET+ register client for student and parent
## Download
You can download the current beta from the Google Play or Fdroid store
You can download the current beta version from the Google Play or the F-Droid store
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
alt="Get it on Google Play"
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on Fdroid"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/io.github.wulkanowy/)
You can also download a [development version](https://wulkanowy.github.io/#download) that includes new features prepared for the next release
You can also download a [development version](https://wulkanowy.github.io/#download) that includes new features being prepared for the next release
## Built With
* [Wulkanowy API](https://github.com/wulkanowy/api)
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
* [RxJava 2](https://github.com/ReactiveX/RxJava)
* [Dagger 2](https://github.com/google/dagger)
* [Room](https://developer.android.com/topic/libraries/architecture/room)

View file

@ -11,7 +11,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
## Funkcje
* logowanie za pomocą e-maila i hasła
* logowanie za pomocą e-maila i hasła LUB tokena i pinu
* funkcje ze strony internetowej dziennika:
* oceny
* statystyki ocen
@ -24,7 +24,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
* zadania domowe
* uwagi
* szczęśliwy numerek
* obliczanie średniej
* obliczanie średniej niezależnie od preferencji szkoły
* powiadomienia np. o nowej ocenie
* ciemny i czarny (AMOLED) motyw
* tryb offilne
@ -32,13 +32,13 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
## Pobierz
Aktualną wersję beta możesz pobrać ze sklepu Google Play lub Fdroid
Aktualną wersję beta możesz pobrać ze sklepu Google Play lub F-Droid
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
alt="Pobierz z Google Play"
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Pobierz z Fdroid"
alt="Pobierz z F-Droid"
height="80">](https://f-droid.org/packages/io.github.wulkanowy/)
@ -47,7 +47,7 @@ Możesz także pobrać [wersję rozwojową](https://wulkanowy.github.io/#downloa
## Zbudowana za pomocą
* [Wulkanowy API](https://github.com/wulkanowy/api)
* [Wulkanowy SDK](https://github.com/wulkanowy/SDK)
* [RxJava 2](https://github.com/ReactiveX/RxJava)
* [Dagger 2](https://github.com/google/dagger)
* [Room](https://developer.android.com/topic/libraries/architecture/room)
@ -59,4 +59,4 @@ Wnieś swój wkład w projekt, tworząc PR lub wysyłając issue na GitHub.
## Licencja
Ten projekt jest licencjonowany w ramach Apache License 2.0 - szczegóły w pliku [LICENSE](LICENSE)
Ten projekt udostępniany jest na licencji Apache License 2.0 - szczegóły w pliku [LICENSE](LICENSE)

View file

@ -17,8 +17,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 16
targetSdkVersion 29
versionCode 48
versionName "0.13.0"
versionCode 52
versionName "0.15.0"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@ -110,9 +110,9 @@ play {
}
ext {
work_manager = "2.3.0-beta01"
room = "2.2.2"
dagger = "2.25.2"
work_manager = "2.3.0"
room = "2.2.3"
dagger = "2.25.4"
chucker = "2.0.4"
mockk = "1.9.2"
}
@ -122,14 +122,14 @@ configurations.all {
}
dependencies {
implementation "io.github.wulkanowy:api:0.13.0"
implementation "io.github.wulkanowy:sdk:0.15.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.2.0-rc01"
implementation "androidx.activity:activity-ktx:1.1.0-rc03"
implementation "androidx.activity:activity-ktx:1.1.0"
implementation "androidx.appcompat:appcompat:1.1.0"
implementation "androidx.appcompat:appcompat-resources:1.1.0"
implementation "androidx.fragment:fragment-ktx:1.2.0-rc03"
implementation "androidx.fragment:fragment-ktx:1.2.0"
implementation "androidx.annotation:annotation:1.1.0"
implementation "androidx.multidex:multidex:2.0.1"
@ -139,7 +139,7 @@ dependencies {
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03"
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
implementation "com.google.android.material:material:1.1.0-beta02"
implementation "com.google.android.material:material:1.1.0-rc02"
implementation "com.github.wulkanowy:material-chips-input:2.0.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
@ -167,17 +167,18 @@ dependencies {
implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6"
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
implementation "io.reactivex.rxjava2:rxjava:2.2.15"
implementation "io.reactivex.rxjava2:rxjava:2.2.17"
implementation "com.google.code.gson:gson:2.8.6"
implementation "com.jakewharton.threetenabp:threetenabp:1.2.1"
implementation "com.jakewharton.threetenabp:threetenabp:1.2.2"
implementation "com.jakewharton.timber:timber:4.7.1"
implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation "com.squareup.okhttp3:logging-interceptor:3.12.6"
implementation "com.mikepenz:aboutlibraries:7.0.4"
implementation "com.mikepenz:aboutlibraries-core:7.1.0"
implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
playImplementation "com.google.firebase:firebase-core:17.2.1"
implementation("io.coil-kt:coil:0.9.2")
playImplementation "com.google.firebase:firebase-core:17.2.2"
playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1"
releaseImplementation "fr.o80.chucker:library-no-op:$chucker"
@ -185,10 +186,10 @@ dependencies {
debugImplementation "fr.o80.chucker:library:$chucker"
debugImplementation "com.amitshekhar.android:debug-db:1.0.6"
testImplementation "junit:junit:4.12"
testImplementation "junit:junit:4.13"
testImplementation "io.mockk:mockk:$mockk"
testImplementation "org.threeten:threetenbp:1.4.0"
testImplementation "org.mockito:mockito-inline:3.2.0"
testImplementation "org.threeten:threetenbp:1.4.1"
testImplementation "org.mockito:mockito-inline:3.2.4"
androidTestImplementation "androidx.test:core:1.2.0"
androidTestImplementation "androidx.test:runner:1.2.0"
@ -196,7 +197,7 @@ dependencies {
androidTestImplementation "io.mockk:mockk-android:$mockk"
androidTestImplementation "androidx.room:room-testing:$room"
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
androidTestImplementation "org.mockito:mockito-android:3.2.0"
androidTestImplementation "org.mockito:mockito-android:3.2.4"
}
apply plugin: 'com.google.gms.google-services'

View file

@ -1,7 +1,7 @@
apply plugin: "jacoco"
jacoco {
toolVersion "0.8.4"
toolVersion "0.8.5"
reportsDir = file("$buildDir/reports")
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,13 @@
package io.github.wulkanowy.data.db.migrations
import androidx.preference.PreferenceManager
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 io.github.wulkanowy.data.db.SharedPrefProvider
import org.junit.Rule
abstract class AbstractMigrationTest {
@ -22,7 +24,9 @@ abstract class AbstractMigrationTest {
fun getMigratedRoomDatabase(): AppDatabase {
val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(),
AppDatabase::class.java, dbName)
.addMigrations(*AppDatabase.getMigrations())
.addMigrations(*AppDatabase.getMigrations(SharedPrefProvider(PreferenceManager
.getDefaultSharedPreferences(ApplicationProvider.getApplicationContext())))
)
.build()
// close the database and release any stream resources when the test finishes
helper.closeWhenFinished(database)

View file

@ -35,9 +35,9 @@ class AttendanceLocalTest {
@Test
fun saveAndReadTest() {
attendanceLocal.saveAttendance(listOf(
Attendance(1, 2, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false),
Attendance(1, 2, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false),
Attendance(1, 2, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false)
Attendance(1, 2, 3, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name),
Attendance(1, 2, 3, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.WAITING.name),
Attendance(1, 2, 3, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name)
))
val attendance = attendanceLocal

View file

@ -6,15 +6,18 @@ import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.SdkHelper
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy
import io.github.wulkanowy.sdk.Sdk
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
import io.mockk.runs
import io.reactivex.Single
import org.junit.After
import org.junit.Before
@ -25,14 +28,13 @@ import org.threeten.bp.LocalDateTime
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import io.github.wulkanowy.api.grades.Grade as GradeApi
@SdkSuppress(minSdkVersion = P)
@RunWith(AndroidJUnit4::class)
class GradeRepositoryTest {
@SpyK
private var mockApi = Api()
@MockK
private lateinit var mockSdk: Sdk
private val settings = InternetObservingSettings.builder()
.strategy(TestInternetObservingStrategy())
@ -55,13 +57,14 @@ class GradeRepositoryTest {
MockKAnnotations.init(this)
testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build()
gradeLocal = GradeLocal(testDb.gradeDao)
gradeRemote = GradeRemote(mockApi)
gradeRemote = GradeRemote(mockSdk)
every { mockApi.diaryId } returns 1
every { studentMock.registrationDate } returns LocalDateTime.of(2019, 2, 27, 12, 0)
every { semesterMock.studentId } returns 1
every { semesterMock.semesterId } returns 1
every { semesterMock.diaryId } returns 1
every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
}
@After
@ -71,7 +74,7 @@ class GradeRepositoryTest {
@Test
fun markOlderThanRegisterDateAsRead() {
every { mockApi.getGrades(1) } returns Single.just(listOf(
every { mockSdk.getGrades(1) } returns Single.just(listOf(
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"),
@ -95,7 +98,7 @@ class GradeRepositoryTest {
createGradeLocal(3, 5.0, of(2019, 2, 27), "Trzecia")
))
every { mockApi.getGrades(1) } returns Single.just(listOf(
every { mockSdk.getGrades(1) } returns Single.just(listOf(
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"),
@ -119,7 +122,7 @@ class GradeRepositoryTest {
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
))
every { mockApi.getGrades(1) } returns Single.just(listOf(
every { mockSdk.getGrades(1) } returns Single.just(listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
))
@ -137,7 +140,7 @@ class GradeRepositoryTest {
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
))
every { mockApi.getGrades(1) } returns Single.just(listOf(
every { mockSdk.getGrades(1) } returns Single.just(listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
@ -153,7 +156,7 @@ class GradeRepositoryTest {
fun emptyLocal() {
gradeLocal.saveGrades(listOf())
every { mockApi.getGrades(1) } returns Single.just(listOf(
every { mockSdk.getGrades(1) } returns Single.just(listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
@ -172,7 +175,7 @@ class GradeRepositoryTest {
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
))
every { mockApi.getGrades(1) } returns Single.just(listOf())
every { mockSdk.getGrades(1) } returns Single.just(listOf())
val grades = GradeRepository(settings, gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true).blockingGet()

View file

@ -1,8 +1,7 @@
package io.github.wulkanowy.data.repositories.grade
import io.github.wulkanowy.api.toDate
import org.threeten.bp.LocalDate
import io.github.wulkanowy.api.grades.Grade as GradeRemote
import io.github.wulkanowy.sdk.pojo.Grade as GradeRemote
import io.github.wulkanowy.data.db.entities.Grade as GradeLocal
fun createGradeLocal(value: Int, weight: Double, date: LocalDate, desc: String, semesterId: Int = 1): GradeLocal {
@ -18,17 +17,25 @@ fun createGradeLocal(value: Int, weight: Double, date: LocalDate, desc: String,
description = desc,
entry = "",
gradeSymbol = "",
value = value,
value = value.toDouble(),
weight = "",
weightValue = weight
)
}
fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String): GradeRemote {
return GradeRemote().apply {
this.value = value
this.weightValue = weight
this.date = date.toDate()
this.description = desc
}
return GradeRemote(
subject = "",
color = "",
comment = "",
date = date,
description = desc,
entry = "",
modifier = .0,
symbol = "",
teacher = "",
value = value.toDouble(),
weight = weight.toString(),
weightValue = weight
)
}

View file

@ -42,7 +42,7 @@ class RecipientLocalTest {
))
val recipients = recipientLocal.getRecipients(
Student("fakelog.cf", "AUTO", "", "", "", 1, "", "", "", "", 1, true, LocalDateTime.now()),
Student("fakelog.cf", "AUTO", "", "", "", "", false, "", "", "", 1, 0, "", "", "", "", 1, true, LocalDateTime.now()),
2,
ReportingUnit(1, 4, "", 0, "", emptyList())
).blockingGet()

View file

@ -39,7 +39,7 @@ class StudentLocalTest {
@Test
fun saveAndReadTest() {
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 = "")))
studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", scrapperBaseUrl = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "", loginMode = "API", certificateKey = "", privateKey = "", mobileBaseUrl = "", userLoginId = 0, isParent = false)))
.blockingGet()
val student = studentLocal.getCurrentStudent(true).blockingGet()

View file

@ -1,13 +1,11 @@
package io.github.wulkanowy.data.repositories.timetable
import io.github.wulkanowy.api.toDate
import io.github.wulkanowy.utils.toDate
import org.threeten.bp.LocalDateTime
import org.threeten.bp.LocalDateTime.now
import io.github.wulkanowy.api.timetable.Timetable as TimetableRemote
import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal
import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote
fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableLocal {
fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableLocal {
return TimetableLocal(
studentId = 1,
diaryId = 2,
@ -23,23 +21,28 @@ fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", s
teacher = teacher,
teacherOld = "",
info = "",
studentPlan = true,
changes = changes,
canceled = false
)
}
fun createTimetableRemote(number: Int, start: LocalDateTime, room: String, subject: String = "", teacher: String = "", changes: Boolean = false): TimetableRemote {
fun createTimetableRemote(start: LocalDateTime, number: Int = 1, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableRemote {
return TimetableRemote(
number = number,
start = start.toDate(),
end = start.plusMinutes(45).toDate(),
date = start.toLocalDate().toDate(),
start = start,
end = start.plusMinutes(45),
date = start.toLocalDate(),
subject = subject,
group = "",
room = room,
teacher = teacher,
info = "",
changes = changes,
canceled = false
canceled = false,
roomOld = "",
subjectOld = "",
teacherOld = "",
studentPlan = true
)
}

View file

@ -35,9 +35,9 @@ class TimetableLocalTest {
@Test
fun saveAndReadTest() {
timetableDb.saveTimetable(listOf(
createTimetableLocal(1, of(2018, 9, 10, 0, 0, 0)),
createTimetableLocal(1, of(2018, 9, 14, 0, 0, 0)),
createTimetableLocal(1, of(2018, 9, 17, 0, 0, 0))
createTimetableLocal(of(2018, 9, 10, 0, 0, 0), 1),
createTimetableLocal(of(2018, 9, 14, 0, 0, 0), 1),
createTimetableLocal(of(2018, 9, 17, 0, 0, 0), 1)
))
val exams = timetableDb.getTimetable(

View file

@ -6,14 +6,13 @@ import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy
import io.github.wulkanowy.sdk.Sdk
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single
import org.junit.After
import org.junit.Before
@ -27,8 +26,8 @@ import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class)
class TimetableRepositoryTest {
@SpyK
private var mockApi = Api()
@MockK
private lateinit var mockSdk: Sdk
private val settings = InternetObservingSettings.builder()
.strategy(TestInternetObservingStrategy())
@ -48,10 +47,13 @@ class TimetableRepositoryTest {
MockKAnnotations.init(this)
testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build()
timetableLocal = TimetableLocal(testDb.timetableDao)
timetableRemote = TimetableRemote(mockApi)
timetableRemote = TimetableRemote(mockSdk)
every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 2
every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
}
@After
@ -62,17 +64,17 @@ class TimetableRepositoryTest {
@Test
fun copyRoomToCompletedFromPrevious() {
timetableLocal.saveTimetable(listOf(
createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda"),
createTimetableLocal(2, of(2019, 3, 5, 8, 50), "321", "Religia"),
createTimetableLocal(3, of(2019, 3, 5, 9, 40), "213", "W-F"),
createTimetableLocal(4, of(2019, 3, 5, 10, 30), "213", "W-F", "Jan Kowalski")
createTimetableLocal(of(2019, 3, 5, 8, 0), 1, "123", "Przyroda"),
createTimetableLocal(of(2019, 3, 5, 8, 50), 2, "321", "Religia"),
createTimetableLocal(of(2019, 3, 5, 9, 40), 3, "213", "W-F"),
createTimetableLocal(of(2019, 3, 5, 10, 30),3, "213", "W-F", "Jan Kowalski")
))
every { mockApi.getTimetable(any(), any()) } returns Single.just(listOf(
createTimetableRemote(1, of(2019, 3, 5, 8, 0), "", "Przyroda"),
createTimetableRemote(2, of(2019, 3, 5, 8, 50), "", "Religia"),
createTimetableRemote(3, of(2019, 3, 5, 9, 40), "", "W-F"),
createTimetableRemote(4, of(2019, 3, 5, 10, 30), "", "W-F")
every { mockSdk.getTimetable(any(), any()) } returns Single.just(listOf(
createTimetableRemote(of(2019, 3, 5, 8, 0), 1, "", "Przyroda"),
createTimetableRemote(of(2019, 3, 5, 8, 50), 2, "", "Religia"),
createTimetableRemote(of(2019, 3, 5, 9, 40), 3, "", "W-F"),
createTimetableRemote(of(2019, 3, 5, 10, 30), 4, "", "W-F")
))
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote)
@ -88,27 +90,58 @@ class TimetableRepositoryTest {
@Test
fun copyTeacherToCompletedFromPrevious() {
timetableLocal.saveTimetable(listOf(
createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda", "Jan Garnkiewicz", false),
createTimetableLocal(2, of(2019, 3, 5, 8, 50), "321", "Religia", "Paweł Jumper", false),
createTimetableLocal(3, of(2019, 3, 5, 9, 40), "213", "W-F", "", true),
createTimetableLocal(4, of(2019, 3, 5, 10, 30), "213", "W-F", "", false)
createTimetableLocal(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false),
createTimetableLocal(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Paweł Poniedziałkowski", false),
createTimetableLocal(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Wtorkowska", true),
createTimetableLocal(of(2019, 12, 23, 10, 40), 4, "126", "Język polski", "Joanna Wtorkowska", true),
createTimetableLocal(of(2019, 12, 24, 8, 0), 1, "123", "Język polski", "Joanna Wtorkowska", false),
createTimetableLocal(of(2019, 12, 24, 8, 50), 2, "124", "Język polski", "Joanna Wtorkowska", false),
createTimetableLocal(of(2019, 12, 24, 9, 40), 3, "125", "Język polski", "Joanna Środowska", true),
createTimetableLocal(of(2019, 12, 24, 10, 40), 4, "126", "Język polski", "Joanna Środowska", true),
createTimetableLocal(of(2019, 12, 25, 8, 0), 1, "123", "Matematyka", "", false),
createTimetableLocal(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "", false),
createTimetableLocal(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "", true),
createTimetableLocal(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "", true)
))
every { mockApi.getTimetable(any(), any()) } returns Single.just(listOf(
createTimetableRemote(1, of(2019, 3, 5, 8, 0), "", "Przyroda", "", true), // should override local
createTimetableRemote(2, of(2019, 3, 5, 8, 50), "", "Religia", "", false),
createTimetableRemote(3, of(2019, 3, 5, 9, 40), "", "W-F", "Jan Garnkiewicz", false),
createTimetableRemote(4, of(2019, 3, 5, 10, 30), "", "W-F", "Paweł Jumper", false)
every { mockSdk.getTimetable(any(), any()) } returns Single.just(listOf(
createTimetableRemote(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false),
createTimetableRemote(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Jakub Wtorkowski", true),
createTimetableRemote(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Poniedziałkowska", false),
createTimetableRemote(of(2019, 12, 23, 10, 40), 4, "126", "Język polski", "Joanna Wtorkowska", true),
createTimetableRemote(of(2019, 12, 24, 8, 0), 1, "123", "Język polski", "", false),
createTimetableRemote(of(2019, 12, 24, 8, 50), 2, "124", "Język polski", "", true),
createTimetableRemote(of(2019, 12, 24, 9, 40), 3, "125", "Język polski", "", false),
createTimetableRemote(of(2019, 12, 24, 10, 40), 4, "126", "Język polski", "", true),
createTimetableRemote(of(2019, 12, 25, 8, 0), 1, "123", "Matematyka", "Paweł Środowski", false),
createTimetableRemote(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "Paweł Czwartkowski", true),
createTimetableRemote(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "Paweł Środowski", false),
createTimetableRemote(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true)
))
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote)
.getTimetable(semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true)
.getTimetable(semesterMock, LocalDate.of(2019, 12, 23), LocalDate.of(2019, 12, 25), true)
.blockingGet()
assertEquals(4, lessons.size)
assertEquals("", lessons[0].teacher)
assertEquals("Paweł Jumper", lessons[1].teacher)
assertEquals("Jan Garnkiewicz", lessons[2].teacher)
assertEquals("Paweł Jumper", lessons[3].teacher)
assertEquals(12, lessons.size)
assertEquals("Paweł Poniedziałkowski", lessons[0].teacher)
assertEquals("Jakub Wtorkowski", lessons[1].teacher)
assertEquals("Joanna Poniedziałkowska", lessons[2].teacher)
assertEquals("Joanna Wtorkowska", lessons[3].teacher)
assertEquals("Joanna Wtorkowska", lessons[4].teacher)
assertEquals("", lessons[5].teacher)
assertEquals("", lessons[6].teacher)
assertEquals("", lessons[7].teacher)
assertEquals("Paweł Środowski", lessons[8].teacher)
assertEquals("Paweł Czwartkowski", lessons[9].teacher)
assertEquals("Paweł Środowski", lessons[10].teacher)
assertEquals("Paweł Czwartkowski", lessons[11].teacher)
}
}

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="30.434782"
android:viewportHeight="30.434782">
<path
android:fillColor="#ffffff"
android:pathData="M 8.131 2.131 C 7.601 2.131 7.092 2.342 6.717 2.717 C 6.342 3.092 6.131 3.601 6.131 4.131 L 6.131 18.131 C 6.131 18.661 6.342 19.17 6.717 19.545 C 7.092 19.92 7.601 20.131 8.131 20.131 L 16.918 20.131 C 17.252 19.39 17.712 18.714 18.277 18.131 L 8.131 18.131 L 8.131 4.131 L 22.131 4.131 L 22.131 16.1 C 22.516 16.034 22.906 16.001 23.297 16 C 23.576 16.002 23.854 16.02 24.131 16.055 L 24.131 4.131 C 24.131 3.601 23.92 3.092 23.545 2.717 C 23.17 2.342 22.661 2.131 22.131 2.131 L 8.131 2.131 Z M 2.131 6.131 L 2.131 22.131 C 2.131 22.661 2.342 23.17 2.717 23.545 C 3.092 23.92 3.601 24.131 4.131 24.131 L 16.391 24.131 C 16.329 23.757 16.297 23.379 16.297 23 C 16.299 22.709 16.319 22.419 16.357 22.131 L 4.131 22.131 L 4.131 6.131 L 2.131 6.131 Z M 14.131 6.131 C 13.601 6.131 13.092 6.342 12.717 6.717 C 12.342 7.092 12.131 7.601 12.131 8.131 L 12.131 14.131 C 12.131 15.231 13.031 16.131 14.131 16.131 L 16.131 16.131 C 16.661 16.131 17.17 15.92 17.545 15.545 C 17.92 15.17 18.131 14.661 18.131 14.131 L 18.131 12.131 C 18.131 11.601 17.92 11.092 17.545 10.717 C 17.17 10.342 16.661 10.131 16.131 10.131 L 14.131 10.131 L 14.131 8.131 L 18.131 8.131 L 18.131 6.131 L 14.131 6.131 Z M 14.131 12.131 L 16.131 12.131 L 16.131 14.131 L 14.131 14.131 L 14.131 12.131 Z" />
<path
android:fillColor="#ffffff"
android:pathData="M 20.174 28 L 20.174 18.521 L 23.091 18.521 Q 24.341 18.521 25.324 19.087 Q 26.314 19.647 26.867 20.689 Q 27.421 21.724 27.421 23.046 L 27.421 23.482 Q 27.421 24.803 26.874 25.832 Q 26.333 26.861 25.344 27.427 Q 24.354 27.993 23.111 28 Z M 22.128 20.103 L 22.128 26.431 L 23.072 26.431 Q 24.217 26.431 24.823 25.682 Q 25.428 24.934 25.441 23.54 L 25.441 23.039 Q 25.441 21.594 24.842 20.852 Q 24.243 20.103 23.091 20.103 Z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="28.26087"
android:viewportHeight="28.26087">
<path
android:fillColor="#ffffff"
android:pathData="M 10.734 3.043 C 9.234 3.043 8.043 4.293 8.043 5.793 C 8.043 7.683 9.743 9.223 13.043 12.223 C 16.343 9.223 18.043 7.684 18.043 5.734 C 18.043 4.234 16.793 3.043 15.293 3.043 C 14.433 3.043 13.613 3.403 13.043 4.043 C 12.473 3.403 11.654 3.043 10.734 3.043 Z M 5.734 8.043 C 4.234 8.043 3.043 9.293 3.043 10.793 C 3.043 11.653 3.403 12.473 4.043 13.043 C 3.403 13.613 3.043 14.434 3.043 15.354 C 3.043 16.854 4.293 18.043 5.793 18.043 C 7.683 18.043 9.223 16.343 12.223 13.043 C 9.223 9.743 7.684 8.043 5.734 8.043 Z M 20.293 8.043 C 18.403 8.043 16.863 9.743 13.873 13.043 C 15.043 14.334 15.987 15.373 16.824 16.168 C 17.476 15.484 18.26 14.94 19.129 14.567 C 19.997 14.195 20.932 14.002 21.877 14 C 22.181 14.002 22.484 14.025 22.785 14.066 C 22.615 13.68 22.365 13.329 22.043 13.043 C 22.683 12.473 23.043 11.654 23.043 10.734 C 23.043 9.234 21.793 8.043 20.293 8.043 Z M 13.043 13.863 C 9.743 16.863 8.043 18.404 8.043 20.354 C 8.043 21.854 9.293 23.043 10.793 23.043 C 11.653 23.043 12.473 22.683 13.043 22.043 C 13.576 22.642 14.333 22.982 15.182 23.025 C 14.981 22.369 14.879 21.686 14.877 21 C 14.88 19.52 15.351 18.078 16.225 16.883 C 15.422 16.032 14.368 15.067 13.043 13.863 Z" />
<path
android:fillColor="#ffffff"
android:pathData="M 18.754 26 L 18.754 16.521 L 21.671 16.521 Q 22.921 16.521 23.904 17.087 Q 24.893 17.647 25.447 18.689 Q 26 19.724 26 21.046 L 26 21.482 Q 26 22.803 25.453 23.832 Q 24.913 24.861 23.923 25.427 Q 22.934 25.993 21.69 26 Z M 20.707 18.103 L 20.707 24.431 L 21.651 24.431 Q 22.797 24.431 23.402 23.682 Q 24.008 22.934 24.021 21.54 L 24.021 21.039 Q 24.021 19.594 23.422 18.852 Q 22.823 18.103 21.671 18.103 Z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="28.26087"
android:viewportHeight="28.26087">
<path
android:fillColor="#ffffff"
android:pathData="M 5.043 3.043 C 3.943 3.043 3.053 3.943 3.053 5.043 L 3.043 23.043 L 7.043 19.043 L 15.16 19.043 C 15.646 17.378 16.733 15.952 18.209 15.043 L 7.043 15.043 L 7.043 13.043 L 19.043 13.043 L 19.043 14.609 C 19.935 14.211 20.9 14.003 21.877 14 C 22.268 14.003 22.658 14.038 23.043 14.105 L 23.043 5.043 C 23.043 3.943 22.143 3.043 21.043 3.043 L 5.043 3.043 Z M 7.043 7.043 L 19.043 7.043 L 19.043 9.043 L 7.043 9.043 L 7.043 7.043 Z M 7.043 10.043 L 19.043 10.043 L 19.043 12.043 L 7.043 12.043 L 7.043 10.043 Z" />
<path
android:fillColor="#ffffff"
android:pathData="M 18.754 26 L 18.754 16.521 L 21.671 16.521 Q 22.921 16.521 23.904 17.087 Q 24.893 17.647 25.447 18.689 Q 26 19.724 26 21.046 L 26 21.482 Q 26 22.803 25.453 23.832 Q 24.913 24.861 23.923 25.427 Q 22.934 25.993 21.69 26 Z M 20.707 18.103 L 20.707 24.431 L 21.651 24.431 Q 22.797 24.431 23.402 23.682 Q 24.008 22.934 24.021 21.54 L 24.021 21.039 Q 24.021 19.594 23.422 18.852 Q 22.823 18.103 21.671 18.103 Z" />
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="28.26087"
android:viewportHeight="28.26087">
<path
android:fillColor="#ffffff"
android:pathData="M 3.043 3.043 L 3.043 12.043 C 3.043 13.043 4.043 14.043 5.043 14.043 L 7.244 14.043 C 7.644 16.043 8.943 17.743 12.043 18.043 L 12.043 20.143 C 9.843 20.343 9.043 21.444 9.043 22.744 L 9.043 23.043 L 15.188 23.043 C 14.983 22.381 14.879 21.693 14.877 21 C 14.877 20.763 14.888 20.527 14.912 20.291 C 14.649 20.226 14.364 20.172 14.043 20.143 L 14.043 18.043 C 14.68 17.981 15.224 17.848 15.717 17.678 C 16.382 16.446 17.401 15.442 18.643 14.795 C 18.725 14.551 18.792 14.299 18.844 14.043 L 21.043 14.043 C 22.043 14.043 23.043 13.043 23.043 12.043 L 23.043 3.043 L 19.043 3.043 C 18.143 3.043 17.043 4.043 17.043 5.043 L 9.043 5.043 C 9.043 4.043 7.943 3.043 7.043 3.043 L 3.043 3.043 Z M 5.043 5.043 L 7.043 5.043 L 7.043 12.043 L 5.043 12.043 L 5.043 5.043 Z M 19.043 5.043 L 21.043 5.043 L 21.043 12.043 L 19.043 12.043 L 19.043 5.043 Z" />
<path
android:fillColor="#ffffff"
android:pathData="M 18.754 26 L 18.754 16.521 L 21.671 16.521 Q 22.921 16.521 23.904 17.087 Q 24.893 17.647 25.447 18.689 Q 26 19.724 26 21.046 L 26 21.482 Q 26 22.803 25.453 23.832 Q 24.913 24.861 23.923 25.427 Q 22.934 25.993 21.69 26 Z M 20.707 18.103 L 20.707 24.431 L 21.651 24.431 Q 22.797 24.431 23.402 23.682 Q 24.008 22.934 24.021 21.54 L 24.021 21.039 Q 24.021 19.594 23.422 18.852 Q 22.823 18.103 21.671 18.103 Z" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 897 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 805 B

View file

@ -0,0 +1,34 @@
[
{
"displayName": "Mikołaj Pich",
"githubUsername": "mklkj"
},
{
"displayName": "Rafał Borcz",
"githubUsername": "Faierbel"
},
{
"displayName": "Dominik Korsa",
"githubUsername": "dominik-korsa"
},
{
"displayName": "Kacper Ziubryniewicz",
"githubUsername": "kapi2289"
},
{
"displayName": "doteq",
"githubUsername": "doteq"
},
{
"displayName": "Pavuloff",
"githubUsername": "pavuloff"
},
{
"displayName": "Piotr Romanowski",
"githubUsername": "v0idzz"
},
{
"displayName": "Dinolek",
"githubUsername": "Dinolek"
}
]

View file

@ -11,8 +11,6 @@ import dagger.android.AndroidInjector
import dagger.android.support.DaggerApplication
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.utils.Log
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY
import io.github.wulkanowy.di.DaggerAppComponent
import io.github.wulkanowy.services.sync.SyncWorkerFactory
import io.github.wulkanowy.ui.base.ThemeManager
@ -35,9 +33,6 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider {
@Inject
lateinit var themeManager: ThemeManager
@Inject
lateinit var sharedPrefProvider: SharedPrefProvider
@Inject
lateinit var appInfo: AppInfo
@ -52,7 +47,6 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider {
RxJavaPlugins.setErrorHandler(::onError)
Lingver.init(this)
themeManager.applyDefaultTheme()
migrateSharedPreferences()
initLogging()
initCrashlytics(this, appInfo)
@ -68,13 +62,6 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider {
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
}
private fun migrateSharedPreferences() {
if (sharedPrefProvider.getLong(APP_VERSION_CODE_KEY, -1) < 48) { // #596
sharedPrefProvider.delete(getString(R.string.pref_key_grade_modifier_plus))
sharedPrefProvider.delete(getString(R.string.pref_key_grade_modifier_minus))
}
}
private fun onError(error: Throwable) {
//RxJava's too deep stack traces may cause SOE on older android devices
val cause = error.cause

View file

@ -1,35 +0,0 @@
package io.github.wulkanowy.data
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Student
import java.net.URL
import javax.inject.Inject
class ApiHelper @Inject constructor(private val api: Api) {
fun initApi(student: Student) {
api.apply {
email = student.email
password = student.password
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)
useNewStudent = true
}
}
fun initApi(email: String, password: String, symbol: String, endpoint: String) {
api.apply {
this.email = email
this.password = password
this.symbol = symbol
host = URL(endpoint).run { host + ":$port".removeSuffix(":-1") }
ssl = endpoint.startsWith("https")
useNewStudent = true
}
}
}

View file

@ -2,6 +2,7 @@ package io.github.wulkanowy.data
import android.content.Context
import android.content.SharedPreferences
import android.content.res.AssetManager
import android.content.res.Resources
import androidx.preference.PreferenceManager
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
@ -11,12 +12,10 @@ import com.readystatesoftware.chuck.api.ChuckInterceptor
import com.readystatesoftware.chuck.api.RetentionManager
import dagger.Module
import dagger.Provides
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import okhttp3.logging.HttpLoggingInterceptor
import okhttp3.logging.HttpLoggingInterceptor.Level.BASIC
import okhttp3.logging.HttpLoggingInterceptor.Level.NONE
import io.github.wulkanowy.sdk.Sdk
import timber.log.Timber
import javax.inject.Singleton
@ -33,15 +32,14 @@ internal class RepositoryModule {
@Singleton
@Provides
fun provideApi(chuckCollector: ChuckCollector, context: Context): Api {
return Api().apply {
logLevel = NONE
fun provideSdk(chuckCollector: ChuckCollector, context: Context): Sdk {
return Sdk().apply {
androidVersion = android.os.Build.VERSION.RELEASE
buildTag = android.os.Build.MODEL
setInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { Timber.d(it) }).setLevel(BASIC))
setSimpleHttpLogger { Timber.d(it) }
// for debug only
setInterceptor(ChuckInterceptor(context, chuckCollector).maxContentLength(250000L), true, 0)
addInterceptor(ChuckInterceptor(context, chuckCollector).maxContentLength(250000L), true)
}
}
@ -55,12 +53,16 @@ internal class RepositoryModule {
@Singleton
@Provides
fun provideDatabase(context: Context) = AppDatabase.newInstance(context)
fun provideDatabase(context: Context, sharedPrefProvider: SharedPrefProvider) = AppDatabase.newInstance(context, sharedPrefProvider)
@Singleton
@Provides
fun provideResources(context: Context): Resources = context.resources
@Singleton
@Provides
fun provideAssets(context: Context): AssetManager = context.assets
@Singleton
@Provides
fun provideSharedPref(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)

View file

@ -0,0 +1,30 @@
package io.github.wulkanowy.data
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import javax.inject.Inject
class SdkHelper @Inject constructor(private val sdk: Sdk) {
fun init(student: Student) {
sdk.apply {
email = student.email
password = student.password
symbol = student.symbol
schoolSymbol = student.schoolSymbol
studentId = student.studentId
classId = student.classId
if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
scrapperBaseUrl = student.scrapperBaseUrl
loginType = Sdk.ScrapperLoginType.valueOf(student.loginType)
}
loginId = student.userLoginId
mode = Sdk.Mode.valueOf(student.loginMode)
mobileBaseUrl = student.mobileBaseUrl
certKey = student.certificateKey
privateKey = student.privateKey
}
}
}

View file

@ -58,7 +58,10 @@ import io.github.wulkanowy.data.db.migrations.Migration15
import io.github.wulkanowy.data.db.migrations.Migration16
import io.github.wulkanowy.data.db.migrations.Migration17
import io.github.wulkanowy.data.db.migrations.Migration18
import io.github.wulkanowy.data.db.migrations.Migration19
import io.github.wulkanowy.data.db.migrations.Migration2
import io.github.wulkanowy.data.db.migrations.Migration20
import io.github.wulkanowy.data.db.migrations.Migration21
import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration4
import io.github.wulkanowy.data.db.migrations.Migration5
@ -100,9 +103,9 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
const val VERSION_SCHEMA = 18
const val VERSION_SCHEMA = 21
fun getMigrations(): Array<Migration> {
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
return arrayOf(
Migration2(),
Migration3(),
@ -120,16 +123,19 @@ abstract class AppDatabase : RoomDatabase() {
Migration15(),
Migration16(),
Migration17(),
Migration18()
Migration18(),
Migration19(sharedPrefProvider),
Migration20(),
Migration21()
)
}
fun newInstance(context: Context): AppDatabase {
fun newInstance(context: Context, sharedPrefProvider: SharedPrefProvider): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
.setJournalMode(TRUNCATE)
.fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1)
.fallbackToDestructiveMigrationOnDowngrade()
.addMigrations(*getMigrations())
.addMigrations(*getMigrations(sharedPrefProvider))
.build()
}
}

View file

@ -18,6 +18,12 @@ class SharedPrefProvider @Inject constructor(private val sharedPref: SharedPrefe
fun getLong(key: String, defaultValue: Long) = sharedPref.getLong(key, defaultValue)
fun getString(key: String, defaultValue: String): String = sharedPref.getString(key, defaultValue) ?: defaultValue
fun putString(key: String, value: String, sync: Boolean = false) {
sharedPref.edit(sync) { putString(key, value) }
}
fun delete(key: String) {
sharedPref.edit().remove(key).apply()
}

View file

@ -15,6 +15,9 @@ data class Attendance(
@ColumnInfo(name = "diary_id")
val diaryId: Int,
@ColumnInfo(name = "time_id")
val timeId: Int,
val date: LocalDate,
val number: Int,
@ -33,7 +36,13 @@ data class Attendance(
val excused: Boolean,
val deleted: Boolean
val deleted: Boolean,
val excusable: Boolean,
@ColumnInfo(name = "excuse_status")
val excuseStatus: String?
) : Serializable {
@PrimaryKey(autoGenerate = true)

View file

@ -19,7 +19,7 @@ data class Grade(
val entry: String,
val value: Int,
val value: Double,
val modifier: Double,

View file

@ -29,6 +29,8 @@ data class Message(
val subject: String,
var content: String,
val date: LocalDateTime,
@ColumnInfo(name = "folder_id")
@ -50,6 +52,4 @@ data class Message(
@ColumnInfo(name = "is_notified")
var isNotified: Boolean = true
var content: String? = null
}

View file

@ -10,10 +10,27 @@ import java.io.Serializable
@Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id", "class_id"], unique = true)])
data class Student(
val endpoint: String,
@ColumnInfo(name = "scrapper_base_url")
val scrapperBaseUrl: String,
@ColumnInfo(name = "mobile_base_url")
val mobileBaseUrl: String,
@ColumnInfo(name = "login_type")
val loginType: String,
@ColumnInfo(name = "login_mode")
val loginMode: String,
@ColumnInfo(name = "certificate_key")
val certificateKey: String,
@ColumnInfo(name = "private_key")
val privateKey: String,
@ColumnInfo(name = "is_parent")
val isParent: Boolean,
val email: String,
var password: String,
@ -23,6 +40,9 @@ data class Student(
@ColumnInfo(name = "student_id")
val studentId: Int,
@ColumnInfo(name = "user_login_id")
val userLoginId: Int,
@ColumnInfo(name = "student_name")
val studentName: String,

View file

@ -40,6 +40,9 @@ data class Timetable(
val info: String,
@ColumnInfo(name = "student_plan")
val studentPlan: Boolean,
val changes: Boolean,
val canceled: Boolean

View file

@ -0,0 +1,119 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import io.github.wulkanowy.data.db.SharedPrefProvider
class Migration19(private val sharedPrefProvider: SharedPrefProvider) : Migration(18, 19) {
override fun migrate(database: SupportSQLiteDatabase) {
migrateMessages(database)
migrateGrades(database)
migrateStudents(database)
migrateSharedPreferences()
}
private fun migrateMessages(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE Messages")
database.execSQL("""
CREATE TABLE IF NOT EXISTS Messages (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
is_notified INTEGER NOT NULL,
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,
content 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
)
""")
}
private fun migrateGrades(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE Grades")
database.execSQL("""
CREATE TABLE IF NOT EXISTS Grades (
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 REAL 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
)
""")
}
private fun migrateStudents(database: SupportSQLiteDatabase) {
database.execSQL("""
CREATE TABLE IF NOT EXISTS Students_tmp (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
scrapper_base_url TEXT NOT NULL,
mobile_base_url TEXT NOT NULL,
is_parent INTEGER NOT NULL,
login_type TEXT NOT NULL,
login_mode TEXT NOT NULL,
certificate_key TEXT NOT NULL,
private_key TEXT NOT NULL,
email TEXT NOT NULL,
password TEXT NOT NULL,
symbol TEXT NOT NULL,
student_id INTEGER NOT NULL,
user_login_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
)
""")
database.execSQL("ALTER TABLE Students ADD COLUMN scrapperBaseUrl TEXT NOT NULL DEFAULT \"\";")
database.execSQL("ALTER TABLE Students ADD COLUMN apiBaseUrl TEXT NOT NULL DEFAULT \"\";")
database.execSQL("ALTER TABLE Students ADD COLUMN is_parent INT NOT NULL DEFAULT 0;")
database.execSQL("ALTER TABLE Students ADD COLUMN loginMode TEXT NOT NULL DEFAULT \"\";")
database.execSQL("ALTER TABLE Students ADD COLUMN certificateKey TEXT NOT NULL DEFAULT \"\";")
database.execSQL("ALTER TABLE Students ADD COLUMN privateKey TEXT NOT NULL DEFAULT \"\";")
database.execSQL("ALTER TABLE Students ADD COLUMN user_login_id INTEGER NOT NULL DEFAULT 0;")
database.execSQL("""
INSERT INTO Students_tmp(
id, scrapper_base_url, mobile_base_url, is_parent, login_type, login_mode, certificate_key, private_key, email, password, symbol, student_id, user_login_id, student_name, school_id, school_name, school_id, school_name, class_name, class_id, is_current, registration_date)
SELECT
id, endpoint, apiBaseUrl, is_parent, loginType, "SCRAPPER", certificateKey, privateKey, email, password, symbol, student_id, user_login_id, student_name, school_id, school_name, school_id, school_name, class_name, class_id, is_current, registration_date
FROM Students
""")
database.execSQL("DROP TABLE Students")
database.execSQL("ALTER TABLE Students_tmp RENAME TO Students")
database.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students (email, symbol, student_id, school_id, class_id)")
}
private fun migrateSharedPreferences() {
if (sharedPrefProvider.getString("grade_modifier_plus", "0.0") == "0.0") {
sharedPrefProvider.putString("grade_modifier_plus", "0.33")
}
if (sharedPrefProvider.getString("grade_modifier_minus", "0.0") == "0.0") {
sharedPrefProvider.putString("grade_modifier_minus", "0.33")
}
}
}

View file

@ -0,0 +1,42 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration20 : Migration(19, 20) {
override fun migrate(database: SupportSQLiteDatabase) {
migrateTimetable(database)
truncateSubjects(database)
}
private fun migrateTimetable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE Timetable")
database.execSQL("""
CREATE TABLE IF NOT EXISTS `Timetable` (
`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,
`student_plan` INTEGER NOT NULL,
`changes` INTEGER NOT NULL,
`canceled` INTEGER NOT NULL
)
""")
}
private fun truncateSubjects(database: SupportSQLiteDatabase) {
database.execSQL("DELETE FROM Subjects")
}
}

View file

@ -0,0 +1,15 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration21 : Migration(20, 21) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Attendance ADD COLUMN excusable INTEGER NOT NULL DEFAULT 0")
database.execSQL("ALTER TABLE Attendance ADD COLUMN time_id INTEGER NOT NULL DEFAULT 0")
database.execSQL("ALTER TABLE Attendance ADD COLUMN excuse_status TEXT DEFAULT NULL")
database.execSQL("DELETE FROM Semesters")
}
}

View file

@ -0,0 +1,3 @@
package io.github.wulkanowy.data.pojos
class AppCreator(val displayName: String, val githubUsername: String)

View file

@ -0,0 +1,20 @@
package io.github.wulkanowy.data.repositories.appcreator
import android.content.res.AssetManager
import com.google.gson.Gson
import io.github.wulkanowy.data.pojos.AppCreator
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class AppCreatorRepository @Inject constructor(private val assets: AssetManager) {
fun getAppCreators(): Single<List<AppCreator>> {
return Single.fromCallable<List<AppCreator>> {
Gson().fromJson(
assets.open("creators.json").bufferedReader().use { it.readText() },
Array<AppCreator>::class.java
).toList()
}
}
}

View file

@ -1,25 +1,28 @@
package io.github.wulkanowy.data.repositories.attendance
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.toLocalDate
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Absent
import io.reactivex.Single
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDateTime
import org.threeten.bp.LocalTime
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class AttendanceRemote @Inject constructor(private val api: Api) {
class AttendanceRemote @Inject constructor(private val sdk: Sdk) {
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Attendance>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getAttendance(startDate, endDate) }.map { attendance ->
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getAttendance(startDate, endDate, semester.semesterId)
.map { attendance ->
attendance.map {
Attendance(
studentId = semester.studentId,
diaryId = semester.diaryId,
date = it.date.toLocalDate(),
date = it.date,
timeId = it.timeId,
number = it.number,
subject = it.subject,
name = it.name,
@ -28,9 +31,20 @@ class AttendanceRemote @Inject constructor(private val api: Api) {
exemption = it.exemption,
lateness = it.lateness,
excused = it.excused,
deleted = it.deleted
deleted = it.deleted,
excusable = it.excusable,
excuseStatus = it.excuseStatus?.name
)
}
}
}
fun excuseAbsence(semester: Semester, absenceList: List<Attendance>, reason: String?): Single<Boolean> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance ->
Absent(
date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)),
timeId = attendance.timeId
)
}, reason)
}
}

View file

@ -41,4 +41,8 @@ class AttendanceRepository @Inject constructor(
}).map { list -> list.filter { it.date in startDate..endDate } }
}
}
fun excuseForAbsence(semester: Semester, attendanceList: List<Attendance>, reason: String? = null): Single<Boolean> {
return remote.excuseAbsence(semester, attendanceList, reason)
}
}

View file

@ -0,0 +1,7 @@
package io.github.wulkanowy.data.repositories.attendance
enum class SentExcuseStatus(val id: Int = 0) {
WAITING,
ACCEPTED,
DENIED
}

View file

@ -1,18 +1,18 @@
package io.github.wulkanowy.data.repositories.attendancesummary
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class AttendanceSummaryRemote @Inject constructor(private val api: Api) {
class AttendanceSummaryRemote @Inject constructor(private val sdk: Sdk) {
fun getAttendanceSummary(semester: Semester, subjectId: Int): Single<List<AttendanceSummary>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { api.getAttendanceSummary(subjectId) }.map { attendance ->
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getAttendanceSummary(subjectId)
.map { attendance ->
attendance.map {
AttendanceSummary(
studentId = semester.studentId,

View file

@ -1,27 +1,25 @@
package io.github.wulkanowy.data.repositories.completedlessons
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.toLocalDate
import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class CompletedLessonsRemote @Inject constructor(private val api: Api) {
class CompletedLessonsRemote @Inject constructor(private val sdk: Sdk) {
fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<CompletedLesson>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getCompletedLessons(startDate, endDate) }
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getCompletedLessons(startDate, endDate)
.map { lessons ->
lessons.map {
it.absence
CompletedLesson(
studentId = semester.studentId,
diaryId = semester.diaryId,
date = it.date.toLocalDate(),
date = it.date,
number = it.number,
subject = it.subject,
topic = it.topic,

View file

@ -1,26 +1,25 @@
package io.github.wulkanowy.data.repositories.exam
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.toLocalDate
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ExamRemote @Inject constructor(private val api: Api) {
class ExamRemote @Inject constructor(private val sdk: Sdk) {
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Exam>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getExams(startDate, endDate) }.map { exams ->
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getExams(startDate, endDate, semester.semesterId)
.map { exams ->
exams.map {
Exam(
studentId = semester.studentId,
diaryId = semester.diaryId,
date = it.date.toLocalDate(),
entryDate = it.entryDate.toLocalDate(),
date = it.date,
entryDate = it.entryDate,
subject = it.subject,
group = it.group,
type = it.type,

View file

@ -1,35 +1,33 @@
package io.github.wulkanowy.data.repositories.grade
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.toLocalDate
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GradeRemote @Inject constructor(private val api: Api) {
class GradeRemote @Inject constructor(private val sdk: Sdk) {
fun getGrades(semester: Semester): Single<List<Grade>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getGrades(semester.semesterId) }
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getGrades(semester.semesterId)
.map { grades ->
grades.map {
Grade(
semesterId = semester.semesterId,
studentId = semester.studentId,
semesterId = semester.semesterId,
subject = it.subject,
entry = it.entry,
value = it.value,
modifier = it.modifier,
comment = it.comment,
color = it.color,
gradeSymbol = it.symbol.orEmpty(),
description = it.description.orEmpty(),
gradeSymbol = it.symbol,
description = it.description,
weight = it.weight,
weightValue = it.weightValue,
date = it.date.toLocalDate(),
date = it.date,
teacher = it.teacher
)
}

View file

@ -1,24 +1,23 @@
package io.github.wulkanowy.data.repositories.gradessummary
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GradeSummaryRemote @Inject constructor(private val api: Api) {
class GradeSummaryRemote @Inject constructor(private val sdk: Sdk) {
fun getGradeSummary(semester: Semester): Single<List<GradeSummary>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getGradesSummary(semester.semesterId) }
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getGradesSummary(semester.semesterId)
.map { gradesSummary ->
gradesSummary.map {
GradeSummary(
semesterId = semester.semesterId,
studentId = semester.studentId,
position = it.order,
position = 0,
subject = it.name,
predictedGrade = it.predicted,
finalGrade = it.final,

View file

@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.roundToDecimalPlaces
import io.reactivex.Maybe
import javax.inject.Inject
import javax.inject.Singleton
@ -40,9 +41,9 @@ class GradeStatisticsLocal @Inject constructor(
"Wszystkie" -> gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId).flatMap { list ->
if (list.isEmpty()) return@flatMap Maybe.empty<GradePointsStatistics>()
Maybe.just(GradePointsStatistics(semester.studentId, semester.semesterId, subjectName,
list.fold(.0) { acc, e -> acc + e.others },
list.fold(.0) { acc, e -> acc + e.student })
)
(list.fold(.0) { acc, e -> acc + e.others } / list.size).roundToDecimalPlaces(2),
(list.fold(.0) { acc, e -> acc + e.student } / list.size).roundToDecimalPlaces(2)
))
}
else -> gradePointsStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName)
}

View file

@ -1,39 +1,36 @@
package io.github.wulkanowy.data.repositories.gradestatistics
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GradeStatisticsRemote @Inject constructor(private val api: Api) {
class GradeStatisticsRemote @Inject constructor(private val sdk: Sdk) {
fun getGradeStatistics(semester: Semester, isSemester: Boolean): Single<List<GradeStatistics>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap {
if (isSemester) it.getGradesAnnualStatistics(semester.semesterId)
else it.getGradesPartialStatistics(semester.semesterId)
}
.map { gradeStatistics ->
gradeStatistics.map {
GradeStatistics(
semesterId = semester.semesterId,
studentId = semester.studentId,
subject = it.subject,
grade = it.gradeValue,
amount = it.amount ?: 0,
semester = isSemester
)
}
return sdk.switchDiary(semester.diaryId, semester.schoolYear).let {
if (isSemester) it.getGradesAnnualStatistics(semester.semesterId)
else it.getGradesPartialStatistics(semester.semesterId)
}.map { gradeStatistics ->
gradeStatistics.map {
GradeStatistics(
semesterId = semester.semesterId,
studentId = semester.studentId,
subject = it.subject,
grade = it.gradeValue,
amount = it.amount,
semester = isSemester
)
}
}
}
fun getGradePointsStatistics(semester: Semester): Single<List<GradePointsStatistics>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getGradesPointsStatistics(semester.semesterId) }
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getGradesPointsStatistics(semester.semesterId)
.map { gradePointsStatistics ->
gradePointsStatistics.map {
GradePointsStatistics(

View file

@ -1,27 +1,25 @@
package io.github.wulkanowy.data.repositories.homework
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.toLocalDate
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class HomeworkRemote @Inject constructor(private val api: Api) {
class HomeworkRemote @Inject constructor(private val sdk: Sdk) {
fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Homework>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getHomework(startDate, endDate) }
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getHomework(startDate, endDate)
.map { homework ->
homework.map {
Homework(
semesterId = semester.semesterId,
studentId = semester.studentId,
date = it.date.toLocalDate(),
entryDate = it.entryDate.toLocalDate(),
date = it.date,
entryDate = it.entryDate,
subject = it.subject,
content = it.content,
teacher = it.teacher,

View file

@ -1,20 +1,18 @@
package io.github.wulkanowy.data.repositories.luckynumber
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Maybe
import io.reactivex.Single
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class LuckyNumberRemote @Inject constructor(private val api: Api) {
class LuckyNumberRemote @Inject constructor(private val sdk: Sdk) {
fun getLuckyNumber(semester: Semester): Maybe<LuckyNumber> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMapMaybe { it.getLuckyNumber() }
return sdk.getLuckyNumber()
.map {
LuckyNumber(
studentId = semester.studentId,

View file

@ -1,23 +1,23 @@
package io.github.wulkanowy.data.repositories.message
import io.github.wulkanowy.api.Api
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.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Folder
import io.github.wulkanowy.sdk.pojo.SentMessage
import io.reactivex.Single
import org.threeten.bp.LocalDateTime.now
import javax.inject.Inject
import javax.inject.Singleton
import io.github.wulkanowy.api.messages.Recipient as ApiRecipient
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
@Singleton
class MessageRemote @Inject constructor(private val api: Api) {
class MessageRemote @Inject constructor(private val sdk: Sdk) {
fun getMessages(student: Student, folder: MessageFolder): Single<List<Message>> {
return api.getMessages(Folder.valueOf(folder.name)).map { messages ->
fun getMessages(student: Student, semester: Semester, folder: MessageFolder): Single<List<Message>> {
return sdk.getMessages(Folder.valueOf(folder.name), semester.start.atStartOfDay(), semester.end.atStartOfDay()).map { messages ->
messages.map {
Message(
studentId = student.id.toInt(),
@ -27,7 +27,8 @@ class MessageRemote @Inject constructor(private val api: Api) {
senderId = it.senderId ?: 0,
recipient = it.recipient.orEmpty(),
subject = it.subject.trim(),
date = it.date?.toLocalDateTime() ?: now(),
date = it.date ?: now(),
content = it.content.orEmpty(),
folderId = it.folderId,
unread = it.unread ?: false,
unreadBy = it.unreadBy ?: 0,
@ -39,27 +40,28 @@ class MessageRemote @Inject constructor(private val api: Api) {
}
fun getMessagesContent(message: Message, markAsRead: Boolean = false): Single<String> {
return api.getMessageContent(message.messageId, message.folderId, markAsRead, message.realId)
return sdk.getMessageContent(message.messageId, message.folderId, markAsRead, message.realId)
}
fun sendMessage(subject: String, content: String, recipients: List<Recipient>): Single<SentMessage> {
return api.sendMessage(
return sdk.sendMessage(
subject = subject,
content = content,
recipients = recipients.map {
ApiRecipient(
SdkRecipient(
id = it.realId,
name = it.realName,
loginId = it.loginId,
reportingUnitId = it.unitId,
role = it.role,
hash = it.hash
hash = it.hash,
shortName = it.name
)
}
)
}
fun deleteMessage(message: Message): Single<Boolean> {
return api.deleteMessages(listOf(Pair(message.realId, message.folderId)))
return sdk.deleteMessages(listOf(Pair(message.realId, message.folderId)))
}
}

View file

@ -2,12 +2,13 @@ package io.github.wulkanowy.data.repositories.message
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.api.messages.SentMessage
import io.github.wulkanowy.data.ApiHelper
import io.github.wulkanowy.data.SdkHelper
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
import io.github.wulkanowy.sdk.pojo.SentMessage
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Completable
import io.reactivex.Maybe
@ -21,16 +22,16 @@ class MessageRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: MessageLocal,
private val remote: MessageRemote,
private val apiHelper: ApiHelper
private val sdkHelper: SdkHelper
) {
fun getMessages(student: Student, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): Single<List<Message>> {
return Single.just(apiHelper.initApi(student))
fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): Single<List<Message>> {
return Single.just(sdkHelper.init(student))
.flatMap { _ ->
local.getMessages(student, folder).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getMessages(student, folder)
if (it) remote.getMessages(student, semester, folder)
else Single.error(UnknownHostException())
}.flatMap { new ->
local.getMessages(student, folder).toSingle(emptyList())
@ -47,10 +48,10 @@ class MessageRepository @Inject constructor(
}
fun getMessage(student: Student, messageDbId: Long, markAsRead: Boolean = false): Single<Message> {
return Single.just(apiHelper.initApi(student))
return Single.just(sdkHelper.init(student))
.flatMap { _ ->
local.getMessage(messageDbId)
.filter { !it.content.isNullOrEmpty() }
.filter { it.content.isNotEmpty() }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) local.getMessage(messageDbId).toSingle()
@ -60,7 +61,7 @@ class MessageRepository @Inject constructor(
remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess {
local.updateMessages(listOf(dbMessage.copy(unread = false).apply {
id = dbMessage.id
content = it
content = content.ifBlank { it }
}))
}
}.flatMap {

View file

@ -1,25 +1,23 @@
package io.github.wulkanowy.data.repositories.mobiledevice
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class MobileDeviceRemote @Inject constructor(private val api: Api) {
class MobileDeviceRemote @Inject constructor(private val sdk: Sdk) {
fun getDevices(semester: Semester): Single<List<MobileDevice>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { api.getRegisteredDevices() }
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getRegisteredDevices()
.map { devices ->
devices.map {
MobileDevice(
studentId = semester.studentId,
date = it.date.toLocalDateTime(),
date = it.date,
deviceId = it.id,
name = it.name
)
@ -28,13 +26,11 @@ class MobileDeviceRemote @Inject constructor(private val api: Api) {
}
fun unregisterDevice(semester: Semester, device: MobileDevice): Single<Boolean> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { api.unregisterDevice(device.deviceId) }
return sdk.switchDiary(semester.diaryId, semester.schoolYear).unregisterDevice(device.deviceId)
}
fun getToken(semester: Semester): Single<MobileDeviceToken> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { api.getToken() }
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getToken()
.map {
MobileDeviceToken(
token = it.token,

View file

@ -1,24 +1,22 @@
package io.github.wulkanowy.data.repositories.note
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.toLocalDate
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class NoteRemote @Inject constructor(private val api: Api) {
class NoteRemote @Inject constructor(private val sdk: Sdk) {
fun getNotes(semester: Semester): Single<List<Note>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getNotes() }
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getNotes(semester.semesterId)
.map { notes ->
notes.map {
Note(
studentId = semester.studentId,
date = it.date.toLocalDate(),
date = it.date,
teacher = it.teacher,
category = it.category,
content = it.content

View file

@ -65,6 +65,9 @@ class PreferencesRepository @Inject constructor(
val fillMessageContent: Boolean
get() = getBoolean(R.string.pref_key_fill_message_content, R.bool.pref_default_fill_message_content)
val showWholeClassPlan: String
get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class)
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
private fun getString(id: String, default: Int) = sharedPref.getString(id, context.getString(default)) ?: context.getString(default)

View file

@ -1,34 +1,34 @@
package io.github.wulkanowy.data.repositories.recipient
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
import io.github.wulkanowy.api.messages.Recipient as ApiRecipient
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
@Singleton
class RecipientRemote @Inject constructor(private val api: Api) {
class RecipientRemote @Inject constructor(private val sdk: Sdk) {
fun getRecipients(role: Int, unit: ReportingUnit): Single<List<Recipient>> {
return api.getRecipients(unit.realId, role)
return sdk.getRecipients(unit.realId, role)
.map { recipients ->
recipients.map { it.toRecipient() }
}
}
fun getMessageRecipients(message: Message): Single<List<Recipient>> {
return api.getMessageRecipients(message.messageId, message.senderId)
return sdk.getMessageRecipients(message.messageId, message.senderId)
.map { recipients ->
recipients.map { it.toRecipient() }
}
}
private fun ApiRecipient.toRecipient(): Recipient {
private fun SdkRecipient.toRecipient(): Recipient {
return Recipient(
studentId = api.studentId,
studentId = sdk.studentId,
realId = id,
realName = name,
name = shortName,

View file

@ -2,7 +2,7 @@ package io.github.wulkanowy.data.repositories.recipient
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.SdkHelper
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit
@ -18,11 +18,11 @@ class RecipientRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: RecipientLocal,
private val remote: RecipientRemote,
private val apiHelper: ApiHelper
private val sdkHelper: SdkHelper
) {
fun getRecipients(student: Student, role: Int, unit: ReportingUnit, forceRefresh: Boolean = false): Single<List<Recipient>> {
return Single.just(apiHelper.initApi(student))
return Single.just(sdkHelper.init(student))
.flatMap { _ ->
local.getRecipients(student, role, unit).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
@ -43,7 +43,7 @@ class RecipientRepository @Inject constructor(
}
fun getMessageRecipients(student: Student, message: Message): Single<List<Recipient>> {
return Single.just(apiHelper.initApi(student))
return Single.just(sdkHelper.init(student))
.flatMap { ReactiveNetwork.checkInternetConnectivity(settings) }
.flatMap {
if (it) remote.getMessageRecipients(message)

View file

@ -1,19 +1,19 @@
package io.github.wulkanowy.data.repositories.reportingunit
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ReportingUnitRemote @Inject constructor(private val api: Api) {
class ReportingUnitRemote @Inject constructor(private val sdk: Sdk) {
fun getReportingUnits(): Single<List<ReportingUnit>> {
return api.getReportingUnits().map {
return sdk.getReportingUnits().map {
it.map { unit ->
ReportingUnit(
studentId = api.studentId,
studentId = sdk.studentId,
realId = unit.id,
roles = unit.roles,
senderId = unit.senderId,

View file

@ -2,7 +2,7 @@ package io.github.wulkanowy.data.repositories.reportingunit
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.SdkHelper
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract
@ -17,11 +17,11 @@ class ReportingUnitRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: ReportingUnitLocal,
private val remote: ReportingUnitRemote,
private val apiHelper: ApiHelper
private val sdkHelper: SdkHelper
) {
fun getReportingUnits(student: Student, forceRefresh: Boolean = false): Single<List<ReportingUnit>> {
return Single.just(apiHelper.initApi(student))
return Single.just(sdkHelper.init(student))
.flatMap { _ ->
local.getReportingUnits(student).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
@ -40,7 +40,7 @@ class ReportingUnitRepository @Inject constructor(
}
fun getReportingUnit(student: Student, unitId: Int): Maybe<ReportingUnit> {
return Maybe.just(apiHelper.initApi(student))
return Maybe.just(sdkHelper.init(student))
.flatMap { _ ->
local.getReportingUnit(student, unitId)
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)

View file

@ -1,16 +1,15 @@
package io.github.wulkanowy.data.repositories.school
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import javax.inject.Inject
class SchoolRemote @Inject constructor(private val api: Api) {
class SchoolRemote @Inject constructor(private val sdk: Sdk) {
fun getSchoolInfo(semester: Semester): Single<School> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getSchool() }
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getSchool()
.map {
School(
studentId = semester.studentId,

View file

@ -1,35 +1,32 @@
package io.github.wulkanowy.data.repositories.semester
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SemesterRemote @Inject constructor(private val api: Api) {
class SemesterRemote @Inject constructor(private val sdk: Sdk) {
fun getSemesters(student: Student): Single<List<Semester>> {
return api.getSemesters().map { semesters ->
semesters.map { semester ->
return sdk.getSemesters().map { semesters ->
semesters.map {
Semester(
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
diaryId = it.diaryId,
diaryName = it.diaryName,
schoolYear = it.schoolYear,
semesterId = it.semesterId,
semesterName = it.semesterNumber,
isCurrent = it.current,
start = it.start,
end = it.end,
classId = it.classId,
unitId = it.unitId
)
}
}
}
}

View file

@ -2,7 +2,7 @@ package io.github.wulkanowy.data.repositories.semester
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.SdkHelper
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract
@ -18,11 +18,11 @@ class SemesterRepository @Inject constructor(
private val remote: SemesterRemote,
private val local: SemesterLocal,
private val settings: InternetObservingSettings,
private val apiHelper: ApiHelper
private val sdkHelper: SdkHelper
) {
fun getSemesters(student: Student, forceRefresh: Boolean = false): Single<List<Semester>> {
return Maybe.just(apiHelper.initApi(student))
return Maybe.just(sdkHelper.init(student))
.flatMap { local.getSemesters(student).filter { !forceRefresh } }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
@ -36,7 +36,7 @@ class SemesterRepository @Inject constructor(
local.saveSemesters(new.uniqueSubtract(old))
}
} else {
Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}")
Timber.i("Current semesters list:\n${new.joinToString(separator = "\n")}")
throw IllegalArgumentException("Current semester can be only one.")
}
}.flatMap { local.getSemesters(student).toSingle(emptyList()) })

View file

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.student
import android.content.Context
import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.security.decrypt
import io.github.wulkanowy.utils.security.encrypt
import io.reactivex.Completable
@ -18,7 +19,12 @@ class StudentLocal @Inject constructor(
) {
fun saveStudents(students: List<Student>): Single<List<Long>> {
return Single.fromCallable { studentDb.insertAll(students.map { it.copy(password = encrypt(it.password, context)) }) }
return Single.fromCallable {
studentDb.insertAll(students.map {
if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) it.copy(password = encrypt(it.password, context))
else it
})
}
}
fun getStudents(decryptPass: Boolean): Maybe<List<Student>> {
@ -28,7 +34,11 @@ class StudentLocal @Inject constructor(
}
fun getCurrentStudent(decryptPass: Boolean): Maybe<Student> {
return studentDb.loadCurrent().map { it.apply { if (decryptPass) password = decrypt(password) } }
return studentDb.loadCurrent().map {
it.apply {
if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
}
}
}
fun setCurrentStudent(student: Student): Completable {

View file

@ -1,34 +1,51 @@
package io.github.wulkanowy.data.repositories.student
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import org.threeten.bp.LocalDateTime.now
import javax.inject.Inject
import javax.inject.Singleton
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
@Singleton
class StudentRemote @Inject constructor(private val api: Api) {
class StudentRemote @Inject constructor(private val sdk: Sdk) {
fun getStudents(email: String, password: String, endpoint: String): Single<List<Student>> {
return api.getStudents().map { students ->
students.map { student ->
Student(
email = email,
password = password,
symbol = student.symbol,
studentId = student.studentId,
studentName = student.studentName,
schoolSymbol = student.schoolSymbol,
schoolName = student.schoolName,
className = student.className,
classId = student.classId,
endpoint = endpoint,
loginType = student.loginType.name,
isCurrent = false,
registrationDate = now()
)
}
private fun mapStudents(students: List<SdkStudent>, email: String, password: String): List<Student> {
return students.map { student ->
Student(
email = email,
password = password,
isParent = student.isParent,
symbol = student.symbol,
studentId = student.studentId,
userLoginId = student.userLoginId,
studentName = student.studentName,
schoolSymbol = student.schoolSymbol,
schoolName = student.schoolName,
className = student.className,
classId = student.classId,
scrapperBaseUrl = student.scrapperBaseUrl,
loginType = student.loginType.name,
isCurrent = false,
registrationDate = now(),
mobileBaseUrl = student.mobileBaseUrl,
privateKey = student.privateKey,
certificateKey = student.certificateKey,
loginMode = student.loginMode.name
)
}
}
fun getStudentsMobileApi(token: String, pin: String, symbol: String): Single<List<Student>> {
return sdk.getStudentsFromMobileApi(token, pin, symbol).map { mapStudents(it, "", "") }
}
fun getStudentsScrapper(email: String, password: String, scrapperBaseUrl: String, symbol: String): Single<List<Student>> {
return sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol).map { mapStudents(it, email, password) }
}
fun getStudentsHybrid(email: String, password: String, scrapperBaseUrl: String, symbol: String): Single<List<Student>> {
return sdk.getStudentsHybrid(email, password, scrapperBaseUrl, symbol).map { mapStudents(it, email, password) }
}
}

View file

@ -2,7 +2,6 @@ package io.github.wulkanowy.data.repositories.student
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
@ -16,21 +15,32 @@ import javax.inject.Singleton
class StudentRepository @Inject constructor(
private val local: StudentLocal,
private val remote: StudentRemote,
private val settings: InternetObservingSettings,
private val apiHelper: ApiHelper
private val settings: InternetObservingSettings
) {
fun isStudentSaved(): Single<Boolean> = local.getStudents(false).isEmpty.map { !it }
fun isCurrentStudentSet(): Single<Boolean> = local.getCurrentStudent(false).isEmpty.map { !it }
fun getStudents(email: String, password: String, endpoint: String, symbol: String = ""): Single<List<Student>> {
return ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
apiHelper.initApi(email, password, symbol, endpoint)
if (it) remote.getStudents(email, password, endpoint)
else Single.error(UnknownHostException("No internet connection"))
}
fun getStudentsApi(pin: String, symbol: String, token: String): Single<List<Student>> {
return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
if (it) remote.getStudentsMobileApi(token, pin, symbol)
else Single.error(UnknownHostException("No internet connection"))
}
}
fun getStudentsScrapper(email: String, password: String, endpoint: String, symbol: String = ""): Single<List<Student>> {
return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
if (it) remote.getStudentsScrapper(email, password, endpoint, symbol)
else Single.error(UnknownHostException("No internet connection"))
}
}
fun getStudentsHybrid(email: String, password: String, endpoint: String, symbol: String): Single<List<Student>> {
return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
if (it) remote.getStudentsHybrid(email, password, endpoint, symbol)
else Single.error(UnknownHostException("No internet connection"))
}
}
fun getSavedStudents(decryptPass: Boolean = true): Single<List<Student>> {

View file

@ -1,25 +1,24 @@
package io.github.wulkanowy.data.repositories.subject
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SubjectRemote @Inject constructor(private val api: Api) {
class SubjectRemote @Inject constructor(private val sdk: Sdk) {
fun getSubjects(semester: Semester): Single<List<Subject>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { api.getSubjects() }
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getSubjects()
.map { subjects ->
subjects.map {
Subject(
studentId = semester.studentId,
diaryId = semester.diaryId,
name = it.name,
realId = it.value
realId = it.id
)
}
}

View file

@ -1,18 +1,17 @@
package io.github.wulkanowy.data.repositories.teacher
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Teacher
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class TeacherRemote @Inject constructor(private val api: Api) {
class TeacherRemote @Inject constructor(private val sdk: Sdk) {
fun getTeachers(semester: Semester): Single<List<Teacher>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getTeachers() }
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getTeachers(semester.semesterId)
.map { teachers ->
teachers.map {
Teacher(

View file

@ -1,30 +1,27 @@
package io.github.wulkanowy.data.repositories.timetable
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.utils.toLocalDate
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class TimetableRemote @Inject constructor(private val api: Api) {
class TimetableRemote @Inject constructor(private val sdk: Sdk) {
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Timetable>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getTimetable(startDate, endDate) }
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getTimetable(startDate, endDate)
.map { lessons ->
lessons.map {
Timetable(
studentId = semester.studentId,
diaryId = semester.diaryId,
number = it.number,
start = it.start.toLocalDateTime(),
end = it.end.toLocalDateTime(),
date = it.date.toLocalDate(),
start = it.start,
end = it.end,
date = it.date,
subject = it.subject,
subjectOld = it.subjectOld,
group = it.group,
@ -33,6 +30,7 @@ class TimetableRemote @Inject constructor(private val api: Api) {
teacher = it.teacher,
teacherOld = it.teacherOld,
info = it.info,
studentPlan = it.studentPlan,
changes = it.changes,
canceled = it.canceled
)

View file

@ -36,7 +36,7 @@ class TimetableRepository @Inject constructor(
old.singleOrNull { new.start == it.start }?.let { old ->
return@map new.copy(
room = if (new.room.isEmpty()) old.room else new.room,
teacher = if (new.teacher.isEmpty() && !new.changes) old.teacher else new.teacher
teacher = if (new.teacher.isEmpty() && !new.changes && !old.changes) old.teacher else new.teacher
)
}
}

View file

@ -11,10 +11,10 @@ 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
import io.github.wulkanowy.sdk.exception.FeatureDisabledException
import io.github.wulkanowy.services.sync.channels.DebugChannel
import io.github.wulkanowy.services.sync.works.Work
import io.github.wulkanowy.utils.getCompatColor
@ -78,4 +78,3 @@ class SyncWorker @AssistedInject constructor(
fun create(appContext: Context, workerParameters: WorkerParameters): ListenableWorker
}
}

View file

@ -30,7 +30,7 @@ class MessageWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return messageRepository.getMessages(student, RECEIVED, true, preferencesRepository.isNotificationsEnable)
return messageRepository.getMessages(student, semester, RECEIVED, true, preferencesRepository.isNotificationsEnable)
.flatMap { messageRepository.getNotNotifiedMessages(student) }
.flatMapCompletable {
if (it.isNotEmpty()) notify(it)

View file

@ -1,10 +1,10 @@
package io.github.wulkanowy.ui.base
import android.app.ActivityManager
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.LOLLIPOP
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.LOLLIPOP
import android.os.Bundle
import android.view.View
import android.widget.Toast

View file

@ -3,11 +3,12 @@ package io.github.wulkanowy.ui.base
import android.content.res.Resources
import com.readystatesoftware.chuck.api.ChuckCollector
import io.github.wulkanowy.R
import io.github.wulkanowy.api.interceptor.FeatureDisabledException
import io.github.wulkanowy.api.interceptor.ServiceUnavailableException
import io.github.wulkanowy.api.login.BadCredentialsException
import io.github.wulkanowy.api.login.NotLoggedInException
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.sdk.exception.BadCredentialsException
import io.github.wulkanowy.sdk.exception.FeatureDisabledException
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
import io.github.wulkanowy.sdk.exception.NotLoggedInException
import io.github.wulkanowy.sdk.exception.ServiceUnavailableException
import io.github.wulkanowy.utils.security.ScramblerException
import timber.log.Timber
import java.net.SocketTimeoutException
@ -38,6 +39,7 @@ open class ErrorHandler @Inject constructor(protected val resources: Resources,
is FeatureDisabledException -> showErrorMessage(getString(R.string.error_feature_disabled), error)
is ScramblerException, is BadCredentialsException -> onSessionExpired()
is NoCurrentStudentException -> onNoCurrentStudent()
is FeatureNotAvailableException -> showErrorMessage(getString(R.string.error_feature_not_available), error)
else -> showErrorMessage(getString(R.string.error_unknown), error)
}
}

View file

@ -16,6 +16,7 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
@ -42,6 +43,11 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
Triple(getString(R.string.about_version), "${appInfo.versionName} (${appInfo.versionCode})", getCompatDrawable(R.drawable.ic_all_about))
}
override val creatorsRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_creator), getString(R.string.about_creator_summary), getCompatDrawable(R.drawable.ic_about_creator))
}
override val feedbackRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_feedback), getString(R.string.about_feedback_summary), getCompatDrawable(R.drawable.ic_about_feedback))
@ -143,6 +149,10 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
(activity as? MainActivity)?.pushView(LicenseFragment.newInstance())
}
override fun openCreators() {
(activity as? MainActivity)?.pushView(CreatorFragment.newInstance())
}
override fun openPrivacyPolicy() {
context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage)
}

View file

@ -52,6 +52,11 @@ class AboutPresenter @Inject constructor(
openLicenses()
analytics.logEvent("about_open", "name" to "licenses")
}
creatorsRes?.first -> {
Timber.i("Opening creators view")
openCreators()
analytics.logEvent("about_open", "name" to "creators")
}
privacyRes?.first -> {
Timber.i("Opening privacy page ")
openPrivacyPolicy()
@ -65,6 +70,7 @@ class AboutPresenter @Inject constructor(
view?.run {
updateData(AboutScrollableHeader(), listOfNotNull(
versionRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
creatorsRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
feedbackRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
faqRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
discordRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },

View file

@ -7,6 +7,8 @@ interface AboutView : BaseView {
val versionRes: Triple<String, String, Drawable?>?
val creatorsRes: Triple<String, String, Drawable?>?
val feedbackRes: Triple<String, String, Drawable?>?
val faqRes: Triple<String, String, Drawable?>?
@ -33,5 +35,7 @@ interface AboutView : BaseView {
fun openLicenses()
fun openCreators()
fun openPrivacyPolicy()
}

View file

@ -0,0 +1,76 @@
package io.github.wulkanowy.ui.modules.about.creator
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_creator.*
import javax.inject.Inject
class CreatorFragment : BaseFragment(), CreatorView, MainView.TitledView {
@Inject
lateinit var presenter: CreatorPresenter
@Inject
lateinit var creatorsAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
override val titleStringId get() = R.string.creators_title
companion object {
fun newInstance() = CreatorFragment()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_creator, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
presenter.onAttachView(this)
}
override fun initView() {
with(creatorRecycler) {
layoutManager = SmoothScrollLinearLayoutManager(context)
adapter = creatorsAdapter
addItemDecoration(FlexibleItemDecoration(context)
.withDefaultDivider()
.withDrawDividerOnLastItem(false))
}
creatorsAdapter.setOnItemClickListener(presenter::onItemSelected)
creatorSeeMore.setOnClickListener { presenter.onSeeMoreClick() }
}
override fun updateData(data: List<CreatorItem>) {
creatorsAdapter.updateDataSet(data)
}
override fun openUserGithubPage(username: String) {
context?.openInternetBrowser("https://github.com/${username}", ::showMessage)
}
override fun openGithubContributorsPage() {
context?.openInternetBrowser("https://github.com/wulkanowy/wulkanowy/graphs/contributors", ::showMessage)
}
override fun showProgress(show: Boolean) {
creatorProgress.visibility = if (show) VISIBLE else GONE
}
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
}
}

View file

@ -0,0 +1,50 @@
package io.github.wulkanowy.ui.modules.about.creator
import android.view.View
import coil.api.load
import coil.transform.RoundedCornersTransformation
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.pojos.AppCreator
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_creator.*
class CreatorItem(val creator: AppCreator) : AbstractFlexibleItem<CreatorItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_creator
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
with(holder) {
creatorItemName.text = creator.displayName
creatorItemAvatar.load("https://github.com/${creator.githubUsername}.png") {
transformations(RoundedCornersTransformation(8f))
crossfade(true)
}
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as CreatorItem
if (creator != other.creator) return false
return true
}
override fun hashCode() = creator.hashCode()
class ViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View? get() = contentView
}
}

View file

@ -0,0 +1,43 @@
package io.github.wulkanowy.ui.modules.about.creator
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.pojos.AppCreator
import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Single
import javax.inject.Inject
class CreatorPresenter @Inject constructor(
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val appCreatorRepository: AppCreatorRepository
) : BasePresenter<CreatorView>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: CreatorView) {
super.onAttachView(view)
view.initView()
loadData()
}
fun onItemSelected(item: AbstractFlexibleItem<*>) {
if (item !is CreatorItem) return
view?.openUserGithubPage(item.creator.githubUsername)
}
fun onSeeMoreClick() {
view?.openGithubContributorsPage()
}
private fun loadData() {
disposable.add(appCreatorRepository.getAppCreators()
.map { it.map { creator -> CreatorItem(creator) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally { view?.showProgress(false) }
.subscribe({ view?.run { updateData(it) } }, { errorHandler.dispatch(it) }))
}
}

View file

@ -0,0 +1,16 @@
package io.github.wulkanowy.ui.modules.about.creator
import io.github.wulkanowy.ui.base.BaseView
interface CreatorView : BaseView {
fun initView()
fun updateData(data: List<CreatorItem>)
fun openUserGithubPage(username: String)
fun openGithubContributorsPage()
fun showProgress(show: Boolean)
}

View file

@ -0,0 +1,12 @@
package io.github.wulkanowy.ui.modules.attendance
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import io.github.wulkanowy.data.db.entities.Attendance
class AttendanceAdapter<T : IFlexible<*>> : FlexibleAdapter<T>(null, null, true) {
var excuseActionMode: Boolean = false
var onExcuseCheckboxSelect: (attendanceItem: Attendance, checked: Boolean) -> Unit = { _, _ -> }
}

View file

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.attendance
import android.content.DialogInterface.BUTTON_POSITIVE
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
@ -10,8 +11,9 @@ import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.view.ActionMode
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
@ -24,6 +26,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.dialog_excuse.*
import kotlinx.android.synthetic.main.fragment_attendance.*
import org.threeten.bp.LocalDate
import javax.inject.Inject
@ -35,7 +38,13 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
lateinit var presenter: AttendancePresenter
@Inject
lateinit var attendanceAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
lateinit var attendanceAdapter: AttendanceAdapter<AbstractFlexibleItem<*>>
override val excuseSuccessString: String
get() = getString(R.string.attendance_excuse_success)
override val excuseNoSelectionString: String
get() = getString(R.string.attendance_excuse_no_selection)
companion object {
private const val SAVED_DATE_KEY = "CURRENT_DATE"
@ -49,6 +58,34 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize
override val excuseActionMode: Boolean get() = attendanceAdapter.excuseActionMode
private var actionMode: ActionMode? = null
private val actionModeCallback = object : ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
val inflater = mode.menuInflater
inflater.inflate(R.menu.context_menu_excuse, menu)
return true
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
mode.title = getString(R.string.attendance_excuse_title)
return presenter.onPrepareActionMode()
}
override fun onDestroyActionMode(mode: ActionMode) {
presenter.onDestroyActionMode()
actionMode = null
}
override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean {
return when (menu.itemId) {
R.id.excuseMenuSubmit -> presenter.onExcuseSubmitButtonClick()
else -> false
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
@ -66,6 +103,7 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
override fun initView() {
attendanceAdapter.setOnItemClickListener(presenter::onAttendanceItemSelected)
attendanceAdapter.onExcuseCheckboxSelect = presenter::onExcuseCheckboxSelect
with(attendanceRecycler) {
layoutManager = SmoothScrollLinearLayoutManager(context)
@ -83,6 +121,8 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
attendanceNavDate.setOnClickListener { presenter.onPickDate() }
attendanceNextButton.setOnClickListener { presenter.onNextDay() }
attendanceExcuseButton.setOnClickListener { presenter.onExcuseButtonClick() }
attendanceNavContainer.setElevationCompat(requireContext().dpToPx(8f))
}
@ -115,6 +155,10 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
if (::presenter.isInitialized) presenter.onViewReselected()
}
override fun onFragmentChanged() {
if (::presenter.isInitialized) presenter.onMainViewChanged()
}
override fun popView() {
(activity as? MainActivity)?.popView()
}
@ -155,6 +199,10 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
attendanceNextButton.visibility = if (show) VISIBLE else INVISIBLE
}
override fun showExcuseButton(show: Boolean) {
attendanceExcuseButton.visibility = if (show) VISIBLE else GONE
}
override fun showAttendanceDialog(lesson: Attendance) {
(activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson))
}
@ -174,10 +222,38 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
}
}
override fun showExcuseDialog() {
AlertDialog.Builder(requireContext())
.setTitle(R.string.attendance_excuse_title)
.setView(R.layout.dialog_excuse)
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.create()
.apply {
setButton(BUTTON_POSITIVE, getString(R.string.attendance_excuse_dialog_submit)) { _, _ ->
presenter.onExcuseDialogSubmit(excuseReason.text?.toString().orEmpty())
}
}.show()
}
override fun openSummaryView() {
(activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance())
}
override fun startActionMode() {
actionMode = (activity as MainActivity?)?.startSupportActionMode(actionModeCallback)
}
override fun showExcuseCheckboxes(show: Boolean) {
attendanceAdapter.apply {
excuseActionMode = show
notifyDataSetChanged()
}
}
override fun finishActionMode() {
actionMode?.finish()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())

View file

@ -1,18 +1,22 @@
package io.github.wulkanowy.ui.modules.attendance
import android.view.View
import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import androidx.core.view.isVisible
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.Attendance
import io.github.wulkanowy.data.repositories.attendance.SentExcuseStatus
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_attendance.*
class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
class AttendanceItem(val attendance: Attendance) :
AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_attendance
@ -26,6 +30,34 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<Attendan
attendanceItemSubject.text = attendance.subject
attendanceItemDescription.text = attendance.name
attendanceItemAlert.visibility = attendance.run { if (absence && !excused) VISIBLE else INVISIBLE }
attendanceItemNumber.visibility = GONE
attendanceItemExcuseInfo.visibility = GONE
attendanceItemExcuseCheckbox.visibility = GONE
attendanceItemExcuseCheckbox.isChecked = false
attendanceItemExcuseCheckbox.setOnCheckedChangeListener { _, checked ->
(adapter as AttendanceAdapter).onExcuseCheckboxSelect(attendance, checked)
}
when (if (attendance.excuseStatus != null) SentExcuseStatus.valueOf(attendance.excuseStatus) else null) {
SentExcuseStatus.WAITING -> {
attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_waiting)
attendanceItemExcuseInfo.visibility = VISIBLE
attendanceItemAlert.visibility = INVISIBLE
}
SentExcuseStatus.DENIED -> {
attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_denied)
attendanceItemExcuseInfo.visibility = VISIBLE
}
else -> {
if (attendance.excusable && (adapter as AttendanceAdapter).excuseActionMode) {
attendanceItemNumber.visibility = GONE
attendanceItemExcuseCheckbox.visibility = VISIBLE
} else {
attendanceItemNumber.visibility = VISIBLE
attendanceItemExcuseCheckbox.visibility = GONE
}
}
}
}
}
@ -46,8 +78,20 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<Attendan
return result
}
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
class ViewHolder(view: View, val adapter: FlexibleAdapter<*>) :
FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View
get() = contentView
override fun onClick(view: View?) {
super.onClick(view)
attendanceItemExcuseCheckbox.apply {
if ((adapter as AttendanceAdapter).excuseActionMode && isVisible) {
isChecked = !isChecked
}
}
}
}
}

View file

@ -0,0 +1,12 @@
package io.github.wulkanowy.ui.modules.attendance
import dagger.Module
import dagger.Provides
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
@Module
class AttendanceModule {
@Provides
fun provideAttendanceFlexibleAdapter() = AttendanceAdapter<AbstractFlexibleItem<*>>()
}

Some files were not shown because too many files have changed in this diff Show more