1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2025-02-22 09:44:45 +01:00

Add account manager (#183)

This commit is contained in:
Rafał Borcz 2018-11-24 18:51:41 +01:00 committed by Mikołaj Pich
parent 1f30deb36e
commit 7a3c0de7ad
106 changed files with 1140 additions and 585 deletions

View File

@ -52,7 +52,7 @@ script:
- ./gradlew createDebugCoverageReport --stacktrace -PdisableCrashlytics --daemon - ./gradlew createDebugCoverageReport --stacktrace -PdisableCrashlytics --daemon
- ./gradlew jacocoTestReport --stacktrace --daemon - ./gradlew jacocoTestReport --stacktrace --daemon
- if [ "$TRAVIS_PULL_REQUEST" != "false" ] || [ "$TRAVIS_BRANCH" == "master" ]; then - if [ "$TRAVIS_PULL_REQUEST" != "false" ] || [ "$TRAVIS_BRANCH" == "master" ]; then
./gradlew sonarqube -x test -x lint -x fabricGenerateResourcesRelease -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=$TRAVIS_BRANCH -PdisableCrashlytics --stacktrace --daemon; ./gradlew sonarqube -x test -x lint -x fabricGenerateResourcesRelease -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} -PdisableCrashlytics --stacktrace --daemon;
fi fi
- | - |
if [ $TRAVIS_TAG ]; then if [ $TRAVIS_TAG ]; then

View File

@ -74,16 +74,18 @@ play {
uploadImages = true uploadImages = true
} }
ext.androidx_version = "1.0.0" configurations.all {
resolutionStrategy.force "com.squareup.okhttp3:okhttp-urlconnection:3.11.0"
}
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation('com.github.wulkanowy:api:a80b8e5') { exclude module: "threetenbp" } implementation('com.github.wulkanowy:api:0ac961607b') { exclude module: "threetenbp" }
implementation "androidx.legacy:legacy-support-v4:$androidx_version" implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.appcompat:appcompat:$androidx_version" implementation "androidx.appcompat:appcompat:1.0.2"
implementation "androidx.cardview:cardview:$androidx_version" implementation "androidx.cardview:cardview:1.0.0"
implementation "com.google.android.material:material:$androidx_version" implementation "com.google.android.material:material:1.0.0"
implementation 'androidx.multidex:multidex:2.0.0' implementation 'androidx.multidex:multidex:2.0.0'
implementation 'com.takisoft.preferencex:preferencex:1.0.0' implementation 'com.takisoft.preferencex:preferencex:1.0.0'
@ -113,7 +115,6 @@ dependencies {
implementation "com.jakewharton.timber:timber:4.7.1" implementation "com.jakewharton.timber:timber:4.7.1"
implementation "at.favre.lib:slf4j-timber:1.0.1" implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation 'com.akaita.java:rxjava2-debug:1.3.0'
implementation("com.crashlytics.sdk.android:crashlytics:2.9.5@aar") { implementation("com.crashlytics.sdk.android:crashlytics:2.9.5@aar") {
transitive = true transitive = true
} }
@ -132,6 +133,5 @@ dependencies {
androidTestImplementation 'androidx.test:runner:1.1.0' androidTestImplementation 'androidx.test:runner:1.1.0'
androidTestImplementation 'androidx.test.ext:junit:1.0.0' androidTestImplementation 'androidx.test.ext:junit:1.0.0'
androidTestImplementation "org.mockito:mockito-android:2.23.0" androidTestImplementation "org.mockito:mockito-android:2.23.0"
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
} }

View File

@ -23,7 +23,7 @@ class AttendanceLocalTest {
@Before @Before
fun createDb() { fun createDb() {
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build() testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build()
attendanceLocal = AttendanceLocal(testDb.attendanceDao()) attendanceLocal = AttendanceLocal(testDb.attendanceDao)
} }
@After @After

View File

@ -23,7 +23,7 @@ class ExamLocalTest {
@Before @Before
fun createDb() { fun createDb() {
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build() testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build()
examLocal = ExamLocal(testDb.examsDao()) examLocal = ExamLocal(testDb.examsDao)
} }
@After @After

View File

@ -14,9 +14,9 @@ import org.junit.runner.RunWith
import kotlin.test.assertEquals import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class SessionLocalTest { class StudentLocalTest {
private lateinit var sessionLocal: SessionLocal private lateinit var studentLocal: StudentLocal
private lateinit var testDb: AppDatabase private lateinit var testDb: AppDatabase
@ -28,7 +28,7 @@ class SessionLocalTest {
testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
.build() .build()
sharedHelper = SharedPrefHelper(context.getSharedPreferences("TEST", Context.MODE_PRIVATE)) sharedHelper = SharedPrefHelper(context.getSharedPreferences("TEST", Context.MODE_PRIVATE))
sessionLocal = SessionLocal(testDb.studentDao(), testDb.semesterDao(), sharedHelper, context) studentLocal = StudentLocal(testDb.studentDao, sharedHelper, context)
} }
@After @After
@ -38,12 +38,11 @@ class SessionLocalTest {
@Test @Test
fun saveAndReadTest() { fun saveAndReadTest() {
sessionLocal.saveStudent(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO")).blockingAwait() studentLocal.saveStudent(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true))
assert(sharedHelper.getLong(SessionLocal.LAST_USER_KEY, 0) == 1L) .blockingAwait()
assert(studentLocal.isStudentSaved)
assert(sessionLocal.isSessionSaved) val student = studentLocal.getCurrentStudent().blockingGet()
val student = sessionLocal.getLastStudent().blockingGet()
assertEquals("23", student.schoolSymbol) assertEquals("23", student.schoolSymbol)
} }
} }

View File

@ -24,7 +24,7 @@ class TimetableLocalTest {
@Before @Before
fun createDb() { fun createDb() {
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build() testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build()
timetableDb = TimetableLocal(testDb.timetableDao()) timetableDb = TimetableLocal(testDb.timetableDao)
} }
@After @After

View File

@ -38,7 +38,6 @@
android:name=".ui.modules.main.MainActivity" android:name=".ui.modules.main.MainActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/main_title" android:label="@string/main_title"
android:launchMode="singleTop"
android:theme="@style/WulkanowyTheme.NoActionBar" /> android:theme="@style/WulkanowyTheme.NoActionBar" />
<service <service

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy
import android.content.Context import android.content.Context
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.multidex.MultiDex import androidx.multidex.MultiDex
import com.akaita.java.rxjava2debug.RxJava2Debug
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import com.crashlytics.android.answers.Answers import com.crashlytics.android.answers.Answers
import com.crashlytics.android.core.CrashlyticsCore import com.crashlytics.android.core.CrashlyticsCore
@ -36,7 +35,6 @@ class WulkanowyApp : DaggerApplication() {
AndroidThreeTen.init(this) AndroidThreeTen.init(this)
initializeFabric() initializeFabric()
if (DEBUG) enableDebugLog() if (DEBUG) enableDebugLog()
RxJava2Debug.enableRxJava2AssemblyTracking(arrayOf(BuildConfig.APPLICATION_ID))
AppCompatDelegate.setDefaultNightMode(prefRepository.currentTheme) AppCompatDelegate.setDefaultNightMode(prefRepository.currentTheme)
} }

View File

@ -0,0 +1,27 @@
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
host = URL(student.endpoint).run { host + ":$port".removeSuffix(":-1") }
ssl = student.endpoint.startsWith("https")
loginType = Api.LoginType.valueOf(student.loginType)
}
}
fun initApi(email: String, password: String, symbol: String, endpoint: String) {
initApi(Student(email = email, password = password, symbol = symbol, endpoint = endpoint, loginType = "AUTO"))
}
}

View File

