1
0

Compare commits

...

21 Commits
0.7.0 ... 0.7.3

Author SHA1 Message Date
c2ab53cfeb Version 0.7.3 2019-03-26 19:46:59 +01:00
87268b3ef6 Fix rejected execution in sync worker (#305) 2019-03-26 16:47:14 +01:00
b3cd7e8ac1 Fix undeliverable network exceptions (#306)
* Remove unnecessary this
2019-03-26 14:32:23 +01:00
5a997dacb7 Version 0.7.2 2019-03-24 22:42:09 +01:00
ed9458d9a5 Fix more than one current semester in database (#304) 2019-03-24 20:21:05 +01:00
3656d3161f Fix crash on duplicate items (#303) 2019-03-24 17:31:39 +01:00
d178c15d2f Update dependencies (#302) 2019-03-24 16:03:51 +01:00
1f65b8465e Add logging to sync worker (#300) 2019-03-23 18:35:56 +01:00
6bb03b3be8 Fix day navigation unevenition (#301) 2019-03-23 17:29:34 +01:00
68b9847927 Fix reselecting root fragments (#299) 2019-03-23 16:35:33 +01:00
e1a83927c4 Fix reset button in timetable widget (#298) 2019-03-23 14:44:52 +01:00
fc9981aa5d Fix issues when loading lucky number (#297) 2019-03-23 13:01:01 +01:00
2d6610e05c Set max concurrency in sync worker (#296) 2019-03-23 01:12:17 +01:00
316cd2f7f9 Add checking current student in background services (#295) 2019-03-23 00:37:13 +01:00
36785f019a Fix restoring the grade fragment (#293) 2019-03-22 23:54:58 +01:00
4b78862486 Remove retry sync work when completed lessons is disabled (#294) 2019-03-22 23:41:41 +01:00
20d0abba29 Fix empty container id in grade fragemnt adapter (#289) 2019-03-21 22:34:41 +01:00
5add95ece1 Version 0.7.1 2019-03-20 21:02:09 +01:00
575e244b3a Add swipe refresh to grade fragment (#287) 2019-03-20 20:45:26 +01:00
8db73e9459 Fix the application finish after selecting an account (#286) 2019-03-19 18:14:55 +01:00
040857ba20 Change grade weightValue type to double (#285) 2019-03-19 13:23:52 +01:00
58 changed files with 430 additions and 266 deletions

View File

@ -14,6 +14,7 @@ cache:
#branches:
# only:
# - master
# - 0.7.x
android:
licenses:

View File

@ -16,8 +16,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 15
targetSdkVersion 28
versionCode 26
versionName "0.7.0"
versionCode 29
versionName "0.7.3"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@ -77,7 +77,7 @@ play {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation('io.github.wulkanowy:api:0.7.0') { exclude module: "threetenbp" }
implementation('io.github.wulkanowy:api:0.7.3') { exclude module: "threetenbp" }
implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.appcompat:appcompat:1.0.2"
@ -86,10 +86,14 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
implementation "androidx.work:work-runtime:2.0.0"
implementation "androidx.work:work-rxjava2:2.0.0"
implementation "android.arch.work:work-rxjava2:1.0.0"
implementation "android.arch.work:work-runtime:1.0.0"
implementation "androidx.room:room-runtime:2.1.0-alpha06"
implementation "androidx.room:room-rxjava2:2.1.0-alpha06"
kapt "androidx.room:room-compiler:2.1.0-alpha06"
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.3.3'
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.3.3'
@ -98,17 +102,13 @@ dependencies {
kapt "com.google.dagger:dagger-compiler:2.21"
kapt "com.google.dagger:dagger-android-processor:2.21"
implementation "androidx.room:room-runtime:2.1.0-alpha05"
implementation "androidx.room:room-rxjava2:2.1.0-alpha05"
kapt "androidx.room:room-compiler:2.1.0-alpha05"
implementation "eu.davidea:flexible-adapter:5.1.0"
implementation "eu.davidea:flexible-adapter-ui:1.0.0"
implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
implementation 'com.ncapdevi:frag-nav:3.1.0'
implementation 'com.ncapdevi:frag-nav:3.2.0'
implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f'
implementation 'com.github.PhilJay:MPAndroidChart:971640b29d'
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
@ -128,15 +128,15 @@ dependencies {
debugImplementation "com.amitshekhar.android:debug-db:1.0.6"
testImplementation "junit:junit:4.12"
testImplementation "io.mockk:mockk:1.9.1"
testImplementation "org.mockito:mockito-inline:2.25.0"
testImplementation "io.mockk:mockk:1.9.2"
testImplementation "org.mockito:mockito-inline:2.25.1"
testImplementation 'org.threeten:threetenbp:1.3.8'
androidTestImplementation "io.mockk:mockk-android:1.9.1"
androidTestImplementation 'androidx.test:core:1.1.0'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'org.mockito:mockito-android:2.25.0'
androidTestImplementation "io.mockk:mockk-android:1.9.2"
androidTestImplementation 'org.mockito:mockito-android:2.25.1'
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
}

View File

@ -34,9 +34,9 @@ class GradeLocalTest {
@Test
fun saveAndReadTest() {
gradeLocal.saveGrades(listOf(
createGradeLocal(5, 3, LocalDate.of(2018, 9, 10), "", 1),
createGradeLocal(4, 4, LocalDate.of(2019, 2, 27), "", 2),
createGradeLocal(3, 5, LocalDate.of(2019, 2, 28), "", 2)
createGradeLocal(5, 3.0, LocalDate.of(2018, 9, 10), "", 1),
createGradeLocal(4, 4.0, LocalDate.of(2019, 2, 27), "", 2),
createGradeLocal(3, 5.0, LocalDate.of(2019, 2, 28), "", 2)
))
val grades = gradeLocal

View File

@ -71,10 +71,10 @@ class GradeRepositoryTest {
@Test
fun markOlderThanRegisterDateAsRead() {
every { mockApi.getGrades(1) } returns Single.just(listOf(
createGradeApi(5, 4, of(2019, 2, 25), "Ocena pojawiła się"),
createGradeApi(5, 4, of(2019, 2, 26), "przed zalogowanie w aplikacji"),
createGradeApi(5, 4, of(2019, 2, 27), "Ocena z dnia logowania"),
createGradeApi(5, 4, of(2019, 2, 28), "Ocena jeszcze nowsza")
createGradeApi(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"),
createGradeApi(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"),
createGradeApi(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"),
createGradeApi(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza")
))
val grades = GradeRepository(settings, gradeLocal, gradeRemote)
@ -89,16 +89,16 @@ class GradeRepositoryTest {
@Test
fun mitigateOldGradesNotifications() {
gradeLocal.saveGrades(listOf(
createGradeLocal(5, 3, of(2019, 2, 25), "Jedna ocena"),
createGradeLocal(4, 4, of(2019, 2, 26), "Druga"),
createGradeLocal(3, 5, of(2019, 2, 27), "Trzecia")
createGradeLocal(5, 3.0, of(2019, 2, 25), "Jedna ocena"),
createGradeLocal(4, 4.0, of(2019, 2, 26), "Druga"),
createGradeLocal(3, 5.0, of(2019, 2, 27), "Trzecia")
))
every { mockApi.getGrades(1) } returns Single.just(listOf(
createGradeApi(5, 2, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"),
createGradeApi(4, 3, of(2019, 2, 26), "starszą niż ostatnia lokalnie"),
createGradeApi(3, 4, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"),
createGradeApi(2, 5, of(2019, 2, 28), "Ta jest już w ogóle nowa")
createGradeApi(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"),
createGradeApi(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"),
createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"),
createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa")
))
val grades = GradeRepository(settings, gradeLocal, gradeRemote)

View File

@ -5,7 +5,7 @@ import org.threeten.bp.LocalDate
import io.github.wulkanowy.api.grades.Grade as GradeRemote
import io.github.wulkanowy.data.db.entities.Grade as GradeLocal
fun createGradeLocal(value: Int, weight: Int, date: LocalDate, desc: String, semesterId: Int = 1): GradeLocal {
fun createGradeLocal(value: Int, weight: Double, date: LocalDate, desc: String, semesterId: Int = 1): GradeLocal {
return GradeLocal(
semesterId = semesterId,
studentId = 1,
@ -24,7 +24,7 @@ fun createGradeLocal(value: Int, weight: Int, date: LocalDate, desc: String, sem
)
}
fun createGradeApi(value: Int, weight: Int, date: LocalDate, desc: String): GradeRemote {
fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String): GradeRemote {
return GradeRemote().apply {
this.value = value
this.weightValue = weight

View File

@ -39,18 +39,12 @@
android:name=".ui.modules.main.MainActivity"
android:configChanges="orientation|screenSize"
android:label="@string/main_title"
android:launchMode="singleTop"
android:theme="@style/WulkanowyTheme.NoActionBar" />
<activity
android:name=".ui.modules.message.send.SendMessageActivity"
android:configChanges="orientation|screenSize"
android:label="@string/send_message_title"
android:parentActivityName=".ui.modules.main.MainActivity"
android:theme="@style/WulkanowyTheme.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.modules.main.MainActivity" />
</activity>
android:theme="@style/WulkanowyTheme.NoActionBar" />
<service
android:name=".services.widgets.TimetableWidgetService"
@ -58,6 +52,7 @@
<receiver
android:name=".ui.widgets.timetable.TimetableWidgetProvider"
android:exported="true"
android:label="@string/timetable_title">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

View File

@ -19,7 +19,11 @@ import io.github.wulkanowy.di.DaggerAppComponent
import io.github.wulkanowy.services.sync.SyncWorkerFactory
import io.github.wulkanowy.utils.CrashlyticsTree
import io.github.wulkanowy.utils.DebugLogTree
import io.reactivex.exceptions.UndeliverableException
import io.reactivex.plugins.RxJavaPlugins
import timber.log.Timber
import java.io.IOException
import java.lang.Exception
import javax.inject.Inject
class WulkanowyApp : DaggerApplication() {
@ -42,6 +46,7 @@ class WulkanowyApp : DaggerApplication() {
if (DEBUG) enableDebugLog()
AppCompatDelegate.setDefaultNightMode(prefRepository.currentTheme)
WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(workerFactory).build())
RxJavaPlugins.setErrorHandler(::onError)
}
private fun enableDebugLog() {
@ -56,6 +61,12 @@ class WulkanowyApp : DaggerApplication() {
Timber.plant(CrashlyticsTree())
}
private fun onError(t: Throwable) {
if (t is UndeliverableException && t.cause is IOException || t.cause is InterruptedException) {
Timber.e(t.cause, "An undeliverable error occurred")
} else throw t
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().create(this)
}

View File

@ -41,6 +41,7 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.migrations.Migration10
import io.github.wulkanowy.data.db.migrations.Migration11
import io.github.wulkanowy.data.db.migrations.Migration2
import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration4
@ -79,7 +80,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
const val VERSION_SCHEMA = 10
const val VERSION_SCHEMA = 11
fun newInstance(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
@ -95,7 +96,8 @@ abstract class AppDatabase : RoomDatabase() {
Migration7(),
Migration8(),
Migration9(),
Migration10()
Migration10(),
Migration11()
)
.build()
}

View File

@ -1,8 +1,8 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy.IGNORE
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
@ -12,15 +12,12 @@ import javax.inject.Singleton
@Dao
interface SemesterDao {
@Insert(onConflict = IGNORE)
@Insert
fun insertAll(semester: List<Semester>)
@Delete
fun deleteAll(semester: List<Semester>)
@Query("SELECT * FROM Semesters WHERE student_id = :studentId")
fun loadAll(studentId: Int): Maybe<List<Semester>>
@Query("UPDATE Semesters SET is_current = 1 WHERE semester_id = :semesterId AND diary_id = :diaryId")
fun updateCurrent(semesterId: Int, diaryId: Int)
@Query("UPDATE Semesters SET is_current = 0 WHERE student_id = :studentId")
fun resetCurrent(studentId: Int)
}

View File

@ -34,7 +34,7 @@ data class Grade(
val weight: String,
val weightValue: Int,
val weightValue: Double,
val date: LocalDate,

View File

@ -0,0 +1,34 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration11 : Migration(10, 11) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
CREATE TABLE IF NOT EXISTS Grades_temp (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
is_read INTEGER NOT NULL,
is_notified INTEGER NOT NULL,
semester_id INTEGER NOT NULL,
student_id INTEGER NOT NULL,
subject TEXT NOT NULL,
entry TEXT NOT NULL,
value INTEGER NOT NULL,
modifier REAL NOT NULL,
comment TEXT NOT NULL,
color TEXT NOT NULL,
grade_symbol TEXT NOT NULL,
description TEXT NOT NULL,
weight TEXT NOT NULL,
weightValue REAL NOT NULL,
date INTEGER NOT NULL,
teacher TEXT NOT NULL
)
""")
database.execSQL("INSERT INTO Grades_temp SELECT * FROM Grades")
database.execSQL("DROP TABLE Grades")
database.execSQL("ALTER TABLE Grades_temp RENAME TO Grades")
}
}

View File

@ -11,17 +11,14 @@ import javax.inject.Singleton
class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) {
fun saveSemesters(semesters: List<Semester>) {
return semesterDb.insertAll(semesters)
semesterDb.insertAll(semesters)
}
fun deleteSemesters(semesters: List<Semester>) {
semesterDb.deleteAll(semesters)
}
fun getSemesters(student: Student): Maybe<List<Semester>> {
return semesterDb.loadAll(student.studentId).filter { !it.isEmpty() }
}
fun setCurrentSemester(semester: Semester) {
semesterDb.run {
resetCurrent(semester.studentId)
updateCurrent(semester.semesterId, semester.diaryId)
}
}
}

View File

@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Maybe
import io.reactivex.Single
import timber.log.Timber
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@ -25,10 +26,17 @@ class SemesterRepository @Inject constructor(
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getSemesters(student) else Single.error(UnknownHostException())
}.map { newSemesters ->
local.apply {
saveSemesters(newSemesters)
setCurrentSemester(newSemesters.single { it.isCurrent })
}.flatMap { new ->
val currentSemesters = new.filter { it.isCurrent }
if (currentSemesters.size == 1) {
local.getSemesters(student).toSingle(emptyList())
.doOnSuccess { old ->
local.deleteSemesters(old - new)
local.saveSemesters(new - old)
}
} else {
Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}")
throw IllegalArgumentException("Current semester can be only one.")
}
}.flatMap { local.getSemesters(student).toSingle(emptyList()) })
}

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import io.github.wulkanowy.di.scopes.PerActivity
import io.github.wulkanowy.services.widgets.TimetableWidgetService
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.login.LoginModule
import io.github.wulkanowy.ui.modules.main.MainActivity
@ -30,9 +29,6 @@ internal abstract class BuilderModule {
@ContributesAndroidInjector
abstract fun bindMessageSendActivity(): SendMessageActivity
@ContributesAndroidInjector
abstract fun bindTimetableWidgetService(): TimetableWidgetService
@ContributesAndroidInjector
abstract fun bindTimetableWidgetProvider(): TimetableWidgetProvider
}

View File

@ -9,6 +9,7 @@ import com.squareup.inject.assisted.dagger2.AssistedModule
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.android.ContributesAndroidInjector
import dagger.multibindings.IntoSet
import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork
import io.github.wulkanowy.services.sync.works.AttendanceWork
@ -24,6 +25,7 @@ import io.github.wulkanowy.services.sync.works.NoteWork
import io.github.wulkanowy.services.sync.works.RecipientWork
import io.github.wulkanowy.services.sync.works.TimetableWork
import io.github.wulkanowy.services.sync.works.Work
import io.github.wulkanowy.services.widgets.TimetableWidgetService
import javax.inject.Singleton
@AssistedModule
@ -48,6 +50,9 @@ abstract class ServicesModule {
fun provideNotificationManager(context: Context) = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
}
@ContributesAndroidInjector
abstract fun bindTimetableWidgetService(): TimetableWidgetService
@Binds
@IntoSet
abstract fun provideGradeWork(work: GradeWork): Work

View File

@ -11,6 +11,7 @@ import androidx.work.WorkerParameters
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import io.github.wulkanowy.R
import io.github.wulkanowy.api.interceptor.FeatureDisabledException
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
@ -33,19 +34,31 @@ class SyncWorker @AssistedInject constructor(
) : RxWorker(appContext, workerParameters) {
override fun createWork(): Single<Result> {
return studentRepository.getCurrentStudent()
Timber.i("SyncWorker is starting")
return studentRepository.isStudentSaved()
.filter { true }
.flatMap { studentRepository.getCurrentStudent().toMaybe() }
.flatMapCompletable { student ->
semesterRepository.getCurrentSemester(student, true)
.flatMapCompletable { semester ->
Completable.mergeDelayError(works.map { it.create(student, semester) })
Completable.mergeDelayError(works.map { work ->
work.create(student, semester)
.doOnSubscribe { Timber.i("${work::class.java.simpleName} is starting") }
.doOnError { Timber.i("${work::class.java.simpleName} result: An exception occurred") }
.doOnComplete { Timber.i("${work::class.java.simpleName} result: Success") }
})
}
}
.toSingleDefault(Result.success())
.onErrorReturn {
Timber.e(it, "There was an error during synchronization")
Result.retry()
if (it is FeatureDisabledException) Result.success()
else Result.retry()
}
.doOnSuccess {
if (preferencesRepository.isDebugNotificationEnable) notify(it)
Timber.i("SyncWorker result: $it")
}
.doOnSuccess { if (preferencesRepository.isDebugNotificationEnable) notify(it) }
}
private fun notify(result: Result) {

View File

@ -13,10 +13,10 @@ class RecipientWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return reportingUnitRepository.getReportingUnits(student)
return reportingUnitRepository.getReportingUnits(student, true)
.flatMapCompletable { units ->
Completable.mergeDelayError(units.map {
recipientRepository.getRecipients(student, 2, it).ignoreElement()
recipientRepository.getRecipients(student, 2, it, true).ignoreElement()
})
}
}

View File

@ -43,7 +43,8 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
.withAboutSpecial3(getString(R.string.about_feedback))
.withFields(R.string::class.java.fields)
.withCheckCachedDetection(false)
.withExcludedLibraries("fastadapter", "AndroidIconics", "gson", "Jsoup", "Retrofit", "okio", "OkHttp")
.withExcludedLibraries("fastadapter", "AndroidIconics", "Jsoup", "Retrofit", "okio",
"OkHttp", "Butterknife", "CircleImageView")
.withOnExtraListener { presenter.onExtraSelect(it) })
}.let {
fragmentCompat.onCreateView(inflater.context, inflater, container, savedInstanceState, it)
@ -68,7 +69,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
data = Uri.parse("mailto:")
putExtra(Intent.EXTRA_EMAIL, Array(1) { "wulkanowyinc@gmail.com" })
putExtra(Intent.EXTRA_SUBJECT, "Zgłoszenie błędu")
putExtra(Intent.EXTRA_TEXT,"Tu umieść treść zgłoszenia\n\n" + "-".repeat(40) + "\n" + """
putExtra(Intent.EXTRA_TEXT, "Tu umieść treść zgłoszenia\n\n" + "-".repeat(40) + "\n" + """
Build: ${BuildConfig.VERSION_CODE}
SDK: ${android.os.Build.VERSION.SDK_INT}
Device: ${android.os.Build.MANUFACTURER} ${android.os.Build.MODEL}

View File

@ -14,12 +14,12 @@ class AccountItem(val student: Student) : AbstractFlexibleItem<AccountItem.ViewH
override fun getLayoutRes() = R.layout.item_account
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
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 {
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)
@ -38,14 +38,13 @@ class AccountItem(val student: Student) : AbstractFlexibleItem<AccountItem.ViewH
}
override fun hashCode(): Int {
return student.hashCode()
var result = student.hashCode()
result = 31 * result + student.id.toInt()
return result
}
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View?
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}
}

View File

@ -14,14 +14,13 @@ import kotlinx.android.synthetic.main.item_attendance.*
class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_attendance
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
return ViewHolder(view, adapter)
}
override fun getLayoutRes(): Int = R.layout.item_attendance
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
attendanceItemNumber.text = attendance.number.toString()
attendanceItemSubject.text = attendance.subject
@ -37,16 +36,17 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<Attendan
other as AttendanceItem
if (attendance != other.attendance) return false
return true
}
override fun hashCode(): Int {
return attendance.hashCode()
var result = attendance.hashCode()
result = 31 * result + attendance.id.toInt()
return result
}
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -135,7 +135,7 @@ class AttendancePresenter @Inject constructor(
clearData()
showNextButton(!currentDate.plusDays(1).isHolidays)
showPreButton(!currentDate.minusDays(1).isHolidays)
updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize())
updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize())
}
}
}

View File

@ -23,12 +23,12 @@ class AttendanceSummaryItem(
override fun getLayoutRes() = R.layout.item_attendance_summary
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
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 {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
attendanceSummaryMonth.text = month
attendanceSummaryPercentage.text = percentage
attendanceSummaryPresent.text = present
@ -73,10 +73,8 @@ class AttendanceSummaryItem(
return result
}
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View?
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}
}

View File

@ -18,8 +18,7 @@ class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem<Exa
return ViewHolder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.run {
examItemSubject.text = exam.subject
examItemTeacher.text = exam.teacher
@ -39,12 +38,12 @@ class ExamItem(header: ExamHeader, val exam: Exam) : AbstractSectionableItem<Exa
}
override fun hashCode(): Int {
return exam.hashCode()
var result = exam.hashCode()
result = 31 * result + exam.id.toInt()
return result
}
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -29,6 +29,8 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView,
@Inject
lateinit var pagerAdapter: BaseFragmentPagerAdapter
private var semesterSwitchMenu: MenuItem? = null
companion object {
private const val SAVED_SEMESTER_KEY = "CURRENT_SEMESTER"
@ -57,6 +59,8 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView,
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
inflater?.inflate(R.menu.action_menu_grade, menu)
semesterSwitchMenu = menu?.findItem(R.id.gradeMenuSemester)
presenter.onCreateMenu()
}
override fun initView() {
@ -75,6 +79,7 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView,
setOnSelectPageListener { presenter.onPageSelected(it) }
}
gradeTabLayout.setupWithViewPager(gradeViewPager)
gradeSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
@ -95,8 +100,20 @@ class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView,
gradeProgress.visibility = if (show) VISIBLE else INVISIBLE
}
override fun showEmpty() {
gradeEmpty.visibility = VISIBLE
override fun showEmpty(show: Boolean) {
gradeEmpty.visibility = if (show) VISIBLE else INVISIBLE
}
override fun showRefresh(show: Boolean) {
gradeSwipe.isRefreshing = show
}
override fun showSemesterSwitch(show: Boolean) {
semesterSwitchMenu?.isVisible = show
}
override fun enableSwipe(enable: Boolean) {
gradeSwipe.isEnabled = enable
}
override fun showSemesterDialog(selectedIndex: Int) {

View File

@ -7,9 +7,7 @@ import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Completable
import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
class GradePresenter @Inject constructor(
@ -30,12 +28,16 @@ class GradePresenter @Inject constructor(
fun onAttachView(view: GradeView, savedIndex: Int?) {
super.onAttachView(view)
Timber.i("Grade view is attached")
disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread)
.subscribe {
selectedIndex = savedIndex ?: 0
view.initView()
loadData()
})
selectedIndex = savedIndex ?: 0
view.run {
initView()
enableSwipe(false)
}
loadData()
}
fun onCreateMenu() {
if (semesters.isEmpty()) view?.showSemesterSwitch(false)
}
fun onViewReselected() {
@ -69,12 +71,17 @@ class GradePresenter @Inject constructor(
view?.apply {
showContent(true)
showProgress(false)
showEmpty(false)
loadedSemesterId[currentPageIndex] = semesterId
}
}
fun onPageSelected(index: Int) {
loadChild(index)
if (semesters.isNotEmpty()) loadChild(index)
}
fun onSwipeRefresh() {
loadData()
}
private fun loadData() {
@ -89,17 +96,21 @@ class GradePresenter @Inject constructor(
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally { view?.showRefresh(false) }
.subscribe({
view?.run {
Timber.i("Loading grade result: Attempt load index $currentPageIndex")
loadChild(currentPageIndex)
enableSwipe(false)
showSemesterSwitch(true)
}
}) {
Timber.i("Loading grade result: An exception occurred")
errorHandler.dispatch(it)
view?.run {
showProgress(false)
showEmpty()
showEmpty(true)
enableSwipe(true)
}
})
}

View File

@ -12,10 +12,16 @@ interface GradeView : BaseSessionView {
fun showProgress(show: Boolean)
fun showEmpty()
fun showEmpty(show: Boolean)
fun showRefresh(show: Boolean)
fun showSemesterSwitch(show: Boolean)
fun showSemesterDialog(selectedIndex: Int)
fun enableSwipe(enable: Boolean)
fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean)
fun notifyChildParentReselected(index: Int)

View File

@ -28,10 +28,7 @@ class GradeDetailsItem(
}
@SuppressLint("SetTextI18n")
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?
) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.run {
gradeItemValue.run {
text = grade.entry
@ -70,9 +67,7 @@ class GradeDetailsItem(
return result
}
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -63,7 +63,7 @@ class GradeStatisticsPresenter @Inject constructor(
}
fun onSubjectSelected(name: String) {
Timber.i("Select attendance stats subject $name")
Timber.i("Select grade stats subject $name")
view?.run {
showContent(false)
showProgress(true)
@ -77,7 +77,7 @@ class GradeStatisticsPresenter @Inject constructor(
}
fun onTypeChange(isSemester: Boolean) {
Timber.i("Select attendance stats semester: $isSemester")
Timber.i("Select grade stats semester: $isSemester")
disposable.clear()
view?.run {
showContent(false)

View File

@ -18,15 +18,12 @@ class GradeSummaryItem(
override fun getLayoutRes() = R.layout.item_grade_summary
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
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?.run {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.run {
gradeSummaryItemTitle.text = title
gradeSummaryItemAverage.text = average
gradeSummaryItemPredicted.text = predictedGrade
@ -56,10 +53,8 @@ class GradeSummaryItem(
return result
}
class ViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View?
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}
}

View File

@ -10,9 +10,8 @@ import io.github.wulkanowy.data.db.entities.Homework
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_homework.*
class HomeworkItem(
header: HomeworkHeader, val homework: Homework
) : AbstractSectionableItem<HomeworkItem.ViewHolder, HomeworkHeader>(header) {
class HomeworkItem(header: HomeworkHeader, val homework: Homework) :
AbstractSectionableItem<HomeworkItem.ViewHolder, HomeworkHeader>(header) {
override fun getLayoutRes() = R.layout.item_homework
@ -20,10 +19,7 @@ class HomeworkItem(
return ViewHolder(view, adapter)
}
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?
) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
homeworkItemSubject.text = homework.subject
homeworkItemTeacher.text = homework.teacher
@ -42,12 +38,12 @@ class HomeworkItem(
}
override fun hashCode(): Int {
return homework.hashCode()
var result = homework.hashCode()
result = 31 * result + homework.id.toInt()
return result
}
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -8,19 +8,21 @@ import eu.davidea.viewholders.FlexibleViewHolder
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_login_student_select.view.*
import kotlinx.android.synthetic.main.item_login_student_select.*
class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginStudentSelectItem.ItemViewHolder>() {
override fun getLayoutRes(): Int = R.layout.item_login_student_select
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ItemViewHolder {
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ItemViewHolder {
return ItemViewHolder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>?, holder: ItemViewHolder?,
position: Int, payloads: MutableList<Any>?) {
holder?.bind(student)
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ItemViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.run {
loginItemName.text = student.studentName
loginItemSchool.text = student.schoolName
}
}
override fun equals(other: Any?): Boolean {
@ -38,17 +40,8 @@ class LoginStudentSelectItem(val student: Student) : AbstractFlexibleItem<LoginS
return student.hashCode()
}
class ItemViewHolder(view: View?, adapter: FlexibleAdapter<*>?)
: FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View?
class ItemViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = itemView
fun bind(item: Student) {
itemView.run {
loginItemName.text = item.studentName
loginItemSchool.text = item.schoolName
}
}
}
}

View File

@ -22,7 +22,11 @@ class LuckyNumberPresenter @Inject constructor(
override fun onAttachView(view: LuckyNumberView) {
super.onAttachView(view)
Timber.i("Lucky number view is attached")
view.initView()
view.run {
initView()
showContent(false)
enableSwipe(false)
}
loadData()
}
@ -35,12 +39,6 @@ class LuckyNumberPresenter @Inject constructor(
.flatMapMaybe { luckyNumberRepository.getLuckyNumber(it, forceRefresh) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.run {
showContent(false)
enableSwipe(false)
}
}
.doFinally {
view?.run {
hideRefresh()

View File

@ -2,7 +2,10 @@ package io.github.wulkanowy.ui.modules.main
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
import android.os.Handler
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AlertDialog
@ -68,11 +71,6 @@ class MainActivity : BaseActivity(), MainView {
return true
}
override fun onStart() {
super.onStart()
presenter.onViewChange()
}
override fun initView() {
mainBottomNav.run {
addItems(
@ -144,7 +142,9 @@ class MainActivity : BaseActivity(), MainView {
}
override fun notifyMenuViewReselected() {
(navController.currentStack?.get(0) as? MainView.MainChildView)?.onFragmentReselected()
Handler().postDelayed({
(navController.currentStack?.get(0) as? MainView.MainChildView)?.onFragmentReselected()
}, 250)
}
fun showDialogFragment(dialog: DialogFragment) {
@ -165,7 +165,7 @@ class MainActivity : BaseActivity(), MainView {
override fun openLoginView() {
startActivity(LoginActivity.getStartIntent(this)
.apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) })
.apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
}
override fun onSaveInstanceState(outState: Bundle?) {

View File

@ -5,7 +5,6 @@ import dagger.Module
import dagger.Provides
import dagger.android.ContributesAndroidInjector
import io.github.wulkanowy.R
import io.github.wulkanowy.di.scopes.PerActivity
import io.github.wulkanowy.di.scopes.PerFragment
import io.github.wulkanowy.ui.modules.about.AboutFragment
import io.github.wulkanowy.ui.modules.about.AboutModule
@ -33,7 +32,6 @@ abstract class MainModule {
companion object {
@JvmStatic
@PerActivity
@Provides
fun provideFragNavController(activity: MainActivity): FragNavController {
return FragNavController(activity.supportFragmentManager, R.id.mainFragmentContainer)

View File

@ -24,9 +24,9 @@ class MainPresenter @Inject constructor(
fun onAttachView(view: MainView, initMenuIndex: Int) {
super.onAttachView(view)
Timber.i("Main view is attached with $initMenuIndex menu index")
view.run {
startMenuIndex = if (initMenuIndex != -1) initMenuIndex else prefRepository.startMenuIndex
Timber.i("Main view is attached with $startMenuIndex menu index")
initView()
}

View File

@ -20,12 +20,9 @@ class MessageItem(val message: Message, private val noSubjectString: String) :
return ViewHolder(view, adapter)
}
override fun getLayoutRes(): Int = R.layout.item_message
override fun getLayoutRes() = R.layout.item_message
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?
) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
val style = if (message.unread) BOLD else NORMAL
@ -58,9 +55,7 @@ class MessageItem(val message: Message, private val noSubjectString: String) :
return message.hashCode()
}
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -72,6 +72,10 @@ class SendMessageActivity : BaseActivity(), SendMessageView {
else false
}
override fun onSupportNavigateUp(): Boolean {
return presenter.onUpNavigate()
}
override fun setReportingUnit(unit: ReportingUnit) {
sendMessageFromTextView.setText(unit.senderName)
}

View File

@ -47,6 +47,11 @@ class SendMessagePresenter @Inject constructor(
}
}
fun onUpNavigate(): Boolean {
view?.popView()
return true
}
private fun loadData(message: Message?) {
var reportingUnit: ReportingUnit? = null
var recipients: List<Recipient> = emptyList()

View File

@ -14,12 +14,12 @@ class MoreItem(val title: String, private val drawable: Drawable?) : AbstractFle
override fun getLayoutRes() = R.layout.item_more
override fun createViewHolder(view: View?, adapter: FlexibleAdapter<IFlexible<*>>?): ViewHolder {
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 {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
moreItemTitle.text = title
moreItemImage.setImageDrawable(drawable)
}
@ -40,9 +40,8 @@ class MoreItem(val title: String, private val drawable: Drawable?) : AbstractFle
return title.hashCode()
}
class ViewHolder(view: View?, adapter: FlexibleAdapter<*>?) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View?
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}
}

View File

@ -15,16 +15,13 @@ import kotlinx.android.synthetic.main.item_note.*
class NoteItem(val note: Note) : AbstractFlexibleItem<NoteItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_note
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): NoteItem.ViewHolder {
return NoteItem.ViewHolder(view, adapter)
}
override fun getLayoutRes(): Int = R.layout.item_note
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<*>>,
holder: NoteItem.ViewHolder, position: Int, payloads: MutableList<Any>?
) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: NoteItem.ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
noteItemDate.apply {
text = note.date.toFormattedString()
@ -57,7 +54,6 @@ class NoteItem(val note: Note) : AbstractFlexibleItem<NoteItem.ViewHolder>() {
}
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -15,18 +15,17 @@ import io.github.wulkanowy.utils.toFormattedString
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_timetable.*
class TimetableItem(val lesson: Timetable, private val roomText: String)
: AbstractFlexibleItem<TimetableItem.ViewHolder>() {
class TimetableItem(val lesson: Timetable, private val roomText: String) :
AbstractFlexibleItem<TimetableItem.ViewHolder>() {
override fun getLayoutRes(): Int = R.layout.item_timetable
override fun getLayoutRes() = R.layout.item_timetable
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
return ViewHolder(view, adapter)
}
@SuppressLint("SetTextI18n")
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder,
position: Int, payloads: MutableList<Any>?) {
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
timetableItemNumber.text = lesson.number.toString()
timetableItemSubject.text = lesson.subject
@ -34,8 +33,8 @@ class TimetableItem(val lesson: Timetable, private val roomText: String)
timetableItemTime.text = "${lesson.start.toFormattedString("HH:mm")} - ${lesson.end.toFormattedString("HH:mm")}"
timetableItemAlert.visibility = if (lesson.changes || lesson.canceled) VISIBLE else GONE
timetableItemSubject.paintFlags =
if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
}
}
@ -50,12 +49,12 @@ class TimetableItem(val lesson: Timetable, private val roomText: String)
}
override fun hashCode(): Int {
return lesson.hashCode()
var result = lesson.hashCode()
result = 31 * result + lesson.id.toInt()
return result
}
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}

View File

@ -128,7 +128,7 @@ class TimetablePresenter @Inject constructor(
clearData()
showNextButton(!currentDate.plusDays(1).isHolidays)
showPreButton(!currentDate.minusDays(1).isHolidays)
updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize())
updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize())
}
}
}

View File

@ -112,7 +112,7 @@ class CompletedLessonsPresenter @Inject constructor(
clearData()
showNextButton(!currentDate.plusDays(1).isHolidays)
showPreButton(!currentDate.minusDays(1).isHolidays)
updateNavigationDay(currentDate.toFormattedString("EEEE \n dd.MM.YYYY").capitalize())
updateNavigationDay(currentDate.toFormattedString("EEEE\ndd.MM.YYYY").capitalize())
}
}
}

View File

@ -17,6 +17,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.toFormattedString
import io.reactivex.Single
import org.threeten.bp.LocalDate
import timber.log.Timber
@ -51,9 +52,13 @@ class TimetableWidgetFactory(
?.let { date ->
try {
lessons = studentRepository.isStudentSaved()
.flatMap { studentRepository.getCurrentStudent() }
.flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { timetableRepository.getTimetable(it, date, date) }
.flatMap { isSaved ->
if (isSaved) {
studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { timetableRepository.getTimetable(it, date, date) }
} else Single.just(emptyList())
}
.map { item -> item.sortedBy { it.number } }
.subscribeOn(schedulers.backgroundThread)
.blockingGet()

View File

@ -62,7 +62,7 @@ class TimetableWidgetProvider : BroadcastReceiver() {
private fun onUpdate(context: Context, intent: Intent) {
if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) {
intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS).forEach { appWidgetId ->
intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId ->
updateWidget(context, appWidgetId, now().nextOrSameSchoolDay)
}
} else {
@ -95,7 +95,7 @@ class TimetableWidgetProvider : BroadcastReceiver() {
.apply { action = createWidgetKey(appWidgetId) })
setOnClickPendingIntent(R.id.timetableWidgetNext, createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT))
setOnClickPendingIntent(R.id.timetableWidgetPrev, createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV))
createNavIntent(context, Int.MAX_VALUE, appWidgetId, BUTTON_RESET).also {
createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET).also {
setOnClickPendingIntent(R.id.timetableWidgetDate, it)
setOnClickPendingIntent(R.id.timetableWidgetDay, it)
}

View File

@ -1,20 +1,6 @@
Wersja 0.7.0
Dodaliśmy:
- szczęśliwy numerek
- lekcje zrealizowane
- wysyłanie wiadomości
- ucznia na tle klasy
- opcję zmiany kolorów ocen
Zmieniliśmy:
- wygląd ekranu logowania
- sposób wyświetlania zastępstw
- widok zadań domowych na tygodniowy
Wersja 0.7.3
Naprawiliśmy:
- automatyczne przełączanie widżetu na nowy dzień
- wyświetlanie powiadomień o starych ocenach
- znikające numery sal w planie lekcji
- naprawiono problemy ze stabilnością podczas odświeżania danych
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.0
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases/tag/0.7.3

View File

@ -17,13 +17,19 @@
app:tabTextColor="@android:color/white"
tools:visibility="visible" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/gradeViewPager"
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/gradeSwipe"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="48dp"
android:visibility="invisible"
tools:visibility="visible" />
android:layout_marginTop="48dp">
<androidx.viewpager.widget.ViewPager
android:id="@+id/gradeViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"
tools:visibility="visible" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<ProgressBar
android:id="@+id/gradeProgress"

View File

@ -0,0 +1,19 @@
package io.github.wulkanowy.data.repositories
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingStrategy
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler
import io.reactivex.Observable
import io.reactivex.Single
class UnitTestInternetObservingStrategy : InternetObservingStrategy {
override fun checkInternetConnectivity(host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Single<Boolean> {
return Single.just(true)
}
override fun observeInternetConnectivity(initialIntervalInMs: Int, intervalInMs: Int, host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Observable<Boolean> {
return Observable.just(true)
}
override fun getDefaultPingHost() = "localhost"
}

View File

@ -1,9 +1,8 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.attendance
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.attendance.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.attendance.AttendanceRemote
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK

View File

@ -1,9 +1,8 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.completedlessons
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.timetable.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRemote
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK

View File

@ -1,9 +1,8 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.exam
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.exams.Exam
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.exam.ExamRemote
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK

View File

@ -1,9 +1,8 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.gradestatistics
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.grades.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.gradestatistics.GradeStatisticsRemote
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.luckynumber
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester

View File

@ -0,0 +1,74 @@
package io.github.wulkanowy.data.repositories.semester
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.ApiHelper
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.UnitTestInternetObservingStrategy
import io.reactivex.Maybe
import io.reactivex.Single
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
class SemesterRepositoryTest {
@Mock
private lateinit var semesterRemote: SemesterRemote
@Mock
private lateinit var semesterLocal: SemesterLocal
@Mock
private lateinit var apiHelper: ApiHelper
@Mock
private lateinit var student: Student
private lateinit var semesterRepository: SemesterRepository
private val settings = InternetObservingSettings.builder()
.strategy(UnitTestInternetObservingStrategy())
.build()
@Before
fun initTest() {
MockitoAnnotations.initMocks(this)
semesterRepository = SemesterRepository(semesterRemote, semesterLocal, settings, apiHelper)
}
@Test
fun singleCurrentSemesterTest() {
val semesters = listOf(
createSemesterEntity(false),
createSemesterEntity(true)
)
doNothing().`when`(apiHelper).initApi(student)
doReturn(Maybe.empty<Semester>()).`when`(semesterLocal).getSemesters(student)
doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student)
semesterRepository.getSemesters(student).blockingGet()
verify(semesterLocal).deleteSemesters(emptyList())
verify(semesterLocal).saveSemesters(semesters)
}
@Test(expected = IllegalArgumentException::class)
fun twoCurrentSemesterTest() {
val semesters = listOf(
createSemesterEntity(true),
createSemesterEntity(true)
)
doNothing().`when`(apiHelper).initApi(student)
doReturn(Maybe.empty<Semester>()).`when`(semesterLocal).getSemesters(student)
doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student)
semesterRepository.getSemesters(student).blockingGet()
}
}

View File

@ -0,0 +1,16 @@
package io.github.wulkanowy.data.repositories.semester
import io.github.wulkanowy.data.db.entities.Semester
fun createSemesterEntity(current: Boolean): Semester {
return Semester(
studentId = 0,
diaryId = 0,
semesterId = 0,
diaryName = "",
classId = 0,
isCurrent = current,
semesterName = 0,
unitId = 0
)
}

View File

@ -1,8 +1,7 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.student
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.register.Student
import io.github.wulkanowy.data.repositories.student.StudentRemote
import io.reactivex.Single
import org.junit.Assert.assertEquals
import org.junit.Before

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.data.repositories.remote
package io.github.wulkanowy.data.repositories.timetable
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.api.timetable.Timetable

View File

@ -22,11 +22,11 @@ class GradeExtensionTest {
@Test
fun calcWeightedAverage() {
assertEquals(3.47, listOf(
createGrade(5, 6, 0.33),
createGrade(5, 5, -0.33),
createGrade(4, 1, 0.0),
createGrade(1, 9, 0.5),
createGrade(0, 0, 0.0)
createGrade(5, 6.0, 0.33),
createGrade(5, 5.0, -0.33),
createGrade(4, 1.0, 0.0),
createGrade(1, 9.0, 0.5),
createGrade(0, .0, 0.0)
).calcAverage(), 0.005)
}
@ -42,23 +42,23 @@ class GradeExtensionTest {
@Test
fun changeModifier_default() {
assertEquals(.33, createGrade(5, 0, .33).changeModifier(.0, .0).modifier, .0)
assertEquals(-.33, createGrade(5, 0, -.33).changeModifier(.0, .0).modifier, .0)
assertEquals(.33, createGrade(5, .0, .33).changeModifier(.0, .0).modifier, .0)
assertEquals(-.33, createGrade(5, .0, -.33).changeModifier(.0, .0).modifier, .0)
}
@Test
fun changeModifier_plus() {
assertEquals(.33, createGrade(5, 0, .25).changeModifier(.33, .50).modifier, .0)
assertEquals(.25, createGrade(5, 0, .33).changeModifier(.25, .0).modifier, .0)
assertEquals(.33, createGrade(5, .0, .25).changeModifier(.33, .50).modifier, .0)
assertEquals(.25, createGrade(5, .0, .33).changeModifier(.25, .0).modifier, .0)
}
@Test
fun changeModifier_minus() {
assertEquals(-.33, createGrade(5, 0, -.25).changeModifier(.25, .33).modifier, .0)
assertEquals(-.25, createGrade(5, 0, -.33).changeModifier(.0, .25).modifier, .0)
assertEquals(-.33, createGrade(5, .0, -.25).changeModifier(.25, .33).modifier, .0)
assertEquals(-.25, createGrade(5, .0, -.33).changeModifier(.0, .25).modifier, .0)
}
private fun createGrade(value: Int, weightValue: Int = 0, modifier: Double = 0.25): Grade {
private fun createGrade(value: Int, weightValue: Double = .0, modifier: Double = 0.25): Grade {
return Grade(
semesterId = 1,
studentId = 1,