Merge branch 'release/0.14.0'

This commit is contained in:
Mikołaj Pich 2019-12-25 16:52:28 +01:00
commit 4f60673e4e
96 changed files with 3343 additions and 601 deletions

View File

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

View File

@ -46,7 +46,7 @@ You can also download a [development version](https://wulkanowy.github.io/#downl
## Built With ## Built With
* [Wulkanowy API](https://github.com/wulkanowy/api) * [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
* [RxJava 2](https://github.com/ReactiveX/RxJava) * [RxJava 2](https://github.com/ReactiveX/RxJava)
* [Dagger 2](https://github.com/google/dagger) * [Dagger 2](https://github.com/google/dagger)
* [Room](https://developer.android.com/topic/libraries/architecture/room) * [Room](https://developer.android.com/topic/libraries/architecture/room)

View File

@ -47,7 +47,7 @@ Możesz także pobrać [wersję rozwojową](https://wulkanowy.github.io/#downloa
## Zbudowana za pomocą ## Zbudowana za pomocą
* [Wulkanowy API](https://github.com/wulkanowy/api) * [Wulkanowy SDK](https://github.com/wulkanowy/SDK)
* [RxJava 2](https://github.com/ReactiveX/RxJava) * [RxJava 2](https://github.com/ReactiveX/RxJava)
* [Dagger 2](https://github.com/google/dagger) * [Dagger 2](https://github.com/google/dagger)
* [Room](https://developer.android.com/topic/libraries/architecture/room) * [Room](https://developer.android.com/topic/libraries/architecture/room)

View File

@ -17,8 +17,8 @@ android {
testApplicationId "io.github.tests.wulkanowy" testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 29 targetSdkVersion 29
versionCode 48 versionCode 49
versionName "0.13.0" versionName "0.14.0"
multiDexEnabled true multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
@ -110,9 +110,9 @@ play {
} }
ext { ext {
work_manager = "2.3.0-beta01" work_manager = "2.3.0-beta02"
room = "2.2.2" room = "2.2.3"
dagger = "2.25.2" dagger = "2.25.3"
chucker = "2.0.4" chucker = "2.0.4"
mockk = "1.9.2" mockk = "1.9.2"
} }
@ -122,14 +122,14 @@ configurations.all {
} }
dependencies { dependencies {
implementation "io.github.wulkanowy:api:0.13.0" implementation "io.github.wulkanowy:sdk:0.14.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.2.0-rc01" implementation "androidx.core:core-ktx:1.2.0-rc01"
implementation "androidx.activity:activity-ktx:1.1.0-rc03" implementation "androidx.activity:activity-ktx:1.1.0-rc03"
implementation "androidx.appcompat:appcompat:1.1.0" implementation "androidx.appcompat:appcompat:1.1.0"
implementation "androidx.appcompat:appcompat-resources: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-rc04"
implementation "androidx.annotation:annotation:1.1.0" implementation "androidx.annotation:annotation:1.1.0"
implementation "androidx.multidex:multidex:2.0.1" implementation "androidx.multidex:multidex:2.0.1"
@ -139,7 +139,7 @@ dependencies {
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03"
implementation "androidx.constraintlayout:constraintlayout:1.1.3" implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" 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-rc01"
implementation "com.github.wulkanowy:material-chips-input:2.0.1" implementation "com.github.wulkanowy:material-chips-input:2.0.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1" implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
@ -167,7 +167,7 @@ dependencies {
implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6" implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6"
implementation "io.reactivex.rxjava2:rxandroid:2.1.1" implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
implementation "io.reactivex.rxjava2:rxjava:2.2.15" implementation "io.reactivex.rxjava2:rxjava:2.2.16"
implementation "com.google.code.gson:gson:2.8.6" implementation "com.google.code.gson:gson:2.8.6"
implementation "com.jakewharton.threetenabp:threetenabp:1.2.1" implementation "com.jakewharton.threetenabp:threetenabp:1.2.1"
@ -188,7 +188,7 @@ dependencies {
testImplementation "junit:junit:4.12" testImplementation "junit:junit:4.12"
testImplementation "io.mockk:mockk:$mockk" testImplementation "io.mockk:mockk:$mockk"
testImplementation "org.threeten:threetenbp:1.4.0" testImplementation "org.threeten:threetenbp:1.4.0"
testImplementation "org.mockito:mockito-inline:3.2.0" testImplementation "org.mockito:mockito-inline:3.2.4"
androidTestImplementation "androidx.test:core:1.2.0" androidTestImplementation "androidx.test:core:1.2.0"
androidTestImplementation "androidx.test:runner:1.2.0" androidTestImplementation "androidx.test:runner:1.2.0"
@ -196,7 +196,7 @@ dependencies {
androidTestImplementation "io.mockk:mockk-android:$mockk" androidTestImplementation "io.mockk:mockk-android:$mockk"
androidTestImplementation "androidx.room:room-testing:$room" androidTestImplementation "androidx.room:room-testing:$room"
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" 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' apply plugin: 'com.google.gms.google-services'

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -1,8 +1,7 @@
package io.github.wulkanowy.data.repositories.grade package io.github.wulkanowy.data.repositories.grade
import io.github.wulkanowy.api.toDate
import org.threeten.bp.LocalDate 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 import io.github.wulkanowy.data.db.entities.Grade as GradeLocal
fun createGradeLocal(value: Int, weight: Double, date: LocalDate, desc: String, semesterId: Int = 1): 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, description = desc,
entry = "", entry = "",
gradeSymbol = "", gradeSymbol = "",
value = value, value = value.toDouble(),
weight = "", weight = "",
weightValue = weight weightValue = weight
) )
} }
fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String): GradeRemote { fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String): GradeRemote {
return GradeRemote().apply { return GradeRemote(
this.value = value subject = "",
this.weightValue = weight color = "",
this.date = date.toDate() comment = "",
this.description = desc 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( 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, 2,
ReportingUnit(1, 4, "", 0, "", emptyList()) ReportingUnit(1, 4, "", 0, "", emptyList())
).blockingGet() ).blockingGet()

View File

@ -39,7 +39,7 @@ class StudentLocalTest {
@Test @Test
fun saveAndReadTest() { 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() .blockingGet()
val student = studentLocal.getCurrentStudent(true).blockingGet() val student = studentLocal.getCurrentStudent(true).blockingGet()

View File

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

View File

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

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

@ -11,12 +11,10 @@ import com.readystatesoftware.chuck.api.ChuckInterceptor
import com.readystatesoftware.chuck.api.RetentionManager import com.readystatesoftware.chuck.api.RetentionManager
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import okhttp3.logging.HttpLoggingInterceptor import io.github.wulkanowy.sdk.Sdk
import okhttp3.logging.HttpLoggingInterceptor.Level.BASIC
import okhttp3.logging.HttpLoggingInterceptor.Level.NONE
import timber.log.Timber import timber.log.Timber
import javax.inject.Singleton import javax.inject.Singleton
@ -33,15 +31,14 @@ internal class RepositoryModule {
@Singleton @Singleton
@Provides @Provides
fun provideApi(chuckCollector: ChuckCollector, context: Context): Api { fun provideSdk(chuckCollector: ChuckCollector, context: Context): Sdk {
return Api().apply { return Sdk().apply {
logLevel = NONE
androidVersion = android.os.Build.VERSION.RELEASE androidVersion = android.os.Build.VERSION.RELEASE
buildTag = android.os.Build.MODEL buildTag = android.os.Build.MODEL
setInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { Timber.d(it) }).setLevel(BASIC)) setSimpleHttpLogger { Timber.d(it) }
// for debug only // for debug only
setInterceptor(ChuckInterceptor(context, chuckCollector).maxContentLength(250000L), true, 0) addInterceptor(ChuckInterceptor(context, chuckCollector).maxContentLength(250000L), true)
} }
} }
@ -55,7 +52,7 @@ internal class RepositoryModule {
@Singleton @Singleton
@Provides @Provides
fun provideDatabase(context: Context) = AppDatabase.newInstance(context) fun provideDatabase(context: Context, sharedPrefProvider: SharedPrefProvider) = AppDatabase.newInstance(context, sharedPrefProvider)
@Singleton @Singleton
@Provides @Provides

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,6 +58,7 @@ import io.github.wulkanowy.data.db.migrations.Migration15
import io.github.wulkanowy.data.db.migrations.Migration16 import io.github.wulkanowy.data.db.migrations.Migration16
import io.github.wulkanowy.data.db.migrations.Migration17 import io.github.wulkanowy.data.db.migrations.Migration17
import io.github.wulkanowy.data.db.migrations.Migration18 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.Migration2
import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration4 import io.github.wulkanowy.data.db.migrations.Migration4
@ -100,9 +101,9 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
companion object { companion object {
const val VERSION_SCHEMA = 18 const val VERSION_SCHEMA = 19
fun getMigrations(): Array<Migration> { fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
return arrayOf( return arrayOf(
Migration2(), Migration2(),
Migration3(), Migration3(),
@ -120,16 +121,17 @@ abstract class AppDatabase : RoomDatabase() {
Migration15(), Migration15(),
Migration16(), Migration16(),
Migration17(), Migration17(),
Migration18() Migration18(),
Migration19(sharedPrefProvider)
) )
} }
fun newInstance(context: Context): AppDatabase { fun newInstance(context: Context, sharedPrefProvider: SharedPrefProvider): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database") return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
.setJournalMode(TRUNCATE) .setJournalMode(TRUNCATE)
.fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1) .fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1)
.fallbackToDestructiveMigrationOnDowngrade() .fallbackToDestructiveMigrationOnDowngrade()
.addMigrations(*getMigrations()) .addMigrations(*getMigrations(sharedPrefProvider))
.build() .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 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) { fun delete(key: String) {
sharedPref.edit().remove(key).apply() sharedPref.edit().remove(key).apply()
} }

View File

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

View File

@ -29,6 +29,8 @@ data class Message(
val subject: String, val subject: String,
var content: String,
val date: LocalDateTime, val date: LocalDateTime,
@ColumnInfo(name = "folder_id") @ColumnInfo(name = "folder_id")
@ -50,6 +52,4 @@ data class Message(
@ColumnInfo(name = "is_notified") @ColumnInfo(name = "is_notified")
var isNotified: Boolean = true 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)]) @Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id", "class_id"], unique = true)])
data class Student( 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, 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, val email: String,
var password: String, var password: String,
@ -23,6 +40,9 @@ data class Student(
@ColumnInfo(name = "student_id") @ColumnInfo(name = "student_id")
val studentId: Int, val studentId: Int,
@ColumnInfo(name = "user_login_id")
val userLoginId: Int,
@ColumnInfo(name = "student_name") @ColumnInfo(name = "student_name")
val studentName: String, val studentName: String,

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

@ -1,25 +1,24 @@
package io.github.wulkanowy.data.repositories.attendance 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.Attendance
import io.github.wulkanowy.data.db.entities.Semester 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 io.reactivex.Single
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@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>> { fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Attendance>> {
return Single.just(api.apply { diaryId = semester.diaryId }) return sdk.switchDiary(semester.diaryId, semester.schoolYear).getAttendance(startDate, endDate, semester.semesterId)
.flatMap { it.getAttendance(startDate, endDate) }.map { attendance -> .map { attendance ->
attendance.map { attendance.map {
Attendance( Attendance(
studentId = semester.studentId, studentId = semester.studentId,
diaryId = semester.diaryId, diaryId = semester.diaryId,
date = it.date.toLocalDate(), date = it.date,
number = it.number, number = it.number,
subject = it.subject, subject = it.subject,
name = it.name, name = it.name,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,23 +1,23 @@
package io.github.wulkanowy.data.repositories.message 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.Message
import io.github.wulkanowy.data.db.entities.Recipient 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.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 io.reactivex.Single
import org.threeten.bp.LocalDateTime.now import org.threeten.bp.LocalDateTime.now
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import io.github.wulkanowy.api.messages.Recipient as ApiRecipient import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
@Singleton @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>> { fun getMessages(student: Student, semester: Semester, folder: MessageFolder): Single<List<Message>> {
return api.getMessages(Folder.valueOf(folder.name)).map { messages -> return sdk.getMessages(Folder.valueOf(folder.name), semester.start.atStartOfDay(), semester.end.atStartOfDay()).map { messages ->
messages.map { messages.map {
Message( Message(
studentId = student.id.toInt(), studentId = student.id.toInt(),
@ -27,7 +27,8 @@ class MessageRemote @Inject constructor(private val api: Api) {
senderId = it.senderId ?: 0, senderId = it.senderId ?: 0,
recipient = it.recipient.orEmpty(), recipient = it.recipient.orEmpty(),
subject = it.subject.trim(), subject = it.subject.trim(),
date = it.date?.toLocalDateTime() ?: now(), date = it.date ?: now(),
content = it.content.orEmpty(),
folderId = it.folderId, folderId = it.folderId,
unread = it.unread ?: false, unread = it.unread ?: false,
unreadBy = it.unreadBy ?: 0, 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> { 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> { fun sendMessage(subject: String, content: String, recipients: List<Recipient>): Single<SentMessage> {
return api.sendMessage( return sdk.sendMessage(
subject = subject, subject = subject,
content = content, content = content,
recipients = recipients.map { recipients = recipients.map {
ApiRecipient( SdkRecipient(
id = it.realId, id = it.realId,
name = it.realName, name = it.realName,
loginId = it.loginId, loginId = it.loginId,
reportingUnitId = it.unitId, reportingUnitId = it.unitId,
role = it.role, role = it.role,
hash = it.hash hash = it.hash,
shortName = it.name
) )
} }
) )
} }
fun deleteMessage(message: Message): Single<Boolean> { 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.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.api.messages.SentMessage import io.github.wulkanowy.data.SdkHelper
import io.github.wulkanowy.data.ApiHelper
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient 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.db.entities.Student
import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
import io.github.wulkanowy.sdk.pojo.SentMessage
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Completable import io.reactivex.Completable
import io.reactivex.Maybe import io.reactivex.Maybe
@ -21,16 +22,16 @@ class MessageRepository @Inject constructor(
private val settings: InternetObservingSettings, private val settings: InternetObservingSettings,
private val local: MessageLocal, private val local: MessageLocal,
private val remote: MessageRemote, 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>> { fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): Single<List<Message>> {
return Single.just(apiHelper.initApi(student)) return Single.just(sdkHelper.init(student))
.flatMap { _ -> .flatMap { _ ->
local.getMessages(student, folder).filter { !forceRefresh } local.getMessages(student, folder).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.getMessages(student, folder) if (it) remote.getMessages(student, semester, folder)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { new ->
local.getMessages(student, folder).toSingle(emptyList()) 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> { fun getMessage(student: Student, messageDbId: Long, markAsRead: Boolean = false): Single<Message> {
return Single.just(apiHelper.initApi(student)) return Single.just(sdkHelper.init(student))
.flatMap { _ -> .flatMap { _ ->
local.getMessage(messageDbId) local.getMessage(messageDbId)
.filter { !it.content.isNullOrEmpty() } .filter { it.content.isNotEmpty() }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) local.getMessage(messageDbId).toSingle() if (it) local.getMessage(messageDbId).toSingle()
@ -60,7 +61,7 @@ class MessageRepository @Inject constructor(
remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess { remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess {
local.updateMessages(listOf(dbMessage.copy(unread = false).apply { local.updateMessages(listOf(dbMessage.copy(unread = false).apply {
id = dbMessage.id id = dbMessage.id
content = it content = content.ifBlank { it }
})) }))
} }
}.flatMap { }.flatMap {

View File

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

View File

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

View File

@ -1,34 +1,34 @@
package io.github.wulkanowy.data.repositories.recipient 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.Message
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import io.github.wulkanowy.api.messages.Recipient as ApiRecipient import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
@Singleton @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>> { fun getRecipients(role: Int, unit: ReportingUnit): Single<List<Recipient>> {
return api.getRecipients(unit.realId, role) return sdk.getRecipients(unit.realId, role)
.map { recipients -> .map { recipients ->
recipients.map { it.toRecipient() } recipients.map { it.toRecipient() }
} }
} }
fun getMessageRecipients(message: Message): Single<List<Recipient>> { fun getMessageRecipients(message: Message): Single<List<Recipient>> {
return api.getMessageRecipients(message.messageId, message.senderId) return sdk.getMessageRecipients(message.messageId, message.senderId)
.map { recipients -> .map { recipients ->
recipients.map { it.toRecipient() } recipients.map { it.toRecipient() }
} }
} }
private fun ApiRecipient.toRecipient(): Recipient { private fun SdkRecipient.toRecipient(): Recipient {
return Recipient( return Recipient(
studentId = api.studentId, studentId = sdk.studentId,
realId = id, realId = id,
realName = name, realName = name,
name = shortName, 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.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.Message
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
@ -18,11 +18,11 @@ class RecipientRepository @Inject constructor(
private val settings: InternetObservingSettings, private val settings: InternetObservingSettings,
private val local: RecipientLocal, private val local: RecipientLocal,
private val remote: RecipientRemote, 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>> { 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 { _ -> .flatMap { _ ->
local.getRecipients(student, role, unit).filter { !forceRefresh } local.getRecipients(student, role, unit).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
@ -43,7 +43,7 @@ class RecipientRepository @Inject constructor(
} }
fun getMessageRecipients(student: Student, message: Message): Single<List<Recipient>> { 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 { ReactiveNetwork.checkInternetConnectivity(settings) }
.flatMap { .flatMap {
if (it) remote.getMessageRecipients(message) if (it) remote.getMessageRecipients(message)

View File

@ -1,19 +1,19 @@
package io.github.wulkanowy.data.repositories.reportingunit 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.data.db.entities.ReportingUnit
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class ReportingUnitRemote @Inject constructor(private val api: Api) { class ReportingUnitRemote @Inject constructor(private val sdk: Sdk) {
fun getReportingUnits(): Single<List<ReportingUnit>> { fun getReportingUnits(): Single<List<ReportingUnit>> {
return api.getReportingUnits().map { return sdk.getReportingUnits().map {
it.map { unit -> it.map { unit ->
ReportingUnit( ReportingUnit(
studentId = api.studentId, studentId = sdk.studentId,
realId = unit.id, realId = unit.id,
roles = unit.roles, roles = unit.roles,
senderId = unit.senderId, 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.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.ReportingUnit
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
@ -17,11 +17,11 @@ class ReportingUnitRepository @Inject constructor(
private val settings: InternetObservingSettings, private val settings: InternetObservingSettings,
private val local: ReportingUnitLocal, private val local: ReportingUnitLocal,
private val remote: ReportingUnitRemote, private val remote: ReportingUnitRemote,
private val apiHelper: ApiHelper private val sdkHelper: SdkHelper
) { ) {
fun getReportingUnits(student: Student, forceRefresh: Boolean = false): Single<List<ReportingUnit>> { fun getReportingUnits(student: Student, forceRefresh: Boolean = false): Single<List<ReportingUnit>> {
return Single.just(apiHelper.initApi(student)) return Single.just(sdkHelper.init(student))
.flatMap { _ -> .flatMap { _ ->
local.getReportingUnits(student).filter { !forceRefresh } local.getReportingUnits(student).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
@ -40,7 +40,7 @@ class ReportingUnitRepository @Inject constructor(
} }
fun getReportingUnit(student: Student, unitId: Int): Maybe<ReportingUnit> { fun getReportingUnit(student: Student, unitId: Int): Maybe<ReportingUnit> {
return Maybe.just(apiHelper.initApi(student)) return Maybe.just(sdkHelper.init(student))
.flatMap { _ -> .flatMap { _ ->
local.getReportingUnit(student, unitId) local.getReportingUnit(student, unitId)
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)

View File

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

View File

@ -1,35 +1,32 @@
package io.github.wulkanowy.data.repositories.semester 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@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>> { fun getSemesters(student: Student): Single<List<Semester>> {
return api.getSemesters().map { semesters -> return sdk.getSemesters().map { semesters ->
semesters.map { semester -> semesters.map {
Semester( Semester(
studentId = student.studentId, studentId = student.studentId,
diaryId = semester.diaryId, diaryId = it.diaryId,
diaryName = semester.diaryName, diaryName = it.diaryName,
schoolYear = semester.schoolYear, schoolYear = it.schoolYear,
semesterId = semester.semesterId, semesterId = it.semesterId,
semesterName = semester.semesterNumber, semesterName = it.semesterNumber,
isCurrent = semester.current, isCurrent = it.current,
start = semester.start, start = it.start,
end = semester.end, end = it.end,
classId = semester.classId, classId = it.classId,
unitId = semester.unitId 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.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
@ -18,11 +18,11 @@ class SemesterRepository @Inject constructor(
private val remote: SemesterRemote, private val remote: SemesterRemote,
private val local: SemesterLocal, private val local: SemesterLocal,
private val settings: InternetObservingSettings, private val settings: InternetObservingSettings,
private val apiHelper: ApiHelper private val sdkHelper: SdkHelper
) { ) {
fun getSemesters(student: Student, forceRefresh: Boolean = false): Single<List<Semester>> { 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 } } .flatMap { local.getSemesters(student).filter { !forceRefresh } }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.student
import android.content.Context import android.content.Context
import io.github.wulkanowy.data.db.dao.StudentDao import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.entities.Student 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.decrypt
import io.github.wulkanowy.utils.security.encrypt import io.github.wulkanowy.utils.security.encrypt
import io.reactivex.Completable import io.reactivex.Completable
@ -18,7 +19,12 @@ class StudentLocal @Inject constructor(
) { ) {
fun saveStudents(students: List<Student>): Single<List<Long>> { 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>> { fun getStudents(decryptPass: Boolean): Maybe<List<Student>> {
@ -28,7 +34,11 @@ class StudentLocal @Inject constructor(
} }
fun getCurrentStudent(decryptPass: Boolean): Maybe<Student> { 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 { fun setCurrentStudent(student: Student): Completable {

View File

@ -1,34 +1,51 @@
package io.github.wulkanowy.data.repositories.student 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.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDateTime.now import org.threeten.bp.LocalDateTime.now
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
@Singleton @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>> { private fun mapStudents(students: List<SdkStudent>, email: String, password: String): List<Student> {
return api.getStudents().map { students -> return students.map { student ->
students.map { student -> Student(
Student( email = email,
email = email, password = password,
password = password, isParent = student.isParent,
symbol = student.symbol, symbol = student.symbol,
studentId = student.studentId, studentId = student.studentId,
studentName = student.studentName, userLoginId = student.userLoginId,
schoolSymbol = student.schoolSymbol, studentName = student.studentName,
schoolName = student.schoolName, schoolSymbol = student.schoolSymbol,
className = student.className, schoolName = student.schoolName,
classId = student.classId, className = student.className,
endpoint = endpoint, classId = student.classId,
loginType = student.loginType.name, scrapperBaseUrl = student.scrapperBaseUrl,
isCurrent = false, loginType = student.loginType.name,
registrationDate = now() 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.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.db.entities.Student
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.reactivex.Completable import io.reactivex.Completable
@ -16,21 +15,32 @@ import javax.inject.Singleton
class StudentRepository @Inject constructor( class StudentRepository @Inject constructor(
private val local: StudentLocal, private val local: StudentLocal,
private val remote: StudentRemote, private val remote: StudentRemote,
private val settings: InternetObservingSettings, private val settings: InternetObservingSettings
private val apiHelper: ApiHelper
) { ) {
fun isStudentSaved(): Single<Boolean> = local.getStudents(false).isEmpty.map { !it } fun isStudentSaved(): Single<Boolean> = local.getStudents(false).isEmpty.map { !it }
fun isCurrentStudentSet(): Single<Boolean> = local.getCurrentStudent(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>> { fun getStudentsApi(pin: String, symbol: String, token: String): Single<List<Student>> {
return ReactiveNetwork.checkInternetConnectivity(settings) return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
.flatMap { if (it) remote.getStudentsMobileApi(token, pin, symbol)
apiHelper.initApi(email, password, symbol, endpoint) else Single.error(UnknownHostException("No internet connection"))
if (it) remote.getStudents(email, password, endpoint) }
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>> { fun getSavedStudents(decryptPass: Boolean = true): Single<List<Student>> {

View File

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

View File

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

View File

@ -1,30 +1,27 @@
package io.github.wulkanowy.data.repositories.timetable 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.Semester
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.utils.toLocalDate import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.toLocalDateTime
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@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>> { fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Timetable>> {
return Single.just(api.apply { diaryId = semester.diaryId }) return sdk.switchDiary(semester.diaryId, semester.schoolYear).getTimetable(startDate, endDate)
.flatMap { it.getTimetable(startDate, endDate) }
.map { lessons -> .map { lessons ->
lessons.map { lessons.map {
Timetable( Timetable(
studentId = semester.studentId, studentId = semester.studentId,
diaryId = semester.diaryId, diaryId = semester.diaryId,
number = it.number, number = it.number,
start = it.start.toLocalDateTime(), start = it.start,
end = it.end.toLocalDateTime(), end = it.end,
date = it.date.toLocalDate(), date = it.date,
subject = it.subject, subject = it.subject,
subjectOld = it.subjectOld, subjectOld = it.subjectOld,
group = it.group, group = it.group,

View File

@ -36,7 +36,7 @@ class TimetableRepository @Inject constructor(
old.singleOrNull { new.start == it.start }?.let { old -> old.singleOrNull { new.start == it.start }?.let { old ->
return@map new.copy( return@map new.copy(
room = if (new.room.isEmpty()) old.room else new.room, 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.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import io.github.wulkanowy.R 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.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository 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.channels.DebugChannel
import io.github.wulkanowy.services.sync.works.Work import io.github.wulkanowy.services.sync.works.Work
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
@ -78,4 +78,3 @@ class SyncWorker @AssistedInject constructor(
fun create(appContext: Context, workerParameters: WorkerParameters): ListenableWorker fun create(appContext: Context, workerParameters: WorkerParameters): ListenableWorker
} }
} }

View File

@ -30,7 +30,7 @@ class MessageWork @Inject constructor(
) : Work { ) : Work {
override fun create(student: Student, semester: Semester): Completable { 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) } .flatMap { messageRepository.getNotNotifiedMessages(student) }
.flatMapCompletable { .flatMapCompletable {
if (it.isNotEmpty()) notify(it) if (it.isNotEmpty()) notify(it)

View File

@ -1,10 +1,10 @@
package io.github.wulkanowy.ui.base package io.github.wulkanowy.ui.base
import android.app.ActivityManager 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_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_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.os.Bundle
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast

View File

@ -3,11 +3,12 @@ package io.github.wulkanowy.ui.base
import android.content.res.Resources import android.content.res.Resources
import com.readystatesoftware.chuck.api.ChuckCollector import com.readystatesoftware.chuck.api.ChuckCollector
import io.github.wulkanowy.R 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.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 io.github.wulkanowy.utils.security.ScramblerException
import timber.log.Timber import timber.log.Timber
import java.net.SocketTimeoutException 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 FeatureDisabledException -> showErrorMessage(getString(R.string.error_feature_disabled), error)
is ScramblerException, is BadCredentialsException -> onSessionExpired() is ScramblerException, is BadCredentialsException -> onSessionExpired()
is NoCurrentStudentException -> onNoCurrentStudent() is NoCurrentStudentException -> onNoCurrentStudent()
is FeatureNotAvailableException -> showErrorMessage(getString(R.string.error_feature_not_available), error)
else -> showErrorMessage(getString(R.string.error_unknown), error) else -> showErrorMessage(getString(R.string.error_unknown), error)
} }
} }

View File

@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.grade.GradeRepository import io.github.wulkanowy.data.repositories.grade.GradeRepository
import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.calcAverage import io.github.wulkanowy.utils.calcAverage
import io.github.wulkanowy.utils.changeModifier import io.github.wulkanowy.utils.changeModifier
import io.reactivex.Maybe import io.reactivex.Maybe
@ -40,7 +41,7 @@ class GradeAverageProvider @Inject constructor(
.map { secondGrades -> secondGrades + firstGrades } .map { secondGrades -> secondGrades + firstGrades }
} }
}.map { grades -> }.map { grades ->
grades.map { it.changeModifier(plusModifier, minusModifier) } grades.map { if (student.loginMode == Sdk.Mode.SCRAPPER.name) it.changeModifier(plusModifier, minusModifier) else it }
.groupBy { it.subject } .groupBy { it.subject }
.mapValues { it.value.calcAverage() } .mapValues { it.value.calcAverage() }
}) })
@ -54,7 +55,7 @@ class GradeAverageProvider @Inject constructor(
return getAverageFromGradeSummary(selectedSemester, forceRefresh) return getAverageFromGradeSummary(selectedSemester, forceRefresh)
.switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh) .switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh)
.map { grades -> .map { grades ->
grades.map { it.changeModifier(plusModifier, minusModifier) } grades.map { if (student.loginMode == Sdk.Mode.SCRAPPER.name) it.changeModifier(plusModifier, minusModifier) else it }
.groupBy { it.subject } .groupBy { it.subject }
.mapValues { it.value.calcAverage() } .mapValues { it.value.calcAverage() }
}) })

View File

@ -13,7 +13,6 @@ import androidx.appcompat.app.AlertDialog
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment
import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment
import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment

View File

@ -18,7 +18,6 @@ import eu.davidea.flexibleadapter.items.IFlexible
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.grade.GradeView import io.github.wulkanowy.ui.modules.grade.GradeView
import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainActivity

View File

@ -8,6 +8,7 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment
import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment
import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
@ -50,7 +51,8 @@ class LoginActivity : BaseActivity<LoginPresenter>(), LoginView {
addFragments(listOf( addFragments(listOf(
LoginFormFragment.newInstance(), LoginFormFragment.newInstance(),
LoginSymbolFragment.newInstance(), LoginSymbolFragment.newInstance(),
LoginStudentSelectFragment.newInstance() LoginStudentSelectFragment.newInstance(),
LoginAdvancedFragment.newInstance()
)) ))
} }
@ -93,4 +95,8 @@ class LoginActivity : BaseActivity<LoginPresenter>(), LoginView {
fun onSymbolFragmentAccountLogged(students: List<Student>) { fun onSymbolFragmentAccountLogged(students: List<Student>) {
presenter.onSymbolViewAccountLogged(students) presenter.onSymbolViewAccountLogged(students)
} }
fun onAdvancedLoginClick() {
presenter.onAdvancedLoginClick()
}
} }

View File

@ -4,7 +4,7 @@ import android.content.res.Resources
import android.database.sqlite.SQLiteConstraintException import android.database.sqlite.SQLiteConstraintException
import com.readystatesoftware.chuck.api.ChuckCollector import com.readystatesoftware.chuck.api.ChuckCollector
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.api.login.BadCredentialsException import io.github.wulkanowy.sdk.exception.BadCredentialsException
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import javax.inject.Inject import javax.inject.Inject

View File

@ -6,6 +6,7 @@ import dagger.android.ContributesAndroidInjector
import io.github.wulkanowy.di.scopes.PerActivity import io.github.wulkanowy.di.scopes.PerActivity
import io.github.wulkanowy.di.scopes.PerFragment import io.github.wulkanowy.di.scopes.PerFragment
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment
import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment
import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
@ -26,6 +27,10 @@ internal abstract class LoginModule {
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindLoginFormFragment(): LoginFormFragment abstract fun bindLoginFormFragment(): LoginFormFragment
@PerFragment
@ContributesAndroidInjector
abstract fun bindLoginAdvancedFragment(): LoginAdvancedFragment
@PerFragment @PerFragment
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindLoginSymbolFragment(): LoginSymbolFragment abstract fun bindLoginSymbolFragment(): LoginSymbolFragment

View File

@ -45,6 +45,10 @@ class LoginPresenter @Inject constructor(
} }
} }
fun onAdvancedLoginClick() {
view?.switchView(3)
}
fun onViewSelected(index: Int) { fun onViewSelected(index: Int) {
view?.apply { view?.apply {
when (index) { when (index) {
@ -58,7 +62,7 @@ class LoginPresenter @Inject constructor(
Timber.i("Back pressed in login view") Timber.i("Back pressed in login view")
view?.apply { view?.apply {
when (currentViewIndex) { when (currentViewIndex) {
1, 2 -> switchView(0) 1, 2, 3 -> switchView(0)
else -> default() else -> default()
} }
} }

View File

@ -0,0 +1,227 @@
package io.github.wulkanowy.ui.modules.login.advanced
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.core.widget.doOnTextChanged
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.login.form.LoginSymbolAdapter
import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.showSoftInput
import kotlinx.android.synthetic.main.fragment_login_advanced.*
import javax.inject.Inject
class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView {
@Inject
lateinit var presenter: LoginAdvancedPresenter
companion object {
fun newInstance() = LoginAdvancedFragment()
}
override val formLoginType: String
get() = when (loginTypeSwitch.checkedRadioButtonId) {
R.id.loginTypeApi -> "API"
R.id.loginTypeScrapper -> "SCRAPPER"
else -> "HYBRID"
}
override val formNameValue: String
get() = loginFormName.text.toString().trim()
override val formPassValue: String
get() = loginFormPass.text.toString().trim()
private lateinit var hostKeys: Array<String>
private lateinit var hostValues: Array<String>
override val formHostValue: String?
get() = hostValues.getOrNull(hostKeys.indexOf(loginFormHost.text.toString()))
override val formPinValue: String
get() = loginFormPin.text.toString().trim()
override val formSymbolValue: String
get() = loginFormSymbol.text.toString().trim()
override val formTokenValue: String
get() = loginFormToken.text.toString().trim()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_login_advanced, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
presenter.onAttachView(this)
}
override fun initView() {
hostKeys = resources.getStringArray(R.array.hosts_keys)
hostValues = resources.getStringArray(R.array.hosts_values)
loginFormName.doOnTextChanged { _, _, _, _ -> presenter.onNameTextChanged() }
loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() }
loginFormPin.doOnTextChanged { _, _, _, _ -> presenter.onPinTextChanged() }
loginFormSymbol.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() }
loginFormToken.doOnTextChanged { _, _, _, _ -> presenter.onTokenTextChanged() }
loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() }
loginFormSignIn.setOnClickListener { presenter.onSignInClick() }
loginTypeSwitch.setOnCheckedChangeListener { _, checkedId ->
presenter.onLoginModeSelected(when (checkedId) {
R.id.loginTypeApi -> Sdk.Mode.API
R.id.loginTypeScrapper -> Sdk.Mode.SCRAPPER
else -> Sdk.Mode.HYBRID
})
}
loginFormSymbol.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values)))
with(loginFormHost) {
setText(hostKeys.getOrElse(0) { "" })
setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys))
}
}
override fun setDefaultCredentials(name: String, pass: String, symbol: String, token: String, pin: String) {
loginFormName.setText(name)
loginFormPass.setText(pass)
loginFormToken.setText(token)
loginFormSymbol.setText(symbol)
loginFormPin.setText(pin)
}
override fun setErrorNameRequired() {
loginFormNameLayout.run {
requestFocus()
error = getString(R.string.login_field_required)
}
}
override fun setErrorPassRequired(focus: Boolean) {
loginFormPassLayout.run {
if (focus) requestFocus()
error = getString(R.string.login_field_required)
}
}
override fun setErrorPassInvalid(focus: Boolean) {
loginFormPassLayout.run {
if (focus) requestFocus()
error = getString(R.string.login_invalid_password)
}
}
override fun setErrorPassIncorrect() {
loginFormPassLayout.run {
requestFocus()
error = getString(R.string.login_incorrect_password)
}
}
override fun setErrorPinRequired() {
loginFormPinLayout.run {
requestFocus()
error = getString(R.string.login_field_required)
}
}
override fun setErrorSymbolRequired() {
loginFormSymbolLayout.run {
requestFocus()
error = getString(R.string.login_field_required)
}
}
override fun setErrorTokenRequired() {
loginFormTokenLayout.run {
requestFocus()
error = getString(R.string.login_field_required)
}
}
override fun clearNameError() {
loginFormNameLayout.error = null
}
override fun clearPassError() {
loginFormPassLayout.error = null
}
override fun clearPinKeyError() {
loginFormPinLayout.error = null
}
override fun clearSymbolError() {
loginFormSymbolLayout.error = null
}
override fun clearTokenError() {
loginFormTokenLayout.error = null
}
override fun showOnlyHybridModeInputs() {
loginFormNameLayout.visibility = View.VISIBLE
loginFormPassLayout.visibility = View.VISIBLE
loginFormHostLayout.visibility = View.VISIBLE
loginFormPinLayout.visibility = View.GONE
loginFormSymbolLayout.visibility = View.VISIBLE
loginFormTokenLayout.visibility = View.GONE
}
override fun showOnlyScrapperModeInputs() {
loginFormNameLayout.visibility = View.VISIBLE
loginFormPassLayout.visibility = View.VISIBLE
loginFormHostLayout.visibility = View.VISIBLE
loginFormPinLayout.visibility = View.GONE
loginFormSymbolLayout.visibility = View.VISIBLE
loginFormTokenLayout.visibility = View.GONE
}
override fun showOnlyMobileApiModeInputs() {
loginFormNameLayout.visibility = View.GONE
loginFormPassLayout.visibility = View.GONE
loginFormHostLayout.visibility = View.GONE
loginFormPinLayout.visibility = View.VISIBLE
loginFormSymbolLayout.visibility = View.VISIBLE
loginFormTokenLayout.visibility = View.VISIBLE
}
override fun showSoftKeyboard() {
activity?.showSoftInput()
}
override fun hideSoftKeyboard() {
activity?.hideSoftInput()
}
override fun showProgress(show: Boolean) {
loginFormProgress.visibility = if (show) View.VISIBLE else View.GONE
}
override fun showContent(show: Boolean) {
loginFormContainer.visibility = if (show) View.VISIBLE else View.GONE
}
override fun notifyParentAccountLogged(students: List<Student>) {
(activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple(
loginFormName.text.toString(),
loginFormPass.text.toString(),
resources.getStringArray(R.array.hosts_values)[1]
))
}
override fun onDestroyView() {
super.onDestroyView()
presenter.onDetachView()
}
}

View File

@ -0,0 +1,185 @@
package io.github.wulkanowy.ui.modules.login.advanced
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.ifNullOrBlank
import io.reactivex.Single
import timber.log.Timber
import javax.inject.Inject
class LoginAdvancedPresenter @Inject constructor(
schedulers: SchedulersProvider,
studentRepository: StudentRepository,
private val loginErrorHandler: LoginErrorHandler,
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<LoginAdvancedView>(loginErrorHandler, studentRepository, schedulers) {
override fun onAttachView(view: LoginAdvancedView) {
super.onAttachView(view)
view.run {
initView()
showOnlyScrapperModeInputs()
loginErrorHandler.onBadCredentials = {
setErrorPassIncorrect()
showSoftKeyboard()
Timber.i("Entered wrong username or password")
}
}
}
fun onHostSelected() {
view?.apply {
clearPassError()
clearNameError()
if (formHostValue?.contains("fakelog") == true) {
setDefaultCredentials("jan@fakelog.cf", "jan123", "powiatwulkanowy", "FK100000", "999999")
}
}
}
fun onLoginModeSelected(type: Sdk.Mode) {
view?.run {
when (type) {
Sdk.Mode.API -> showOnlyMobileApiModeInputs()
Sdk.Mode.SCRAPPER -> showOnlyScrapperModeInputs()
Sdk.Mode.HYBRID -> showOnlyHybridModeInputs()
}
}
}
fun onPassTextChanged() {
view?.clearPassError()
}
fun onNameTextChanged() {
view?.clearNameError()
}
fun onPinTextChanged() {
view?.clearPinKeyError()
}
fun onSymbolTextChanged() {
view?.clearSymbolError()
}
fun onTokenTextChanged() {
view?.clearTokenError()
}
fun onSignInClick() {
if (!validateCredentials()) return
disposable.add(getStudentsAppropriatesToLoginType()
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.apply {
hideSoftKeyboard()
showProgress(true)
showContent(false)
}
Timber.i("Login started")
}
.doFinally {
view?.apply {
showProgress(false)
showContent(true)
}
}
.subscribe({
Timber.i("Login result: Success")
analytics.logEvent("registration_form", "success" to true, "students" to it.size, "error" to "No error")
view?.notifyParentAccountLogged(it)
}, {
Timber.i("Login result: An exception occurred")
analytics.logEvent("registration_form", "success" to false, "students" to -1, "error" to it.message.ifNullOrBlank { "No message" })
loginErrorHandler.dispatch(it)
}))
}
private fun getStudentsAppropriatesToLoginType(): Single<List<Student>> {
val email = view?.formNameValue.orEmpty()
val password = view?.formPassValue.orEmpty()
val endpoint = view?.formHostValue.orEmpty()
val pin = view?.formPinValue.orEmpty()
val symbol = view?.formSymbolValue.orEmpty()
val token = view?.formTokenValue.orEmpty()
return when (Sdk.Mode.valueOf(view?.formLoginType ?: "")) {
Sdk.Mode.API -> studentRepository.getStudentsApi(pin, symbol, token)
Sdk.Mode.SCRAPPER -> studentRepository.getStudentsScrapper(email, password, endpoint, symbol)
Sdk.Mode.HYBRID -> studentRepository.getStudentsHybrid(email, password, endpoint, symbol)
}
}
private fun validateCredentials(): Boolean {
val login = view?.formNameValue.orEmpty()
val password = view?.formPassValue.orEmpty()
val pin = view?.formPinValue.orEmpty()
val symbol = view?.formSymbolValue.orEmpty()
val token = view?.formTokenValue.orEmpty()
var isCorrect = true
when (Sdk.Mode.valueOf(view?.formLoginType ?: "")) {
Sdk.Mode.API -> {
if (pin.isEmpty()) {
view?.setErrorPinRequired()
isCorrect = false
}
if (symbol.isEmpty()) {
view?.setErrorSymbolRequired()
isCorrect = false
}
if (token.isEmpty()) {
view?.setErrorTokenRequired()
isCorrect = false
}
}
Sdk.Mode.SCRAPPER -> {
if (login.isEmpty()) {
view?.setErrorNameRequired()
isCorrect = false
}
if (password.isEmpty()) {
view?.setErrorPassRequired(focus = isCorrect)
isCorrect = false
}
if (password.length < 6 && password.isNotEmpty()) {
view?.setErrorPassInvalid(focus = isCorrect)
isCorrect = false
}
}
Sdk.Mode.HYBRID -> {
if (login.isEmpty()) {
view?.setErrorNameRequired()
isCorrect = false
}
if (password.isEmpty()) {
view?.setErrorPassRequired(focus = isCorrect)
isCorrect = false
}
if (password.length < 6 && password.isNotEmpty()) {
view?.setErrorPassInvalid(focus = isCorrect)
isCorrect = false
}
}
}
return isCorrect
}
}

View File

@ -0,0 +1,65 @@
package io.github.wulkanowy.ui.modules.login.advanced
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.ui.base.BaseView
interface LoginAdvancedView : BaseView {
val formNameValue: String
val formPassValue: String
val formHostValue: String?
val formLoginType: String
val formPinValue: String
val formSymbolValue: String
val formTokenValue: String
fun initView()
fun setDefaultCredentials(name: String, pass: String, symbol: String, token: String, pin: String)
fun setErrorNameRequired()
fun setErrorPassRequired(focus: Boolean)
fun setErrorPassInvalid(focus: Boolean)
fun setErrorPassIncorrect()
fun clearNameError()
fun clearPassError()
fun clearPinKeyError()
fun clearSymbolError()
fun clearTokenError()
fun showSoftKeyboard()
fun hideSoftKeyboard()
fun showProgress(show: Boolean)
fun showContent(show: Boolean)
fun notifyParentAccountLogged(students: List<Student>)
fun setErrorPinRequired()
fun setErrorSymbolRequired()
fun setErrorTokenRequired()
fun showOnlyHybridModeInputs()
fun showOnlyScrapperModeInputs()
fun showOnlyMobileApiModeInputs()
}

View File

@ -61,6 +61,7 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() } loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() }
loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() } loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() }
loginFormSignIn.setOnClickListener { presenter.onSignInClick() } loginFormSignIn.setOnClickListener { presenter.onSignInClick() }
loginFormAdvancedButton.setOnClickListener { presenter.onAdvancedLoginClick() }
loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() } loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() }
loginFormFaq.setOnClickListener { presenter.onFaqClick() } loginFormFaq.setOnClickListener { presenter.onFaqClick() }
loginFormContactEmail.setOnClickListener { presenter.onEmailClick() } loginFormContactEmail.setOnClickListener { presenter.onEmailClick() }
@ -134,14 +135,7 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun showVersion() { override fun showVersion() {
with(loginFormVersion) { loginFormVersion.text = "v${appInfo.versionName}"
visibility = VISIBLE
text = "${getString(R.string.app_name)} ${appInfo.versionName}"
}
}
override fun showPrivacyPolicy() {
loginFormPrivacyLink.visibility = VISIBLE
} }
override fun notifyParentAccountLogged(students: List<Student>, loginData: Triple<String, String, String>) { override fun notifyParentAccountLogged(students: List<Student>, loginData: Triple<String, String, String>) {
@ -156,6 +150,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
loginFormContact.visibility = if (show) VISIBLE else GONE loginFormContact.visibility = if (show) VISIBLE else GONE
} }
override fun openAdvancedLogin() {
(activity as? LoginActivity)?.onAdvancedLoginClick()
}
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
presenter.onDetachView() presenter.onDetachView()

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.login.form
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.ifNullOrBlank import io.github.wulkanowy.utils.ifNullOrBlank
@ -14,8 +13,7 @@ class LoginFormPresenter @Inject constructor(
schedulers: SchedulersProvider, schedulers: SchedulersProvider,
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val loginErrorHandler: LoginErrorHandler, private val loginErrorHandler: LoginErrorHandler,
private val analytics: FirebaseAnalyticsHelper, private val analytics: FirebaseAnalyticsHelper
private val appInfo: AppInfo
) : BasePresenter<LoginFormView>(loginErrorHandler, studentRepository, schedulers) { ) : BasePresenter<LoginFormView>(loginErrorHandler, studentRepository, schedulers) {
override fun onAttachView(view: LoginFormView) { override fun onAttachView(view: LoginFormView) {
@ -23,7 +21,7 @@ class LoginFormPresenter @Inject constructor(
view.run { view.run {
initView() initView()
showContact(false) showContact(false)
if (appInfo.isDebug) showVersion() else showPrivacyPolicy() showVersion()
loginErrorHandler.onBadCredentials = { loginErrorHandler.onBadCredentials = {
setErrorPassIncorrect() setErrorPassIncorrect()
@ -37,6 +35,10 @@ class LoginFormPresenter @Inject constructor(
view?.openPrivacyPolicyPage() view?.openPrivacyPolicyPage()
} }
fun onAdvancedLoginClick() {
view?.openAdvancedLogin()
}
fun onHostSelected() { fun onHostSelected() {
view?.apply { view?.apply {
clearPassError() clearPassError()
@ -56,13 +58,13 @@ class LoginFormPresenter @Inject constructor(
} }
fun onSignInClick() { fun onSignInClick() {
val email = view?.formNameValue.orEmpty() val email = view?.formNameValue.orEmpty().trim()
val password = view?.formPassValue.orEmpty() val password = view?.formPassValue.orEmpty().trim()
val endpoint = view?.formHostValue.orEmpty() val endpoint = view?.formHostValue.orEmpty().trim()
if (!validateCredentials(email, password)) return if (!validateCredentials(email, password)) return
disposable.add(studentRepository.getStudents(email, password, endpoint) disposable.add(studentRepository.getStudentsScrapper(email, password, endpoint)
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.doOnSubscribe { .doOnSubscribe {
@ -81,11 +83,11 @@ class LoginFormPresenter @Inject constructor(
} }
.subscribe({ .subscribe({
Timber.i("Login result: Success") Timber.i("Login result: Success")
analytics.logEvent("registration_form", "success" to true, "students" to it.size, "endpoint" to endpoint, "error" to "No error") analytics.logEvent("registration_form", "success" to true, "students" to it.size, "scrapperBaseUrl" to endpoint, "error" to "No error")
view?.notifyParentAccountLogged(it, Triple(email, password, endpoint)) view?.notifyParentAccountLogged(it, Triple(email, password, endpoint))
}, { }, {
Timber.i("Login result: An exception occurred") Timber.i("Login result: An exception occurred")
analytics.logEvent("registration_form", "success" to false, "students" to -1, "endpoint" to endpoint, "error" to it.message.ifNullOrBlank { "No message" }) analytics.logEvent("registration_form", "success" to false, "students" to -1, "scrapperBaseUrl" to endpoint, "error" to it.message.ifNullOrBlank { "No message" })
loginErrorHandler.dispatch(it) loginErrorHandler.dispatch(it)
view?.showContact(true) view?.showContact(true)
})) }))

View File

@ -37,8 +37,6 @@ interface LoginFormView : BaseView {
fun showVersion() fun showVersion()
fun showPrivacyPolicy()
fun notifyParentAccountLogged(students: List<Student>, loginData: Triple<String, String, String>) fun notifyParentAccountLogged(students: List<Student>, loginData: Triple<String, String, String>)
fun openPrivacyPolicyPage() fun openPrivacyPolicyPage()
@ -48,4 +46,6 @@ interface LoginFormView : BaseView {
fun openFaqPage() fun openFaqPage()
fun openEmail() fun openEmail()
fun openAdvancedLogin()
} }

View File

@ -79,11 +79,11 @@ class LoginStudentSelectPresenter @Inject constructor(
Timber.i("Registration started") Timber.i("Registration started")
} }
.subscribe({ .subscribe({
students.forEach { analytics.logEvent("registration_student_select", "success" to true, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to "No error") } students.forEach { analytics.logEvent("registration_student_select", "success" to true, "scrapperBaseUrl" to it.scrapperBaseUrl, "symbol" to it.symbol, "error" to "No error") }
Timber.i("Registration result: Success") Timber.i("Registration result: Success")
view?.openMainView() view?.openMainView()
}, { error -> }, { error ->
students.forEach { analytics.logEvent("registration_student_select", "success" to false, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to error.message.ifNullOrBlank { "No message" }) } students.forEach { analytics.logEvent("registration_student_select", "success" to false, "scrapperBaseUrl" to it.scrapperBaseUrl, "symbol" to it.symbol, "error" to error.message.ifNullOrBlank { "No message" }) }
Timber.i("Registration result: An exception occurred ") Timber.i("Registration result: An exception occurred ")
loginErrorHandler.dispatch(error) loginErrorHandler.dispatch(error)
view?.apply { view?.apply {

View File

@ -44,7 +44,7 @@ class LoginSymbolPresenter @Inject constructor(
disposable.add( disposable.add(
Single.fromCallable { if (loginData == null) throw IllegalArgumentException("Login data is null") else loginData } Single.fromCallable { if (loginData == null) throw IllegalArgumentException("Login data is null") else loginData }
.flatMap { studentRepository.getStudents(it.first, it.second, it.third, symbol) } .flatMap { studentRepository.getStudentsScrapper(it.first, it.second, it.third, symbol) }
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.doOnSubscribe { .doOnSubscribe {
@ -62,7 +62,7 @@ class LoginSymbolPresenter @Inject constructor(
} }
} }
.subscribe({ .subscribe({
analytics.logEvent("registration_symbol", "success" to true, "students" to it.size, "endpoint" to loginData?.third, "symbol" to symbol, "error" to "No error") analytics.logEvent("registration_symbol", "success" to true, "students" to it.size, "scrapperBaseUrl" to loginData?.third, "symbol" to symbol, "error" to "No error")
view?.apply { view?.apply {
if (it.isEmpty()) { if (it.isEmpty()) {
Timber.i("Login with symbol result: Empty student list") Timber.i("Login with symbol result: Empty student list")
@ -75,7 +75,7 @@ class LoginSymbolPresenter @Inject constructor(
} }
}, { }, {
Timber.i("Login with symbol result: An exception occurred") Timber.i("Login with symbol result: An exception occurred")
analytics.logEvent("registration_symbol", "success" to false, "students" to -1, "endpoint" to loginData?.third, "symbol" to symbol, "error" to it.message.ifNullOrBlank { "No message" }) analytics.logEvent("registration_symbol", "success" to false, "students" to -1, "scrapperBaseUrl" to loginData?.third, "symbol" to symbol, "error" to it.message.ifNullOrBlank { "No message" })
loginErrorHandler.dispatch(it) loginErrorHandler.dispatch(it)
view?.showContact(true) view?.showContact(true)
})) }))

View File

@ -66,7 +66,7 @@ class LuckyNumberPresenter @Inject constructor(
view?.run { view?.run {
showContent(false) showContent(false)
showEmpty(true) showEmpty(true)
showEmpty(false) showErrorView(false)
} }
}) })
) )

View File

@ -9,6 +9,7 @@ import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.repositories.message.MessageFolder
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
import kotlinx.android.extensions.LayoutContainer import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_message.* import kotlinx.android.synthetic.main.item_message.*
@ -27,7 +28,7 @@ class MessageItem(val message: Message, private val noSubjectString: String) :
val style = if (message.unread) BOLD else NORMAL val style = if (message.unread) BOLD else NORMAL
messageItemAuthor.run { messageItemAuthor.run {
text = if (message.recipient.isNotBlank()) message.recipient else message.sender text = if (message.folderId == MessageFolder.SENT.id) message.recipient else message.sender
setTypeface(null, style) setTypeface(null, style)
} }
messageItemSubject.run { messageItemSubject.run {

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.modules.message.preview package io.github.wulkanowy.ui.modules.message.preview
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.repositories.message.MessageFolder
import io.github.wulkanowy.data.repositories.message.MessageRepository import io.github.wulkanowy.data.repositories.message.MessageRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
@ -63,14 +64,14 @@ class MessagePreviewPresenter @Inject constructor(
message.let { message.let {
setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString) setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString)
setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss"))
setContent(it.content.orEmpty()) setContent(it.content)
initOptions() initOptions()
if (it.recipient.isNotBlank()) setRecipient(it.recipient) if (it.folderId == MessageFolder.SENT.id) setRecipient(it.recipient)
else setSender(it.sender) else setSender(it.sender)
} }
} }
analytics.logEvent("load_message_preview", "length" to message.content?.length) analytics.logEvent("load_message_preview", "length" to message.content.length)
}) { }) {
Timber.i("Loading message $id preview result: An exception occurred ") Timber.i("Loading message $id preview result: An exception occurred ")
retryCallback = { onMessageLoadRetry() } retryCallback = { onMessageLoadRetry() }

View File

@ -4,6 +4,7 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.repositories.message.MessageFolder import io.github.wulkanowy.data.repositories.message.MessageFolder
import io.github.wulkanowy.data.repositories.message.MessageRepository import io.github.wulkanowy.data.repositories.message.MessageRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
@ -18,6 +19,7 @@ class MessageTabPresenter @Inject constructor(
errorHandler: ErrorHandler, errorHandler: ErrorHandler,
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val messageRepository: MessageRepository, private val messageRepository: MessageRepository,
private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<MessageTabView>(errorHandler, studentRepository, schedulers) { ) : BasePresenter<MessageTabView>(errorHandler, studentRepository, schedulers) {
@ -76,8 +78,11 @@ class MessageTabPresenter @Inject constructor(
disposable.apply { disposable.apply {
clear() clear()
add(studentRepository.getCurrentStudent() add(studentRepository.getCurrentStudent()
.flatMap { messageRepository.getMessages(it, folder, forceRefresh) } .flatMap { student ->
.map { items -> items.map { MessageItem(it, view?.noSubjectString.orEmpty()) } } semesterRepository.getCurrentSemester(student)
.flatMap { messageRepository.getMessages(student, it, folder, forceRefresh) }
.map { items -> items.map { MessageItem(it, view?.noSubjectString.orEmpty()) } }
}
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.doFinally { .doFinally {

View File

@ -20,7 +20,6 @@ import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
import io.github.wulkanowy.ui.modules.settings.SettingsFragment import io.github.wulkanowy.ui.modules.settings.SettingsFragment
import io.github.wulkanowy.ui.modules.schoolandteachers.teacher.TeacherFragment
import io.github.wulkanowy.utils.getCompatDrawable import io.github.wulkanowy.utils.getCompatDrawable
import io.github.wulkanowy.utils.setOnItemClickListener import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_more.* import kotlinx.android.synthetic.main.fragment_more.*

View File

@ -2,7 +2,7 @@ package io.github.wulkanowy.ui.modules.timetable.completed
import android.content.res.Resources import android.content.res.Resources
import com.readystatesoftware.chuck.api.ChuckCollector import com.readystatesoftware.chuck.api.ChuckCollector
import io.github.wulkanowy.api.interceptor.FeatureDisabledException import io.github.wulkanowy.sdk.exception.FeatureDisabledException
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import javax.inject.Inject import javax.inject.Inject

View File

@ -28,7 +28,6 @@ import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.nextOrSameSchoolDay import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.nextSchoolDay import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.previousSchoolDay import io.github.wulkanowy.utils.previousSchoolDay
import io.github.wulkanowy.utils.shortcutWeekDayName
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
import io.reactivex.Maybe import io.reactivex.Maybe
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate

View File

@ -32,7 +32,7 @@ fun Grade.getBackgroundColor(theme: String): Int {
"B16CF1" -> R.color.grade_purple "B16CF1" -> R.color.grade_purple
else -> R.color.grade_material_default else -> R.color.grade_material_default
} }
"material" -> when (value) { "material" -> when (value.toInt()) {
6 -> R.color.grade_material_six 6 -> R.color.grade_material_six
5 -> R.color.grade_material_five 5 -> R.color.grade_material_five
4 -> R.color.grade_material_four 4 -> R.color.grade_material_four
@ -41,7 +41,7 @@ fun Grade.getBackgroundColor(theme: String): Int {
1 -> R.color.grade_material_one 1 -> R.color.grade_material_one
else -> R.color.grade_material_default else -> R.color.grade_material_default
} }
else -> when (value) { else -> when (value.toInt()) {
6 -> R.color.grade_vulcan_six 6 -> R.color.grade_vulcan_six
5 -> R.color.grade_vulcan_five 5 -> R.color.grade_vulcan_five
4 -> R.color.grade_vulcan_four 4 -> R.color.grade_vulcan_four

View File

@ -14,7 +14,6 @@ import android.security.keystore.KeyProperties.DIGEST_SHA512
import android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_OAEP import android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_OAEP
import android.security.keystore.KeyProperties.PURPOSE_DECRYPT import android.security.keystore.KeyProperties.PURPOSE_DECRYPT
import android.security.keystore.KeyProperties.PURPOSE_ENCRYPT import android.security.keystore.KeyProperties.PURPOSE_ENCRYPT
import android.util.Base64
import android.util.Base64.DEFAULT import android.util.Base64.DEFAULT
import android.util.Base64.decode import android.util.Base64.decode
import android.util.Base64.encode import android.util.Base64.encode
@ -60,7 +59,7 @@ fun encrypt(plainText: String, context: Context): String {
if (plainText.isEmpty()) throw ScramblerException("Text to be encrypted is empty") if (plainText.isEmpty()) throw ScramblerException("Text to be encrypted is empty")
if (SDK_INT < JELLY_BEAN_MR2) { if (SDK_INT < JELLY_BEAN_MR2) {
return String(Base64.encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET) return String(encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET)
} }
return try { return try {

View File

@ -1,9 +1,7 @@
Wersja 0.13.0 Wersja 0.14.0
- naprawiony plan lekcji po ostatniej aktualizacji VULCANa (19.11) - dodane zostały nowe opcje logowania, m.in. logowanie przez token
- naprawiona opcja wyboru motywu dla widżetu ze szczęśliwym numerkiem - naprawione zostało wyświetlanie nauczyciela, kiedy zastępstwo zostanie usunięte z danej lekcji
- nowa opcja zmiany języka i język rosyjski - naprawiony został biały ekran w szczęśliwym numerku
- możliwość łatwego nawigowania między dniami w planie lekcji przez kalendarz
- opcja ustawienia modyfikatora oceny na wartość 0,0
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases

View File

@ -0,0 +1,269 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.modules.login.advanced.LoginAdvancedFragment">
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/loginFormProgress"
style="@style/Widget.MaterialProgressBar.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
android:visibility="gone" />
<androidx.core.widget.NestedScrollView
android:id="@+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/loginFormContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/loginFormHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="32dp"
android:layout_marginRight="32dp"
android:gravity="center_horizontal"
android:text="@string/login_header_default"
android:textSize="16sp"
app:fontFamily="sans-serif-light"
app:layout_constraintBottom_toTopOf="@+id/loginTypeSwitch"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<RadioGroup
android:id="@+id/loginTypeSwitch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal"
android:paddingTop="24dp"
app:layout_constraintBottom_toTopOf="@+id/loginFormNameLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginFormHeader">
<RadioButton
android:id="@+id/loginTypeApi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="api"
android:text="@string/login_type_api" />
<RadioButton
android:id="@+id/loginTypeScrapper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:tag="scrapper"
android:text="@string/login_type_scrapper" />
<RadioButton
android:id="@+id/loginTypeHybrid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="hybrid"
android:text="@string/login_type_hybrid" />
</RadioGroup>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/loginFormNameLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:hint="@string/login_nickname_hint"
app:errorEnabled="true"
app:layout_constraintBottom_toTopOf="@+id/loginFormPassLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginTypeSwitch">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/loginFormName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autofillHints="emailAddress"
android:inputType="textEmailAddress"
android:maxLines="1"
tools:targetApi="o" />
<requestFocus />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/loginFormPassLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:hint="@string/login_password_hint"
app:errorEnabled="true"
app:layout_constraintBottom_toTopOf="@+id/loginFormHostLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginFormNameLayout"
app:passwordToggleEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/loginFormPass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autofillHints="password"
android:imeActionLabel="@string/login_sign_in"
android:imeOptions="actionDone"
android:inputType="textPassword"
android:maxLines="1"
app:fontFamily="sans-serif"
tools:targetApi="o" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/loginFormHostLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:layout_marginBottom="16dp"
android:hint="@string/login_host_hint"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/loginFormTokenLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginFormPassLayout">
<AutoCompleteTextView
android:id="@+id/loginFormHost"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false"
tools:ignore="Deprecated,LabelFor" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/loginFormTokenLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:hint="@string/login_token_hint"
app:errorEnabled="true"
app:layout_constraintBottom_toTopOf="@+id/loginFormSymbolLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/loginFormToken"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeActionLabel="@string/login_sign_in"
android:imeOptions="actionDone"
android:inputType="textCapCharacters"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/loginFormSymbolLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:hint="@string/login_symbol_hint"
app:errorEnabled="true"
app:layout_constraintBottom_toTopOf="@+id/loginFormPinLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginFormTokenLayout">
<AutoCompleteTextView
android:id="@+id/loginFormSymbol"
style="@style/Widget.MaterialComponents.TextInputEditText.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeActionLabel="@string/login_sign_in"
android:imeOptions="actionDone"
android:inputType="textAutoComplete|textNoSuggestions"
android:maxLines="1"
tools:ignore="LabelFor" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/loginFormPinLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:hint="@string/login_pin_hint"
app:errorEnabled="true"
app:layout_constraintBottom_toTopOf="@id/loginFormSignIn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/loginFormSymbolLayout">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/loginFormPin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeActionLabel="@string/login_sign_in"
android:imeOptions="actionDone"
android:inputType="number"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/loginFormSignIn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:layout_marginBottom="16dp"
android:text="@string/login_sign_in"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginFormPinLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</FrameLayout>

View File

@ -24,7 +24,8 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/loginFormContainer" android:id="@+id/loginFormContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:paddingBottom="16dp">
<LinearLayout <LinearLayout
android:id="@+id/loginFormContact" android:id="@+id/loginFormContact"
@ -197,50 +198,64 @@
tools:ignore="Deprecated,LabelFor" /> tools:ignore="Deprecated,LabelFor" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/loginFormAdvancedButton"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:text="@string/login_advanced"
android:textAppearance="?android:textAppearance"
android:textColor="@color/colorPrimary"
app:backgroundTint="?android:windowBackground"
app:fontFamily="sans-serif-medium"
app:layout_constraintBottom_toBottomOf="@id/loginFormSignIn"
app:layout_constraintEnd_toStartOf="@+id/loginFormSignIn"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@id/loginFormHostLayout"
app:layout_constraintTop_toTopOf="@+id/loginFormSignIn" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/loginFormSignIn" android:id="@+id/loginFormSignIn"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginTop="48dp" android:layout_marginTop="48dp"
android:layout_marginBottom="32dp" android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:layout_marginBottom="16dp"
android:text="@string/login_sign_in" android:text="@string/login_sign_in"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/loginFormHostLayout" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout" /> app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout" />
<TextView
android:id="@+id/loginFormPrivacyLink"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:gravity="start|center_vertical"
android:text="@string/login_privacy_policy"
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
app:fontFamily="sans-serif-medium"
app:layout_constraintStart_toStartOf="@id/loginFormAdvancedButton"
app:layout_constraintTop_toBottomOf="@+id/loginFormAdvancedButton"
tools:visibility="visible" />
<TextView <TextView
android:id="@+id/loginFormVersion" android:id="@+id/loginFormVersion"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="24dp" android:layout_marginTop="24dp"
android:layout_marginRight="24dp"
android:maxLines="2" android:maxLines="2"
android:textColor="?android:textColorSecondary" android:textColor="?android:textColorSecondary"
android:textSize="12sp" android:textSize="12sp"
android:visibility="invisible" android:visibility="visible"
app:layout_constraintBottom_toBottomOf="@id/loginFormSignIn" app:layout_constraintEnd_toEndOf="@+id/loginFormSignIn"
app:layout_constraintEnd_toStartOf="@+id/loginFormSignIn" app:layout_constraintTop_toBottomOf="@id/loginFormSignIn"
app:layout_constraintStart_toStartOf="@id/loginFormHostLayout"
app:layout_constraintTop_toTopOf="@+id/loginFormSignIn"
tools:text="Version 1.0.0" /> tools:text="Version 1.0.0" />
<com.google.android.material.button.MaterialButton
android:id="@+id/loginFormPrivacyLink"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:gravity="start|center_vertical"
android:text="@string/login_privacy_policy"
android:visibility="invisible"
app:fontFamily="sans-serif-medium"
app:layout_constraintBottom_toBottomOf="@id/loginFormSignIn"
app:layout_constraintEnd_toStartOf="@+id/loginFormSignIn"
app:layout_constraintStart_toStartOf="@id/loginFormHostLayout"
app:layout_constraintTop_toTopOf="@+id/loginFormSignIn"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</FrameLayout> </FrameLayout>

View File

@ -141,7 +141,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:imeActionLabel="@string/login_sign_in" android:imeActionLabel="@string/login_sign_in"
android:imeOptions="actionDone" android:imeOptions="actionDone"
android:inputType="textAutoComplete" android:inputType="textAutoComplete|textNoSuggestions"
android:maxLines="1" android:maxLines="1"
tools:ignore="LabelFor" /> tools:ignore="LabelFor" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>

View File

@ -27,6 +27,12 @@
<string name="login_password_hint">Hasło</string> <string name="login_password_hint">Hasło</string>
<string name="login_host_hint">Dziennik</string> <string name="login_host_hint">Dziennik</string>
<string name="login_symbol_hint">Symbol</string> <string name="login_symbol_hint">Symbol</string>
<string name="login_type_api">Mobilne API</string>
<string name="login_type_scrapper">Scrapper</string>
<string name="login_type_hybrid">Hybrydowe</string>
<string name="login_token_hint">Token</string>
<string name="login_pin_hint">PIN</string>
<string name="login_api_key_hint">Klucz API</string>
<string name="login_sign_in">Zaloguj</string> <string name="login_sign_in">Zaloguj</string>
<string name="login_invalid_password">To hasło jest za krótkie</string> <string name="login_invalid_password">To hasło jest za krótkie</string>
<string name="login_incorrect_password">Dane logowania są niepoprawne</string> <string name="login_incorrect_password">Dane logowania są niepoprawne</string>
@ -35,6 +41,7 @@
<string name="login_duplicate_student">Wybrany uczeń jest już zalogowany</string> <string name="login_duplicate_student">Wybrany uczeń jest już zalogowany</string>
<string name="login_symbol_helper">Symbol znajdziesz na stronie dziennika w Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne</string> <string name="login_symbol_helper">Symbol znajdziesz na stronie dziennika w Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne</string>
<string name="login_select_student">Wybierz uczniów do zalogowania w aplikacji</string> <string name="login_select_student">Wybierz uczniów do zalogowania w aplikacji</string>
<string name="login_advanced">Inne opcje</string>
<string name="login_privacy_policy">Polityka prywatności</string> <string name="login_privacy_policy">Polityka prywatności</string>
<string name="login_contact_header">Problemy z logowaniem? Napisz do nas!</string> <string name="login_contact_header">Problemy z logowaniem? Napisz do nas!</string>
<string name="login_contact_email">Email</string> <string name="login_contact_email">Email</string>
@ -261,8 +268,8 @@
<string name="about_feedback_summary">Wyślij zgłoszenie o błędzie poprzez e-maila</string> <string name="about_feedback_summary">Wyślij zgłoszenie o błędzie poprzez e-maila</string>
<string name="about_faq">FAQ</string> <string name="about_faq">FAQ</string>
<string name="about_faq_summary">Zobacz Najczęściej Zadawane Pytania</string> <string name="about_faq_summary">Zobacz Najczęściej Zadawane Pytania</string>
<string name="about_discord">Serwer discord</string> <string name="about_discord">Serwer Discord</string>
<string name="about_discord_summary">Dołącz do społeczności wulkanowego</string> <string name="about_discord_summary">Dołącz do społeczności Wulkanowego</string>
<string name="about_privacy">Polityka prywatności</string> <string name="about_privacy">Polityka prywatności</string>
<string name="about_privacy_summary">Zasady zbierania danych osobowych</string> <string name="about_privacy_summary">Zasady zbierania danych osobowych</string>
<string name="about_homepage">Strona domowa</string> <string name="about_homepage">Strona domowa</string>
@ -353,4 +360,5 @@
<string name="error_service_unavailable">Dziennik jest niedostępny. Spróbuj ponownie później</string> <string name="error_service_unavailable">Dziennik jest niedostępny. Spróbuj ponownie później</string>
<string name="error_unknown">Wystąpił nieoczekiwany błąd</string> <string name="error_unknown">Wystąpił nieoczekiwany błąd</string>
<string name="error_feature_disabled">Funkcja wyłączona przez szkołę</string> <string name="error_feature_disabled">Funkcja wyłączona przez szkołę</string>
<string name="error_feature_not_available">Funkcja niedostępna w tym trybie</string>
</resources> </resources>

View File

@ -26,6 +26,12 @@
<string name="login_nickname_hint">Email or nick</string> <string name="login_nickname_hint">Email or nick</string>
<string name="login_password_hint">Password</string> <string name="login_password_hint">Password</string>
<string name="login_host_hint">Register</string> <string name="login_host_hint">Register</string>
<string name="login_type_api">Mobile API</string>
<string name="login_type_scrapper">Scrapper</string>
<string name="login_type_hybrid">Hybrid</string>
<string name="login_token_hint">Token</string>
<string name="login_pin_hint">PIN</string>
<string name="login_api_key_hint">API key</string>
<string name="login_symbol_hint">Symbol</string> <string name="login_symbol_hint">Symbol</string>
<string name="login_sign_in">Sign in</string> <string name="login_sign_in">Sign in</string>
<string name="login_invalid_password">This password is too short</string> <string name="login_invalid_password">This password is too short</string>
@ -35,6 +41,7 @@
<string name="login_duplicate_student">The selected student is already logged in</string> <string name="login_duplicate_student">The selected student is already logged in</string>
<string name="login_symbol_helper">The symbol can be found on the register page in Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne</string> <string name="login_symbol_helper">The symbol can be found on the register page in Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne</string>
<string name="login_select_student">Select students to log in to the application</string> <string name="login_select_student">Select students to log in to the application</string>
<string name="login_advanced">Other options</string>
<string name="login_privacy_policy">Privacy policy</string> <string name="login_privacy_policy">Privacy policy</string>
<string name="login_contact_header">Trouble signing in? Contact us!</string> <string name="login_contact_header">Trouble signing in? Contact us!</string>
<string name="login_contact_email">Email</string> <string name="login_contact_email">Email</string>
@ -243,7 +250,7 @@
<string name="about_faq">FAQ</string> <string name="about_faq">FAQ</string>
<string name="about_faq_summary">Read Frequently Asked Questions</string> <string name="about_faq_summary">Read Frequently Asked Questions</string>
<string name="about_discord">Discord server</string> <string name="about_discord">Discord server</string>
<string name="about_discord_summary">Join the wulkanowy community</string> <string name="about_discord_summary">Join the Wulkanowy community</string>
<string name="about_privacy">Privacy policy</string> <string name="about_privacy">Privacy policy</string>
<string name="about_privacy_summary">Rules for collecting personal data</string> <string name="about_privacy_summary">Rules for collecting personal data</string>
<string name="about_homepage">Homepage</string> <string name="about_homepage">Homepage</string>
@ -334,4 +341,5 @@
<string name="error_service_unavailable">The log is not available. Try again later</string> <string name="error_service_unavailable">The log is not available. Try again later</string>
<string name="error_unknown">An unexpected error occurred</string> <string name="error_unknown">An unexpected error occurred</string>
<string name="error_feature_disabled">Feature disabled by your school</string> <string name="error_feature_disabled">Feature disabled by your school</string>
<string name="error_feature_not_available">Feature not available in this mode</string>
</resources> </resources>

View File

@ -1,23 +1,22 @@
package io.github.wulkanowy.data.repositories.attendance package io.github.wulkanowy.data.repositories.attendance
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.attendance.Attendance
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Attendance
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single import io.reactivex.Single
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import java.sql.Date import org.threeten.bp.LocalDate.of
class AttendanceRemoteTest { class AttendanceRemoteTest {
@SpyK @MockK
private var mockApi = Api() private lateinit var mockSdk: Sdk
@MockK @MockK
private lateinit var semesterMock: Semester private lateinit var semesterMock: Semester
@ -29,29 +28,44 @@ class AttendanceRemoteTest {
@Test @Test
fun getAttendanceTest() { fun getAttendanceTest() {
every { mockApi.getAttendance( every {
LocalDate.of(2018, 9, 10), mockSdk.getAttendance(
LocalDate.of(2018, 9, 15) of(2018, 9, 10),
) } returns Single.just(listOf( of(2018, 9, 15),
getAttendance("2018-09-10"), 1
getAttendance("2018-09-17") )
} returns Single.just(listOf(
getAttendance(of(2018, 9, 10)),
getAttendance(of(2018, 9, 17))
)) ))
every { mockApi.diaryId } returns 1
every { semesterMock.studentId } returns 1 every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 1 every { semesterMock.diaryId } returns 1
every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
val attendance = AttendanceRemote(mockApi).getAttendance(semesterMock, val attendance = AttendanceRemote(mockSdk).getAttendance(semesterMock,
LocalDate.of(2018, 9, 10), of(2018, 9, 10),
LocalDate.of(2018, 9, 15)).blockingGet() of(2018, 9, 15)
).blockingGet()
assertEquals(2, attendance.size) assertEquals(2, attendance.size)
} }
private fun getAttendance(dateString: String): Attendance { private fun getAttendance(date: LocalDate): Attendance {
return Attendance().apply { return Attendance(
subject = "Fizyka" subject = "Fizyka",
name = "Obecność" name = "Obecność",
date = Date.valueOf(dateString) date = date,
} number = 0,
deleted = false,
excusable = false,
excused = false,
exemption = false,
lateness = false,
presence = false,
categoryId = 1,
absence = false
)
} }
} }

View File

@ -1,23 +1,22 @@
package io.github.wulkanowy.data.repositories.completedlessons package io.github.wulkanowy.data.repositories.completedlessons
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.timetable.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.CompletedLesson
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single import io.reactivex.Single
import org.junit.Assert import org.junit.Assert
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import java.sql.Date import org.threeten.bp.LocalDate.of
class CompletedLessonsRemoteTest { class CompletedLessonsRemoteTest {
@SpyK @MockK
private var mockApi = Api() private lateinit var mockSdk: Sdk
@MockK @MockK
private lateinit var semesterMock: Semester private lateinit var semesterMock: Semester
@ -30,27 +29,39 @@ class CompletedLessonsRemoteTest {
@Test @Test
fun getCompletedLessonsTest() { fun getCompletedLessonsTest() {
every { every {
mockApi.getCompletedLessons( mockSdk.getCompletedLessons(
LocalDate.of(2018, 9, 10), of(2018, 9, 10),
LocalDate.of(2018, 9, 15) of(2018, 9, 15)
) )
} returns Single.just(listOf( } returns Single.just(listOf(
getCompletedLesson("2018-09-10"), getCompletedLesson(of(2018, 9, 10)),
getCompletedLesson("2018-09-17") getCompletedLesson(of(2018, 9, 17))
)) ))
every { mockApi.diaryId } returns 1
every { semesterMock.studentId } returns 1 every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 1 every { semesterMock.diaryId } returns 1
every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
val completed = CompletedLessonsRemote(mockApi).getCompletedLessons(semesterMock, val completed = CompletedLessonsRemote(mockSdk).getCompletedLessons(semesterMock,
LocalDate.of(2018, 9, 10), of(2018, 9, 10),
LocalDate.of(2018, 9, 15) of(2018, 9, 15)
).blockingGet() ).blockingGet()
Assert.assertEquals(2, completed.size) Assert.assertEquals(2, completed.size)
} }
private fun getCompletedLesson(dateString: String): CompletedLesson { private fun getCompletedLesson(date: LocalDate): CompletedLesson {
return CompletedLesson().apply { date = Date.valueOf(dateString) } return CompletedLesson(
date = date,
subject = "",
absence = "",
resources = "",
substitution = "",
teacherSymbol = "",
teacher = "",
topic = "",
number = 1
)
} }
} }

View File

@ -1,23 +1,22 @@
package io.github.wulkanowy.data.repositories.exam package io.github.wulkanowy.data.repositories.exam
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.exams.Exam
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Exam
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single import io.reactivex.Single
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import java.sql.Date import org.threeten.bp.LocalDate.of
class ExamRemoteTest { class ExamRemoteTest {
@SpyK @MockK
private var mockApi = Api() private lateinit var mockSdk: Sdk
@MockK @MockK
private lateinit var semesterMock: Semester private lateinit var semesterMock: Semester
@ -29,35 +28,41 @@ class ExamRemoteTest {
@Test @Test
fun getExamsTest() { fun getExamsTest() {
every { mockApi.getExams( every {
LocalDate.of(2018, 9, 10), mockSdk.getExams(
LocalDate.of(2018, 9, 15) of(2018, 9, 10),
) } returns Single.just(listOf( of(2018, 9, 15),
getExam("2018-09-10"), 1
getExam("2018-09-17") )
} returns Single.just(listOf(
getExam(of(2018, 9, 10)),
getExam(of(2018, 9, 17))
)) ))
every { mockApi.diaryId } returns 1
every { semesterMock.studentId } returns 1 every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 1 every { semesterMock.diaryId } returns 1
every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
val exams = ExamRemote(mockApi).getExams(semesterMock, val exams = ExamRemote(mockSdk)
LocalDate.of(2018, 9, 10), .getExams(semesterMock,
LocalDate.of(2018, 9, 15) of(2018, 9, 10),
).blockingGet() of(2018, 9, 15)
).blockingGet()
assertEquals(2, exams.size) assertEquals(2, exams.size)
} }
private fun getExam(dateString: String): Exam { private fun getExam(date: LocalDate): Exam {
return Exam().apply { return Exam(
subject = "" subject = "",
group = "" group = "",
type = "" type = "",
description = "" description = "",
teacher = "" teacher = "",
teacherSymbol = "" teacherSymbol = "",
date = Date.valueOf(dateString) date = date,
entryDate = Date.valueOf(dateString) entryDate = date
} )
} }
} }

View File

@ -1,13 +1,12 @@
package io.github.wulkanowy.data.repositories.gradestatistics package io.github.wulkanowy.data.repositories.gradestatistics
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.grades.GradePointsSummary
import io.github.wulkanowy.api.grades.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.GradePointsStatistics
import io.github.wulkanowy.sdk.pojo.GradeStatistics
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single import io.reactivex.Single
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
@ -15,8 +14,8 @@ import org.junit.Test
class GradeStatisticsRemoteTest { class GradeStatisticsRemoteTest {
@SpyK @MockK
private var mockApi = Api() private lateinit var mockSdk: Sdk
@MockK @MockK
private lateinit var semesterMock: Semester private lateinit var semesterMock: Semester
@ -28,48 +27,51 @@ class GradeStatisticsRemoteTest {
@Test @Test
fun getGradeStatisticsTest() { fun getGradeStatisticsTest() {
every { mockApi.getGradesPartialStatistics(1) } returns Single.just(listOf( every { mockSdk.getGradesPartialStatistics(1) } returns Single.just(listOf(
getGradeStatistics("Fizyka"), getGradeStatistics("Fizyka"),
getGradeStatistics("Matematyka") getGradeStatistics("Matematyka")
)) ))
every { mockApi.diaryId } returns 1
every { semesterMock.studentId } returns 1 every { semesterMock.studentId } returns 1
every { semesterMock.semesterId } returns 1
every { semesterMock.semesterName } returns 2
every { semesterMock.diaryId } returns 1 every { semesterMock.diaryId } returns 1
every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
val stats = GradeStatisticsRemote(mockApi).getGradeStatistics(semesterMock, false).blockingGet() val stats = GradeStatisticsRemote(mockSdk).getGradeStatistics(semesterMock, false).blockingGet()
assertEquals(2, stats.size) assertEquals(2, stats.size)
} }
@Test @Test
fun getGradePointsStatisticsTest() { fun getGradePointsStatisticsTest() {
every { mockApi.getGradesPointsStatistics(1) } returns Single.just(listOf( every { mockSdk.getGradesPointsStatistics(1) } returns Single.just(listOf(
getGradePointsStatistics("Fizyka"), getGradePointsStatistics("Fizyka"),
getGradePointsStatistics("Matematyka") getGradePointsStatistics("Matematyka")
)) ))
every { mockApi.diaryId } returns 1
every { semesterMock.studentId } returns 1 every { semesterMock.studentId } returns 1
every { semesterMock.semesterId } returns 1
every { semesterMock.semesterName } returns 2
every { semesterMock.diaryId } returns 1 every { semesterMock.diaryId } returns 1
every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
val stats = GradeStatisticsRemote(mockApi).getGradePointsStatistics(semesterMock).blockingGet() val stats = GradeStatisticsRemote(mockSdk).getGradePointsStatistics(semesterMock).blockingGet()
assertEquals(2, stats.size) assertEquals(2, stats.size)
} }
private fun getGradeStatistics(subjectName: String): GradeStatistics { private fun getGradeStatistics(subjectName: String): GradeStatistics {
return GradeStatistics().apply { return GradeStatistics(
subject = subjectName subject = subjectName,
gradeValue = 5 gradeValue = 5,
amount = 10 amount = 10,
} grade = "",
semesterId = 1
)
} }
private fun getGradePointsStatistics(subjectName: String): GradePointsSummary { private fun getGradePointsStatistics(subjectName: String): GradePointsStatistics {
return GradePointsSummary( return GradePointsStatistics(
semesterId = 1,
subject = subjectName, subject = subjectName,
student = 0.80, student = 0.80,
others = 0.40 others = 0.40

View File

@ -1,8 +1,7 @@
package io.github.wulkanowy.data.repositories.luckynumber package io.github.wulkanowy.data.repositories.luckynumber
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRemote import io.github.wulkanowy.sdk.Sdk
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
@ -16,7 +15,7 @@ import org.threeten.bp.LocalDate
class LuckyNumberRemoteTest { class LuckyNumberRemoteTest {
@SpyK @SpyK
private var mockApi = Api() private var mockSdk = Sdk()
@MockK @MockK
private lateinit var semesterMock: Semester private lateinit var semesterMock: Semester
@ -28,13 +27,13 @@ class LuckyNumberRemoteTest {
@Test @Test
fun getLuckyNumberTest() { fun getLuckyNumberTest() {
every { mockApi.getLuckyNumber() } returns Maybe.just(14) every { mockSdk.getLuckyNumber() } returns Maybe.just(14)
every { mockApi.diaryId } returns 1 every { mockSdk.diaryId } returns 1
every { semesterMock.studentId } returns 1 every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 1 every { semesterMock.diaryId } returns 1
val luckyNumber = LuckyNumberRemote(mockApi) val luckyNumber = LuckyNumberRemote(mockSdk)
.getLuckyNumber(semesterMock) .getLuckyNumber(semesterMock)
.blockingGet() .blockingGet()

View File

@ -1,7 +1,7 @@
package io.github.wulkanowy.data.repositories.semester package io.github.wulkanowy.data.repositories.semester
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.UnitTestInternetObservingStrategy import io.github.wulkanowy.data.repositories.UnitTestInternetObservingStrategy
@ -24,7 +24,7 @@ class SemesterRepositoryTest {
private lateinit var semesterLocal: SemesterLocal private lateinit var semesterLocal: SemesterLocal
@Mock @Mock
private lateinit var apiHelper: ApiHelper private lateinit var sdkHelper: SdkHelper
@Mock @Mock
private lateinit var student: Student private lateinit var student: Student
@ -38,7 +38,7 @@ class SemesterRepositoryTest {
@Before @Before
fun initTest() { fun initTest() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
semesterRepository = SemesterRepository(semesterRemote, semesterLocal, settings, apiHelper) semesterRepository = SemesterRepository(semesterRemote, semesterLocal, settings, sdkHelper)
} }
@Test @Test
@ -48,7 +48,7 @@ class SemesterRepositoryTest {
createSemesterEntity(true) createSemesterEntity(true)
) )
doNothing().`when`(apiHelper).initApi(student) doNothing().`when`(sdkHelper).init(student)
doReturn(Maybe.empty<Semester>()).`when`(semesterLocal).getSemesters(student) doReturn(Maybe.empty<Semester>()).`when`(semesterLocal).getSemesters(student)
doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student) doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student)
@ -65,7 +65,7 @@ class SemesterRepositoryTest {
createSemesterEntity(true) createSemesterEntity(true)
) )
doNothing().`when`(apiHelper).initApi(student) doNothing().`when`(sdkHelper).init(student)
doReturn(Maybe.empty<Semester>()).`when`(semesterLocal).getSemesters(student) doReturn(Maybe.empty<Semester>()).`when`(semesterLocal).getSemesters(student)
doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student) doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student)

View File

@ -1,19 +1,23 @@
package io.github.wulkanowy.data.repositories.student package io.github.wulkanowy.data.repositories.student
import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.SdkHelper
import io.github.wulkanowy.api.register.Student import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Student
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single import io.reactivex.Single
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyString
import org.mockito.Mockito.doReturn import org.mockito.Mockito.doReturn
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
class StudentRemoteTest { class StudentRemoteTest {
@Mock @Mock
private lateinit var mockApi: Api private lateinit var mockSdk: Sdk
@Before @Before
fun initApi() { fun initApi() {
@ -22,11 +26,31 @@ class StudentRemoteTest {
@Test @Test
fun testRemoteAll() { fun testRemoteAll() {
doReturn(Single.just(listOf(Student("", "", 1, "test", "", "", "", 1, Api.LoginType.AUTO)))) doReturn(Single.just(listOf(getStudent("test")))).`when`(mockSdk).getStudentsFromScrapper(anyString(), anyString(), anyString(), anyString())
.`when`(mockApi).getStudents()
val students = StudentRemote(mockApi).getStudents("", "", "").blockingGet() val students = StudentRemote(mockSdk).getStudentsScrapper("", "", "http://fakelog.cf", "").blockingGet()
assertEquals(1, students.size) assertEquals(1, students.size)
assertEquals("test", students.first().studentName) assertEquals("test", students.first().studentName)
} }
private fun getStudent(name: String): Student {
return Student(
email = "",
symbol = "",
studentId = 0,
userLoginId = 0,
studentName = name,
schoolSymbol = "",
schoolName = "",
className = "",
classId = 0,
certificateKey = "",
privateKey = "",
loginMode = Sdk.Mode.SCRAPPER,
mobileBaseUrl = "",
loginType = Sdk.ScrapperLoginType.STANDARD,
scrapperBaseUrl = "",
isParent = false
)
}
} }

View File

@ -1,24 +1,23 @@
package io.github.wulkanowy.data.repositories.timetable package io.github.wulkanowy.data.repositories.timetable
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.timetable.Timetable
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.timetable.TimetableRemote import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Timetable
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single import io.reactivex.Single
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import java.sql.Date import org.threeten.bp.LocalDate.of
import org.threeten.bp.LocalDateTime.now
class TimetableRemoteTest { class TimetableRemoteTest {
@SpyK @MockK
private var mockApi = Api() private lateinit var mockSdk: Sdk
@MockK @MockK
private lateinit var semesterMock: Semester private lateinit var semesterMock: Semester
@ -30,26 +29,46 @@ class TimetableRemoteTest {
@Test @Test
fun getTimetableTest() { fun getTimetableTest() {
every { mockApi.getTimetable( every {
LocalDate.of(2018, 9, 10), mockSdk.getTimetable(
LocalDate.of(2018, 9, 15) of(2018, 9, 10),
) } returns Single.just(listOf( of(2018, 9, 15)
getTimetable("2018-09-10"), )
getTimetable("2018-09-17") } returns Single.just(listOf(
getTimetable(of(2018, 9, 10)),
getTimetable(of(2018, 9, 17))
)) ))
every { mockApi.diaryId } returns 1
every { semesterMock.studentId } returns 1 every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 1 every { semesterMock.diaryId } returns 1
every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
val timetable = TimetableRemote(mockApi).getTimetable(semesterMock, val timetable = TimetableRemote(mockSdk).getTimetable(semesterMock,
LocalDate.of(2018, 9, 10), of(2018, 9, 10),
LocalDate.of(2018, 9, 15) of(2018, 9, 15)
).blockingGet() ).blockingGet()
assertEquals(2, timetable.size) assertEquals(2, timetable.size)
} }
private fun getTimetable(dateString: String): Timetable { private fun getTimetable(date: LocalDate): Timetable {
return Timetable(date = Date.valueOf(dateString)) return Timetable(
date = date,
number = 0,
teacherOld = "",
subjectOld = "",
roomOld = "",
subject = "",
teacher = "",
group = "",
canceled = false,
changes = false,
info = "",
room = "",
end = now(),
start = now(),
studentPlan = true
)
} }
} }

View File

@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.grade.GradeRepository import io.github.wulkanowy.data.repositories.grade.GradeRepository
import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single import io.reactivex.Single
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
@ -30,7 +31,7 @@ class GradeAverageProviderTest {
private lateinit var gradeAverageProvider: GradeAverageProvider private lateinit var gradeAverageProvider: GradeAverageProvider
private val student = Student("", "", "", "", "", 101, "", "", "", "", 1, true, LocalDateTime.now()) private val student = Student("", "", "", "SCRAPPER", "", "", false, "", "", "", 101, 0, "", "", "", "", 1, true, LocalDateTime.now())
private val semesters = mutableListOf( private val semesters = mutableListOf(
Semester(101, 10, "", 1, 21, 1, false, now(), now(), 1, 1), Semester(101, 10, "", 1, 21, 1, false, now(), now(), 1, 1),
@ -39,17 +40,22 @@ class GradeAverageProviderTest {
) )
private val firstGrades = listOf( private val firstGrades = listOf(
getGrade(22, "Matematyka", 4), getGrade(22, "Matematyka", 4.0),
getGrade(22, "Matematyka", 3), getGrade(22, "Matematyka", 3.0),
getGrade(22, "Fizyka", 6), getGrade(22, "Fizyka", 6.0),
getGrade(22, "Fizyka", 1) getGrade(22, "Fizyka", 1.0)
) )
private val secondGrade = listOf( private val secondGrade = listOf(
getGrade(23, "Matematyka", 2), getGrade(23, "Matematyka", 2.0),
getGrade(23, "Matematyka", 3), getGrade(23, "Matematyka", 3.0),
getGrade(23, "Fizyka", 4), getGrade(23, "Fizyka", 4.0),
getGrade(23, "Fizyka", 2) getGrade(23, "Fizyka", 2.0)
)
private val secondGradeWithModifier = listOf(
getGrade(24, "Język polski", 3.0, -0.50),
getGrade(24, "Język polski", 4.0, 0.25)
) )
@Before @Before
@ -78,6 +84,54 @@ class GradeAverageProviderTest {
assertEquals(3.0, averages["Fizyka"]) assertEquals(3.0, averages["Fizyka"])
} }
@Test
fun onlyOneSemester_gradesWithModifiers_default() {
doReturn("only_one_semester").`when`(preferencesRepository).gradeAverageMode
doReturn(Single.just(emptyList<GradeSummary>())).`when`(gradeSummaryRepository).getGradesSummary(semesters[2], true)
doReturn(Single.just(secondGradeWithModifier)).`when`(gradeRepository).getGrades(student, semesters[2], true)
val averages = gradeAverageProvider.getGradeAverage(student, semesters, semesters[2].semesterId, true)
.blockingGet()
assertEquals(3.5, averages["Język polski"])
}
@Test
fun onlyOneSemester_gradesWithModifiers_api() {
doReturn("only_one_semester").`when`(preferencesRepository).gradeAverageMode
doReturn(Single.just(emptyList<GradeSummary>())).`when`(gradeSummaryRepository).getGradesSummary(semesters[2], true)
doReturn(Single.just(secondGradeWithModifier)).`when`(gradeRepository).getGrades(student.copy(loginMode = Sdk.Mode.API.name), semesters[2], true)
val averages = gradeAverageProvider.getGradeAverage(student.copy(loginMode = Sdk.Mode.API.name), semesters, semesters[2].semesterId, true)
.blockingGet()
assertEquals(3.375, averages["Język polski"])
}
@Test
fun onlyOneSemester_gradesWithModifiers_scrapper() {
doReturn("only_one_semester").`when`(preferencesRepository).gradeAverageMode
doReturn(Single.just(emptyList<GradeSummary>())).`when`(gradeSummaryRepository).getGradesSummary(semesters[2], true)
doReturn(Single.just(secondGradeWithModifier)).`when`(gradeRepository).getGrades(student.copy(loginMode = Sdk.Mode.SCRAPPER.name), semesters[2], true)
val averages = gradeAverageProvider.getGradeAverage(student.copy(loginMode = Sdk.Mode.SCRAPPER.name), semesters, semesters[2].semesterId, true)
.blockingGet()
assertEquals(3.5, averages["Język polski"])
}
@Test
fun onlyOneSemester_gradesWithModifiers_hybrid() {
doReturn("only_one_semester").`when`(preferencesRepository).gradeAverageMode
doReturn(Single.just(emptyList<GradeSummary>())).`when`(gradeSummaryRepository).getGradesSummary(semesters[2], true)
doReturn(Single.just(secondGradeWithModifier)).`when`(gradeRepository).getGrades(student.copy(loginMode = Sdk.Mode.HYBRID.name), semesters[2], true)
val averages = gradeAverageProvider.getGradeAverage(student.copy(loginMode = Sdk.Mode.HYBRID.name), semesters, semesters[2].semesterId, true)
.blockingGet()
assertEquals(3.375, averages["Język polski"])
}
@Test @Test
fun allYearFirstSemesterTest() { fun allYearFirstSemesterTest() {
doReturn("all_year").`when`(preferencesRepository).gradeAverageMode doReturn("all_year").`when`(preferencesRepository).gradeAverageMode
@ -147,13 +201,13 @@ class GradeAverageProviderTest {
assertEquals(3.25, averages["Fizyka"]) assertEquals(3.25, averages["Fizyka"])
} }
private fun getGrade(semesterId: Int, subject: String, value: Int): Grade { private fun getGrade(semesterId: Int, subject: String, value: Double, modifier: Double = 0.0): Grade {
return Grade( return Grade(
studentId = 101, studentId = 101,
semesterId = semesterId, semesterId = semesterId,
subject = subject, subject = subject,
value = value, value = value,
modifier = .0, modifier = modifier,
weightValue = 1.0, weightValue = 1.0,
teacher = "", teacher = "",
date = now(), date = now(),

View File

@ -43,7 +43,7 @@ class LoginFormPresenterTest {
fun initPresenter() { fun initPresenter() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
clearInvocations(repository, loginFormView) clearInvocations(repository, loginFormView)
presenter = LoginFormPresenter(TestSchedulersProvider(), repository, errorHandler, analytics, appInfo) presenter = LoginFormPresenter(TestSchedulersProvider(), repository, errorHandler, analytics)
presenter.onAttachView(loginFormView) presenter.onAttachView(loginFormView)
} }
@ -90,9 +90,8 @@ class LoginFormPresenterTest {
@Test @Test
fun loginTest() { fun loginTest() {
val studentTest = Student(email = "test@", password = "123", endpoint = "https://fakelog.cf", loginType = "AUTO", studentName = "", schoolSymbol = "", schoolName = "", studentId = 0, classId = 1, isCurrent = false, symbol = "", registrationDate = now(), className = "") val studentTest = Student(email = "test@", password = "123", scrapperBaseUrl = "https://fakelog.cf", loginType = "AUTO", studentName = "", schoolSymbol = "", schoolName = "", studentId = 0, classId = 1, isCurrent = false, symbol = "", registrationDate = now(), className = "", mobileBaseUrl = "", privateKey = "", certificateKey = "", loginMode = "", userLoginId = 0, isParent = false)
doReturn(Single.just(listOf(studentTest))) doReturn(Single.just(listOf(studentTest))).`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString())
.`when`(repository).getStudents(anyString(), anyString(), anyString(), anyString())
`when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formNameValue).thenReturn("@")
`when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formPassValue).thenReturn("123456")
@ -109,7 +108,7 @@ class LoginFormPresenterTest {
@Test @Test
fun loginEmptyTest() { fun loginEmptyTest() {
doReturn(Single.just(emptyList<Student>())) doReturn(Single.just(emptyList<Student>()))
.`when`(repository).getStudents(anyString(), anyString(), anyString(), anyString()) .`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString())
`when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formNameValue).thenReturn("@")
`when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formPassValue).thenReturn("123456")
`when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf")
@ -125,7 +124,7 @@ class LoginFormPresenterTest {
@Test @Test
fun loginEmptyTwiceTest() { fun loginEmptyTwiceTest() {
doReturn(Single.just(emptyList<Student>())) doReturn(Single.just(emptyList<Student>()))
.`when`(repository).getStudents(anyString(), anyString(), anyString(), anyString()) .`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString())
`when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formNameValue).thenReturn("@")
`when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formPassValue).thenReturn("123456")
`when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf")
@ -142,8 +141,7 @@ class LoginFormPresenterTest {
@Test @Test
fun loginErrorTest() { fun loginErrorTest() {
val testException = RuntimeException("test") val testException = RuntimeException("test")
doReturn(Single.error<List<Student>>(testException)) doReturn(Single.error<List<Student>>(testException)).`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString())
.`when`(repository).getStudents(anyString(), anyString(), anyString(), anyString())
`when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formNameValue).thenReturn("@")
`when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formPassValue).thenReturn("123456")
`when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf")
@ -157,4 +155,3 @@ class LoginFormPresenterTest {
verify(errorHandler).dispatch(testException) verify(errorHandler).dispatch(testException)
} }
} }

View File

@ -32,7 +32,7 @@ class LoginStudentSelectPresenterTest {
private lateinit var presenter: LoginStudentSelectPresenter private lateinit var presenter: LoginStudentSelectPresenter
private val testStudent by lazy { Student(email = "test", password = "test123", endpoint = "https://fakelog.cf", loginType = "AUTO", symbol = "", isCurrent = false, studentId = 0, schoolName = "", schoolSymbol = "", classId = 1, studentName = "", registrationDate = now(), className = "") } private val testStudent by lazy { Student(email = "test", password = "test123", scrapperBaseUrl = "https://fakelog.cf", loginType = "AUTO", symbol = "", isCurrent = false, studentId = 0, schoolName = "", schoolSymbol = "", classId = 1, studentName = "", registrationDate = now(), className = "", loginMode = "", certificateKey = "", privateKey = "", mobileBaseUrl = "", userLoginId = 1, isParent = false) }
private val testException by lazy { RuntimeException("Problem") } private val testException by lazy { RuntimeException("Problem") }

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.utils package io.github.wulkanowy.utils
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.GradeSummary
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
@ -22,11 +23,11 @@ class GradeExtensionTest {
@Test @Test
fun calcWeightedAverage() { fun calcWeightedAverage() {
assertEquals(3.47, listOf( assertEquals(3.47, listOf(
createGrade(5, 6.0, 0.33), createGrade(5.0, 6.0, 0.33),
createGrade(5, 5.0, -0.33), createGrade(5.0, 5.0, -0.33),
createGrade(4, 1.0, 0.0), createGrade(4.0, 1.0, 0.0),
createGrade(1, 9.0, 0.5), createGrade(1.0, 9.0, 0.5),
createGrade(0, .0, 0.0) createGrade(0.0, .0, 0.0)
).calcAverage(), 0.005) ).calcAverage(), 0.005)
} }
@ -40,25 +41,33 @@ class GradeExtensionTest {
).calcAverage(), 0.005) ).calcAverage(), 0.005)
} }
@Test
fun getBackgroundColor() {
assertEquals(R.color.grade_material_five, createGrade(5.0).getBackgroundColor("material"))
assertEquals(R.color.grade_material_five, createGrade(5.5).getBackgroundColor("material"))
assertEquals(R.color.grade_material_five, createGrade(5.9).getBackgroundColor("material"))
assertEquals(R.color.grade_vulcan_five, createGrade(5.9).getBackgroundColor("whatever"))
}
@Test @Test
fun changeModifier_zero() { fun changeModifier_zero() {
assertEquals(.0, createGrade(5, .0, .5).changeModifier(.0, .0).modifier, .0) assertEquals(.0, createGrade(5.0, .0, .5).changeModifier(.0, .0).modifier, .0)
assertEquals(.0, createGrade(5, .0, -.5).changeModifier(.0, .0).modifier, .0) assertEquals(.0, createGrade(5.0, .0, -.5).changeModifier(.0, .0).modifier, .0)
} }
@Test @Test
fun changeModifier_plus() { fun changeModifier_plus() {
assertEquals(.33, createGrade(5, .0, .25).changeModifier(.33, .50).modifier, .0) assertEquals(.33, createGrade(5.0, .0, .25).changeModifier(.33, .50).modifier, .0)
assertEquals(.25, createGrade(5, .0, .33).changeModifier(.25, .0).modifier, .0) assertEquals(.25, createGrade(5.0, .0, .33).changeModifier(.25, .0).modifier, .0)
} }
@Test @Test
fun changeModifier_minus() { fun changeModifier_minus() {
assertEquals(-.33, createGrade(5, .0, -.25).changeModifier(.25, .33).modifier, .0) assertEquals(-.33, createGrade(5.0, .0, -.25).changeModifier(.25, .33).modifier, .0)
assertEquals(-.25, createGrade(5, .0, -.33).changeModifier(.0, .25).modifier, .0) assertEquals(-.25, createGrade(5.0, .0, -.33).changeModifier(.0, .25).modifier, .0)
} }
private fun createGrade(value: Int, weightValue: Double = .0, modifier: Double = 0.25): Grade { private fun createGrade(value: Double, weightValue: Double = .0, modifier: Double = 0.25): Grade {
return Grade( return Grade(
semesterId = 1, semesterId = 1,
studentId = 1, studentId = 1,