@ -1,7 +1,6 @@
package io.github.wulkanowy.data package io.github.wulkanowy.data
import android.content.res.Resources import android.content.res.Resources
import com.akaita.java.rxjava2debug.RxJava2Debug
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.api.login.NotLoggedInException import io.github.wulkanowy.api.login.NotLoggedInException
import timber.log.Timber import timber.log.Timber
@ -10,12 +9,12 @@ import java.net.SocketTimeoutException
import java.net.UnknownHostException import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
open class ErrorHandler @Inject constructor(private val resources: Resources) { open class ErrorHandler @Inject constructor(protected val resources: Resources) {
var showErrorMessage: (String) -> Unit = {} var showErrorMessage: (String) -> Unit = {}
open fun proceed(error: Throwable) { open fun proceed(error: Throwable) {
Timber.e(RxJava2Debug.getEnhancedStackTrace(error), "An exception occurred while the Wulkanowy was running") Timber.e(error, "An exception occurred while the Wulkanowy was running")
showErrorMessage((when (error) { showErrorMessage((when (error) {
is UnknownHostException -> resources.getString(R.string.all_no_internet) is UnknownHostException -> resources.getString(R.string.all_no_internet)

View File

@ -9,6 +9,10 @@ import dagger.Module
import dagger.Provides import dagger.Provides
import io.github.wulkanowy.api.Api import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import okhttp3.logging.HttpLoggingInterceptor
import okhttp3.logging.HttpLoggingInterceptor.Level.BASIC
import okhttp3.logging.HttpLoggingInterceptor.Level.NONE
import timber.log.Timber
import javax.inject.Singleton import javax.inject.Singleton
@Module @Module
@ -25,7 +29,12 @@ internal class RepositoryModule {
@Singleton @Singleton
@Provides @Provides
fun provideApi() = Api() fun provideApi(): Api {
return Api().apply {
logLevel = NONE
setInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { Timber.d(it) }).setLevel(BASIC))
}
}
@Singleton @Singleton
@Provides @Provides
@ -36,43 +45,41 @@ internal class RepositoryModule {
@Singleton @Singleton
@Provides @Provides
fun provideSharedPref(context: Context): SharedPreferences { fun provideSharedPref(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
return PreferenceManager.getDefaultSharedPreferences(context)
} @Singleton
@Provides
@Singleton fun provideStudentDao(database: AppDatabase) = database.studentDao
@Provides
fun provideStudentDao(database: AppDatabase) = database.studentDao() @Singleton
@Provides
@Singleton fun provideSemesterDao(database: AppDatabase) = database.semesterDao
@Provides
fun provideSemesterDao(database: AppDatabase) = database.semesterDao() @Singleton
@Provides
@Singleton fun provideGradeDao(database: AppDatabase) = database.gradeDao
@Provides
fun provideGradeDao(database: AppDatabase) = database.gradeDao() @Singleton
@Provides
@Singleton fun provideGradeSummaryDao(database: AppDatabase) = database.gradeSummaryDao
@Provides
fun provideGradeSummaryDao(database: AppDatabase) = database.gradeSummaryDao() @Singleton
@Provides
@Singleton fun provideExamDao(database: AppDatabase) = database.examsDao
@Provides
fun provideExamDao(database: AppDatabase) = database.examsDao() @Singleton
@Provides
@Singleton fun provideAttendanceDao(database: AppDatabase) = database.attendanceDao
@Provides
fun provideAttendanceDao(database: AppDatabase) = database.attendanceDao() @Singleton
@Provides
@Singleton fun provideTimetableDao(database: AppDatabase) = database.timetableDao
@Provides
fun provideTimetableDao(database: AppDatabase) = database.timetableDao() @Singleton
@Provides
@Singleton fun provideNoteDao(database: AppDatabase) = database.noteDao
@Provides
fun provideNoteDao(database: AppDatabase) = database.noteDao() @Singleton
@Provides
@Singleton fun provideHomeworkDao(database: AppDatabase) = database.homeworkDao
@Provides
fun provideHomeworkDao(database: AppDatabase) = database.homeworkDao()
} }

View File

@ -51,21 +51,21 @@ abstract class AppDatabase : RoomDatabase() {
} }
} }
abstract fun studentDao(): StudentDao abstract val studentDao: StudentDao
abstract fun semesterDao(): SemesterDao abstract val semesterDao: SemesterDao
abstract fun examsDao(): ExamDao abstract val examsDao: ExamDao
abstract fun timetableDao(): TimetableDao abstract val timetableDao: TimetableDao
abstract fun attendanceDao(): AttendanceDao abstract val attendanceDao: AttendanceDao
abstract fun gradeDao(): GradeDao abstract val gradeDao: GradeDao
abstract fun gradeSummaryDao(): GradeSummaryDao abstract val gradeSummaryDao: GradeSummaryDao
abstract fun noteDao(): NoteDao abstract val noteDao: NoteDao
abstract fun homeworkDao(): HomeworkDao abstract val homeworkDao: HomeworkDao
} }

View File

@ -19,6 +19,14 @@ class SharedPrefHelper @Inject constructor(private val sharedPref: SharedPrefere
return sharedPref.getLong(key, defaultValue) return sharedPref.getLong(key, defaultValue)
} }
fun putBoolean(key: String, value: Boolean) {
sharedPref.edit().putBoolean(key, value).apply()
}
fun getBoolean(key: String, defaultValue: Boolean): Boolean {
return sharedPref.getBoolean(key, defaultValue)
}
fun delete(key: String) { fun delete(key: String) {
sharedPref.edit().remove(key).apply() sharedPref.edit().remove(key).apply()
} }

View File

@ -18,5 +18,5 @@ interface AttendanceDao {
fun deleteAll(exams: List<Attendance>) fun deleteAll(exams: List<Attendance>)
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") @Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun getExams(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Attendance>> fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Attendance>>
} }

View File

@ -18,5 +18,5 @@ interface ExamDao {
fun deleteAll(exams: List<Exam>) fun deleteAll(exams: List<Exam>)
@Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") @Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun getExams(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Exam>> fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Exam>>
} }

View File

@ -1,6 +1,10 @@
package io.github.wulkanowy.data.db.dao package io.github.wulkanowy.data.db.dao
import androidx.room.* import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import io.reactivex.Maybe import io.reactivex.Maybe
@ -20,8 +24,8 @@ interface GradeDao {
fun deleteAll(grades: List<Grade>) fun deleteAll(grades: List<Grade>)
@Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId") @Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId")
fun getGrades(semesterId: Int, studentId: Int): Maybe<List<Grade>> fun load(semesterId: Int, studentId: Int): Maybe<List<Grade>>
@Query("SELECT * FROM Grades WHERE is_read = 0 AND semester_id = :semesterId AND student_id = :studentId") @Query("SELECT * FROM Grades WHERE is_read = 0 AND semester_id = :semesterId AND student_id = :studentId")
fun getNewGrades(semesterId: Int, studentId: Int): Maybe<List<Grade>> fun loadNew(semesterId: Int, studentId: Int): Maybe<List<Grade>>
} }

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy.REPLACE
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.GradeSummary
import io.reactivex.Maybe import io.reactivex.Maybe
@ -18,5 +17,5 @@ interface GradeSummaryDao {
fun deleteAll(gradesSummary: List<GradeSummary>) fun deleteAll(gradesSummary: List<GradeSummary>)
@Query("SELECT * FROM grades_summary WHERE student_id = :studentId AND semester_id = :semesterId") @Query("SELECT * FROM grades_summary WHERE student_id = :studentId AND semester_id = :semesterId")
fun getGradesSummary(semesterId: Int, studentId: Int): Maybe<List<GradeSummary>> fun load(semesterId: Int, studentId: Int): Maybe<List<GradeSummary>>
} }

View File

@ -18,5 +18,5 @@ interface HomeworkDao {
fun deleteAll(homework: List<Homework>) fun deleteAll(homework: List<Homework>)
@Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date = :date") @Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date = :date")
fun getHomework(semesterId: Int, studentId: Int, date: LocalDate): Maybe<List<Homework>> fun load(semesterId: Int, studentId: Int, date: LocalDate): Maybe<List<Homework>>
} }

View File

@ -24,8 +24,8 @@ interface NoteDao {
fun deleteAll(notes: List<Note>) fun deleteAll(notes: List<Note>)
@Query("SELECT * FROM Notes WHERE semester_id = :semesterId AND student_id = :studentId") @Query("SELECT * FROM Notes WHERE semester_id = :semesterId AND student_id = :studentId")
fun getNotes(semesterId: Int, studentId: Int): Maybe<List<Note>> fun load(semesterId: Int, studentId: Int): Maybe<List<Note>>
@Query("SELECT * FROM Notes WHERE is_read = 0 AND semester_id = :semesterId AND student_id = :studentId") @Query("SELECT * FROM Notes WHERE is_read = 0 AND semester_id = :semesterId AND student_id = :studentId")
fun getNewNotes(semesterId: Int, studentId: Int): Maybe<List<Note>> fun loadNew(semesterId: Int, studentId: Int): Maybe<List<Note>>
} }

View File

@ -2,22 +2,23 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy.IGNORE
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Single import io.reactivex.Maybe
@Dao @Dao
interface SemesterDao { interface SemesterDao {
@Insert @Insert(onConflict = IGNORE)
fun insertAll(semester: List<Semester>) fun insertAll(semester: List<Semester>)
@Query("UPDATE Semesters SET is_current = 1 WHERE semester_id = :semesterId AND diary_id = :diaryId")
fun update(semesterId: Int, diaryId: Int)
@Query("SELECT * FROM Semesters WHERE student_id = :studentId") @Query("SELECT * FROM Semesters WHERE student_id = :studentId")
fun getSemester(studentId: Int): Single<List<Semester>> fun load(studentId: Int): Maybe<List<Semester>>
@Query("UPDATE Semesters SET is_current = 0") @Query("UPDATE Semesters SET is_current = 0 WHERE student_id = :studentId")
fun resetCurrentSemester() fun resetCurrent(studentId: Int)
@Query("UPDATE Semesters SET is_current = 1 WHERE semester_id = :semesterId")
fun setCurrentSemester(semesterId: Int)
} }

View File

@ -1,17 +1,32 @@
package io.github.wulkanowy.data.db.dao package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy.FAIL
import androidx.room.Query import androidx.room.Query
import androidx.room.Update
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Maybe import io.reactivex.Maybe
@Dao @Dao
interface StudentDao { interface StudentDao {
@Insert @Insert(onConflict = FAIL)
fun insert(student: Student): Long fun insert(student: Student)
@Query("SELECT * FROM Students WHERE id = :id") @Update
fun load(id: Long): Maybe<Student> fun update(student: Student)
@Delete
fun delete(student: Student)
@Query("SELECT * FROM Students WHERE is_current = 1")
fun loadCurrent(): Maybe<Student>
@Query("SELECT * FROM Students")
fun loadAll(): Maybe<List<Student>>
@Query("UPDATE Students SET is_current = 0")
fun resetCurrent()
} }

View File

@ -18,5 +18,5 @@ interface TimetableDao {
fun deleteAll(exams: List<Timetable>) fun deleteAll(exams: List<Timetable>)
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") @Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun getTimetable(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Timetable>> fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Timetable>>
} }

View File

@ -2,9 +2,10 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@Entity(tableName = "Semesters") @Entity(tableName = "Semesters", indices = [Index(value = ["student_id", "diary_id", "semester_id"], unique = true)])
data class Semester( data class Semester(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
@ -26,5 +27,5 @@ data class Semester(
var semesterName: Int, var semesterName: Int,
@ColumnInfo(name = "is_current") @ColumnInfo(name = "is_current")
var current: Boolean = false var isCurrent: Boolean = false
) )

View File

@ -2,9 +2,10 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@Entity(tableName = "Students") @Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id"], unique = true)])
data class Student( data class Student(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
@ -30,5 +31,8 @@ data class Student(
var schoolSymbol: String = "", var schoolSymbol: String = "",
@ColumnInfo(name = "school_name") @ColumnInfo(name = "school_name")
var schoolName: String = "" var schoolName: String = "",
@ColumnInfo(name = "is_current")
var isCurrent: Boolean = false
) )

View File

@ -0,0 +1,42 @@
package io.github.wulkanowy.data.repositories
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.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.local.SemesterLocal
import io.github.wulkanowy.data.repositories.remote.SemesterRemote
import io.reactivex.Maybe
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SemesterRepository @Inject constructor(
private val remote: SemesterRemote,
private val local: SemesterLocal,
private val settings: InternetObservingSettings,
private val apiHelper: ApiHelper
) {
fun getSemesters(student: Student, forceRefresh: Boolean = false): Single<List<Semester>> {
return Maybe.just(apiHelper.initApi(student))
.flatMap { local.getSemesters(student).filter { !forceRefresh } }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getSemesters(student) else Single.error(UnknownHostException())
}.map { newSemesters ->
local.apply {
saveSemesters(newSemesters)
setCurrentSemester(newSemesters.single { it.isCurrent })
}
}.flatMap { local.getSemesters(student).toSingle(emptyList()) })
}
fun getCurrentSemester(student: Student, forceRefresh: Boolean = false): Single<Semester> {
return getSemesters(student, forceRefresh).map { item -> item.single { it.isCurrent } }
}
}

View File

@ -1,64 +0,0 @@
package io.github.wulkanowy.data.repositories
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.local.SessionLocal
import io.github.wulkanowy.data.repositories.remote.SessionRemote
import io.reactivex.Completable
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SessionRepository @Inject constructor(
private val local: SessionLocal,
private val remote: SessionRemote,
private val settings: InternetObservingSettings
) {
val isSessionSaved
get() = local.isSessionSaved
lateinit var cachedStudents: Single<List<Student>>
private set
fun getConnectedStudents(email: String, password: String, symbol: String, endpoint: String): Single<List<Student>> {
cachedStudents = ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { isConnected ->
if (isConnected) remote.getConnectedStudents(email, password, symbol, endpoint)
else Single.error<List<Student>>(UnknownHostException("No internet connection"))
}.doOnSuccess { cachedStudents = Single.just(it) }
return cachedStudents
}
fun saveStudent(student: Student): Completable {
return remote.getSemesters(student)
.flatMapCompletable { local.saveSemesters(it) }
.concatWith(local.saveStudent(student))
}
fun getSemesters(forceRefresh: Boolean = false): Single<List<Semester>> {
return local.getLastStudent()
.flatMapSingle { student ->
remote.initApi(student)
local.getSemesters(student).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
if (it) remote.getCurrentSemester(student)
else Single.error(UnknownHostException())
}.flatMap { current ->
local.getSemesters(student).doOnSuccess { semesters ->
if (semesters.single { it.current }.semesterId != current.semesterId) {
local.saveSemesters(listOf(current)).andThen {
local.setCurrentSemester(current.semesterId)
}
}
}
}.flatMap {
local.getSemesters(student)
})
}
}
}

View File

