1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-19 23:09:09 -05:00

Migration to Wulkanowy SDK (#336)

This commit is contained in:
Mikołaj Pich 2019-12-22 00:14:46 +01:00 committed by Rafał Borcz
parent 826ea32fc0
commit 304c49d61e
90 changed files with 3287 additions and 570 deletions

View File

@ -122,7 +122,7 @@ configurations.all {
}
dependencies {
implementation "io.github.wulkanowy:api:0.13.0"
implementation "io.github.wulkanowy:sdk:4e21f7d"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.2.0-rc01"

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,13 +1,11 @@
package io.github.wulkanowy.data.repositories.timetable
import io.github.wulkanowy.api.toDate
import io.github.wulkanowy.utils.toDate
import org.threeten.bp.LocalDateTime
import org.threeten.bp.LocalDateTime.now
import io.github.wulkanowy.api.timetable.Timetable as TimetableRemote
import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote
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(
studentId = 1,
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(
number = number,
start = start.toDate(),
end = start.plusMinutes(45).toDate(),
date = start.toLocalDate().toDate(),
start = start,
end = start.plusMinutes(45),
date = start.toLocalDate(),
subject = subject,
group = "",
room = room,
teacher = teacher,
info = "",
changes = changes,
canceled = false
canceled = false,
roomOld = "",
subjectOld = "",
teacherOld = "",
studentPlan = true
)
}

View File

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

View File

@ -6,14 +6,17 @@ import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.SdkHelper
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy
import io.github.wulkanowy.sdk.Sdk
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
import io.mockk.runs
import io.reactivex.Single
import org.junit.After
import org.junit.Before
@ -27,8 +30,8 @@ import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class)
class TimetableRepositoryTest {
@SpyK
private var mockApi = Api()
@MockK
private lateinit var mockSdk: Sdk
private val settings = InternetObservingSettings.builder()
.strategy(TestInternetObservingStrategy())
@ -48,10 +51,13 @@ class TimetableRepositoryTest {
MockKAnnotations.init(this)
testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build()
timetableLocal = TimetableLocal(testDb.timetableDao)
timetableRemote = TimetableRemote(mockApi)
timetableRemote = TimetableRemote(mockSdk)
every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 2
every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
}
@After
@ -62,17 +68,17 @@ class TimetableRepositoryTest {
@Test
fun copyRoomToCompletedFromPrevious() {
timetableLocal.saveTimetable(listOf(
createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda"),
createTimetableLocal(2, of(2019, 3, 5, 8, 50), "321", "Religia"),
createTimetableLocal(3, of(2019, 3, 5, 9, 40), "213", "W-F"),
createTimetableLocal(4, of(2019, 3, 5, 10, 30), "213", "W-F", "Jan Kowalski")
createTimetableLocal(of(2019, 3, 5, 8, 0), 1, "123", "Przyroda"),
createTimetableLocal(of(2019, 3, 5, 8, 50), 2, "321", "Religia"),
createTimetableLocal(of(2019, 3, 5, 9, 40), 3, "213", "W-F"),
createTimetableLocal(of(2019, 3, 5, 10, 30),3, "213", "W-F", "Jan Kowalski")
))
every { mockApi.getTimetable(any(), any()) } returns Single.just(listOf(
createTimetableRemote(1, of(2019, 3, 5, 8, 0), "", "Przyroda"),
createTimetableRemote(2, of(2019, 3, 5, 8, 50), "", "Religia"),
createTimetableRemote(3, of(2019, 3, 5, 9, 40), "", "W-F"),
createTimetableRemote(4, of(2019, 3, 5, 10, 30), "", "W-F")
every { mockSdk.getTimetable(any(), any()) } returns Single.just(listOf(
createTimetableRemote(of(2019, 3, 5, 8, 0), 1, "", "Przyroda"),
createTimetableRemote(of(2019, 3, 5, 8, 50), 2, "", "Religia"),
createTimetableRemote(of(2019, 3, 5, 9, 40), 3, "", "W-F"),
createTimetableRemote(of(2019, 3, 5, 10, 30), 4, "", "W-F")
))
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote)
@ -88,17 +94,17 @@ class TimetableRepositoryTest {
@Test
fun copyTeacherToCompletedFromPrevious() {
timetableLocal.saveTimetable(listOf(
createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda", "Jan Garnkiewicz", false),
createTimetableLocal(2, of(2019, 3, 5, 8, 50), "321", "Religia", "Paweł Jumper", false),
createTimetableLocal(3, of(2019, 3, 5, 9, 40), "213", "W-F", "", true),
createTimetableLocal(4, of(2019, 3, 5, 10, 30), "213", "W-F", "", false)
createTimetableLocal(of(2019, 3, 5, 8, 0), 1, "123", "Przyroda", "Jan Garnkiewicz", false),
createTimetableLocal(of(2019, 3, 5, 8, 50), 2, "321", "Religia", "Paweł Jumper", false),
createTimetableLocal(of(2019, 3, 5, 9, 40), 3, "213", "W-F", "", true),
createTimetableLocal(of(2019, 3, 5, 10, 30), 4, "213", "W-F", "", false)
))
every { mockApi.getTimetable(any(), any()) } returns Single.just(listOf(
createTimetableRemote(1, of(2019, 3, 5, 8, 0), "", "Przyroda", "", true), // should override local
createTimetableRemote(2, of(2019, 3, 5, 8, 50), "", "Religia", "", false),
createTimetableRemote(3, of(2019, 3, 5, 9, 40), "", "W-F", "Jan Garnkiewicz", false),
createTimetableRemote(4, of(2019, 3, 5, 10, 30), "", "W-F", "Paweł Jumper", false)
every { mockSdk.getTimetable(any(), any()) } returns Single.just(listOf(
createTimetableRemote(of(2019, 3, 5, 8, 0), 1, "", "Przyroda", "", true), // should override local
createTimetableRemote(of(2019, 3, 5, 8, 50), 2, "", "Religia", "", false),
createTimetableRemote(of(2019, 3, 5, 9, 40), 3, "", "W-F", "Jan Garnkiewicz", false),
createTimetableRemote(of(2019, 3, 5, 10, 30), 4, "", "W-F", "Paweł Jumper", false)
))
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote)

View File

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

View File

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

View File

@ -11,12 +11,10 @@ import com.readystatesoftware.chuck.api.ChuckInterceptor
import com.readystatesoftware.chuck.api.RetentionManager
import dagger.Module
import dagger.Provides
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import okhttp3.logging.HttpLoggingInterceptor
import okhttp3.logging.HttpLoggingInterceptor.Level.BASIC
import okhttp3.logging.HttpLoggingInterceptor.Level.NONE
import io.github.wulkanowy.sdk.Sdk
import timber.log.Timber
import javax.inject.Singleton
@ -33,15 +31,14 @@ internal class RepositoryModule {
@Singleton
@Provides
fun provideApi(chuckCollector: ChuckCollector, context: Context): Api {
return Api().apply {
logLevel = NONE
fun provideSdk(chuckCollector: ChuckCollector, context: Context): Sdk {
return Sdk().apply {
androidVersion = android.os.Build.VERSION.RELEASE
buildTag = android.os.Build.MODEL
setInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { Timber.d(it) }).setLevel(BASIC))
setSimpleHttpLogger { Timber.d(it) }
// for debug only
setInterceptor(ChuckInterceptor(context, chuckCollector).maxContentLength(250000L), true, 0)
addInterceptor(ChuckInterceptor(context, chuckCollector).maxContentLength(250000L), true)
}
}
@ -55,7 +52,7 @@ internal class RepositoryModule {
@Singleton
@Provides
fun provideDatabase(context: Context) = AppDatabase.newInstance(context)
fun provideDatabase(context: Context, sharedPrefProvider: SharedPrefProvider) = AppDatabase.newInstance(context, sharedPrefProvider)
@Singleton
@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.Migration17
import io.github.wulkanowy.data.db.migrations.Migration18
import io.github.wulkanowy.data.db.migrations.Migration19
import io.github.wulkanowy.data.db.migrations.Migration2
import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration4
@ -100,9 +101,9 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
const val VERSION_SCHEMA = 18
const val VERSION_SCHEMA = 19
fun getMigrations(): Array<Migration> {
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
return arrayOf(
Migration2(),
Migration3(),
@ -120,16 +121,17 @@ abstract class AppDatabase : RoomDatabase() {
Migration15(),
Migration16(),
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")
.setJournalMode(TRUNCATE)
.fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1)
.fallbackToDestructiveMigrationOnDowngrade()
.addMigrations(*getMigrations())
.addMigrations(*getMigrations(sharedPrefProvider))
.build()
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@ package io.github.wulkanowy.data.repositories.semester
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.ApiHelper
import io.github.wulkanowy.data.SdkHelper
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract
@ -18,11 +18,11 @@ class SemesterRepository @Inject constructor(
private val remote: SemesterRemote,
private val local: SemesterLocal,
private val settings: InternetObservingSettings,
private val apiHelper: ApiHelper
private val sdkHelper: SdkHelper
) {
fun getSemesters(student: Student, forceRefresh: Boolean = false): Single<List<Semester>> {
return Maybe.just(apiHelper.initApi(student))
return Maybe.just(sdkHelper.init(student))
.flatMap { local.getSemesters(student).filter { !forceRefresh } }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {

View File

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

View File

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

View File

@ -2,7 +2,6 @@ package io.github.wulkanowy.data.repositories.student
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.ApiHelper
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.reactivex.Completable
@ -16,19 +15,30 @@ import javax.inject.Singleton
class StudentRepository @Inject constructor(
private val local: StudentLocal,
private val remote: StudentRemote,
private val settings: InternetObservingSettings,
private val apiHelper: ApiHelper
private val settings: InternetObservingSettings
) {
fun isStudentSaved(): Single<Boolean> = local.getStudents(false).isEmpty.map { !it }
fun isCurrentStudentSet(): Single<Boolean> = local.getCurrentStudent(false).isEmpty.map { !it }
fun getStudents(email: String, password: String, endpoint: String, symbol: String = ""): Single<List<Student>> {
return ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
apiHelper.initApi(email, password, symbol, endpoint)
if (it) remote.getStudents(email, password, endpoint)
fun getStudentsApi(pin: String, symbol: String, token: String): Single<List<Student>> {
return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
if (it) remote.getStudentsMobileApi(token, pin, symbol)
else Single.error(UnknownHostException("No internet connection"))
}
}
fun getStudentsScrapper(email: String, password: String, endpoint: String, symbol: String = ""): Single<List<Student>> {
return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
if (it) remote.getStudentsScrapper(email, password, endpoint, symbol)
else Single.error(UnknownHostException("No internet connection"))
}
}
fun getStudentsHybrid(email: String, password: String, endpoint: String, symbol: String): Single<List<Student>> {
return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
if (it) remote.getStudentsHybrid(email, password, endpoint, symbol)
else Single.error(UnknownHostException("No internet connection"))
}
}

View File

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

View File

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

View File

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

View File

@ -11,10 +11,10 @@ import androidx.work.WorkerParameters
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import io.github.wulkanowy.R
import io.github.wulkanowy.api.interceptor.FeatureDisabledException
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.sdk.exception.FeatureDisabledException
import io.github.wulkanowy.services.sync.channels.DebugChannel
import io.github.wulkanowy.services.sync.works.Work
import io.github.wulkanowy.utils.getCompatColor
@ -78,4 +78,3 @@ class SyncWorker @AssistedInject constructor(
fun create(appContext: Context, workerParameters: WorkerParameters): ListenableWorker
}
}

View File

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

View File

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

View File

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

View File

@ -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.gradessummary.GradeSummaryRepository
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.changeModifier
import io.reactivex.Maybe
@ -40,7 +41,7 @@ class GradeAverageProvider @Inject constructor(
.map { secondGrades -> secondGrades + firstGrades }
}
}.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 }
.mapValues { it.value.calcAverage() }
})
@ -54,7 +55,7 @@ class GradeAverageProvider @Inject constructor(
return getAverageFromGradeSummary(selectedSemester, forceRefresh)
.switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh)
.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 }
.mapValues { it.value.calcAverage() }
})

View File

@ -13,7 +13,6 @@ import androidx.appcompat.app.AlertDialog
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment
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.statistics.GradeStatisticsFragment
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.data.db.entities.Grade
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.GradeView
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.ui.base.BaseActivity
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.studentselect.LoginStudentSelectFragment
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
@ -50,7 +51,8 @@ class LoginActivity : BaseActivity<LoginPresenter>(), LoginView {
addFragments(listOf(
LoginFormFragment.newInstance(),
LoginSymbolFragment.newInstance(),
LoginStudentSelectFragment.newInstance()
LoginStudentSelectFragment.newInstance(),
LoginAdvancedFragment.newInstance()
))
}
@ -93,4 +95,8 @@ class LoginActivity : BaseActivity<LoginPresenter>(), LoginView {
fun onSymbolFragmentAccountLogged(students: List<Student>) {
presenter.onSymbolViewAccountLogged(students)
}
fun onAdvancedLoginClick() {
presenter.onAdvancedLoginClick()
}
}

View File

@ -4,7 +4,7 @@ import android.content.res.Resources
import android.database.sqlite.SQLiteConstraintException
import com.readystatesoftware.chuck.api.ChuckCollector
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 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.PerFragment
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.studentselect.LoginStudentSelectFragment
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
@ -26,6 +27,10 @@ internal abstract class LoginModule {
@ContributesAndroidInjector
abstract fun bindLoginFormFragment(): LoginFormFragment
@PerFragment
@ContributesAndroidInjector
abstract fun bindLoginAdvancedFragment(): LoginAdvancedFragment
@PerFragment
@ContributesAndroidInjector
abstract fun bindLoginSymbolFragment(): LoginSymbolFragment

View File

@ -45,6 +45,10 @@ class LoginPresenter @Inject constructor(
}
}
fun onAdvancedLoginClick() {
view?.switchView(3)
}
fun onViewSelected(index: Int) {
view?.apply {
when (index) {
@ -58,7 +62,7 @@ class LoginPresenter @Inject constructor(
Timber.i("Back pressed in login view")
view?.apply {
when (currentViewIndex) {
1, 2 -> switchView(0)
1, 2, 3 -> switchView(0)
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() }
loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() }
loginFormSignIn.setOnClickListener { presenter.onSignInClick() }
loginFormAdvancedButton.setOnClickListener { presenter.onAdvancedLoginClick() }
loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() }
loginFormFaq.setOnClickListener { presenter.onFaqClick() }
loginFormContactEmail.setOnClickListener { presenter.onEmailClick() }
@ -134,14 +135,7 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
@SuppressLint("SetTextI18n")
override fun showVersion() {
with(loginFormVersion) {
visibility = VISIBLE
text = "${getString(R.string.app_name)} ${appInfo.versionName}"
}
}
override fun showPrivacyPolicy() {
loginFormPrivacyLink.visibility = VISIBLE
loginFormVersion.text = "v${appInfo.versionName}"
}
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
}
override fun openAdvancedLogin() {
(activity as? LoginActivity)?.onAdvancedLoginClick()
}
override fun onDestroyView() {
super.onDestroyView()
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.ui.base.BasePresenter
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.SchedulersProvider
import io.github.wulkanowy.utils.ifNullOrBlank
@ -14,8 +13,7 @@ class LoginFormPresenter @Inject constructor(
schedulers: SchedulersProvider,
studentRepository: StudentRepository,
private val loginErrorHandler: LoginErrorHandler,
private val analytics: FirebaseAnalyticsHelper,
private val appInfo: AppInfo
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<LoginFormView>(loginErrorHandler, studentRepository, schedulers) {
override fun onAttachView(view: LoginFormView) {
@ -23,7 +21,7 @@ class LoginFormPresenter @Inject constructor(
view.run {
initView()
showContact(false)
if (appInfo.isDebug) showVersion() else showPrivacyPolicy()
showVersion()
loginErrorHandler.onBadCredentials = {
setErrorPassIncorrect()
@ -37,6 +35,10 @@ class LoginFormPresenter @Inject constructor(
view?.openPrivacyPolicyPage()
}
fun onAdvancedLoginClick() {
view?.openAdvancedLogin()
}
fun onHostSelected() {
view?.apply {
clearPassError()
@ -56,13 +58,13 @@ class LoginFormPresenter @Inject constructor(
}
fun onSignInClick() {
val email = view?.formNameValue.orEmpty()
val password = view?.formPassValue.orEmpty()
val endpoint = view?.formHostValue.orEmpty()
val email = view?.formNameValue.orEmpty().trim()
val password = view?.formPassValue.orEmpty().trim()
val endpoint = view?.formHostValue.orEmpty().trim()
if (!validateCredentials(email, password)) return
disposable.add(studentRepository.getStudents(email, password, endpoint)
disposable.add(studentRepository.getStudentsScrapper(email, password, endpoint)
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
@ -81,11 +83,11 @@ class LoginFormPresenter @Inject constructor(
}
.subscribe({
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))
}, {
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)
view?.showContact(true)
}))

View File

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

View File

@ -79,11 +79,11 @@ class LoginStudentSelectPresenter @Inject constructor(
Timber.i("Registration started")
}
.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")
view?.openMainView()
}, { 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 ")
loginErrorHandler.dispatch(error)
view?.apply {

View File

@ -44,7 +44,7 @@ class LoginSymbolPresenter @Inject constructor(
disposable.add(
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)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
@ -62,7 +62,7 @@ class LoginSymbolPresenter @Inject constructor(
}
}
.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 {
if (it.isEmpty()) {
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")
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)
view?.showContact(true)
}))

View File

@ -9,6 +9,7 @@ import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.repositories.message.MessageFolder
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.android.extensions.LayoutContainer
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
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)
}
messageItemSubject.run {

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.modules.message.preview
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.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
@ -63,14 +64,14 @@ class MessagePreviewPresenter @Inject constructor(
message.let {
setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString)
setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss"))
setContent(it.content.orEmpty())
setContent(it.content)
initOptions()
if (it.recipient.isNotBlank()) setRecipient(it.recipient)
if (it.folderId == MessageFolder.SENT.id) setRecipient(it.recipient)
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 ")
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.repositories.message.MessageFolder
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.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
@ -18,6 +19,7 @@ class MessageTabPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val messageRepository: MessageRepository,
private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<MessageTabView>(errorHandler, studentRepository, schedulers) {
@ -76,8 +78,11 @@ class MessageTabPresenter @Inject constructor(
disposable.apply {
clear()
add(studentRepository.getCurrentStudent()
.flatMap { messageRepository.getMessages(it, folder, forceRefresh) }
.flatMap { student ->
semesterRepository.getCurrentSemester(student)
.flatMap { messageRepository.getMessages(student, it, folder, forceRefresh) }
.map { items -> items.map { MessageItem(it, view?.noSubjectString.orEmpty()) } }
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.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.schoolandteachers.SchoolAndTeachersFragment
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.setOnItemClickListener
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 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 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.nextSchoolDay
import io.github.wulkanowy.utils.previousSchoolDay
import io.github.wulkanowy.utils.shortcutWeekDayName
import io.github.wulkanowy.utils.toFormattedString
import io.reactivex.Maybe
import org.threeten.bp.LocalDate

View File

@ -32,7 +32,7 @@ fun Grade.getBackgroundColor(theme: String): Int {
"B16CF1" -> R.color.grade_purple
else -> R.color.grade_material_default
}
"material" -> when (value) {
"material" -> when (value.toInt()) {
6 -> R.color.grade_material_six
5 -> R.color.grade_material_five
4 -> R.color.grade_material_four
@ -41,7 +41,7 @@ fun Grade.getBackgroundColor(theme: String): Int {
1 -> R.color.grade_material_one
else -> R.color.grade_material_default
}
else -> when (value) {
else -> when (value.toInt()) {
6 -> R.color.grade_vulcan_six
5 -> R.color.grade_vulcan_five
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.PURPOSE_DECRYPT
import android.security.keystore.KeyProperties.PURPOSE_ENCRYPT
import android.util.Base64
import android.util.Base64.DEFAULT
import android.util.Base64.decode
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 (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 {

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
android:id="@+id/loginFormContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:paddingBottom="16dp">
<LinearLayout
android:id="@+id/loginFormContact"
@ -197,50 +198,64 @@
tools:ignore="Deprecated,LabelFor" />
</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
android:id="@+id/loginFormSignIn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
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"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/loginFormHostLayout"
app:layout_constraintEnd_toEndOf="parent"
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
android:id="@+id/loginFormVersion"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:layout_marginTop="24dp"
android:maxLines="2"
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@id/loginFormSignIn"
app:layout_constraintEnd_toStartOf="@+id/loginFormSignIn"
app:layout_constraintStart_toStartOf="@id/loginFormHostLayout"
app:layout_constraintTop_toTopOf="@+id/loginFormSignIn"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="@+id/loginFormSignIn"
app:layout_constraintTop_toBottomOf="@id/loginFormSignIn"
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.core.widget.NestedScrollView>
</FrameLayout>

View File

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

View File

@ -27,6 +27,12 @@
<string name="login_password_hint">Hasło</string>
<string name="login_host_hint">Dziennik</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_invalid_password">To hasło jest za krótkie</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_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_advanced">Inne opcje</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_email">Email</string>
@ -353,4 +360,5 @@
<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_feature_disabled">Funkcja wyłączona przez szkołę</string>
<string name="error_feature_not_available">Funkcja niedostępna w tym trybie</string>
</resources>

View File

@ -26,6 +26,12 @@
<string name="login_nickname_hint">Email or nick</string>
<string name="login_password_hint">Password</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_sign_in">Sign in</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_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_advanced">Other options</string>
<string name="login_privacy_policy">Privacy policy</string>
<string name="login_contact_header">Trouble signing in? Contact us!</string>
<string name="login_contact_email">Email</string>
@ -334,4 +341,5 @@
<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_feature_disabled">Feature disabled by your school</string>
<string name="error_feature_not_available">Feature not available in this mode</string>
</resources>

View File

@ -1,23 +1,22 @@
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.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Attendance
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.threeten.bp.LocalDate
import java.sql.Date
import org.threeten.bp.LocalDate.of
class AttendanceRemoteTest {
@SpyK
private var mockApi = Api()
@MockK
private lateinit var mockSdk: Sdk
@MockK
private lateinit var semesterMock: Semester
@ -29,29 +28,44 @@ class AttendanceRemoteTest {
@Test
fun getAttendanceTest() {
every { mockApi.getAttendance(
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 15)
) } returns Single.just(listOf(
getAttendance("2018-09-10"),
getAttendance("2018-09-17")
every {
mockSdk.getAttendance(
of(2018, 9, 10),
of(2018, 9, 15),
1
)
} 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.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,
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 15)).blockingGet()
val attendance = AttendanceRemote(mockSdk).getAttendance(semesterMock,
of(2018, 9, 10),
of(2018, 9, 15)
).blockingGet()
assertEquals(2, attendance.size)
}
private fun getAttendance(dateString: String): Attendance {
return Attendance().apply {
subject = "Fizyka"
name = "Obecność"
date = Date.valueOf(dateString)
}
private fun getAttendance(date: LocalDate): Attendance {
return Attendance(
subject = "Fizyka",
name = "Obecność",
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
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.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.CompletedLesson
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.threeten.bp.LocalDate
import java.sql.Date
import org.threeten.bp.LocalDate.of
class CompletedLessonsRemoteTest {
@SpyK
private var mockApi = Api()
@MockK
private lateinit var mockSdk: Sdk
@MockK
private lateinit var semesterMock: Semester
@ -30,27 +29,39 @@ class CompletedLessonsRemoteTest {
@Test
fun getCompletedLessonsTest() {
every {
mockApi.getCompletedLessons(
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 15)
mockSdk.getCompletedLessons(
of(2018, 9, 10),
of(2018, 9, 15)
)
} returns Single.just(listOf(
getCompletedLesson("2018-09-10"),
getCompletedLesson("2018-09-17")
getCompletedLesson(of(2018, 9, 10)),
getCompletedLesson(of(2018, 9, 17))
))
every { mockApi.diaryId } returns 1
every { semesterMock.studentId } 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,
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 15)
val completed = CompletedLessonsRemote(mockSdk).getCompletedLessons(semesterMock,
of(2018, 9, 10),
of(2018, 9, 15)
).blockingGet()
Assert.assertEquals(2, completed.size)
}
private fun getCompletedLesson(dateString: String): CompletedLesson {
return CompletedLesson().apply { date = Date.valueOf(dateString) }
private fun getCompletedLesson(date: LocalDate): CompletedLesson {
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
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.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Exam
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.threeten.bp.LocalDate
import java.sql.Date
import org.threeten.bp.LocalDate.of
class ExamRemoteTest {
@SpyK
private var mockApi = Api()
@MockK
private lateinit var mockSdk: Sdk
@MockK
private lateinit var semesterMock: Semester
@ -29,35 +28,41 @@ class ExamRemoteTest {
@Test
fun getExamsTest() {
every { mockApi.getExams(
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 15)
) } returns Single.just(listOf(
getExam("2018-09-10"),
getExam("2018-09-17")
every {
mockSdk.getExams(
of(2018, 9, 10),
of(2018, 9, 15),
1
)
} 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.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,
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 15)
val exams = ExamRemote(mockSdk)
.getExams(semesterMock,
of(2018, 9, 10),
of(2018, 9, 15)
).blockingGet()
assertEquals(2, exams.size)
}
private fun getExam(dateString: String): Exam {
return Exam().apply {
subject = ""
group = ""
type = ""
description = ""
teacher = ""
teacherSymbol = ""
date = Date.valueOf(dateString)
entryDate = Date.valueOf(dateString)
}
private fun getExam(date: LocalDate): Exam {
return Exam(
subject = "",
group = "",
type = "",
description = "",
teacher = "",
teacherSymbol = "",
date = date,
entryDate = date
)
}
}

View File

@ -1,13 +1,12 @@
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.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.GradePointsStatistics
import io.github.wulkanowy.sdk.pojo.GradeStatistics
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single
import org.junit.Assert.assertEquals
import org.junit.Before
@ -15,8 +14,8 @@ import org.junit.Test
class GradeStatisticsRemoteTest {
@SpyK
private var mockApi = Api()
@MockK
private lateinit var mockSdk: Sdk
@MockK
private lateinit var semesterMock: Semester
@ -28,48 +27,51 @@ class GradeStatisticsRemoteTest {
@Test
fun getGradeStatisticsTest() {
every { mockApi.getGradesPartialStatistics(1) } returns Single.just(listOf(
every { mockSdk.getGradesPartialStatistics(1) } returns Single.just(listOf(
getGradeStatistics("Fizyka"),
getGradeStatistics("Matematyka")
))
every { mockApi.diaryId } returns 1
every { semesterMock.studentId } returns 1
every { semesterMock.semesterId } returns 1
every { semesterMock.semesterName } returns 2
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)
}
@Test
fun getGradePointsStatisticsTest() {
every { mockApi.getGradesPointsStatistics(1) } returns Single.just(listOf(
every { mockSdk.getGradesPointsStatistics(1) } returns Single.just(listOf(
getGradePointsStatistics("Fizyka"),
getGradePointsStatistics("Matematyka")
))
every { mockApi.diaryId } returns 1
every { semesterMock.studentId } returns 1
every { semesterMock.semesterId } returns 1
every { semesterMock.semesterName } returns 2
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)
}
private fun getGradeStatistics(subjectName: String): GradeStatistics {
return GradeStatistics().apply {
subject = subjectName
gradeValue = 5
amount = 10
}
return GradeStatistics(
subject = subjectName,
gradeValue = 5,
amount = 10,
grade = "",
semesterId = 1
)
}
private fun getGradePointsStatistics(subjectName: String): GradePointsSummary {
return GradePointsSummary(
private fun getGradePointsStatistics(subjectName: String): GradePointsStatistics {
return GradePointsStatistics(
semesterId = 1,
subject = subjectName,
student = 0.80,
others = 0.40

View File

@ -1,8 +1,7 @@
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.repositories.luckynumber.LuckyNumberRemote
import io.github.wulkanowy.sdk.Sdk
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
@ -16,7 +15,7 @@ import org.threeten.bp.LocalDate
class LuckyNumberRemoteTest {
@SpyK
private var mockApi = Api()
private var mockSdk = Sdk()
@MockK
private lateinit var semesterMock: Semester
@ -28,13 +27,13 @@ class LuckyNumberRemoteTest {
@Test
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.diaryId } returns 1
val luckyNumber = LuckyNumberRemote(mockApi)
val luckyNumber = LuckyNumberRemote(mockSdk)
.getLuckyNumber(semesterMock)
.blockingGet()

View File

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

View File

@ -1,19 +1,23 @@
package io.github.wulkanowy.data.repositories.student
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.register.Student
import io.github.wulkanowy.data.SdkHelper
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Student
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyString
import org.mockito.Mockito.doReturn
import org.mockito.MockitoAnnotations
class StudentRemoteTest {
@Mock
private lateinit var mockApi: Api
private lateinit var mockSdk: Sdk
@Before
fun initApi() {
@ -22,11 +26,31 @@ class StudentRemoteTest {
@Test
fun testRemoteAll() {
doReturn(Single.just(listOf(Student("", "", 1, "test", "", "", "", 1, Api.LoginType.AUTO))))
.`when`(mockApi).getStudents()
doReturn(Single.just(listOf(getStudent("test")))).`when`(mockSdk).getStudentsFromScrapper(anyString(), anyString(), anyString(), anyString())
val students = StudentRemote(mockApi).getStudents("", "", "").blockingGet()
val students = StudentRemote(mockSdk).getStudentsScrapper("", "", "http://fakelog.cf", "").blockingGet()
assertEquals(1, students.size)
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
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.repositories.timetable.TimetableRemote
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Timetable
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.threeten.bp.LocalDate
import java.sql.Date
import org.threeten.bp.LocalDate.of
import org.threeten.bp.LocalDateTime.now
class TimetableRemoteTest {
@SpyK
private var mockApi = Api()
@MockK
private lateinit var mockSdk: Sdk
@MockK
private lateinit var semesterMock: Semester
@ -30,26 +29,46 @@ class TimetableRemoteTest {
@Test
fun getTimetableTest() {
every { mockApi.getTimetable(
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 15)
) } returns Single.just(listOf(
getTimetable("2018-09-10"),
getTimetable("2018-09-17")
every {
mockSdk.getTimetable(
of(2018, 9, 10),
of(2018, 9, 15)
)
} 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.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,
LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 15)
val timetable = TimetableRemote(mockSdk).getTimetable(semesterMock,
of(2018, 9, 10),
of(2018, 9, 15)
).blockingGet()
assertEquals(2, timetable.size)
}
private fun getTimetable(dateString: String): Timetable {
return Timetable(date = Date.valueOf(dateString))
private fun getTimetable(date: LocalDate): Timetable {
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.gradessummary.GradeSummaryRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import org.junit.Assert.assertEquals
import org.junit.Before
@ -30,7 +31,7 @@ class GradeAverageProviderTest {
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(
Semester(101, 10, "", 1, 21, 1, false, now(), now(), 1, 1),
@ -39,17 +40,22 @@ class GradeAverageProviderTest {
)
private val firstGrades = listOf(
getGrade(22, "Matematyka", 4),
getGrade(22, "Matematyka", 3),
getGrade(22, "Fizyka", 6),
getGrade(22, "Fizyka", 1)
getGrade(22, "Matematyka", 4.0),
getGrade(22, "Matematyka", 3.0),
getGrade(22, "Fizyka", 6.0),
getGrade(22, "Fizyka", 1.0)
)
private val secondGrade = listOf(
getGrade(23, "Matematyka", 2),
getGrade(23, "Matematyka", 3),
getGrade(23, "Fizyka", 4),
getGrade(23, "Fizyka", 2)
getGrade(23, "Matematyka", 2.0),
getGrade(23, "Matematyka", 3.0),
getGrade(23, "Fizyka", 4.0),
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
@ -78,6 +84,54 @@ class GradeAverageProviderTest {
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
fun allYearFirstSemesterTest() {
doReturn("all_year").`when`(preferencesRepository).gradeAverageMode
@ -147,13 +201,13 @@ class GradeAverageProviderTest {
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(
studentId = 101,
semesterId = semesterId,
subject = subject,
value = value,
modifier = .0,
modifier = modifier,
weightValue = 1.0,
teacher = "",
date = now(),

View File

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

View File

@ -32,7 +32,7 @@ class LoginStudentSelectPresenterTest {
private lateinit var presenter: LoginStudentSelectPresenter
private val testStudent by lazy { Student(email = "test", password = "test123", endpoint = "https://fakelog.cf", loginType = "AUTO", symbol = "", isCurrent = false, studentId = 0, schoolName = "", schoolSymbol = "", classId = 1, studentName = "", registrationDate = now(), 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") }

View File

@ -1,5 +1,6 @@
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.GradeSummary
import org.junit.Assert.assertEquals
@ -22,11 +23,11 @@ class GradeExtensionTest {
@Test
fun calcWeightedAverage() {
assertEquals(3.47, listOf(
createGrade(5, 6.0, 0.33),
createGrade(5, 5.0, -0.33),
createGrade(4, 1.0, 0.0),
createGrade(1, 9.0, 0.5),
createGrade(0, .0, 0.0)
createGrade(5.0, 6.0, 0.33),
createGrade(5.0, 5.0, -0.33),
createGrade(4.0, 1.0, 0.0),
createGrade(1.0, 9.0, 0.5),
createGrade(0.0, .0, 0.0)
).calcAverage(), 0.005)
}
@ -40,25 +41,33 @@ class GradeExtensionTest {
).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
fun changeModifier_zero() {
assertEquals(.0, createGrade(5, .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)
assertEquals(.0, createGrade(5.0, .0, -.5).changeModifier(.0, .0).modifier, .0)
}
@Test
fun changeModifier_plus() {
assertEquals(.33, createGrade(5, .0, .25).changeModifier(.33, .50).modifier, .0)
assertEquals(.25, createGrade(5, .0, .33).changeModifier(.25, .0).modifier, .0)
assertEquals(.33, createGrade(5.0, .0, .25).changeModifier(.33, .50).modifier, .0)
assertEquals(.25, createGrade(5.0, .0, .33).changeModifier(.25, .0).modifier, .0)
}
@Test
fun changeModifier_minus() {
assertEquals(-.33, createGrade(5, .0, -.25).changeModifier(.25, .33).modifier, .0)
assertEquals(-.25, createGrade(5, .0, -.33).changeModifier(.0, .25).modifier, .0)
assertEquals(-.33, createGrade(5.0, .0, -.25).changeModifier(.25, .33).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(
semesterId = 1,
studentId = 1,