@ -0,0 +1,58 @@
package io.github.wulkanowy.data.repositories
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.repositories.local.StudentLocal
import io.github.wulkanowy.data.repositories.remote.StudentRemote
import io.reactivex.Completable
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class StudentRepository @Inject constructor(
private val local: StudentLocal,
private val remote: StudentRemote,
private val settings: InternetObservingSettings,
private val apiHelper: ApiHelper
) {
val isStudentSaved
get() = local.isStudentSaved
lateinit var cachedStudents: Single<List<Student>>
private set
fun getStudents(email: String, password: String, symbol: String, endpoint: String): Single<List<Student>> {
cachedStudents = ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
apiHelper.initApi(email, password, symbol, endpoint)
if (it) remote.getStudents(email, password, endpoint)
else Single.error(UnknownHostException("No internet connection"))
}.doOnSuccess { cachedStudents = Single.just(it) }
return cachedStudents
}
fun getSavedStudents(): Single<List<Student>> {
return local.getStudents().toSingle(emptyList())
}
fun getCurrentStudent(): Single<Student> {
return local.getCurrentStudent().toSingle()
}
fun saveStudent(student: Student): Completable {
return local.saveStudent(student)
}
fun switchStudent(student: Student): Completable {
return local.setCurrentStudent(student)
}
fun logoutCurrentStudent(): Completable {
return local.logoutCurrentStudent()
}
}

View File

@ -10,7 +10,7 @@ import javax.inject.Inject
class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDao) { class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDao) {
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Attendance>> { fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Attendance>> {
return attendanceDb.getExams(semester.diaryId, semester.studentId, startDate, endDate) return attendanceDb.load(semester.diaryId, semester.studentId, startDate, endDate)
.filter { !it.isEmpty() } .filter { !it.isEmpty() }
} }

View File

@ -10,7 +10,7 @@ import javax.inject.Inject
class ExamLocal @Inject constructor(private val examDb: ExamDao) { class ExamLocal @Inject constructor(private val examDb: ExamDao) {
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Exam>> { fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Exam>> {
return examDb.getExams(semester.diaryId, semester.studentId, startDate, endDate) return examDb.load(semester.diaryId, semester.studentId, startDate, endDate)
.filter { !it.isEmpty() } .filter { !it.isEmpty() }
} }

View File

@ -12,11 +12,11 @@ import javax.inject.Singleton
class GradeLocal @Inject constructor(private val gradeDb: GradeDao) { class GradeLocal @Inject constructor(private val gradeDb: GradeDao) {
fun getGrades(semester: Semester): Maybe<List<Grade>> { fun getGrades(semester: Semester): Maybe<List<Grade>> {
return gradeDb.getGrades(semester.semesterId, semester.studentId).filter { !it.isEmpty() } return gradeDb.load(semester.semesterId, semester.studentId).filter { !it.isEmpty() }
} }
fun getNewGrades(semester: Semester): Maybe<List<Grade>> { fun getNewGrades(semester: Semester): Maybe<List<Grade>> {
return gradeDb.getNewGrades(semester.semesterId, semester.studentId) return gradeDb.loadNew(semester.semesterId, semester.studentId)
} }
fun saveGrades(grades: List<Grade>) { fun saveGrades(grades: List<Grade>) {

View File

@ -11,7 +11,7 @@ import javax.inject.Singleton
class GradeSummaryLocal @Inject constructor(private val gradeSummaryDb: GradeSummaryDao) { class GradeSummaryLocal @Inject constructor(private val gradeSummaryDb: GradeSummaryDao) {
fun getGradesSummary(semester: Semester): Maybe<List<GradeSummary>> { fun getGradesSummary(semester: Semester): Maybe<List<GradeSummary>> {
return gradeSummaryDb.getGradesSummary(semester.semesterId, semester.studentId) return gradeSummaryDb.load(semester.semesterId, semester.studentId)
.filter { !it.isEmpty() } .filter { !it.isEmpty() }
} }

View File

@ -12,7 +12,7 @@ import javax.inject.Singleton
class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) { class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) {
fun getHomework(semester: Semester, date: LocalDate): Maybe<List<Homework>> { fun getHomework(semester: Semester, date: LocalDate): Maybe<List<Homework>> {
return homeworkDb.getHomework(semester.semesterId, semester.studentId, date).filter { !it.isEmpty() } return homeworkDb.load(semester.semesterId, semester.studentId, date).filter { !it.isEmpty() }
} }
fun saveHomework(homework: List<Homework>) { fun saveHomework(homework: List<Homework>) {

View File

@ -12,11 +12,11 @@ import javax.inject.Singleton
class NoteLocal @Inject constructor(private val noteDb: NoteDao) { class NoteLocal @Inject constructor(private val noteDb: NoteDao) {
fun getNotes(semester: Semester): Maybe<List<Note>> { fun getNotes(semester: Semester): Maybe<List<Note>> {
return noteDb.getNotes(semester.semesterId, semester.studentId).filter { !it.isEmpty() } return noteDb.load(semester.semesterId, semester.studentId).filter { !it.isEmpty() }
} }
fun getNewNotes(semester: Semester): Maybe<List<Note>> { fun getNewNotes(semester: Semester): Maybe<List<Note>> {
return noteDb.getNewNotes(semester.semesterId, semester.studentId) return noteDb.loadNew(semester.semesterId, semester.studentId)
} }
fun saveNotes(notes: List<Note>) { fun saveNotes(notes: List<Note>) {

View File

@ -0,0 +1,27 @@
package io.github.wulkanowy.data.repositories.local
import io.github.wulkanowy.data.db.dao.SemesterDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Maybe
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) {
fun saveSemesters(semesters: List<Semester>) {
return semesterDb.insertAll(semesters)
}
fun getSemesters(student: Student): Maybe<List<Semester>> {
return semesterDb.load(student.studentId).filter { !it.isEmpty() }
}
fun setCurrentSemester(semester: Semester) {
semesterDb.run {
resetCurrent(semester.studentId)
update(semester.semesterId, semester.diaryId)
}
}
}

View File

@ -1,55 +0,0 @@
package io.github.wulkanowy.data.repositories.local
import android.content.Context
import io.github.wulkanowy.data.db.SharedPrefHelper
import io.github.wulkanowy.data.db.dao.SemesterDao
import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.security.decrypt
import io.github.wulkanowy.utils.security.encrypt
import io.reactivex.Completable
import io.reactivex.Maybe
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SessionLocal @Inject constructor(
private val studentDb: StudentDao,
private val semesterDb: SemesterDao,
private val sharedPref: SharedPrefHelper,
private val context: Context) {
companion object {
const val LAST_USER_KEY: String = "last_user_id"
}
val isSessionSaved
get() = sharedPref.getLong(LAST_USER_KEY, defaultValue = 0L) != 0L
fun saveStudent(student: Student): Completable {
return Single.fromCallable { studentDb.insert(student.copy(password = encrypt(student.password, context))) }
.map { sharedPref.putLong(LAST_USER_KEY, it) }
.ignoreElement()
}
fun getLastStudent(): Maybe<Student> {
return studentDb.load(sharedPref.getLong(LAST_USER_KEY, defaultValue = 0))
.map { it.apply { password = decrypt(password) } }
}
fun saveSemesters(semesters: List<Semester>): Completable {
return Single.fromCallable { semesterDb.insertAll(semesters) }.ignoreElement()
}
fun getSemesters(student: Student): Single<List<Semester>> {
return semesterDb.getSemester(student.studentId)
}
fun setCurrentSemester(semesterId: Int): Completable {
return Single.fromCallable { semesterDb.resetCurrentSemester() }.ignoreElement().andThen {
semesterDb.setCurrentSemester(semesterId)
}
}
}

View File

@ -0,0 +1,60 @@
package io.github.wulkanowy.data.repositories.local
import android.content.Context
import io.github.wulkanowy.data.db.SharedPrefHelper
import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.security.decrypt
import io.github.wulkanowy.utils.security.encrypt
import io.reactivex.Completable
import io.reactivex.Maybe
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class StudentLocal @Inject constructor(
private val studentDb: StudentDao,
private val sharedPref: SharedPrefHelper,
private val context: Context
) {
companion object {
const val STUDENT_SAVED_KEY: String = "is_student_saved"
}
val isStudentSaved
get() = sharedPref.getBoolean(STUDENT_SAVED_KEY, false)
fun saveStudent(student: Student): Completable {
return Completable.fromCallable {
studentDb.run {
resetCurrent()
studentDb.insert(student.copy(password = encrypt(student.password, context)))
}
}.doOnComplete { sharedPref.putBoolean(STUDENT_SAVED_KEY, true) }
}
fun getCurrentStudent(): Maybe<Student> {
return studentDb.loadCurrent().map { it.apply { password = decrypt(password) } }
}
fun getStudents(): Maybe<List<Student>> {
return studentDb.loadAll()
}
fun setCurrentStudent(student: Student): Completable {
return Completable.fromCallable {
studentDb.run {
resetCurrent()
update(student.apply { isCurrent = true })
}
}.doOnComplete { sharedPref.putBoolean(STUDENT_SAVED_KEY, true) }
}
fun logoutCurrentStudent(): Completable {
return studentDb.loadCurrent().doOnSuccess {
studentDb.delete(it)
sharedPref.putBoolean(STUDENT_SAVED_KEY, false)
}.ignoreElement()
}
}

View File

@ -10,7 +10,7 @@ import javax.inject.Inject
class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao) { class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao) {
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Timetable>> { fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Timetable>> {
return timetableDb.getTimetable(semester.diaryId, semester.studentId, startDate, endDate) return timetableDb.load(semester.diaryId, semester.studentId, startDate, endDate)
.filter { !it.isEmpty() } .filter { !it.isEmpty() }
} }

View File

@ -11,12 +11,8 @@ import javax.inject.Inject
class AttendanceRemote @Inject constructor(private val api: Api) { class AttendanceRemote @Inject constructor(private val api: Api) {
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Attendance>> { fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Attendance>> {
return Single.just(api.run { return Single.just(api.apply { diaryId = semester.diaryId })
if (diaryId != semester.diaryId) { .flatMap { it.getAttendance(startDate, endDate) }.map { attendance ->
diaryId = semester.diaryId
notifyDataChanged()
}
}).flatMap { api.getAttendance(startDate, endDate) }.map { attendance ->
attendance.map { attendance.map {
Attendance( Attendance(
studentId = semester.studentId, studentId = semester.studentId,

View File

@ -11,12 +11,8 @@ import javax.inject.Inject
class ExamRemote @Inject constructor(private val api: Api) { class ExamRemote @Inject constructor(private val api: Api) {
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Exam>> { fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Exam>> {
return Single.just(api.run { return Single.just(api.apply { diaryId = semester.diaryId })
if (diaryId != semester.diaryId) { .flatMap { it.getExams(startDate, endDate) }.map { exams ->
diaryId = semester.diaryId
notifyDataChanged()
}
}).flatMap { api.getExams(startDate, endDate) }.map { exams ->
exams.map { exams.map {
Exam( Exam(
studentId = semester.studentId, studentId = semester.studentId,

View File

@ -12,12 +12,8 @@ import javax.inject.Singleton
class GradeRemote @Inject constructor(private val api: Api) { class GradeRemote @Inject constructor(private val api: Api) {
fun getGrades(semester: Semester): Single<List<Grade>> { fun getGrades(semester: Semester): Single<List<Grade>> {
return Single.just(api.run { return Single.just(api.apply { diaryId = semester.diaryId })
if (diaryId != semester.diaryId) { .flatMap { it.getGrades(semester.semesterId) }
diaryId = semester.diaryId
notifyDataChanged()
}
}).flatMap { api.getGrades(semester.semesterId) }
.map { grades -> .map { grades ->
grades.map { grades.map {
Grade( Grade(

View File

@ -11,12 +11,8 @@ import javax.inject.Singleton
class GradeSummaryRemote @Inject constructor(private val api: Api) { class GradeSummaryRemote @Inject constructor(private val api: Api) {
fun getGradeSummary(semester: Semester): Single<List<GradeSummary>> { fun getGradeSummary(semester: Semester): Single<List<GradeSummary>> {
return Single.just(api.run { return Single.just(api.apply { diaryId = semester.diaryId })
if (diaryId != semester.diaryId) { .flatMap { it.getGradesSummary(semester.semesterId) }
diaryId = semester.diaryId
notifyDataChanged()
}
}).flatMap { api.getGradesSummary(semester.semesterId) }
.map { gradesSummary -> .map { gradesSummary ->
gradesSummary.map { gradesSummary.map {
GradeSummary( GradeSummary(

View File

@ -13,12 +13,8 @@ import javax.inject.Singleton
class HomeworkRemote @Inject constructor(private val api: Api) { class HomeworkRemote @Inject constructor(private val api: Api) {
fun getHomework(semester: Semester, date: LocalDate): Single<List<Homework>> { fun getHomework(semester: Semester, date: LocalDate): Single<List<Homework>> {
return Single.just(api.run { return Single.just(api.apply { diaryId = semester.diaryId })
if (diaryId != semester.diaryId) { .flatMap { it.getHomework(date, date) }
diaryId = semester.diaryId
notifyDataChanged()
}
}).flatMap { api.getHomework(date, date) }
.map { homework -> .map { homework ->
homework.map { homework.map {
Homework( Homework(

View File

@ -12,12 +12,8 @@ import javax.inject.Singleton
class NoteRemote @Inject constructor(private val api: Api) { class NoteRemote @Inject constructor(private val api: Api) {
fun getNotes(semester: Semester): Single<List<Note>> { fun getNotes(semester: Semester): Single<List<Note>> {
return Single.just(api.run { return Single.just(api.apply { diaryId = semester.diaryId })
if (diaryId != semester.diaryId) { .flatMap { it.getNotes() }
diaryId = semester.diaryId
notifyDataChanged()
}
}).flatMap { api.getNotes() }
.map { notes -> .map { notes ->
notes.map { notes.map {
Note( Note(

View File

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

View File

@ -1,96 +0,0 @@
package io.github.wulkanowy.data.repositories.remote
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Single
import okhttp3.logging.HttpLoggingInterceptor
import timber.log.Timber
import java.net.URL
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SessionRemote @Inject constructor(private val api: Api) {
fun getConnectedStudents(email: String, password: String, symbol: String, endpoint: String): Single<List<Student>> {
return Single.just(
initApi(
Student(
email = email,
password = password,
symbol = symbol,
endpoint = endpoint,
loginType = "AUTO"
), true
)
).flatMap {
api.getPupils().map { students ->
students.map { pupil ->
Student(
email = email,
password = password,
symbol = pupil.symbol,
studentId = pupil.studentId,
studentName = pupil.studentName,
schoolSymbol = pupil.schoolSymbol,
schoolName = pupil.schoolName,
endpoint = endpoint,
loginType = pupil.loginType.name
)
}
}
}
}
fun getSemesters(student: Student): Single<List<Semester>> {
return Single.just(initApi(student)).flatMap {
api.getSemesters().map { semesters ->
semesters.map { semester ->
Semester(
studentId = student.studentId,
diaryId = semester.diaryId,
diaryName = semester.diaryName,
semesterId = semester.semesterId,
semesterName = semester.semesterNumber,
current = semester.current
)
}
}
}
}
fun getCurrentSemester(student: Student): Single<Semester> {
return api.getCurrentSemester().map {
Semester(
studentId = student.studentId,
diaryId = it.diaryId,
diaryName = it.diaryName,
semesterId = it.semesterId,
semesterName = it.semesterNumber,
current = it.current
)
}
}
fun initApi(student: Student, reInitialize: Boolean = false) {
if (if (reInitialize) true else 0 == api.studentId) {
api.run {
logLevel = HttpLoggingInterceptor.Level.NONE
email = student.email
password = student.password
symbol = student.symbol
host = URL(student.endpoint).run { host + ":$port".removeSuffix(":-1") }
ssl = student.endpoint.startsWith("https")
schoolSymbol = student.schoolSymbol
studentId = student.studentId
loginType = Api.LoginType.valueOf(student.loginType)
notifyDataChanged()
setInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger {
Timber.d(it)
}).setLevel(HttpLoggingInterceptor.Level.BASIC))
}
}
}
}

View File

@ -0,0 +1,29 @@
package io.github.wulkanowy.data.repositories.remote
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class StudentRemote @Inject constructor(private val api: Api) {
fun getStudents(email: String, password: String, endpoint: String): Single<List<Student>> {
return api.getPupils().map { students ->
students.map { pupil ->
Student(
email = email,
password = password,
symbol = pupil.symbol,
studentId = pupil.studentId,
studentName = pupil.studentName,
schoolSymbol = pupil.schoolSymbol,
schoolName = pupil.schoolName,
endpoint = endpoint,
loginType = pupil.loginType.name
)
}
}
}
}

View File

@ -12,12 +12,9 @@ import javax.inject.Inject
class TimetableRemote @Inject constructor(private val api: Api) { class TimetableRemote @Inject constructor(private val api: Api) {
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Timetable>> { fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Timetable>> {
return Single.just(api.run { return Single.just(api.apply { diaryId = semester.diaryId })
if (diaryId != semester.diaryId) { .flatMap { it.getTimetable(startDate, endDate) }
diaryId = semester.diaryId .map { lessons ->
notifyDataChanged()
}
}).flatMap { api.getTimetable(startDate, endDate) }.map { lessons ->
lessons.map { lessons.map {
Timetable( Timetable(
studentId = semester.studentId, studentId = semester.studentId,

View File

@ -10,7 +10,8 @@ import io.github.wulkanowy.data.repositories.GradeSummaryRepository
import io.github.wulkanowy.data.repositories.HomeworkRepository import io.github.wulkanowy.data.repositories.HomeworkRepository
import io.github.wulkanowy.data.repositories.NoteRepository import io.github.wulkanowy.data.repositories.NoteRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.services.notification.GradeNotification import io.github.wulkanowy.services.notification.GradeNotification
import io.github.wulkanowy.services.notification.NoteNotification import io.github.wulkanowy.services.notification.NoteNotification
@ -26,7 +27,10 @@ import javax.inject.Inject
class SyncWorker : SimpleJobService() { class SyncWorker : SimpleJobService() {
@Inject @Inject
lateinit var session: SessionRepository lateinit var student: StudentRepository
@Inject
lateinit var semester: SemesterRepository
@Inject @Inject
lateinit var gradesDetails: GradeRepository lateinit var gradesDetails: GradeRepository
@ -73,8 +77,8 @@ class SyncWorker : SimpleJobService() {
var error: Throwable? = null var error: Throwable? = null
disposable.add(session.getSemesters(true) disposable.add(student.getCurrentStudent()
.map { it.single { semester -> semester.current } } .flatMap { semester.getCurrentSemester(it, true) }
.flatMapPublisher { .flatMapPublisher {
Single.merge( Single.merge(
listOf( listOf(
@ -107,8 +111,8 @@ class SyncWorker : SimpleJobService() {
} }
private fun sendGradeNotifications() { private fun sendGradeNotifications() {
disposable.add(session.getSemesters() disposable.add(student.getCurrentStudent()
.map { it.single { semester -> semester.current } } .flatMap { semester.getCurrentSemester(it, true) }
.flatMap { gradesDetails.getNewGrades(it) } .flatMap { gradesDetails.getNewGrades(it) }
.map { it.filter { grade -> !grade.isNotified } } .map { it.filter { grade -> !grade.isNotified } }
.subscribe({ .subscribe({
@ -121,8 +125,8 @@ class SyncWorker : SimpleJobService() {
} }
private fun sendNoteNotification() { private fun sendNoteNotification() {
disposable.add(session.getSemesters() disposable.add(student.getCurrentStudent()
.map { it.single { semester -> semester.current } } .flatMap { semester.getCurrentSemester(it, true) }
.flatMap { note.getNewNotes(it) } .flatMap { note.getNewNotes(it) }
.map { it.filter { note -> !note.isNotified } } .map { it.filter { note -> !note.isNotified } }
.subscribe({ .subscribe({

View File

@ -4,7 +4,8 @@ import android.content.Intent
import android.widget.RemoteViewsService import android.widget.RemoteViewsService
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
import io.github.wulkanowy.data.db.SharedPrefHelper import io.github.wulkanowy.data.db.SharedPrefHelper
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetFactory import io.github.wulkanowy.ui.widgets.timetable.TimetableWidgetFactory
import javax.inject.Inject import javax.inject.Inject
@ -12,16 +13,19 @@ import javax.inject.Inject
class TimetableWidgetService : RemoteViewsService() { class TimetableWidgetService : RemoteViewsService() {
@Inject @Inject
lateinit var timetableRepository: TimetableRepository lateinit var timetableRepo: TimetableRepository
@Inject @Inject
lateinit var sessionRepository: SessionRepository lateinit var studentRepo: StudentRepository
@Inject
lateinit var semesterRepo: SemesterRepository
@Inject @Inject
lateinit var sharedPref: SharedPrefHelper lateinit var sharedPref: SharedPrefHelper
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory { override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
AndroidInjection.inject(this) AndroidInjection.inject(this)
return TimetableWidgetFactory(timetableRepository, sessionRepository, sharedPref, applicationContext, intent) return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, sharedPref, applicationContext, intent)
} }
} }

View File

@ -0,0 +1,99 @@
package io.github.wulkanowy.ui.modules.account
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import androidx.appcompat.app.AlertDialog
import dagger.android.support.DaggerAppCompatDialogFragment
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.dialog_account.*
import javax.inject.Inject
class AccountDialog : DaggerAppCompatDialogFragment(), AccountView {
@Inject
lateinit var presenter: AccountPresenter
@Inject
lateinit var accountAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
companion object {
fun newInstance() = AccountDialog()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, 0)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.dialog_account, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
presenter.onAttachView(this)
}
override fun initView() {
accountAdapter.setOnItemClickListener { presenter.onItemSelected(it) }
accountDialogAdd.setOnClickListener { presenter.onAddSelected() }
accountDialogRemove.setOnClickListener { presenter.onRemoveSelected() }
accountDialogRecycler.apply {
layoutManager = SmoothScrollLinearLayoutManager(context)
adapter = accountAdapter
}
}
override fun updateData(data: List<AccountItem>) {
accountAdapter.updateDataSet(data)
}
override fun showMessage(text: String) {
Toast.makeText(context, text, LENGTH_LONG).show()
}
override fun dismissView() {
dismiss()
}
override fun openLoginView() {
activity?.also {
startActivity(LoginActivity.getStartIntent(it))
}
}
override fun showConfirmDialog() {
context?.let {
AlertDialog.Builder(it)
.setTitle(R.string.account_logout_student)
.setMessage(R.string.account_confirm)
.setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() }
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
}
}
override fun recreateView() {
activity?.also {
startActivity(MainActivity.getStartIntent(it))
it.finish()
}
}
override fun onDestroy() {
presenter.onDetachView()
super.onDestroy()
}
}

View File

@ -0,0 +1,51 @@
package io.github.wulkanowy.ui.modules.account
import android.view.View
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_account.*
class AccountItem(val student: Student) : AbstractFlexibleItem<AccountItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_account
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
return ViewHolder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?, position: Int, payloads: MutableList<Any>?) {
holder?.apply {
accountItemName.text = student.studentName
accountItemSchool.text = student.schoolName
accountItemImage.setBackgroundResource(if (student.isCurrent) R.drawable.ic_account_circular_border else 0)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as AccountItem
if (student != other.student) return false
return true
}
override fun hashCode(): Int {
return student.hashCode()
}
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View?
get() = contentView
}
}

View File

@ -0,0 +1,70 @@
package io.github.wulkanowy.ui.modules.account
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Single
import javax.inject.Inject
class AccountPresenter @Inject constructor(
private val errorHandler: ErrorHandler,
private val studentRepository: StudentRepository,
private val schedulers: SchedulersProvider
) : BasePresenter<AccountView>(errorHandler) {
override fun onAttachView(view: AccountView) {
super.onAttachView(view)
view.initView()
loadData()
}
fun onAddSelected() {
view?.openLoginView()
}
fun onRemoveSelected() {
view?.showConfirmDialog()
}
fun onLogoutConfirm() {
disposable.add(studentRepository.logoutCurrentStudent()
.andThen(studentRepository.getSavedStudents())
.flatMap {
if (it.isNotEmpty()) studentRepository.switchStudent(it[0]).toSingle { it }
else Single.just(it)
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally { view?.dismissView() }
.subscribe({
view?.apply {
if (it.isEmpty()) openLoginView()
else recreateView()
}
}, { errorHandler.proceed(it) }))
}
fun onItemSelected(item: AbstractFlexibleItem<*>) {
if (item is AccountItem) {
if (item.student.isCurrent) {
view?.dismissView()
} else {
disposable.add(studentRepository.switchStudent(item.student)
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({ view?.recreateView() }, { errorHandler.proceed(it) }))
}
}
}
private fun loadData() {
disposable.add(studentRepository.getSavedStudents()
.map { it.map { item -> AccountItem(item) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({ view?.updateData(it) }, { errorHandler.proceed(it) }))
}
}

View File

@ -0,0 +1,19 @@
package io.github.wulkanowy.ui.modules.account
import io.github.wulkanowy.ui.base.BaseView
interface AccountView : BaseView {
fun initView()
fun updateData(data: List<AccountItem>)
fun dismissView()
fun showConfirmDialog()
fun openLoginView()
fun recreateView()
}

View File

@ -47,7 +47,7 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
override fun initView() { override fun initView() {
attendanceAdapter.apply { attendanceAdapter.apply {
setOnItemClickListener { presenter.onAttendanceItemSelected(getItem(it)) } setOnItemClickListener { presenter.onAttendanceItemSelected(it) }
} }
attendanceRecycler.run { attendanceRecycler.run {

View File

@ -4,7 +4,8 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.ErrorHandler import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.repositories.AttendanceRepository import io.github.wulkanowy.data.repositories.AttendanceRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.isHolidays import io.github.wulkanowy.utils.isHolidays
@ -23,7 +24,8 @@ class AttendancePresenter @Inject constructor(
private val errorHandler: ErrorHandler, private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider, private val schedulers: SchedulersProvider,
private val attendanceRepository: AttendanceRepository, private val attendanceRepository: AttendanceRepository,
private val sessionRepository: SessionRepository, private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val prefRepository: PreferencesRepository private val prefRepository: PreferencesRepository
) : BasePresenter<AttendanceView>(errorHandler) { ) : BasePresenter<AttendanceView>(errorHandler) {
@ -66,9 +68,9 @@ class AttendancePresenter @Inject constructor(
currentDate = date currentDate = date
disposable.apply { disposable.apply {
clear() clear()
add(sessionRepository.getSemesters() add(studentRepository.getCurrentStudent()
.delay(200, MILLISECONDS) .delay(200, MILLISECONDS)
.map { it.single { semester -> semester.current } } .flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { attendanceRepository.getAttendance(it, date, date, forceRefresh) } .flatMap { attendanceRepository.getAttendance(it, date, date, forceRefresh) }
.map { list -> .map { list ->
if (prefRepository.isShowPresent) list if (prefRepository.isShowPresent) list

View File

@ -3,7 +3,9 @@ package io.github.wulkanowy.ui.modules.exam
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.View.* import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
@ -48,7 +50,7 @@ class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView.
override fun initView() { override fun initView() {
examAdapter.run { examAdapter.run {
setOnItemClickListener { presenter.onExamItemSelected(getItem(it)) } setOnItemClickListener { presenter.onExamItemSelected(it) }
} }
examRecycler.run { examRecycler.run {
layoutManager = SmoothScrollLinearLayoutManager(context) layoutManager = SmoothScrollLinearLayoutManager(context)

View File

@ -4,7 +4,8 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.ErrorHandler import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.repositories.ExamRepository import io.github.wulkanowy.data.repositories.ExamRepository
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.utils.friday
@ -23,7 +24,8 @@ class ExamPresenter @Inject constructor(
private val errorHandler: ErrorHandler, private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider, private val schedulers: SchedulersProvider,
private val examRepository: ExamRepository, private val examRepository: ExamRepository,
private val sessionRepository: SessionRepository private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository
) : BasePresenter<ExamView>(errorHandler) { ) : BasePresenter<ExamView>(errorHandler) {
lateinit var currentDate: LocalDate lateinit var currentDate: LocalDate
@ -65,9 +67,9 @@ class ExamPresenter @Inject constructor(
currentDate = date currentDate = date
disposable.apply { disposable.apply {
clear() clear()
add(sessionRepository.getSemesters() add(studentRepository.getCurrentStudent()
.delay(200, MILLISECONDS) .delay(200, MILLISECONDS)
.map { it.single { semester -> semester.current } } .flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { .flatMap {
examRepository.getExams(it, currentDate.monday, currentDate.friday, forceRefresh) examRepository.getExams(it, currentDate.monday, currentDate.friday, forceRefresh)
}.map { it.groupBy { exam -> exam.date }.toSortedMap() } }.map { it.groupBy { exam -> exam.date }.toSortedMap() }

View File

@ -1,9 +1,14 @@
package io.github.wulkanowy.ui.modules.grade package io.github.wulkanowy.ui.modules.grade
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.View.INVISIBLE import android.view.View.INVISIBLE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
@ -93,7 +98,7 @@ class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainVie
dialog.dismiss() dialog.dismiss()
} }
.setTitle(R.string.grade_switch_semester) .setTitle(R.string.grade_switch_semester)
.setNegativeButton(R.string.all_cancel) { dialog, _ -> dialog.dismiss() } .setNegativeButton(android.R.string.cancel) { _, _ -> }
.show() .show()
} }
} }

View File

@ -2,19 +2,21 @@ package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.ErrorHandler import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.logEvent import io.github.wulkanowy.utils.logEvent
import io.reactivex.Completable import io.reactivex.Completable
import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject import javax.inject.Inject
class GradePresenter @Inject constructor( class GradePresenter @Inject constructor(
private val errorHandler: ErrorHandler, private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider, private val schedulers: SchedulersProvider,
private val sessionRepository: SessionRepository) : BasePresenter<GradeView>(errorHandler) { private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository
) : BasePresenter<GradeView>(errorHandler) {
var selectedIndex = 0 var selectedIndex = 0
private set private set
@ -71,9 +73,10 @@ class GradePresenter @Inject constructor(
} }
private fun loadData() { private fun loadData() {
disposable.add(sessionRepository.getSemesters() disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getSemesters(it) }
.doOnSuccess { .doOnSuccess {
it.first { item -> item.current }.also { current -> it.first { item -> item.isCurrent }.also { current ->
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
semesters = it.filter { semester -> semester.diaryId == current.diaryId } semesters = it.filter { semester -> semester.diaryId == current.diaryId }
} }

View File

@ -59,7 +59,7 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
gradeDetailsAdapter.run { gradeDetailsAdapter.run {
isAutoCollapseOnExpand = true isAutoCollapseOnExpand = true
isAutoScrollOnExpand = true isAutoScrollOnExpand = true
setOnItemClickListener { presenter.onGradeItemSelected(getItem(it)) } setOnItemClickListener { presenter.onGradeItemSelected(it) }
} }
gradeDetailsRecycler.run { gradeDetailsRecycler.run {

View File

@ -5,7 +5,8 @@ import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.repositories.GradeRepository import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.calcAverage import io.github.wulkanowy.utils.calcAverage
@ -19,7 +20,8 @@ class GradeDetailsPresenter @Inject constructor(
private val errorHandler: ErrorHandler, private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider, private val schedulers: SchedulersProvider,
private val gradeRepository: GradeRepository, private val gradeRepository: GradeRepository,
private val sessionRepository: SessionRepository, private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val preferencesRepository: PreferencesRepository private val preferencesRepository: PreferencesRepository
) : BasePresenter<GradeDetailsView>(errorHandler) { ) : BasePresenter<GradeDetailsView>(errorHandler) {
@ -29,7 +31,8 @@ class GradeDetailsPresenter @Inject constructor(
} }
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
disposable.add(sessionRepository.getSemesters() disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getSemesters(it) }
.flatMap { gradeRepository.getGrades(it.first { item -> item.semesterId == semesterId }, forceRefresh) } .flatMap { gradeRepository.getGrades(it.first { item -> item.semesterId == semesterId }, forceRefresh) }
.map { it.map { item -> item.changeModifier(preferencesRepository.gradeModifier) } } .map { it.map { item -> item.changeModifier(preferencesRepository.gradeModifier) } }
.map { createGradeItems(it.groupBy { grade -> grade.subject }.toSortedMap()) } .map { createGradeItems(it.groupBy { grade -> grade.subject }.toSortedMap()) }

View File

@ -3,7 +3,9 @@ package io.github.wulkanowy.ui.modules.grade.summary
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.View.* import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
@ -56,7 +58,7 @@ class GradeSummaryFragment : BaseFragment(), GradeSummaryView, GradeView.GradeCh
gradeSummarySwipe.setOnRefreshListener { presenter.onSwipeRefresh() } gradeSummarySwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
} }
override fun updateDataSet(data: List<GradeSummaryItem>, header: GradeSummaryScrollableHeader) { override fun updateData(data: List<GradeSummaryItem>, header: GradeSummaryScrollableHeader) {
gradeSummaryAdapter.apply { gradeSummaryAdapter.apply {
updateDataSet(data, true) updateDataSet(data, true)
removeAllScrollableHeaders() removeAllScrollableHeaders()

View File

@ -5,7 +5,8 @@ import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.repositories.GradeRepository import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.GradeSummaryRepository import io.github.wulkanowy.data.repositories.GradeSummaryRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.calcAverage import io.github.wulkanowy.utils.calcAverage
@ -19,7 +20,8 @@ class GradeSummaryPresenter @Inject constructor(
private val errorHandler: ErrorHandler, private val errorHandler: ErrorHandler,
private val gradeSummaryRepository: GradeSummaryRepository, private val gradeSummaryRepository: GradeSummaryRepository,
private val gradeRepository: GradeRepository, private val gradeRepository: GradeRepository,
private val sessionRepository: SessionRepository, private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val preferencesRepository: PreferencesRepository, private val preferencesRepository: PreferencesRepository,
private val schedulers: SchedulersProvider private val schedulers: SchedulersProvider
) : BasePresenter<GradeSummaryView>(errorHandler) { ) : BasePresenter<GradeSummaryView>(errorHandler) {
@ -30,7 +32,8 @@ class GradeSummaryPresenter @Inject constructor(
} }
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
disposable.add(sessionRepository.getSemesters() disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getSemesters(it) }
.map { semester -> semester.first { it.semesterId == semesterId } } .map { semester -> semester.first { it.semesterId == semesterId } }
.flatMap { .flatMap {
gradeSummaryRepository.getGradesSummary(it, forceRefresh) gradeSummaryRepository.getGradesSummary(it, forceRefresh)
@ -63,7 +66,7 @@ class GradeSummaryPresenter @Inject constructor(
view?.run { view?.run {
showEmpty(it.first.isEmpty()) showEmpty(it.first.isEmpty())
showContent(it.first.isNotEmpty()) showContent(it.first.isNotEmpty())
updateDataSet(it.first, it.second) updateData(it.first, it.second)
} }
logEvent("Grade summary load", mapOf("items" to it.first.size, "forceRefresh" to forceRefresh)) logEvent("Grade summary load", mapOf("items" to it.first.size, "forceRefresh" to forceRefresh))
}) { }) {

View File

@ -12,7 +12,7 @@ interface GradeSummaryView : BaseView {
fun initView() fun initView()
fun updateDataSet(data: List<GradeSummaryItem>, header: GradeSummaryScrollableHeader) fun updateData(data: List<GradeSummaryItem>, header: GradeSummaryScrollableHeader)
fun resetView() fun resetView()

View File

@ -44,7 +44,7 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
override fun initView() { override fun initView() {
homeworkAdapter.run { homeworkAdapter.run {
setOnItemClickListener { presenter.onHomeworkItemSelected(getItem(it)) } setOnItemClickListener { presenter.onHomeworkItemSelected(it) }
} }
homeworkRecycler.run { homeworkRecycler.run {

View File

@ -3,7 +3,8 @@ package io.github.wulkanowy.ui.modules.homework
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.ErrorHandler import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.repositories.HomeworkRepository import io.github.wulkanowy.data.repositories.HomeworkRepository
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.isHolidays import io.github.wulkanowy.utils.isHolidays
@ -20,7 +21,8 @@ class HomeworkPresenter @Inject constructor(
private val errorHandler: ErrorHandler, private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider, private val schedulers: SchedulersProvider,
private val homeworkRepository: HomeworkRepository, private val homeworkRepository: HomeworkRepository,
private val sessionRepository: SessionRepository private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository
) : BasePresenter<HomeworkView>(errorHandler) { ) : BasePresenter<HomeworkView>(errorHandler) {
lateinit var currentDate: LocalDate lateinit var currentDate: LocalDate
@ -57,9 +59,9 @@ class HomeworkPresenter @Inject constructor(
currentDate = date currentDate = date
disposable.apply { disposable.apply {
clear() clear()
add(sessionRepository.getSemesters() add(studentRepository.getCurrentStudent()
.delay(200, TimeUnit.MILLISECONDS) .delay(200, TimeUnit.MILLISECONDS)
.map { it.single { semester -> semester.current } } .flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { homeworkRepository.getHomework(it, currentDate, forceRefresh) } .flatMap { homeworkRepository.getHomework(it, currentDate, forceRefresh) }
.map { items -> items.map { HomeworkItem(it) } } .map { items -> items.map { HomeworkItem(it) } }
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)

View File

@ -1,22 +1,25 @@
package io.github.wulkanowy.ui.modules.login package io.github.wulkanowy.ui.modules.login
import android.content.res.Resources import android.content.res.Resources
import android.database.sqlite.SQLiteConstraintException
import io.github.wulkanowy.R
import io.github.wulkanowy.api.login.BadCredentialsException import io.github.wulkanowy.api.login.BadCredentialsException
import io.github.wulkanowy.data.ErrorHandler import io.github.wulkanowy.data.ErrorHandler
class LoginErrorHandler(resources: Resources) : ErrorHandler(resources) { class LoginErrorHandler(resources: Resources) : ErrorHandler(resources) {
var doOnBadCredentials: () -> Unit = {} var onBadCredentials: () -> Unit = {}
override fun proceed(error: Throwable) { override fun proceed(error: Throwable) {
when (error) { when (error) {
is BadCredentialsException -> doOnBadCredentials() is BadCredentialsException -> onBadCredentials()
is SQLiteConstraintException -> showErrorMessage(resources.getString(R.string.login_duplicate_student))
else -> super.proceed(error) else -> super.proceed(error)
} }
} }
override fun clear() { override fun clear() {
super.clear() super.clear()
doOnBadCredentials = {} onBadCredentials = {}
} }
} }

View File

@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.login.form package io.github.wulkanowy.ui.modules.login.form
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
@ -12,7 +12,7 @@ import javax.inject.Inject
class LoginFormPresenter @Inject constructor( class LoginFormPresenter @Inject constructor(
private val schedulers: SchedulersProvider, private val schedulers: SchedulersProvider,
private val errorHandler: LoginErrorHandler, private val errorHandler: LoginErrorHandler,
private val sessionRepository: SessionRepository private val studentRepository: StudentRepository
) : BasePresenter<LoginFormView>(errorHandler) { ) : BasePresenter<LoginFormView>(errorHandler) {
private var wasEmpty = false private var wasEmpty = false
@ -21,7 +21,7 @@ class LoginFormPresenter @Inject constructor(
super.onAttachView(view) super.onAttachView(view)
view.run { view.run {
initView() initView()
errorHandler.doOnBadCredentials = { errorHandler.onBadCredentials = {
setErrorPassIncorrect() setErrorPassIncorrect()
showSoftKeyboard() showSoftKeyboard()
Timber.i("Entered wrong username or password") Timber.i("Entered wrong username or password")
@ -32,7 +32,7 @@ class LoginFormPresenter @Inject constructor(
fun attemptLogin(email: String, password: String, symbol: String, endpoint: String) { fun attemptLogin(email: String, password: String, symbol: String, endpoint: String) {
if (!validateCredentials(email, password, symbol)) return if (!validateCredentials(email, password, symbol)) return
disposable.add(sessionRepository.getConnectedStudents(email, password, symbol, endpoint) disposable.add(studentRepository.getStudents(email, password, symbol, endpoint)
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.doOnSubscribe { .doOnSubscribe {

View File

@ -1,5 +1,7 @@
package io.github.wulkanowy.ui.modules.login.options package io.github.wulkanowy.ui.modules.login.options
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -39,7 +41,7 @@ class LoginOptionsFragment : BaseFragment(), LoginOptionsView {
} }
override fun initView() { override fun initView() {
loginAdapter.apply { setOnItemClickListener { presenter.onSelectItem(getItem(it)) } } loginAdapter.apply { setOnItemClickListener { presenter.onItemSelected(it) } }
loginOptionsRecycler.apply { loginOptionsRecycler.apply {
adapter = loginAdapter adapter = loginAdapter
@ -57,8 +59,8 @@ class LoginOptionsFragment : BaseFragment(), LoginOptionsView {
override fun openMainView() { override fun openMainView() {
activity?.let { activity?.let {
startActivity(MainActivity.getStartIntent(it)) startActivity(MainActivity.getStartIntent(it)
it.finish() .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
} }
} }

View File

@ -1,17 +1,20 @@
package io.github.wulkanowy.ui.modules.login.options package io.github.wulkanowy.ui.modules.login.options
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.logRegister import io.github.wulkanowy.utils.logRegister
import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
class LoginOptionsPresenter @Inject constructor( class LoginOptionsPresenter @Inject constructor(
private val errorHandler: ErrorHandler, private val errorHandler: LoginErrorHandler,
private val repository: SessionRepository, private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val schedulers: SchedulersProvider private val schedulers: SchedulersProvider
) : BasePresenter<LoginOptionsView>(errorHandler) { ) : BasePresenter<LoginOptionsView>(errorHandler) {
@ -21,25 +24,27 @@ class LoginOptionsPresenter @Inject constructor(
} }
fun onParentViewLoadData() { fun onParentViewLoadData() {
disposable.add(repository.cachedStudents disposable.add(studentRepository.cachedStudents
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.doOnSubscribe { view?.showActionBar(true) } .doOnSubscribe { view?.showActionBar(true) }
.subscribe({ view?.updateData(it.map { student -> LoginOptionsItem(student) }) }, { errorHandler.proceed(it) })) .subscribe({ view?.updateData(it.map { student -> LoginOptionsItem(student) }) }, { errorHandler.proceed(it) }))
} }
fun onSelectItem(item: AbstractFlexibleItem<*>?) { fun onItemSelected(item: AbstractFlexibleItem<*>?) {
if (item is LoginOptionsItem) { if (item is LoginOptionsItem) {
registerStudent(item.student) registerStudent(item.student)
} }
} }
private fun registerStudent(student: Student) { private fun registerStudent(student: Student) {
disposable.add(repository.saveStudent(student) disposable.add(studentRepository.saveStudent(student.apply { isCurrent = true })
.andThen(semesterRepository.getSemesters(student, true))
.onErrorResumeNext { studentRepository.logoutCurrentStudent().andThen(Single.error(it)) }
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.doOnSubscribe { .doOnSubscribe {
view?.run { view?.apply {
showProgress(true) showProgress(true)
showContent(false) showContent(false)
showActionBar(false) showActionBar(false)
@ -48,6 +53,13 @@ class LoginOptionsPresenter @Inject constructor(
.subscribe({ .subscribe({
logRegister("Success", true, student.symbol, student.endpoint) logRegister("Success", true, student.symbol, student.endpoint)
view?.openMainView() view?.openMainView()
}, { errorHandler.proceed(it) })) }, {
errorHandler.proceed(it)
view?.apply {
showProgress(false)
showContent(true)
showActionBar(true)
}
}))
} }
} }

View File

@ -3,6 +3,8 @@ package io.github.wulkanowy.ui.modules.main
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.TitleState.ALWAYS_SHOW import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.TitleState.ALWAYS_SHOW
@ -12,6 +14,7 @@ import com.ncapdevi.fragnav.FragNavController.Companion.HIDE
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.services.notification.GradeNotification import io.github.wulkanowy.services.notification.GradeNotification
import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.modules.account.AccountDialog
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
import io.github.wulkanowy.ui.modules.exam.ExamFragment import io.github.wulkanowy.ui.modules.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment
@ -58,15 +61,16 @@ class MainActivity : BaseActivity(), MainView {
navController.initialize(startMenuIndex, savedInstanceState) navController.initialize(startMenuIndex, savedInstanceState)
} }
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.action_menu_main, menu)
return true
}
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
presenter.onViewStart() presenter.onViewStart()
} }
override fun onSupportNavigateUp(): Boolean {
return presenter.onUpNavigate()
}
override fun initView() { override fun initView() {
mainBottomNav.run { mainBottomNav.run {
addItems( addItems(
@ -103,6 +107,15 @@ class MainActivity : BaseActivity(), MainView {
} }
} }
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
return if (item?.itemId == R.id.mainMenuAccount) presenter.onAccountManagerSelected()
else false
}
override fun onSupportNavigateUp(): Boolean {
return presenter.onUpNavigate()
}
override fun switchMenuView(position: Int) { override fun switchMenuView(position: Int) {
navController.switchTab(position) navController.switchTab(position)
} }
@ -115,6 +128,10 @@ class MainActivity : BaseActivity(), MainView {
supportActionBar?.setDisplayHomeAsUpEnabled(show) supportActionBar?.setDisplayHomeAsUpEnabled(show)
} }
override fun showAccountPicker() {
navController.showDialogFragment(AccountDialog.newInstance())
}
override fun notifyMenuViewReselected() { override fun notifyMenuViewReselected() {
(navController.currentStack?.get(0) as? MainView.MainChildView)?.onFragmentReselected() (navController.currentStack?.get(0) as? MainView.MainChildView)?.onFragmentReselected()
} }

View File

@ -9,6 +9,7 @@ import io.github.wulkanowy.di.scopes.PerActivity
import io.github.wulkanowy.di.scopes.PerFragment import io.github.wulkanowy.di.scopes.PerFragment
import io.github.wulkanowy.ui.modules.about.AboutFragment import io.github.wulkanowy.ui.modules.about.AboutFragment
import io.github.wulkanowy.ui.modules.about.AboutModule import io.github.wulkanowy.ui.modules.about.AboutModule
import io.github.wulkanowy.ui.modules.account.AccountDialog
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
import io.github.wulkanowy.ui.modules.exam.ExamFragment import io.github.wulkanowy.ui.modules.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment
@ -57,12 +58,19 @@ abstract class MainModule {
@ContributesAndroidInjector(modules = [AboutModule::class]) @ContributesAndroidInjector(modules = [AboutModule::class])
abstract fun bindAboutFragment(): AboutFragment abstract fun bindAboutFragment(): AboutFragment
@PerFragment
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindSettingsFragment(): SettingsFragment abstract fun bindSettingsFragment(): SettingsFragment
@PerFragment
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindNoteFragment(): NoteFragment abstract fun bindNoteFragment(): NoteFragment
@PerFragment
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindHomeworkFragment(): HomeworkFragment abstract fun bindHomeworkFragment(): HomeworkFragment
@PerFragment
@ContributesAndroidInjector
abstract fun bindsAccountDialog(): AccountDialog
} }

View File

@ -41,6 +41,11 @@ class MainPresenter @Inject constructor(
} }
} }
fun onAccountManagerSelected(): Boolean {
view?.showAccountPicker()
return true
}
fun onUpNavigate(): Boolean { fun onUpNavigate(): Boolean {
view?.popView() view?.popView()
return true return true

View File

@ -18,6 +18,8 @@ interface MainView : BaseView {
fun showHomeArrow(show: Boolean) fun showHomeArrow(show: Boolean)
fun showAccountPicker()
fun notifyMenuViewReselected() fun notifyMenuViewReselected()
fun setViewTitle(title: String) fun setViewTitle(title: String)

View File

@ -76,7 +76,7 @@ class MoreFragment : BaseFragment(), MoreView, MainView.TitledView, MainView.Mai
} }
override fun initView() { override fun initView() {
moreAdapter.run { setOnItemClickListener { presenter.onItemSelected(getItem(it)) } } moreAdapter.run { setOnItemClickListener { presenter.onItemSelected(it) } }
moreRecycler.apply { moreRecycler.apply {
layoutManager = SmoothScrollLinearLayoutManager(context) layoutManager = SmoothScrollLinearLayoutManager(context)

View File

@ -10,8 +10,7 @@ import io.github.wulkanowy.R
import kotlinx.android.extensions.LayoutContainer import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_more.* import kotlinx.android.synthetic.main.item_more.*
class MoreItem(val title: String, private val drawable: Drawable?) class MoreItem(val title: String, private val drawable: Drawable?) : AbstractFlexibleItem<MoreItem.ViewHolder>() {
: AbstractFlexibleItem<MoreItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_more override fun getLayoutRes() = R.layout.item_more
@ -19,8 +18,7 @@ class MoreItem(val title: String, private val drawable: Drawable?)
return ViewHolder(view, adapter) return ViewHolder(view, adapter)
} }
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?, override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ViewHolder?, position: Int, payloads: MutableList<Any>?) {
position: Int, payloads: MutableList<Any>?) {
holder?.apply { holder?.apply {
moreItemTitle.text = title moreItemTitle.text = title
moreItemImage.setImageDrawable(drawable) moreItemImage.setImageDrawable(drawable)
@ -42,8 +40,7 @@ class MoreItem(val title: String, private val drawable: Drawable?)
return title.hashCode() return title.hashCode()
} }
class ViewHolder(view: View?, adapter: FlexibleAdapter<*>?) class ViewHolder(view: View?, adapter: FlexibleAdapter<*>?) : FlexibleViewHolder(view, adapter), LayoutContainer {
: FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View? override val containerView: View?
get() = contentView get() = contentView

View File

@ -46,7 +46,7 @@ class NoteFragment : BaseFragment(), NoteView, MainView.TitledView {
override fun initView() { override fun initView() {
noteAdapter.run { noteAdapter.run {
setOnItemClickListener { presenter.onNoteItemSelected(getItem(it)) } setOnItemClickListener { presenter.onNoteItemSelected(it) }
} }
noteRecycler.run { noteRecycler.run {

View File

@ -4,7 +4,8 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.ErrorHandler import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.repositories.NoteRepository import io.github.wulkanowy.data.repositories.NoteRepository
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.logEvent import io.github.wulkanowy.utils.logEvent
@ -14,8 +15,9 @@ import javax.inject.Inject
class NotePresenter @Inject constructor( class NotePresenter @Inject constructor(
private val errorHandler: ErrorHandler, private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider, private val schedulers: SchedulersProvider,
private val sessionRepository: SessionRepository, private val studentRepository: StudentRepository,
private val noteRepository: NoteRepository private val noteRepository: NoteRepository,
private val semesterRepository: SemesterRepository
) : BasePresenter<NoteView>(errorHandler) { ) : BasePresenter<NoteView>(errorHandler) {
override fun onAttachView(view: NoteView) { override fun onAttachView(view: NoteView) {
@ -29,8 +31,8 @@ class NotePresenter @Inject constructor(
} }
private fun loadData(forceRefresh: Boolean = false) { private fun loadData(forceRefresh: Boolean = false) {
disposable.add(sessionRepository.getSemesters() disposable.add(studentRepository.getCurrentStudent()
.map { it.single { semester -> semester.current } } .flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { noteRepository.getNotes(it, forceRefresh) } .flatMap { noteRepository.getNotes(it, forceRefresh) }
.map { items -> items.map { NoteItem(it) } } .map { items -> items.map { NoteItem(it) } }
.map { items -> items.sortedByDescending { it.note.date } } .map { items -> items.sortedByDescending { it.note.date } }

View File

@ -1,20 +1,20 @@
package io.github.wulkanowy.ui.modules.splash package io.github.wulkanowy.ui.modules.splash
import io.github.wulkanowy.data.ErrorHandler import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.logLogin import io.github.wulkanowy.utils.logLogin
import javax.inject.Inject import javax.inject.Inject
class SplashPresenter @Inject constructor( class SplashPresenter @Inject constructor(
private val sessionRepository: SessionRepository, private val studentRepository: StudentRepository,
errorHandler: ErrorHandler errorHandler: ErrorHandler
) : BasePresenter<SplashView>(errorHandler) { ) : BasePresenter<SplashView>(errorHandler) {
override fun onAttachView(view: SplashView) { override fun onAttachView(view: SplashView) {
super.onAttachView(view) super.onAttachView(view)
view.run { view.run {
if (sessionRepository.isSessionSaved) { if (studentRepository.isStudentSaved) {
logLogin("Open app") logLogin("Open app")
openMainView() openMainView()
} else openLoginView() } else openLoginView()

View File

@ -47,7 +47,7 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView,
override fun initView() { override fun initView() {
timetableAdapter.run { timetableAdapter.run {
setOnItemClickListener { presenter.onTimetableItemSelected(getItem(it)) } setOnItemClickListener { presenter.onTimetableItemSelected(it) }
} }
timetableRecycler.run { timetableRecycler.run {

View File

@ -2,7 +2,8 @@ package io.github.wulkanowy.ui.modules.timetable
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.ErrorHandler import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
@ -22,7 +23,8 @@ class TimetablePresenter @Inject constructor(
private val errorHandler: ErrorHandler, private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider, private val schedulers: SchedulersProvider,
private val timetableRepository: TimetableRepository, private val timetableRepository: TimetableRepository,
private val sessionRepository: SessionRepository private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository
) : BasePresenter<TimetableView>(errorHandler) { ) : BasePresenter<TimetableView>(errorHandler) {
lateinit var currentDate: LocalDate lateinit var currentDate: LocalDate
@ -64,9 +66,9 @@ class TimetablePresenter @Inject constructor(
currentDate = date currentDate = date
disposable.apply { disposable.apply {
clear() clear()
add(sessionRepository.getSemesters() add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getCurrentSemester(it) }
.delay(200, MILLISECONDS) .delay(200, MILLISECONDS)
.map { it.single { semester -> semester.current } }
.flatMap { timetableRepository.getTimetable(it, currentDate, currentDate, forceRefresh) } .flatMap { timetableRepository.getTimetable(it, currentDate, currentDate, forceRefresh) }
.map { items -> items.map { TimetableItem(it, view?.roomString.orEmpty()) } } .map { items -> items.map { TimetableItem(it, view?.roomString.orEmpty()) } }
.map { items -> items.sortedBy { it.lesson.number } } .map { items -> items.sortedBy { it.lesson.number } }

View File

@ -12,7 +12,8 @@ import android.widget.RemoteViewsService
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.SharedPrefHelper import io.github.wulkanowy.data.db.SharedPrefHelper
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.repositories.SessionRepository import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
@ -21,7 +22,8 @@ import timber.log.Timber
class TimetableWidgetFactory( class TimetableWidgetFactory(
private val timetableRepository: TimetableRepository, private val timetableRepository: TimetableRepository,
private val sessionRepository: SessionRepository, private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val sharedPref: SharedPrefHelper, private val sharedPref: SharedPrefHelper,
private val context: Context, private val context: Context,
private val intent: Intent? private val intent: Intent?
@ -46,9 +48,9 @@ class TimetableWidgetFactory(
override fun onDataSetChanged() { override fun onDataSetChanged() {
intent?.action?.let { LocalDate.ofEpochDay(sharedPref.getLong(it, 0)) } intent?.action?.let { LocalDate.ofEpochDay(sharedPref.getLong(it, 0)) }
?.let { date -> ?.let { date ->
if (sessionRepository.isSessionSaved) { if (studentRepository.isStudentSaved) {
disposable.add(sessionRepository.getSemesters() disposable.add(studentRepository.getCurrentStudent()
.map { it.single { item -> item.current } } .flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { timetableRepository.getTimetable(it, date, date) } .flatMap { timetableRepository.getTimetable(it, date, date) }
.map { item -> item.sortedBy { it.number } } .map { item -> item.sortedBy { it.number } }
.subscribe({ lessons = it }) .subscribe({ lessons = it })

View File

@ -1,10 +1,11 @@
package io.github.wulkanowy.utils package io.github.wulkanowy.utils
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
inline fun FlexibleAdapter<*>.setOnItemClickListener(crossinline listener: (position: Int) -> Unit) { inline fun FlexibleAdapter<*>.setOnItemClickListener(crossinline listener: (item: AbstractFlexibleItem<*>) -> Unit) {
addListener(FlexibleAdapter.OnItemClickListener { _, position -> addListener(FlexibleAdapter.OnItemClickListener { _, position ->
listener(position) listener(getItem(position) as AbstractFlexibleItem<*>)
true true
}) })
} }

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13h-4v4h-2v-4L7,
13v-2h4L11,7h2v4h4v2z" />
</vector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/transparent" />
<stroke
android:width="3dp"
android:color="@color/colorPrimary" />
</shape>

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFF"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2
12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22
0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z" />
</vector>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="280dp"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/accountDialogTitle"
android:layout_width="match_parent"
android:layout_height="64dp"
android:paddingStart="24dp"
android:paddingLeft="24dp"
android:paddingEnd="24dp"
android:paddingRight="24dp"
android:text="@string/account_title"
android:textSize="20sp"
android:textStyle="bold"
app:firstBaselineToTopHeight="40dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/accountDialogRecycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/accountDialogTitle"
android:overScrollMode="never"
tools:itemCount="3"
tools:listitem="@layout/item_account" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/accountDialogAdd"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/accountDialogRecycler"
android:layout_margin="8dp"
android:text="@string/account_add_new" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/accountDialogRemove"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/accountDialogRecycler"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_margin="8dp"
android:text="@string/account_logout"
android:textColor="@color/colorPrimary" />
</RelativeLayout>

View File

@ -10,7 +10,6 @@
android:padding="20dp"> android:padding="20dp">
<TextView <TextView
android:id="@+id/attendance_dialog_details"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp" android:layout_marginTop="20dp"

View File

@ -10,7 +10,6 @@
android:padding="20dp"> android:padding="20dp">
<TextView <TextView
android:id="@+id/exam_dialog_details"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp" android:layout_marginTop="20dp"

View File

@ -10,7 +10,6 @@
android:padding="20dp"> android:padding="20dp">
<TextView <TextView
android:id="@+id/homework_dialog_details"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp" android:layout_marginTop="20dp"

View File

@ -10,7 +10,6 @@
android:padding="20dp"> android:padding="20dp">
<TextView <TextView
android:id="@+id/note_dialog_details"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp" android:layout_marginTop="20dp"

View File

@ -10,7 +10,6 @@
android:padding="20dp"> android:padding="20dp">
<TextView <TextView
android:id="@+id/exam_dialog_details"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp" android:layout_marginTop="20dp"

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingStart="24dp"
android:paddingLeft="24dp"
android:paddingEnd="24dp"
android:paddingRight="24dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/accountItemImage"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:background="@drawable/ic_account_circular_border"
android:tint="#7E7E7E"
app:srcCompat="@drawable/ic_all_account_24dp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/accountItemName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/accountItemImage"
android:layout_marginStart="20dp"
android:layout_marginLeft="20dp"
android:layout_toEndOf="@id/accountItemImage"
android:layout_toRightOf="@id/accountItemImage"
android:text="@string/app_name"
android:textSize="16sp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/accountItemSchool"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/accountItemName"
android:layout_marginStart="20dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="3dp"
android:layout_toEndOf="@id/accountItemImage"
android:layout_toRightOf="@id/accountItemImage"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/app_name"
android:textSize="12sp" />
</RelativeLayout>

View File

@ -16,7 +16,7 @@
android:layout_height="24dp" android:layout_height="24dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
app:srcCompat="@drawable/ic_more_settings_24dp" app:srcCompat="@drawable/ic_more_settings_24dp"
android:tint="?android:attr/android:textColorSecondary"/> app:tint="?android:attr/android:textColorSecondary" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/moreItemTitle" android:id="@+id/moreItemTitle"
@ -27,5 +27,4 @@
android:layout_marginLeft="32dp" android:layout_marginLeft="32dp"
android:text="@string/app_name" android:text="@string/app_name"
android:textSize="16sp" /> android:textSize="16sp" />
</LinearLayout> </LinearLayout>

View File

@ -1,12 +1,10 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:tools="http://schemas.android.com/tools"
tools:context="io.github.wulkanowy.timetable.MainActivity">
<item <item
android:id="@+id/gradeMenuSemester" android:id="@+id/gradeMenuSemester"
android:icon="@drawable/ic_menu_grade_semester_24dp" android:icon="@drawable/ic_menu_grade_semester_24dp"
android:orderInCategory="2" android:orderInCategory="1"
android:title="@string/grade_switch_semester" android:title="@string/grade_switch_semester"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
</menu> </menu>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/mainMenuAccount"
android:icon="@drawable/ic_all_account_24dp"
android:orderInCategory="2"
android:title="@string/main_account_picker"
app:showAsAction="always" />
</menu>

View File

@ -13,9 +13,10 @@
<string name="about_title">O aplikacji</string> <string name="about_title">O aplikacji</string>
<string name="note_title">Uwagi i osiągnięcia</string> <string name="note_title">Uwagi i osiągnięcia</string>
<string name="homework_title">Zadania domowe</string> <string name="homework_title">Zadania domowe</string>
<string name="account_title">Wybierz konto</string>
<!--Login form--> <!--Login-->
<string name="login_header_default">Zaloguj się za pomocą konta ucznia lub rodzica</string> <string name="login_header_default">Zaloguj się za pomocą konta ucznia lub rodzica</string>
<string name="login_header_symbol">Podaj symbol dziennika VULCAN</string> <string name="login_header_symbol">Podaj symbol dziennika VULCAN</string>
<string name="login_nickname_hint">Email lub nick</string> <string name="login_nickname_hint">Email lub nick</string>
@ -29,6 +30,11 @@
<string name="login_incorrect_password">To hasło jest niepoprawne</string> <string name="login_incorrect_password">To hasło jest niepoprawne</string>
<string name="login_incorrect_symbol">Nie znaleziono ucznia. Sprwadź symbol</string> <string name="login_incorrect_symbol">Nie znaleziono ucznia. Sprwadź symbol</string>
<string name="login_field_required">To pole jest wymagane</string> <string name="login_field_required">To pole jest wymagane</string>
<string name="login_duplicate_student">Ten student jest już zalogowany</string>
<!--Main-->
<string name="main_account_picker">Menadżer kont</string>
<!--Grade--> <!--Grade-->
@ -140,6 +146,13 @@
<string name="homework_no_items">Brak zadań domowych</string> <string name="homework_no_items">Brak zadań domowych</string>
<!--Account-->
<string name="account_add_new">Dodaj konto</string>
<string name="account_logout">Wyloguj</string>
<string name="account_confirm">Czy chcesz wylogować aktualnego ucznia?</string>
<string name="account_logout_student">Wylogowanie ucznia</string>
<!--Generic--> <!--Generic-->
<string name="all_content">Treść</string> <string name="all_content">Treść</string>
<string name="all_description">Opis</string> <string name="all_description">Opis</string>
@ -151,7 +164,6 @@
<string name="all_details">Szczegóły</string> <string name="all_details">Szczegóły</string>
<string name="all_category">Kategoria</string> <string name="all_category">Kategoria</string>
<string name="all_close">Zamknij</string> <string name="all_close">Zamknij</string>
<string name="all_cancel">Anuluj</string>
<string name="all_no_data">Brak danych</string> <string name="all_no_data">Brak danych</string>
<string name="all_subject">Przedmiot</string> <string name="all_subject">Przedmiot</string>
<string name="all_prev">Poprzedni</string> <string name="all_prev">Poprzedni</string>

View File

@ -13,9 +13,10 @@
<string name="about_title">About</string> <string name="about_title">About</string>
<string name="note_title">Notes and achievements</string> <string name="note_title">Notes and achievements</string>
<string name="homework_title">Homework</string> <string name="homework_title">Homework</string>
<string name="account_title">Choose account</string>
<!--Login form--> <!--Login-->
<string name="login_header_default">Sign in with the student or parent account</string> <string name="login_header_default">Sign in with the student or parent account</string>
<string name="login_header_symbol">Enter the VULCAN diary symbol</string> <string name="login_header_symbol">Enter the VULCAN diary symbol</string>
<string name="login_nickname_hint">Email or nick</string> <string name="login_nickname_hint">Email or nick</string>
@ -29,6 +30,11 @@
<string name="login_incorrect_password">This password is incorrect</string> <string name="login_incorrect_password">This password is incorrect</string>
<string name="login_incorrect_symbol">Student not found. Check the symbol</string> <string name="login_incorrect_symbol">Student not found. Check the symbol</string>
<string name="login_field_required">This field is required</string> <string name="login_field_required">This field is required</string>
<string name="login_duplicate_student">This student has already been logged in</string>
<!--Main-->
<string name="main_account_picker">Account manager</string>
<!--Grade--> <!--Grade-->
@ -125,6 +131,13 @@
<string name="homework_no_items">No info about homework</string> <string name="homework_no_items">No info about homework</string>
<!--Account-->
<string name="account_add_new">Add account</string>
<string name="account_logout">Logout</string>
<string name="account_confirm">Do you want to log out of an active student?</string>
<string name="account_logout_student">Student logout</string>
<!--Generic--> <!--Generic-->
<string name="all_content">Content</string> <string name="all_content">Content</string>
<string name="all_description">Description</string> <string name="all_description">Description</string>
@ -136,7 +149,6 @@
<string name="all_details">Details</string> <string name="all_details">Details</string>
<string name="all_category">Category</string> <string name="all_category">Category</string>
<string name="all_close">Close</string> <string name="all_close">Close</string>
<string name="all_cancel">Cancel</string>
<string name="all_no_data">No data</string> <string name="all_no_data">No data</string>
<string name="all_subject">Subject</string> <string name="all_subject">Subject</string>
<string name="all_prev">Prev</string> <string name="all_prev">Prev</string>

View File

@ -14,7 +14,6 @@
<item name="subtitleTextColor">@android:color/primary_text_dark</item> <item name="subtitleTextColor">@android:color/primary_text_dark</item>
<item name="android:colorBackground">@android:color/white</item> <item name="android:colorBackground">@android:color/white</item>
<item name="bottomNavBackground">@color/bottom_nav_background_inverse</item> <item name="bottomNavBackground">@color/bottom_nav_background_inverse</item>
<item name="android:windowAnimationStyle">@null</item>
<!-- AboutLibraries specific values --> <!-- AboutLibraries specific values -->
<item name="about_libraries_window_background">@color/about_libraries_window_background</item> <item name="about_libraries_window_background">@color/about_libraries_window_background</item>

View File

@ -6,6 +6,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.reactivex.Single import io.reactivex.Single
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
@ -15,8 +16,8 @@ import java.sql.Date
class AttendanceRemoteTest { class AttendanceRemoteTest {
@MockK @SpyK
private lateinit var mockApi: Api private var mockApi = Api()
@MockK @MockK
private lateinit var semesterMock: Semester private lateinit var semesterMock: Semester
@ -27,7 +28,7 @@ class AttendanceRemoteTest {
} }
@Test @Test
fun getExamsTest() { fun getAttendanceTest() {
every { mockApi.getAttendance( every { mockApi.getAttendance(
LocalDate.of(2018, 9, 10), LocalDate.of(2018, 9, 10),
LocalDate.of(2018, 9, 15) LocalDate.of(2018, 9, 15)

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