Merge branch 'release/0.15.0'
@ -14,7 +14,7 @@ cache:
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- 0.14.2
|
||||
- 0.15.0
|
||||
|
||||
android:
|
||||
licenses:
|
||||
|
@ -17,8 +17,8 @@ android {
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
versionCode 51
|
||||
versionName "0.14.2"
|
||||
versionCode 52
|
||||
versionName "0.15.0"
|
||||
multiDexEnabled true
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
@ -110,7 +110,7 @@ play {
|
||||
}
|
||||
|
||||
ext {
|
||||
work_manager = "2.3.0-rc01"
|
||||
work_manager = "2.3.0"
|
||||
room = "2.2.3"
|
||||
dagger = "2.25.4"
|
||||
chucker = "2.0.4"
|
||||
@ -122,14 +122,14 @@ configurations.all {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "io.github.wulkanowy:sdk:0.14.2"
|
||||
implementation "io.github.wulkanowy:sdk:0.15.0"
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation "androidx.core:core-ktx:1.2.0-rc01"
|
||||
implementation "androidx.activity:activity-ktx:1.1.0-rc03"
|
||||
implementation "androidx.activity:activity-ktx:1.1.0"
|
||||
implementation "androidx.appcompat:appcompat:1.1.0"
|
||||
implementation "androidx.appcompat:appcompat-resources:1.1.0"
|
||||
implementation "androidx.fragment:fragment-ktx:1.2.0-rc05"
|
||||
implementation "androidx.fragment:fragment-ktx:1.2.0"
|
||||
implementation "androidx.annotation:annotation:1.1.0"
|
||||
implementation "androidx.multidex:multidex:2.0.1"
|
||||
|
||||
@ -139,7 +139,7 @@ dependencies {
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03"
|
||||
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
|
||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
|
||||
implementation "com.google.android.material:material:1.1.0-rc01"
|
||||
implementation "com.google.android.material:material:1.1.0-rc02"
|
||||
implementation "com.github.wulkanowy:material-chips-input:2.0.1"
|
||||
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
||||
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
|
||||
@ -167,16 +167,18 @@ dependencies {
|
||||
|
||||
implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6"
|
||||
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
|
||||
implementation "io.reactivex.rxjava2:rxjava:2.2.16"
|
||||
implementation "io.reactivex.rxjava2:rxjava:2.2.17"
|
||||
|
||||
implementation "com.google.code.gson:gson:2.8.6"
|
||||
implementation "com.jakewharton.threetenabp:threetenabp:1.2.1"
|
||||
implementation "com.jakewharton.threetenabp:threetenabp:1.2.2"
|
||||
implementation "com.jakewharton.timber:timber:4.7.1"
|
||||
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
||||
implementation "com.mikepenz:aboutlibraries-core:7.1.0"
|
||||
implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
|
||||
|
||||
playImplementation "com.google.firebase:firebase-core:17.2.1"
|
||||
implementation("io.coil-kt:coil:0.9.2")
|
||||
|
||||
playImplementation "com.google.firebase:firebase-core:17.2.2"
|
||||
playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1"
|
||||
|
||||
releaseImplementation "fr.o80.chucker:library-no-op:$chucker"
|
||||
@ -186,7 +188,7 @@ dependencies {
|
||||
|
||||
testImplementation "junit:junit:4.13"
|
||||
testImplementation "io.mockk:mockk:$mockk"
|
||||
testImplementation "org.threeten:threetenbp:1.4.0"
|
||||
testImplementation "org.threeten:threetenbp:1.4.1"
|
||||
testImplementation "org.mockito:mockito-inline:3.2.4"
|
||||
|
||||
androidTestImplementation "androidx.test:core:1.2.0"
|
||||
|
@ -1,7 +1,7 @@
|
||||
apply plugin: "jacoco"
|
||||
|
||||
jacoco {
|
||||
toolVersion "0.8.4"
|
||||
toolVersion "0.8.5"
|
||||
reportsDir = file("$buildDir/reports")
|
||||
}
|
||||
|
||||
|
1634
app/schemas/io.github.wulkanowy.data.db.AppDatabase/20.json
Normal file
1652
app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json
Normal file
@ -35,9 +35,9 @@ class AttendanceLocalTest {
|
||||
@Test
|
||||
fun saveAndReadTest() {
|
||||
attendanceLocal.saveAttendance(listOf(
|
||||
Attendance(1, 2, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false),
|
||||
Attendance(1, 2, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false),
|
||||
Attendance(1, 2, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false)
|
||||
Attendance(1, 2, 3, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name),
|
||||
Attendance(1, 2, 3, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.WAITING.name),
|
||||
Attendance(1, 2, 3, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name)
|
||||
))
|
||||
|
||||
val attendance = attendanceLocal
|
||||
|
@ -2,8 +2,8 @@ package io.github.wulkanowy.data.repositories.timetable
|
||||
|
||||
import org.threeten.bp.LocalDateTime
|
||||
import org.threeten.bp.LocalDateTime.now
|
||||
import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote
|
||||
import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal
|
||||
import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote
|
||||
|
||||
fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableLocal {
|
||||
return TimetableLocal(
|
||||
@ -21,6 +21,7 @@ fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", s
|
||||
teacher = teacher,
|
||||
teacherOld = "",
|
||||
info = "",
|
||||
studentPlan = true,
|
||||
changes = changes,
|
||||
canceled = false
|
||||
)
|
||||
|
12
app/src/debug/res/drawable-anydpi-v24/ic_stat_grade.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="30.434782"
|
||||
android:viewportHeight="30.434782">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 8.131 2.131 C 7.601 2.131 7.092 2.342 6.717 2.717 C 6.342 3.092 6.131 3.601 6.131 4.131 L 6.131 18.131 C 6.131 18.661 6.342 19.17 6.717 19.545 C 7.092 19.92 7.601 20.131 8.131 20.131 L 16.918 20.131 C 17.252 19.39 17.712 18.714 18.277 18.131 L 8.131 18.131 L 8.131 4.131 L 22.131 4.131 L 22.131 16.1 C 22.516 16.034 22.906 16.001 23.297 16 C 23.576 16.002 23.854 16.02 24.131 16.055 L 24.131 4.131 C 24.131 3.601 23.92 3.092 23.545 2.717 C 23.17 2.342 22.661 2.131 22.131 2.131 L 8.131 2.131 Z M 2.131 6.131 L 2.131 22.131 C 2.131 22.661 2.342 23.17 2.717 23.545 C 3.092 23.92 3.601 24.131 4.131 24.131 L 16.391 24.131 C 16.329 23.757 16.297 23.379 16.297 23 C 16.299 22.709 16.319 22.419 16.357 22.131 L 4.131 22.131 L 4.131 6.131 L 2.131 6.131 Z M 14.131 6.131 C 13.601 6.131 13.092 6.342 12.717 6.717 C 12.342 7.092 12.131 7.601 12.131 8.131 L 12.131 14.131 C 12.131 15.231 13.031 16.131 14.131 16.131 L 16.131 16.131 C 16.661 16.131 17.17 15.92 17.545 15.545 C 17.92 15.17 18.131 14.661 18.131 14.131 L 18.131 12.131 C 18.131 11.601 17.92 11.092 17.545 10.717 C 17.17 10.342 16.661 10.131 16.131 10.131 L 14.131 10.131 L 14.131 8.131 L 18.131 8.131 L 18.131 6.131 L 14.131 6.131 Z M 14.131 12.131 L 16.131 12.131 L 16.131 14.131 L 14.131 14.131 L 14.131 12.131 Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 20.174 28 L 20.174 18.521 L 23.091 18.521 Q 24.341 18.521 25.324 19.087 Q 26.314 19.647 26.867 20.689 Q 27.421 21.724 27.421 23.046 L 27.421 23.482 Q 27.421 24.803 26.874 25.832 Q 26.333 26.861 25.344 27.427 Q 24.354 27.993 23.111 28 Z M 22.128 20.103 L 22.128 26.431 L 23.072 26.431 Q 24.217 26.431 24.823 25.682 Q 25.428 24.934 25.441 23.54 L 25.441 23.039 Q 25.441 21.594 24.842 20.852 Q 24.243 20.103 23.091 20.103 Z" />
|
||||
</vector>
|
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="28.26087"
|
||||
android:viewportHeight="28.26087">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 10.734 3.043 C 9.234 3.043 8.043 4.293 8.043 5.793 C 8.043 7.683 9.743 9.223 13.043 12.223 C 16.343 9.223 18.043 7.684 18.043 5.734 C 18.043 4.234 16.793 3.043 15.293 3.043 C 14.433 3.043 13.613 3.403 13.043 4.043 C 12.473 3.403 11.654 3.043 10.734 3.043 Z M 5.734 8.043 C 4.234 8.043 3.043 9.293 3.043 10.793 C 3.043 11.653 3.403 12.473 4.043 13.043 C 3.403 13.613 3.043 14.434 3.043 15.354 C 3.043 16.854 4.293 18.043 5.793 18.043 C 7.683 18.043 9.223 16.343 12.223 13.043 C 9.223 9.743 7.684 8.043 5.734 8.043 Z M 20.293 8.043 C 18.403 8.043 16.863 9.743 13.873 13.043 C 15.043 14.334 15.987 15.373 16.824 16.168 C 17.476 15.484 18.26 14.94 19.129 14.567 C 19.997 14.195 20.932 14.002 21.877 14 C 22.181 14.002 22.484 14.025 22.785 14.066 C 22.615 13.68 22.365 13.329 22.043 13.043 C 22.683 12.473 23.043 11.654 23.043 10.734 C 23.043 9.234 21.793 8.043 20.293 8.043 Z M 13.043 13.863 C 9.743 16.863 8.043 18.404 8.043 20.354 C 8.043 21.854 9.293 23.043 10.793 23.043 C 11.653 23.043 12.473 22.683 13.043 22.043 C 13.576 22.642 14.333 22.982 15.182 23.025 C 14.981 22.369 14.879 21.686 14.877 21 C 14.88 19.52 15.351 18.078 16.225 16.883 C 15.422 16.032 14.368 15.067 13.043 13.863 Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 18.754 26 L 18.754 16.521 L 21.671 16.521 Q 22.921 16.521 23.904 17.087 Q 24.893 17.647 25.447 18.689 Q 26 19.724 26 21.046 L 26 21.482 Q 26 22.803 25.453 23.832 Q 24.913 24.861 23.923 25.427 Q 22.934 25.993 21.69 26 Z M 20.707 18.103 L 20.707 24.431 L 21.651 24.431 Q 22.797 24.431 23.402 23.682 Q 24.008 22.934 24.021 21.54 L 24.021 21.039 Q 24.021 19.594 23.422 18.852 Q 22.823 18.103 21.671 18.103 Z" />
|
||||
</vector>
|
12
app/src/debug/res/drawable-anydpi-v24/ic_stat_message.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="28.26087"
|
||||
android:viewportHeight="28.26087">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 5.043 3.043 C 3.943 3.043 3.053 3.943 3.053 5.043 L 3.043 23.043 L 7.043 19.043 L 15.16 19.043 C 15.646 17.378 16.733 15.952 18.209 15.043 L 7.043 15.043 L 7.043 13.043 L 19.043 13.043 L 19.043 14.609 C 19.935 14.211 20.9 14.003 21.877 14 C 22.268 14.003 22.658 14.038 23.043 14.105 L 23.043 5.043 C 23.043 3.943 22.143 3.043 21.043 3.043 L 5.043 3.043 Z M 7.043 7.043 L 19.043 7.043 L 19.043 9.043 L 7.043 9.043 L 7.043 7.043 Z M 7.043 10.043 L 19.043 10.043 L 19.043 12.043 L 7.043 12.043 L 7.043 10.043 Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 18.754 26 L 18.754 16.521 L 21.671 16.521 Q 22.921 16.521 23.904 17.087 Q 24.893 17.647 25.447 18.689 Q 26 19.724 26 21.046 L 26 21.482 Q 26 22.803 25.453 23.832 Q 24.913 24.861 23.923 25.427 Q 22.934 25.993 21.69 26 Z M 20.707 18.103 L 20.707 24.431 L 21.651 24.431 Q 22.797 24.431 23.402 23.682 Q 24.008 22.934 24.021 21.54 L 24.021 21.039 Q 24.021 19.594 23.422 18.852 Q 22.823 18.103 21.671 18.103 Z" />
|
||||
</vector>
|
12
app/src/debug/res/drawable-anydpi-v24/ic_stat_note.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="28.26087"
|
||||
android:viewportHeight="28.26087">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 3.043 3.043 L 3.043 12.043 C 3.043 13.043 4.043 14.043 5.043 14.043 L 7.244 14.043 C 7.644 16.043 8.943 17.743 12.043 18.043 L 12.043 20.143 C 9.843 20.343 9.043 21.444 9.043 22.744 L 9.043 23.043 L 15.188 23.043 C 14.983 22.381 14.879 21.693 14.877 21 C 14.877 20.763 14.888 20.527 14.912 20.291 C 14.649 20.226 14.364 20.172 14.043 20.143 L 14.043 18.043 C 14.68 17.981 15.224 17.848 15.717 17.678 C 16.382 16.446 17.401 15.442 18.643 14.795 C 18.725 14.551 18.792 14.299 18.844 14.043 L 21.043 14.043 C 22.043 14.043 23.043 13.043 23.043 12.043 L 23.043 3.043 L 19.043 3.043 C 18.143 3.043 17.043 4.043 17.043 5.043 L 9.043 5.043 C 9.043 4.043 7.943 3.043 7.043 3.043 L 3.043 3.043 Z M 5.043 5.043 L 7.043 5.043 L 7.043 12.043 L 5.043 12.043 L 5.043 5.043 Z M 19.043 5.043 L 21.043 5.043 L 21.043 12.043 L 19.043 12.043 L 19.043 5.043 Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 18.754 26 L 18.754 16.521 L 21.671 16.521 Q 22.921 16.521 23.904 17.087 Q 24.893 17.647 25.447 18.689 Q 26 19.724 26 21.046 L 26 21.482 Q 26 22.803 25.453 23.832 Q 24.913 24.861 23.923 25.427 Q 22.934 25.993 21.69 26 Z M 20.707 18.103 L 20.707 24.431 L 21.651 24.431 Q 22.797 24.431 23.402 23.682 Q 24.008 22.934 24.021 21.54 L 24.021 21.039 Q 24.021 19.594 23.422 18.852 Q 22.823 18.103 21.671 18.103 Z" />
|
||||
</vector>
|
BIN
app/src/debug/res/drawable-hdpi/ic_stat_grade.png
Normal file
After Width: | Height: | Size: 478 B |
BIN
app/src/debug/res/drawable-hdpi/ic_stat_luckynumber.png
Normal file
After Width: | Height: | Size: 652 B |
BIN
app/src/debug/res/drawable-hdpi/ic_stat_message.png
Normal file
After Width: | Height: | Size: 384 B |
BIN
app/src/debug/res/drawable-hdpi/ic_stat_note.png
Normal file
After Width: | Height: | Size: 460 B |
BIN
app/src/debug/res/drawable-mdpi/ic_stat_grade.png
Normal file
After Width: | Height: | Size: 355 B |
BIN
app/src/debug/res/drawable-mdpi/ic_stat_luckynumber.png
Normal file
After Width: | Height: | Size: 451 B |
BIN
app/src/debug/res/drawable-mdpi/ic_stat_message.png
Normal file
After Width: | Height: | Size: 297 B |
BIN
app/src/debug/res/drawable-mdpi/ic_stat_note.png
Normal file
After Width: | Height: | Size: 339 B |
BIN
app/src/debug/res/drawable-xhdpi/ic_stat_grade.png
Normal file
After Width: | Height: | Size: 588 B |
BIN
app/src/debug/res/drawable-xhdpi/ic_stat_luckynumber.png
Normal file
After Width: | Height: | Size: 897 B |
BIN
app/src/debug/res/drawable-xhdpi/ic_stat_message.png
Normal file
After Width: | Height: | Size: 508 B |
BIN
app/src/debug/res/drawable-xhdpi/ic_stat_note.png
Normal file
After Width: | Height: | Size: 586 B |
BIN
app/src/debug/res/drawable-xxhdpi/ic_stat_grade.png
Normal file
After Width: | Height: | Size: 781 B |
BIN
app/src/debug/res/drawable-xxhdpi/ic_stat_luckynumber.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/debug/res/drawable-xxhdpi/ic_stat_message.png
Normal file
After Width: | Height: | Size: 692 B |
BIN
app/src/debug/res/drawable-xxhdpi/ic_stat_note.png
Normal file
After Width: | Height: | Size: 805 B |
34
app/src/main/assets/creators.json
Normal file
@ -0,0 +1,34 @@
|
||||
[
|
||||
{
|
||||
"displayName": "Mikołaj Pich",
|
||||
"githubUsername": "mklkj"
|
||||
},
|
||||
{
|
||||
"displayName": "Rafał Borcz",
|
||||
"githubUsername": "Faierbel"
|
||||
},
|
||||
{
|
||||
"displayName": "Dominik Korsa",
|
||||
"githubUsername": "dominik-korsa"
|
||||
},
|
||||
{
|
||||
"displayName": "Kacper Ziubryniewicz",
|
||||
"githubUsername": "kapi2289"
|
||||
},
|
||||
{
|
||||
"displayName": "doteq",
|
||||
"githubUsername": "doteq"
|
||||
},
|
||||
{
|
||||
"displayName": "Pavuloff",
|
||||
"githubUsername": "pavuloff"
|
||||
},
|
||||
{
|
||||
"displayName": "Piotr Romanowski",
|
||||
"githubUsername": "v0idzz"
|
||||
},
|
||||
{
|
||||
"displayName": "Dinolek",
|
||||
"githubUsername": "Dinolek"
|
||||
}
|
||||
]
|
@ -2,6 +2,7 @@ package io.github.wulkanowy.data
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.AssetManager
|
||||
import android.content.res.Resources
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
@ -58,6 +59,10 @@ internal class RepositoryModule {
|
||||
@Provides
|
||||
fun provideResources(context: Context): Resources = context.resources
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAssets(context: Context): AssetManager = context.assets
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideSharedPref(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
@ -60,6 +60,8 @@ import io.github.wulkanowy.data.db.migrations.Migration17
|
||||
import io.github.wulkanowy.data.db.migrations.Migration18
|
||||
import io.github.wulkanowy.data.db.migrations.Migration19
|
||||
import io.github.wulkanowy.data.db.migrations.Migration2
|
||||
import io.github.wulkanowy.data.db.migrations.Migration20
|
||||
import io.github.wulkanowy.data.db.migrations.Migration21
|
||||
import io.github.wulkanowy.data.db.migrations.Migration3
|
||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||
@ -101,7 +103,7 @@ import javax.inject.Singleton
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 19
|
||||
const val VERSION_SCHEMA = 21
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
|
||||
return arrayOf(
|
||||
@ -122,7 +124,9 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
Migration16(),
|
||||
Migration17(),
|
||||
Migration18(),
|
||||
Migration19(sharedPrefProvider)
|
||||
Migration19(sharedPrefProvider),
|
||||
Migration20(),
|
||||
Migration21()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,9 @@ data class Attendance(
|
||||
@ColumnInfo(name = "diary_id")
|
||||
val diaryId: Int,
|
||||
|
||||
@ColumnInfo(name = "time_id")
|
||||
val timeId: Int,
|
||||
|
||||
val date: LocalDate,
|
||||
|
||||
val number: Int,
|
||||
@ -33,7 +36,13 @@ data class Attendance(
|
||||
|
||||
val excused: Boolean,
|
||||
|
||||
val deleted: Boolean
|
||||
val deleted: Boolean,
|
||||
|
||||
val excusable: Boolean,
|
||||
|
||||
@ColumnInfo(name = "excuse_status")
|
||||
val excuseStatus: String?
|
||||
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
|
@ -40,6 +40,9 @@ data class Timetable(
|
||||
|
||||
val info: String,
|
||||
|
||||
@ColumnInfo(name = "student_plan")
|
||||
val studentPlan: Boolean,
|
||||
|
||||
val changes: Boolean,
|
||||
|
||||
val canceled: Boolean
|
||||
|
@ -0,0 +1,42 @@
|
||||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration20 : Migration(19, 20) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
migrateTimetable(database)
|
||||
truncateSubjects(database)
|
||||
}
|
||||
|
||||
private fun migrateTimetable(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE Timetable")
|
||||
database.execSQL("""
|
||||
CREATE TABLE IF NOT EXISTS `Timetable` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`student_id` INTEGER NOT NULL,
|
||||
`diary_id` INTEGER NOT NULL,
|
||||
`number` INTEGER NOT NULL,
|
||||
`start` INTEGER NOT NULL,
|
||||
`end` INTEGER NOT NULL,
|
||||
`date` INTEGER NOT NULL,
|
||||
`subject` TEXT NOT NULL,
|
||||
`subjectOld` TEXT NOT NULL,
|
||||
`group` TEXT NOT NULL,
|
||||
`room` TEXT NOT NULL,
|
||||
`roomOld` TEXT NOT NULL,
|
||||
`teacher` TEXT NOT NULL,
|
||||
`teacherOld` TEXT NOT NULL,
|
||||
`info` TEXT NOT NULL,
|
||||
`student_plan` INTEGER NOT NULL,
|
||||
`changes` INTEGER NOT NULL,
|
||||
`canceled` INTEGER NOT NULL
|
||||
)
|
||||
""")
|
||||
}
|
||||
|
||||
private fun truncateSubjects(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DELETE FROM Subjects")
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration21 : Migration(20, 21) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE Attendance ADD COLUMN excusable INTEGER NOT NULL DEFAULT 0")
|
||||
database.execSQL("ALTER TABLE Attendance ADD COLUMN time_id INTEGER NOT NULL DEFAULT 0")
|
||||
database.execSQL("ALTER TABLE Attendance ADD COLUMN excuse_status TEXT DEFAULT NULL")
|
||||
|
||||
database.execSQL("DELETE FROM Semesters")
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package io.github.wulkanowy.data.pojos
|
||||
|
||||
class AppCreator(val displayName: String, val githubUsername: String)
|
@ -0,0 +1,20 @@
|
||||
package io.github.wulkanowy.data.repositories.appcreator
|
||||
|
||||
import android.content.res.AssetManager
|
||||
import com.google.gson.Gson
|
||||
import io.github.wulkanowy.data.pojos.AppCreator
|
||||
import io.reactivex.Single
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AppCreatorRepository @Inject constructor(private val assets: AssetManager) {
|
||||
fun getAppCreators(): Single<List<AppCreator>> {
|
||||
return Single.fromCallable<List<AppCreator>> {
|
||||
Gson().fromJson(
|
||||
assets.open("creators.json").bufferedReader().use { it.readText() },
|
||||
Array<AppCreator>::class.java
|
||||
).toList()
|
||||
}
|
||||
}
|
||||
}
|
@ -3,8 +3,11 @@ package io.github.wulkanowy.data.repositories.attendance
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.pojo.Absent
|
||||
import io.reactivex.Single
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDateTime
|
||||
import org.threeten.bp.LocalTime
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@ -19,6 +22,7 @@ class AttendanceRemote @Inject constructor(private val sdk: Sdk) {
|
||||
studentId = semester.studentId,
|
||||
diaryId = semester.diaryId,
|
||||
date = it.date,
|
||||
timeId = it.timeId,
|
||||
number = it.number,
|
||||
subject = it.subject,
|
||||
name = it.name,
|
||||
@ -27,9 +31,20 @@ class AttendanceRemote @Inject constructor(private val sdk: Sdk) {
|
||||
exemption = it.exemption,
|
||||
lateness = it.lateness,
|
||||
excused = it.excused,
|
||||
deleted = it.deleted
|
||||
deleted = it.deleted,
|
||||
excusable = it.excusable,
|
||||
excuseStatus = it.excuseStatus?.name
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun excuseAbsence(semester: Semester, absenceList: List<Attendance>, reason: String?): Single<Boolean> {
|
||||
return sdk.switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance ->
|
||||
Absent(
|
||||
date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)),
|
||||
timeId = attendance.timeId
|
||||
)
|
||||
}, reason)
|
||||
}
|
||||
}
|
||||
|
@ -41,4 +41,8 @@ class AttendanceRepository @Inject constructor(
|
||||
}).map { list -> list.filter { it.date in startDate..endDate } }
|
||||
}
|
||||
}
|
||||
|
||||
fun excuseForAbsence(semester: Semester, attendanceList: List<Attendance>, reason: String? = null): Single<Boolean> {
|
||||
return remote.excuseAbsence(semester, attendanceList, reason)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package io.github.wulkanowy.data.repositories.attendance
|
||||
|
||||
enum class SentExcuseStatus(val id: Int = 0) {
|
||||
WAITING,
|
||||
ACCEPTED,
|
||||
DENIED
|
||||
}
|
@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
|
||||
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.utils.roundToDecimalPlaces
|
||||
import io.reactivex.Maybe
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@ -40,9 +41,9 @@ class GradeStatisticsLocal @Inject constructor(
|
||||
"Wszystkie" -> gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId).flatMap { list ->
|
||||
if (list.isEmpty()) return@flatMap Maybe.empty<GradePointsStatistics>()
|
||||
Maybe.just(GradePointsStatistics(semester.studentId, semester.semesterId, subjectName,
|
||||
list.fold(.0) { acc, e -> acc + e.others },
|
||||
list.fold(.0) { acc, e -> acc + e.student })
|
||||
)
|
||||
(list.fold(.0) { acc, e -> acc + e.others } / list.size).roundToDecimalPlaces(2),
|
||||
(list.fold(.0) { acc, e -> acc + e.student } / list.size).roundToDecimalPlaces(2)
|
||||
))
|
||||
}
|
||||
else -> gradePointsStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName)
|
||||
}
|
||||
|
@ -65,6 +65,9 @@ class PreferencesRepository @Inject constructor(
|
||||
val fillMessageContent: Boolean
|
||||
get() = getBoolean(R.string.pref_key_fill_message_content, R.bool.pref_default_fill_message_content)
|
||||
|
||||
val showWholeClassPlan: String
|
||||
get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class)
|
||||
|
||||
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
|
||||
|
||||
private fun getString(id: String, default: Int) = sharedPref.getString(id, context.getString(default)) ?: context.getString(default)
|
||||
|
@ -36,7 +36,7 @@ class SemesterRepository @Inject constructor(
|
||||
local.saveSemesters(new.uniqueSubtract(old))
|
||||
}
|
||||
} else {
|
||||
Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}")
|
||||
Timber.i("Current semesters list:\n${new.joinToString(separator = "\n")}")
|
||||
throw IllegalArgumentException("Current semester can be only one.")
|
||||
}
|
||||
}.flatMap { local.getSemesters(student).toSingle(emptyList()) })
|
||||
|
@ -30,6 +30,7 @@ class TimetableRemote @Inject constructor(private val sdk: Sdk) {
|
||||
teacher = it.teacher,
|
||||
teacherOld = it.teacherOld,
|
||||
info = it.info,
|
||||
studentPlan = it.studentPlan,
|
||||
changes = it.changes,
|
||||
canceled = it.canceled
|
||||
)
|
||||
|
@ -16,6 +16,7 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment
|
||||
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
@ -42,6 +43,11 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
||||
Triple(getString(R.string.about_version), "${appInfo.versionName} (${appInfo.versionCode})", getCompatDrawable(R.drawable.ic_all_about))
|
||||
}
|
||||
|
||||
override val creatorsRes: Triple<String, String, Drawable?>?
|
||||
get() = context?.run {
|
||||
Triple(getString(R.string.about_creator), getString(R.string.about_creator_summary), getCompatDrawable(R.drawable.ic_about_creator))
|
||||
}
|
||||
|
||||
override val feedbackRes: Triple<String, String, Drawable?>?
|
||||
get() = context?.run {
|
||||
Triple(getString(R.string.about_feedback), getString(R.string.about_feedback_summary), getCompatDrawable(R.drawable.ic_about_feedback))
|
||||
@ -143,6 +149,10 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
||||
(activity as? MainActivity)?.pushView(LicenseFragment.newInstance())
|
||||
}
|
||||
|
||||
override fun openCreators() {
|
||||
(activity as? MainActivity)?.pushView(CreatorFragment.newInstance())
|
||||
}
|
||||
|
||||
override fun openPrivacyPolicy() {
|
||||
context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage)
|
||||
}
|
||||
|
@ -52,6 +52,11 @@ class AboutPresenter @Inject constructor(
|
||||
openLicenses()
|
||||
analytics.logEvent("about_open", "name" to "licenses")
|
||||
}
|
||||
creatorsRes?.first -> {
|
||||
Timber.i("Opening creators view")
|
||||
openCreators()
|
||||
analytics.logEvent("about_open", "name" to "creators")
|
||||
}
|
||||
privacyRes?.first -> {
|
||||
Timber.i("Opening privacy page ")
|
||||
openPrivacyPolicy()
|
||||
@ -65,6 +70,7 @@ class AboutPresenter @Inject constructor(
|
||||
view?.run {
|
||||
updateData(AboutScrollableHeader(), listOfNotNull(
|
||||
versionRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
creatorsRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
feedbackRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
faqRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
discordRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
|
@ -7,6 +7,8 @@ interface AboutView : BaseView {
|
||||
|
||||
val versionRes: Triple<String, String, Drawable?>?
|
||||
|
||||
val creatorsRes: Triple<String, String, Drawable?>?
|
||||
|
||||
val feedbackRes: Triple<String, String, Drawable?>?
|
||||
|
||||
val faqRes: Triple<String, String, Drawable?>?
|
||||
@ -33,5 +35,7 @@ interface AboutView : BaseView {
|
||||
|
||||
fun openLicenses()
|
||||
|
||||
fun openCreators()
|
||||
|
||||
fun openPrivacyPolicy()
|
||||
}
|
||||
|
@ -0,0 +1,76 @@
|
||||
package io.github.wulkanowy.ui.modules.about.creator
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_creator.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class CreatorFragment : BaseFragment(), CreatorView, MainView.TitledView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: CreatorPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var creatorsAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
|
||||
override val titleStringId get() = R.string.creators_title
|
||||
|
||||
companion object {
|
||||
fun newInstance() = CreatorFragment()
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_creator, container, false)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
presenter.onAttachView(this)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
with(creatorRecycler) {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
adapter = creatorsAdapter
|
||||
addItemDecoration(FlexibleItemDecoration(context)
|
||||
.withDefaultDivider()
|
||||
.withDrawDividerOnLastItem(false))
|
||||
}
|
||||
creatorsAdapter.setOnItemClickListener(presenter::onItemSelected)
|
||||
creatorSeeMore.setOnClickListener { presenter.onSeeMoreClick() }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<CreatorItem>) {
|
||||
creatorsAdapter.updateDataSet(data)
|
||||
}
|
||||
|
||||
override fun openUserGithubPage(username: String) {
|
||||
context?.openInternetBrowser("https://github.com/${username}", ::showMessage)
|
||||
}
|
||||
|
||||
override fun openGithubContributorsPage() {
|
||||
context?.openInternetBrowser("https://github.com/wulkanowy/wulkanowy/graphs/contributors", ::showMessage)
|
||||
}
|
||||
|
||||
override fun showProgress(show: Boolean) {
|
||||
creatorProgress.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package io.github.wulkanowy.ui.modules.about.creator
|
||||
|
||||
import android.view.View
|
||||
import coil.api.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.pojos.AppCreator
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_creator.*
|
||||
|
||||
class CreatorItem(val creator: AppCreator) : AbstractFlexibleItem<CreatorItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_creator
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
with(holder) {
|
||||
creatorItemName.text = creator.displayName
|
||||
|
||||
creatorItemAvatar.load("https://github.com/${creator.githubUsername}.png") {
|
||||
transformations(RoundedCornersTransformation(8f))
|
||||
crossfade(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as CreatorItem
|
||||
|
||||
if (creator != other.creator) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode() = creator.hashCode()
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View? get() = contentView
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package io.github.wulkanowy.ui.modules.about.creator
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.pojos.AppCreator
|
||||
import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import io.reactivex.Single
|
||||
import javax.inject.Inject
|
||||
|
||||
class CreatorPresenter @Inject constructor(
|
||||
schedulers: SchedulersProvider,
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
private val appCreatorRepository: AppCreatorRepository
|
||||
) : BasePresenter<CreatorView>(errorHandler, studentRepository, schedulers) {
|
||||
|
||||
override fun onAttachView(view: CreatorView) {
|
||||
super.onAttachView(view)
|
||||
view.initView()
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onItemSelected(item: AbstractFlexibleItem<*>) {
|
||||
if (item !is CreatorItem) return
|
||||
view?.openUserGithubPage(item.creator.githubUsername)
|
||||
}
|
||||
|
||||
fun onSeeMoreClick() {
|
||||
view?.openGithubContributorsPage()
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
disposable.add(appCreatorRepository.getAppCreators()
|
||||
.map { it.map { creator -> CreatorItem(creator) } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally { view?.showProgress(false) }
|
||||
.subscribe({ view?.run { updateData(it) } }, { errorHandler.dispatch(it) }))
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package io.github.wulkanowy.ui.modules.about.creator
|
||||
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface CreatorView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<CreatorItem>)
|
||||
|
||||
fun openUserGithubPage(username: String)
|
||||
|
||||
fun openGithubContributorsPage()
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance
|
||||
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
|
||||
class AttendanceAdapter<T : IFlexible<*>> : FlexibleAdapter<T>(null, null, true) {
|
||||
|
||||
var excuseActionMode: Boolean = false
|
||||
|
||||
var onExcuseCheckboxSelect: (attendanceItem: Attendance, checked: Boolean) -> Unit = { _, _ -> }
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance
|
||||
|
||||
import android.content.DialogInterface.BUTTON_POSITIVE
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
@ -10,8 +11,9 @@ import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
@ -24,6 +26,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.dialog_excuse.*
|
||||
import kotlinx.android.synthetic.main.fragment_attendance.*
|
||||
import org.threeten.bp.LocalDate
|
||||
import javax.inject.Inject
|
||||
@ -35,7 +38,13 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
||||
lateinit var presenter: AttendancePresenter
|
||||
|
||||
@Inject
|
||||
lateinit var attendanceAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
lateinit var attendanceAdapter: AttendanceAdapter<AbstractFlexibleItem<*>>
|
||||
|
||||
override val excuseSuccessString: String
|
||||
get() = getString(R.string.attendance_excuse_success)
|
||||
|
||||
override val excuseNoSelectionString: String
|
||||
get() = getString(R.string.attendance_excuse_no_selection)
|
||||
|
||||
companion object {
|
||||
private const val SAVED_DATE_KEY = "CURRENT_DATE"
|
||||
@ -49,6 +58,34 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
||||
|
||||
override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize
|
||||
|
||||
override val excuseActionMode: Boolean get() = attendanceAdapter.excuseActionMode
|
||||
|
||||
private var actionMode: ActionMode? = null
|
||||
private val actionModeCallback = object : ActionMode.Callback {
|
||||
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
val inflater = mode.menuInflater
|
||||
inflater.inflate(R.menu.context_menu_excuse, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
mode.title = getString(R.string.attendance_excuse_title)
|
||||
return presenter.onPrepareActionMode()
|
||||
}
|
||||
|
||||
override fun onDestroyActionMode(mode: ActionMode) {
|
||||
presenter.onDestroyActionMode()
|
||||
actionMode = null
|
||||
}
|
||||
|
||||
override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean {
|
||||
return when (menu.itemId) {
|
||||
R.id.excuseMenuSubmit -> presenter.onExcuseSubmitButtonClick()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
@ -66,6 +103,7 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
||||
|
||||
override fun initView() {
|
||||
attendanceAdapter.setOnItemClickListener(presenter::onAttendanceItemSelected)
|
||||
attendanceAdapter.onExcuseCheckboxSelect = presenter::onExcuseCheckboxSelect
|
||||
|
||||
with(attendanceRecycler) {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
@ -83,6 +121,8 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
||||
attendanceNavDate.setOnClickListener { presenter.onPickDate() }
|
||||
attendanceNextButton.setOnClickListener { presenter.onNextDay() }
|
||||
|
||||
attendanceExcuseButton.setOnClickListener { presenter.onExcuseButtonClick() }
|
||||
|
||||
attendanceNavContainer.setElevationCompat(requireContext().dpToPx(8f))
|
||||
}
|
||||
|
||||
@ -115,6 +155,10 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
||||
if (::presenter.isInitialized) presenter.onViewReselected()
|
||||
}
|
||||
|
||||
override fun onFragmentChanged() {
|
||||
if (::presenter.isInitialized) presenter.onMainViewChanged()
|
||||
}
|
||||
|
||||
override fun popView() {
|
||||
(activity as? MainActivity)?.popView()
|
||||
}
|
||||
@ -155,6 +199,10 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
||||
attendanceNextButton.visibility = if (show) VISIBLE else INVISIBLE
|
||||
}
|
||||
|
||||
override fun showExcuseButton(show: Boolean) {
|
||||
attendanceExcuseButton.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun showAttendanceDialog(lesson: Attendance) {
|
||||
(activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson))
|
||||
}
|
||||
@ -174,10 +222,38 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
||||
}
|
||||
}
|
||||
|
||||
override fun showExcuseDialog() {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.attendance_excuse_title)
|
||||
.setView(R.layout.dialog_excuse)
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
.create()
|
||||
.apply {
|
||||
setButton(BUTTON_POSITIVE, getString(R.string.attendance_excuse_dialog_submit)) { _, _ ->
|
||||
presenter.onExcuseDialogSubmit(excuseReason.text?.toString().orEmpty())
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
|
||||
override fun openSummaryView() {
|
||||
(activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance())
|
||||
}
|
||||
|
||||
override fun startActionMode() {
|
||||
actionMode = (activity as MainActivity?)?.startSupportActionMode(actionModeCallback)
|
||||
}
|
||||
|
||||
override fun showExcuseCheckboxes(show: Boolean) {
|
||||
attendanceAdapter.apply {
|
||||
excuseActionMode = show
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun finishActionMode() {
|
||||
actionMode?.finish()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
|
||||
|
@ -1,18 +1,22 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance
|
||||
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.core.view.isVisible
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.repositories.attendance.SentExcuseStatus
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_attendance.*
|
||||
|
||||
class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
|
||||
class AttendanceItem(val attendance: Attendance) :
|
||||
AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_attendance
|
||||
|
||||
@ -26,6 +30,34 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<Attendan
|
||||
attendanceItemSubject.text = attendance.subject
|
||||
attendanceItemDescription.text = attendance.name
|
||||
attendanceItemAlert.visibility = attendance.run { if (absence && !excused) VISIBLE else INVISIBLE }
|
||||
attendanceItemNumber.visibility = GONE
|
||||
attendanceItemExcuseInfo.visibility = GONE
|
||||
attendanceItemExcuseCheckbox.visibility = GONE
|
||||
attendanceItemExcuseCheckbox.isChecked = false
|
||||
attendanceItemExcuseCheckbox.setOnCheckedChangeListener { _, checked ->
|
||||
(adapter as AttendanceAdapter).onExcuseCheckboxSelect(attendance, checked)
|
||||
}
|
||||
|
||||
when (if (attendance.excuseStatus != null) SentExcuseStatus.valueOf(attendance.excuseStatus) else null) {
|
||||
SentExcuseStatus.WAITING -> {
|
||||
attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_waiting)
|
||||
attendanceItemExcuseInfo.visibility = VISIBLE
|
||||
attendanceItemAlert.visibility = INVISIBLE
|
||||
}
|
||||
SentExcuseStatus.DENIED -> {
|
||||
attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_denied)
|
||||
attendanceItemExcuseInfo.visibility = VISIBLE
|
||||
}
|
||||
else -> {
|
||||
if (attendance.excusable && (adapter as AttendanceAdapter).excuseActionMode) {
|
||||
attendanceItemNumber.visibility = GONE
|
||||
attendanceItemExcuseCheckbox.visibility = VISIBLE
|
||||
} else {
|
||||
attendanceItemNumber.visibility = VISIBLE
|
||||
attendanceItemExcuseCheckbox.visibility = GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,8 +78,20 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<Attendan
|
||||
return result
|
||||
}
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
||||
class ViewHolder(view: View, val adapter: FlexibleAdapter<*>) :
|
||||
FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View
|
||||
get() = contentView
|
||||
|
||||
override fun onClick(view: View?) {
|
||||
super.onClick(view)
|
||||
attendanceItemExcuseCheckbox.apply {
|
||||
if ((adapter as AttendanceAdapter).excuseActionMode && isVisible) {
|
||||
isChecked = !isChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
|
||||
@Module
|
||||
class AttendanceModule {
|
||||
|
||||
@Provides
|
||||
fun provideAttendanceFlexibleAdapter() = AttendanceAdapter<AbstractFlexibleItem<*>>()
|
||||
}
|
@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.attendance
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
@ -40,6 +41,8 @@ class AttendancePresenter @Inject constructor(
|
||||
|
||||
private lateinit var lastError: Throwable
|
||||
|
||||
private val attendanceToExcuseList = mutableListOf<Attendance>()
|
||||
|
||||
fun onAttachView(view: AttendanceView, date: Long?) {
|
||||
super.onAttachView(view)
|
||||
view.initView()
|
||||
@ -51,11 +54,15 @@ class AttendancePresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onPreviousDay() {
|
||||
view?.finishActionMode()
|
||||
attendanceToExcuseList.clear()
|
||||
loadData(currentDate.previousSchoolDay)
|
||||
reloadView()
|
||||
}
|
||||
|
||||
fun onNextDay() {
|
||||
view?.finishActionMode()
|
||||
attendanceToExcuseList.clear()
|
||||
loadData(currentDate.nextSchoolDay)
|
||||
reloadView()
|
||||
}
|
||||
@ -100,10 +107,59 @@ class AttendancePresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun onMainViewChanged() {
|
||||
view?.finishActionMode()
|
||||
}
|
||||
|
||||
fun onAttendanceItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||
if (item is AttendanceItem) {
|
||||
Timber.i("Select attendance item ${item.attendance.id}")
|
||||
view?.showAttendanceDialog(item.attendance)
|
||||
view?.apply {
|
||||
if (item is AttendanceItem && !excuseActionMode) {
|
||||
Timber.i("Select attendance item ${item.attendance.id}")
|
||||
showAttendanceDialog(item.attendance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onExcuseButtonClick() {
|
||||
view?.startActionMode()
|
||||
}
|
||||
|
||||
fun onExcuseCheckboxSelect(attendanceItem: Attendance, checked: Boolean) {
|
||||
if (checked) attendanceToExcuseList.add(attendanceItem)
|
||||
else attendanceToExcuseList.remove(attendanceItem)
|
||||
}
|
||||
|
||||
fun onExcuseSubmitButtonClick(): Boolean {
|
||||
view?.apply {
|
||||
return if (attendanceToExcuseList.isNotEmpty()) {
|
||||
showExcuseDialog()
|
||||
true
|
||||
} else {
|
||||
showMessage(excuseNoSelectionString)
|
||||
false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun onExcuseDialogSubmit(reason: String) {
|
||||
view?.finishActionMode()
|
||||
excuseAbsence(if (reason != "") reason else null, attendanceToExcuseList.toList())
|
||||
}
|
||||
|
||||
fun onPrepareActionMode(): Boolean {
|
||||
view?.apply {
|
||||
showExcuseCheckboxes(true)
|
||||
showExcuseButton(false)
|
||||
}
|
||||
attendanceToExcuseList.clear()
|
||||
return true
|
||||
}
|
||||
|
||||
fun onDestroyActionMode() {
|
||||
view?.apply {
|
||||
showExcuseCheckboxes(false)
|
||||
showExcuseButton(true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,6 +213,7 @@ class AttendancePresenter @Inject constructor(
|
||||
showEmpty(it.isEmpty())
|
||||
showErrorView(false)
|
||||
showContent(it.isNotEmpty())
|
||||
showExcuseButton(it.any { item -> item.attendance.excusable })
|
||||
}
|
||||
analytics.logEvent("load_attendance", "items" to it.size, "force_refresh" to forceRefresh)
|
||||
}) {
|
||||
@ -167,6 +224,39 @@ class AttendancePresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun excuseAbsence(reason: String?, toExcuseList: List<Attendance>) {
|
||||
Timber.i("Excusing absence started")
|
||||
disposable.apply {
|
||||
add(studentRepository.getCurrentStudent()
|
||||
.delay(200, MILLISECONDS)
|
||||
.flatMap { semesterRepository.getCurrentSemester(it) }
|
||||
.flatMap { attendanceRepository.excuseForAbsence(it, toExcuseList, reason) }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doOnSubscribe {
|
||||
view?.apply {
|
||||
showProgress(true)
|
||||
showContent(false)
|
||||
showExcuseButton(false)
|
||||
}
|
||||
}
|
||||
.subscribe({
|
||||
Timber.i("Excusing for absence result: Success")
|
||||
analytics.logEvent("excuse_absence", "items" to attendanceToExcuseList.size)
|
||||
attendanceToExcuseList.clear()
|
||||
view?.apply {
|
||||
showExcuseButton(false)
|
||||
showMessage(excuseSuccessString)
|
||||
}
|
||||
loadData(currentDate, true)
|
||||
}) {
|
||||
Timber.i("Excusing for absence result: An exception occurred")
|
||||
view?.showProgress(false)
|
||||
errorHandler.dispatch(it)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||
view?.run {
|
||||
if (isViewEmpty) {
|
||||
|
@ -10,6 +10,12 @@ interface AttendanceView : BaseView {
|
||||
|
||||
val currentStackSize: Int?
|
||||
|
||||
val excuseSuccessString: String
|
||||
|
||||
val excuseNoSelectionString: String
|
||||
|
||||
val excuseActionMode: Boolean
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<AttendanceItem>)
|
||||
@ -38,11 +44,21 @@ interface AttendanceView : BaseView {
|
||||
|
||||
fun showNextButton(show: Boolean)
|
||||
|
||||
fun showExcuseButton(show: Boolean)
|
||||
|
||||
fun showAttendanceDialog(lesson: Attendance)
|
||||
|
||||
fun showDatePickerDialog(currentDate: LocalDate)
|
||||
|
||||
fun showExcuseDialog()
|
||||
|
||||
fun openSummaryView()
|
||||
|
||||
fun startActionMode()
|
||||
|
||||
fun showExcuseCheckboxes(show: Boolean)
|
||||
|
||||
fun finishActionMode()
|
||||
|
||||
fun popView()
|
||||
}
|
||||
|
@ -18,7 +18,11 @@ class GradeAverageProvider @Inject constructor(
|
||||
private val gradeSummaryRepository: GradeSummaryRepository
|
||||
) {
|
||||
|
||||
fun getGradeAverage(student: Student, semesters: List<Semester>, selectedSemesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
|
||||
private val plusModifier = preferencesRepository.gradePlusModifier
|
||||
|
||||
private val minusModifier = preferencesRepository.gradeMinusModifier
|
||||
|
||||
fun getGradeAverage(student: Student, semesters: List<Semester>, selectedSemesterId: Int, forceRefresh: Boolean): Single<List<Triple<String, Double, String>>> {
|
||||
return when (preferencesRepository.gradeAverageMode) {
|
||||
"all_year" -> getAllYearAverage(student, semesters, selectedSemesterId, forceRefresh)
|
||||
"only_one_semester" -> getOnlyOneSemesterAverage(student, semesters, selectedSemesterId, forceRefresh)
|
||||
@ -26,11 +30,9 @@ class GradeAverageProvider @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAllYearAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
|
||||
private fun getAllYearAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<List<Triple<String, Double, String>>> {
|
||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||
val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
||||
val plusModifier = preferencesRepository.gradePlusModifier
|
||||
val minusModifier = preferencesRepository.gradeMinusModifier
|
||||
|
||||
return getAverageFromGradeSummary(selectedSemester, forceRefresh)
|
||||
.switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh)
|
||||
@ -43,30 +45,28 @@ class GradeAverageProvider @Inject constructor(
|
||||
}.map { grades ->
|
||||
grades.map { if (student.loginMode == Sdk.Mode.SCRAPPER.name) it.changeModifier(plusModifier, minusModifier) else it }
|
||||
.groupBy { it.subject }
|
||||
.mapValues { it.value.calcAverage() }
|
||||
.map { Triple(it.key, it.value.calcAverage(), "") }
|
||||
})
|
||||
}
|
||||
|
||||
private fun getOnlyOneSemesterAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
|
||||
private fun getOnlyOneSemesterAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<List<Triple<String, Double, String>>> {
|
||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||
val plusModifier = preferencesRepository.gradePlusModifier
|
||||
val minusModifier = preferencesRepository.gradeMinusModifier
|
||||
|
||||
return getAverageFromGradeSummary(selectedSemester, forceRefresh)
|
||||
.switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh)
|
||||
.map { grades ->
|
||||
grades.map { if (student.loginMode == Sdk.Mode.SCRAPPER.name) it.changeModifier(plusModifier, minusModifier) else it }
|
||||
.groupBy { it.subject }
|
||||
.mapValues { it.value.calcAverage() }
|
||||
.map { Triple(it.key, it.value.calcAverage(), "") }
|
||||
})
|
||||
}
|
||||
|
||||
private fun getAverageFromGradeSummary(selectedSemester: Semester, forceRefresh: Boolean): Maybe<Map<String, Double>> {
|
||||
private fun getAverageFromGradeSummary(selectedSemester: Semester, forceRefresh: Boolean): Maybe<List<Triple<String, Double, String>>> {
|
||||
return gradeSummaryRepository.getGradesSummary(selectedSemester, forceRefresh)
|
||||
.toMaybe()
|
||||
.flatMap {
|
||||
if (it.any { summary -> summary.average != .0 }) {
|
||||
Maybe.just(it.map { summary -> summary.subject to summary.average }.toMap())
|
||||
Maybe.just(it.map { summary -> Triple(summary.subject, summary.average, summary.pointsSum) })
|
||||
} else Maybe.empty()
|
||||
}.filter { !preferencesRepository.gradeAverageForceCalc }
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
import io.github.wulkanowy.utils.colorStringId
|
||||
import io.github.wulkanowy.utils.getBackgroundColor
|
||||
import io.github.wulkanowy.utils.getGradeColor
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import kotlinx.android.synthetic.main.dialog_grade.*
|
||||
|
||||
@ -50,7 +51,12 @@ class GradeDetailsDialog : DialogFragment() {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
gradeDialogSubject.text = grade.subject
|
||||
gradeDialogWeightValue.text = grade.weight
|
||||
|
||||
gradeDialogColorAndWeightValue.run {
|
||||
text = context.getString(R.string.grade_weight_value, grade.weight)
|
||||
setBackgroundResource(grade.getGradeColor())
|
||||
}
|
||||
|
||||
gradeDialogDateValue.text = grade.date.toFormattedString()
|
||||
gradeDialogColorValue.text = getString(grade.colorStringId)
|
||||
|
||||
|
@ -45,6 +45,9 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh
|
||||
override val averageString: String
|
||||
get() = getString(R.string.grade_average)
|
||||
|
||||
override val pointsSumString: String
|
||||
get() = getString(R.string.grade_points_sum)
|
||||
|
||||
override val weightString: String
|
||||
get() = getString(R.string.grade_weight)
|
||||
|
||||
|
@ -15,6 +15,7 @@ class GradeDetailsHeader(
|
||||
private val subject: String,
|
||||
private val number: String,
|
||||
private val average: String,
|
||||
private val pointsSum: String,
|
||||
var newGrades: Int,
|
||||
private val isExpandable: Boolean
|
||||
) : AbstractExpandableItem<GradeDetailsHeader.ViewHolder, GradeDetailsItem>() {
|
||||
@ -36,6 +37,8 @@ class GradeDetailsHeader(
|
||||
maxLines = if (isExpanded) 2 else 1
|
||||
}
|
||||
gradeHeaderAverage.text = average
|
||||
gradeHeaderPointsSum.text = pointsSum
|
||||
gradeHeaderPointsSum.visibility = if (pointsSum.isNotEmpty()) VISIBLE else GONE
|
||||
gradeHeaderNumber.text = number
|
||||
gradeHeaderNote.visibility = if (newGrades > 0) VISIBLE else GONE
|
||||
if (newGrades > 0) gradeHeaderNote.text = newGrades.toString(10)
|
||||
|
@ -180,22 +180,24 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun createGradeItems(items: Map<String, List<Grade>>, averages: Map<String, Double>): List<GradeDetailsHeader> {
|
||||
private fun createGradeItems(items: Map<String, List<Grade>>, averages: List<Triple<String, Double, String>>): List<GradeDetailsHeader> {
|
||||
val isGradeExpandable = preferencesRepository.isGradeExpandable
|
||||
val gradeColorTheme = preferencesRepository.gradeColorTheme
|
||||
|
||||
val noDescriptionString = view?.noDescriptionString.orEmpty()
|
||||
val weightString = view?.weightString.orEmpty()
|
||||
val pointsSumString = view?.pointsSumString.orEmpty()
|
||||
|
||||
return items.map {
|
||||
return items.map { subject ->
|
||||
GradeDetailsHeader(
|
||||
subject = it.key,
|
||||
average = formatAverage(averages[it.key]),
|
||||
number = view?.getGradeNumberString(it.value.size).orEmpty(),
|
||||
newGrades = it.value.filter { grade -> !grade.isRead }.size,
|
||||
subject = subject.key,
|
||||
average = formatAverage(averages.singleOrNull { subject.key == it.first }?.second),
|
||||
pointsSum = averages.singleOrNull { subject.key == it.first }?.takeIf { it.third.isNotEmpty() }?.let { pointsSumString.format(it.third) }.orEmpty(),
|
||||
number = view?.getGradeNumberString(subject.value.size).orEmpty(),
|
||||
newGrades = subject.value.filter { grade -> !grade.isRead }.size,
|
||||
isExpandable = isGradeExpandable
|
||||
).apply {
|
||||
subItems = it.value.map { item ->
|
||||
subItems = subject.value.map { item ->
|
||||
GradeDetailsItem(
|
||||
grade = item,
|
||||
valueBgColor = item.getBackgroundColor(gradeColorTheme),
|
||||
|
@ -14,6 +14,8 @@ interface GradeDetailsView : BaseView {
|
||||
|
||||
val averageString: String
|
||||
|
||||
val pointsSumString: String
|
||||
|
||||
val weightString: String
|
||||
|
||||
val noDescriptionString: String
|
||||
|
@ -108,6 +108,17 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G
|
||||
|
||||
animateXY(1000, 1000)
|
||||
legend.textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary)
|
||||
|
||||
with(axisLeft) {
|
||||
axisMinimum = 0f
|
||||
axisMaximum = 100f
|
||||
labelCount = 11
|
||||
}
|
||||
with(axisRight) {
|
||||
axisMinimum = 0f
|
||||
axisMaximum = 100f
|
||||
labelCount = 11
|
||||
}
|
||||
}
|
||||
|
||||
subjectsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf())
|
||||
|
@ -95,7 +95,7 @@ class GradeStatisticsPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onTypeChange() {
|
||||
val type = view?.let { it.currentType } ?: ViewType.POINTS
|
||||
val type = view?.currentType ?: ViewType.POINTS
|
||||
Timber.i("Select grade stats semester: $type")
|
||||
disposable.clear()
|
||||
view?.run {
|
||||
|
@ -115,27 +115,26 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
disposable.clear()
|
||||
}
|
||||
|
||||
private fun createGradeSummaryItemsAndHeader(gradesSummary: List<GradeSummary>, averages: Map<String, Double>)
|
||||
: Pair<List<GradeSummaryItem>, GradeSummaryScrollableHeader> {
|
||||
return averages.filterValues { value -> value != 0.0 }
|
||||
private fun createGradeSummaryItemsAndHeader(gradesSummary: List<GradeSummary>, averages: List<Triple<String, Double, String>>): Pair<List<GradeSummaryItem>, GradeSummaryScrollableHeader> {
|
||||
return averages.filter { value -> value.second != 0.0 }
|
||||
.let { filteredAverages ->
|
||||
gradesSummary.filter { !checkEmpty(it, filteredAverages) }
|
||||
.map {
|
||||
.map { gradeSummary ->
|
||||
GradeSummaryItem(
|
||||
summary = it,
|
||||
average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, "")
|
||||
summary = gradeSummary,
|
||||
average = formatAverage(filteredAverages.singleOrNull { gradeSummary.subject == it.first }?.second ?: .0, "")
|
||||
)
|
||||
}.let {
|
||||
it to GradeSummaryScrollableHeader(
|
||||
formatAverage(gradesSummary.calcAverage()),
|
||||
formatAverage(filteredAverages.values.average()))
|
||||
formatAverage(filteredAverages.map { values -> values.second }.average()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkEmpty(gradeSummary: GradeSummary, averages: Map<String, Double>): Boolean {
|
||||
private fun checkEmpty(gradeSummary: GradeSummary, averages: List<Triple<String, Double, String>>): Boolean {
|
||||
return gradeSummary.run {
|
||||
finalGrade.isBlank() && predictedGrade.isBlank() && averages[subject] == null
|
||||
finalGrade.isBlank() && predictedGrade.isBlank() && averages.singleOrNull { it.first == subject } == null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.safelyPopFragments
|
||||
import io.github.wulkanowy.utils.setOnViewChangeListener
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class MainActivity : BaseActivity<MainPresenter>(), MainView {
|
||||
@ -167,6 +168,11 @@ class MainActivity : BaseActivity<MainPresenter>(), MainView {
|
||||
(navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected()
|
||||
}
|
||||
|
||||
override fun notifyMenuViewChanged() {
|
||||
Timber.d("Menu view changed")
|
||||
(navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentChanged()
|
||||
}
|
||||
|
||||
fun showDialogFragment(dialog: DialogFragment) {
|
||||
navController.showDialogFragment(dialog)
|
||||
}
|
||||
|
@ -8,10 +8,12 @@ import dagger.android.ContributesAndroidInjector
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.di.scopes.PerFragment
|
||||
import io.github.wulkanowy.ui.modules.about.AboutFragment
|
||||
import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment
|
||||
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
|
||||
import io.github.wulkanowy.ui.modules.about.license.LicenseModule
|
||||
import io.github.wulkanowy.ui.modules.account.AccountDialog
|
||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceModule
|
||||
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
|
||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
@ -50,7 +52,7 @@ abstract class MainModule {
|
||||
}
|
||||
|
||||
@PerFragment
|
||||
@ContributesAndroidInjector
|
||||
@ContributesAndroidInjector(modules = [AttendanceModule::class])
|
||||
abstract fun bindAttendanceFragment(): AttendanceFragment
|
||||
|
||||
@PerFragment
|
||||
@ -121,6 +123,10 @@ abstract class MainModule {
|
||||
@ContributesAndroidInjector(modules = [LicenseModule::class])
|
||||
abstract fun bindLicenseFragment(): LicenseFragment
|
||||
|
||||
@PerFragment
|
||||
@ContributesAndroidInjector()
|
||||
abstract fun bindCreatorsFragment(): CreatorFragment
|
||||
|
||||
@PerFragment
|
||||
@ContributesAndroidInjector(modules = [SchoolAndTeachersModule::class])
|
||||
abstract fun bindSchoolAndTeachersFragment(): SchoolAndTeachersFragment
|
||||
|
@ -75,6 +75,7 @@ class MainPresenter @Inject constructor(
|
||||
notifyMenuViewReselected()
|
||||
false
|
||||
} else {
|
||||
notifyMenuViewChanged()
|
||||
switchMenuView(index)
|
||||
true
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ interface MainView : BaseView {
|
||||
|
||||
fun notifyMenuViewReselected()
|
||||
|
||||
fun notifyMenuViewChanged()
|
||||
|
||||
fun setViewTitle(title: String)
|
||||
|
||||
fun popView(depth: Int = 1)
|
||||
@ -33,6 +35,8 @@ interface MainView : BaseView {
|
||||
interface MainChildView {
|
||||
|
||||
fun onFragmentReselected()
|
||||
|
||||
fun onFragmentChanged() {}
|
||||
}
|
||||
|
||||
interface TitledView {
|
||||
|
@ -15,11 +15,15 @@ import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_timetable.*
|
||||
import kotlinx.android.synthetic.main.item_timetable_small.*
|
||||
|
||||
class TimetableItem(val lesson: Timetable) :
|
||||
class TimetableItem(val lesson: Timetable, private val showWholeClassPlan: String) :
|
||||
AbstractFlexibleItem<TimetableItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_timetable
|
||||
override fun getLayoutRes() = when {
|
||||
showWholeClassPlan == "small" && !lesson.studentPlan -> R.layout.item_timetable_small
|
||||
else -> R.layout.item_timetable
|
||||
}
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
|
||||
return ViewHolder(view, adapter)
|
||||
@ -27,16 +31,29 @@ class TimetableItem(val lesson: Timetable) :
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
|
||||
updateFields(holder)
|
||||
when (itemViewType) {
|
||||
R.layout.item_timetable_small -> {
|
||||
with(holder) {
|
||||
timetableSmallItemNumber.text = lesson.number.toString()
|
||||
timetableSmallItemSubject.text = lesson.subject
|
||||
timetableSmallItemTimeStart.text = lesson.start.toFormattedString("HH:mm")
|
||||
timetableSmallItemRoom.text = lesson.room
|
||||
timetableSmallItemTeacher.text = lesson.teacher
|
||||
}
|
||||
}
|
||||
R.layout.item_timetable -> {
|
||||
updateFields(holder)
|
||||
|
||||
with(holder) {
|
||||
timetableItemSubject.paintFlags =
|
||||
if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
||||
else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
||||
with(holder) {
|
||||
timetableItemSubject.paintFlags =
|
||||
if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
||||
else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
||||
}
|
||||
|
||||
updateDescription(holder)
|
||||
updateColors(holder)
|
||||
}
|
||||
}
|
||||
|
||||
updateDescription(holder)
|
||||
updateColors(holder)
|
||||
}
|
||||
|
||||
private fun updateFields(holder: ViewHolder) {
|
||||
|
@ -2,6 +2,8 @@ package io.github.wulkanowy.ui.modules.timetable
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
|
||||
@ -29,6 +31,7 @@ class TimetablePresenter @Inject constructor(
|
||||
studentRepository: StudentRepository,
|
||||
private val timetableRepository: TimetableRepository,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val prefRepository: PreferencesRepository,
|
||||
private val analytics: FirebaseAnalyticsHelper
|
||||
) : BasePresenter<TimetableView>(errorHandler, studentRepository, schedulers) {
|
||||
|
||||
@ -134,7 +137,7 @@ class TimetablePresenter @Inject constructor(
|
||||
.flatMap { semesterRepository.getCurrentSemester(it) }
|
||||
.delay(200, MILLISECONDS)
|
||||
.flatMap { timetableRepository.getTimetable(it, currentDate, currentDate, forceRefresh) }
|
||||
.map { items -> items.map { TimetableItem(it) } }
|
||||
.map { createTimetableItems(it) }
|
||||
.map { items -> items.sortedBy { it.lesson.number } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
@ -172,6 +175,12 @@ class TimetablePresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTimetableItems(items: List<Timetable>): List<TimetableItem> {
|
||||
return items
|
||||
.filter { if (prefRepository.showWholeClassPlan == "no") it.studentPlan else true }
|
||||
.map { TimetableItem(it, prefRepository.showWholeClassPlan) }
|
||||
}
|
||||
|
||||
private fun reloadView() {
|
||||
Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}")
|
||||
view?.apply {
|
||||
|
@ -24,14 +24,7 @@ fun List<GradeSummary>.calcAverage(): Double {
|
||||
|
||||
fun Grade.getBackgroundColor(theme: String): Int {
|
||||
return when (theme) {
|
||||
"grade_color" -> when (color) {
|
||||
"000000" -> R.color.grade_black
|
||||
"F04C4C" -> R.color.grade_red
|
||||
"20A4F7" -> R.color.grade_blue
|
||||
"6ECD07" -> R.color.grade_green
|
||||
"B16CF1" -> R.color.grade_purple
|
||||
else -> R.color.grade_material_default
|
||||
}
|
||||
"grade_color" -> getGradeColor()
|
||||
"material" -> when (value.toInt()) {
|
||||
6 -> R.color.grade_material_six
|
||||
5 -> R.color.grade_material_five
|
||||
@ -53,6 +46,17 @@ fun Grade.getBackgroundColor(theme: String): Int {
|
||||
}
|
||||
}
|
||||
|
||||
fun Grade.getGradeColor(): Int {
|
||||
return when (color) {
|
||||
"000000" -> R.color.grade_black
|
||||
"F04C4C" -> R.color.grade_red
|
||||
"20A4F7" -> R.color.grade_blue
|
||||
"6ECD07" -> R.color.grade_green
|
||||
"B16CF1" -> R.color.grade_purple
|
||||
else -> R.color.grade_material_default
|
||||
}
|
||||
}
|
||||
|
||||
inline val Grade.colorStringId: Int
|
||||
get() {
|
||||
return when (color) {
|
||||
|
@ -0,0 +1,7 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import kotlin.math.round
|
||||
|
||||
fun Double.roundToDecimalPlaces(places: Int = 2): Double {
|
||||
return round(this * 10 * places) / (10 * places)
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
Wersja 0.14.2
|
||||
Wersja 0.15.0
|
||||
|
||||
- naprawiliśmy błąd powodujący zatrzymanie aplikacji na urządzeniach z Androidem poniżej 5.0
|
||||
- zmieniliśmy mało mówiący komunikat o niedostępnym dzienniku na taki mówiący o przerwie technicznej dziennika
|
||||
- dodaliśmy funkcję usprawiedliwiania nieobecności
|
||||
- w trybie hybrydowym i mobilnym pokazują się teraz domyślnie tylko lekcje grupy ucznia
|
||||
- odświezyliśmy wygląd okienka ze szczegółami oceny
|
||||
- dokonaliśmy kilku poprawek dla systemu punktowego: suma punktów wyświetla się teraz w szczegółach ocen, a w widoku ucznia na tle klasy wykres jest w końcu poprawnie skalowany
|
||||
|
||||
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
||||
|
@ -3,11 +3,7 @@
|
||||
android:height="24dp"
|
||||
android:viewportWidth="28.26087"
|
||||
android:viewportHeight="28.26087">
|
||||
<group
|
||||
android:translateX="1.1304348"
|
||||
android:translateY="1.1304348">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M13 11h2v2h-2m0 2h2a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2h-2V7h4V5h-4a2 2 0 0 0-2 2v6c0 1.1 0.9 2 2 2m8 2H7V3h14m0-2H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2M3 5H1v16a2 2 0 0 0 2 2h16v-2H3V5z" />
|
||||
</group>
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M13 11h2v2h-2m0 2h2a2 2 0 0 0 2-2v-2a2 2 0 0 0-2-2h-2V7h4V5h-4a2 2 0 0 0-2 2v6c0 1.1 0.9 2 2 2m8 2H7V3h14m0-2H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2M3 5H1v16a2 2 0 0 0 2 2h16v-2H3V5z" />
|
||||
</vector>
|
||||
|
@ -3,11 +3,7 @@
|
||||
android:height="24dp"
|
||||
android:viewportWidth="26.086956"
|
||||
android:viewportHeight="26.086956">
|
||||
<group
|
||||
android:translateX="1.0434783"
|
||||
android:translateY="1.0434783">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M12,11.18C15.3,8.18 17,6.64 17,4.69C17,3.19 15.75,2 14.25,2C13.39,2 12.57,2.36 12,3C11.43,2.36 10.61,2 9.69,2C8.19,2 7,3.25 7,4.75C7,6.64 8.7,8.18 12,11.18M11.18,12C8.18,8.7 6.64,7 4.69,7C3.19,7 2,8.25 2, 9.75C2,10.61 2.36,11.43 3,12C2.36,12.57 2,13.39 2,14.31C2,15.81 3.25,17 4.75,17C6.64,17 8.18,15.3 11.18,12M12.83, 12C15.82,15.3 17.36,17 19.31,17C20.81,17 22,15.75 22,14.25C22,13.39 21.64,12.57 21,12C21.64,11.43 22,10.61 22,9.69C22, 8.19 20.75,7 19.25,7C17.36,7 15.82,8.7 12.83,12M12,12.82C8.7,15.82 7,17.36 7,19.31C7,20.81 8.25,22 9.75,22C10.61,22 11.43,21.64 12,21C12.57,21.64 13.39,22 14.31,22C15.81,22 17,20.75 17,19.25C17,17.36 15.3,15.82 12,12.82Z" />
|
||||
</group>
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M12,11.18C15.3,8.18 17,6.64 17,4.69C17,3.19 15.75,2 14.25,2C13.39,2 12.57,2.36 12,3C11.43,2.36 10.61,2 9.69,2C8.19,2 7,3.25 7,4.75C7,6.64 8.7,8.18 12,11.18M11.18,12C8.18,8.7 6.64,7 4.69,7C3.19,7 2,8.25 2, 9.75C2,10.61 2.36,11.43 3,12C2.36,12.57 2,13.39 2,14.31C2,15.81 3.25,17 4.75,17C6.64,17 8.18,15.3 11.18,12M12.83, 12C15.82,15.3 17.36,17 19.31,17C20.81,17 22,15.75 22,14.25C22,13.39 21.64,12.57 21,12C21.64,11.43 22,10.61 22,9.69C22, 8.19 20.75,7 19.25,7C17.36,7 15.82,8.7 12.83,12M12,12.82C8.7,15.82 7,17.36 7,19.31C7,20.81 8.25,22 9.75,22C10.61,22 11.43,21.64 12,21C12.57,21.64 13.39,22 14.31,22C15.81,22 17,20.75 17,19.25C17,17.36 15.3,15.82 12,12.82Z" />
|
||||
</vector>
|
||||
|
@ -3,11 +3,7 @@
|
||||
android:height="24dp"
|
||||
android:viewportWidth="26.086956"
|
||||
android:viewportHeight="26.086956">
|
||||
<group
|
||||
android:translateX="1.0434783"
|
||||
android:translateY="1.0434783">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z" />
|
||||
</group>
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z" />
|
||||
</vector>
|
||||
|
@ -3,11 +3,7 @@
|
||||
android:height="24dp"
|
||||
android:viewportWidth="26.086956"
|
||||
android:viewportHeight="26.086956">
|
||||
<group
|
||||
android:translateX="1.0434783"
|
||||
android:translateY="1.0434783">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M20.2,2H19.5H18C17.1,2 16,3 16,4H8C8,3 6.9,2 6,2H4.5H3.8H2V11C2,12 3,13 4,13H6.2C6.6,15 7.9,16.7 11,17V19.1C8.8,19.3 8,20.4 8,21.7V22H16V21.7C16,20.4 15.2,19.3 13,19.1V17C16.1,16.7 17.4,15 17.8,13H20C21,13 22,12 22,11V2H20.2M4,11V4H6V6V11C5.1,11 4.3,11 4,11M20,11C19.7,11 18.9,11 18,11V6V4H20V11Z" />
|
||||
</group>
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M20.2,2H19.5H18C17.1,2 16,3 16,4H8C8,3 6.9,2 6,2H4.5H3.8H2V11C2,12 3,13 4,13H6.2C6.6,15 7.9,16.7 11,17V19.1C8.8,19.3 8,20.4 8,21.7V22H16V21.7C16,20.4 15.2,19.3 13,19.1V17C16.1,16.7 17.4,15 17.8,13H20C21,13 22,12 22,11V2H20.2M4,11V4H6V6V11C5.1,11 4.3,11 4,11M20,11C19.7,11 18.9,11 18,11V6V4H20V11Z" />
|
||||
</vector>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/colorPrimaryLight"/>
|
||||
<corners android:radius="12dp"/>
|
||||
<solid android:color="@color/colorPrimaryLight" />
|
||||
<corners android:radius="12dp" />
|
||||
</shape>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/colorPrimary"/>
|
||||
<corners android:radius="12dp"/>
|
||||
</shape>
|
||||
<solid android:color="@color/colorPrimary" />
|
||||
<corners android:radius="12dp" />
|
||||
</shape>
|
||||
|
9
app/src/main/res/drawable/ic_about_creator.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M9,13.75c-2.34,0 -7,1.17 -7,3.5L2,19h14v-1.75c0,-2.33 -4.66,-3.5 -7,-3.5zM4.34,17c0.84,-0.58 2.87,-1.25 4.66,-1.25s3.82,0.67 4.66,1.25L4.34,17zM9,12c1.93,0 3.5,-1.57 3.5,-3.5S10.93,5 9,5 5.5,6.57 5.5,8.5 7.07,12 9,12zM9,7c0.83,0 1.5,0.67 1.5,1.5S9.83,10 9,10s-1.5,-0.67 -1.5,-1.5S8.17,7 9,7zM16.04,13.81c1.16,0.84 1.96,1.96 1.96,3.44L18,19h4v-1.75c0,-2.02 -3.5,-3.17 -5.96,-3.44zM15,12c1.93,0 3.5,-1.57 3.5,-3.5S16.93,5 15,5c-0.54,0 -1.04,0.13 -1.5,0.35 0.63,0.89 1,1.98 1,3.15s-0.37,2.26 -1,3.15c0.46,0.22 0.96,0.35 1.5,0.35z"/>
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_all_done.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFF"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_all_done_all.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFF"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zM22.24,5.59L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_excuse_denied.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFF"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M7,11v2h10v-2L7,11zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_excuse_waiting.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFF"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z" />
|
||||
</vector>
|
@ -1,7 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#FFF" android:pathData="M13,11H15V13H13M13,15H15A2,2 0 0,0 17,13V11C17,9.89 16.1,9 15,9H13V7H17V5H13A2,2 0 0,0 11,7V13C11,14.11 11.9,15 13,15M21,17H7V3H21M21,1H7A2,2 0 0,0 5,3V17A2,2 0 0,0 7,19H21A2,2 0 0,0 23,17V3A2,2 0 0,0 21,1M3,5H1V21A2,2 0 0,0 3,23H19V21H3V5Z" />
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M13,11H15V13H13M13,15H15A2,2 0 0,0 17,13V11C17,9.89 16.1,9 15,9H13V7H17V5H13A2,2 0 0,0 11,7V13C11,14.11 11.9,15 13,15M21,17H7V3H21M21,1H7A2,2 0 0,0 5,3V17A2,2 0 0,0 7,19H21A2,2 0 0,0 23,17V3A2,2 0 0,0 21,1M3,5H1V21A2,2 0 0,0 3,23H19V21H3V5Z" />
|
||||
</vector>
|
||||
|
@ -1,7 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#FFF" android:pathData="M4,5H20V7H4V5M4,9H20V11H4V9M4,13H20V15H4V13M4,17H14V19H4V17Z" />
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M4,5H20V7H4V5M4,9H20V11H4V9M4,13H20V15H4V13M4,17H14V19H4V17Z" />
|
||||
</vector>
|
||||
|
21
app/src/main/res/layout/dialog_excuse.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/excuseReason"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/attendance_excuse_dialog_reason" />
|
||||
|
||||
<requestFocus />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</LinearLayout>
|
@ -17,90 +17,84 @@
|
||||
android:padding="20dp"
|
||||
tools:ignore="UselessParent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeDialogValue"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
<LinearLayout
|
||||
android:id="@+id/gradeDialogValueLayout"
|
||||
android:layout_width="86dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_gravity="end"
|
||||
android:background="@color/grade_material_default"
|
||||
android:gravity="center"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="30sp"
|
||||
tools:text="6" />
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeDialogSubject"
|
||||
android:layout_width="match_parent"
|
||||
<TextView
|
||||
android:id="@+id/gradeDialogValue"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="86dp"
|
||||
android:background="@color/grade_material_default"
|
||||
android:gravity="center"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="30sp"
|
||||
tools:text="6" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeDialogColorAndWeightValue"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="@color/grade_black"
|
||||
android:gravity="center"
|
||||
android:maxLines="2"
|
||||
android:minHeight="32dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="14sp"
|
||||
tools:text="Waga: 1.00" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/gradeDialogHeader"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_toStartOf="@+id/gradeDialogValueLayout"
|
||||
android:layout_toLeftOf="@+id/gradeDialogValueLayout"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginEnd="90dp"
|
||||
android:layout_marginRight="90dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="5"
|
||||
android:minHeight="80dp"
|
||||
android:minLines="2"
|
||||
android:text="@string/grade_header"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="20sp" />
|
||||
android:minHeight="120dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeDialogDescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/gradeDialogSubject"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:text="@string/all_description"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="17sp" />
|
||||
<TextView
|
||||
android:id="@+id/gradeDialogSubject"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="2"
|
||||
android:text="@string/grade_header"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeDialogDescriptionValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/gradeDialogDescription"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginTop="3dp"
|
||||
android:text="@string/all_no_description"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeDialogWeight"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/gradeDialogDescriptionValue"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/grade_weight"
|
||||
android:textSize="17sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeDialogWeightValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/gradeDialogWeight"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginTop="3dp"
|
||||
android:text="@string/grade_weight"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="12sp" />
|
||||
<TextView
|
||||
android:id="@+id/gradeDialogDescriptionValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/all_no_description"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeDialogComment"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/gradeDialogWeightValue"
|
||||
android:layout_below="@+id/gradeDialogHeader"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginTop="10dp"
|
||||
|
@ -56,6 +56,20 @@
|
||||
android:textSize="20sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
android:id="@+id/attendanceExcuseButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:text="@string/attendance_excuse_title"
|
||||
android:tint="?colorOnSecondary"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_all_done_all"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/attendanceError"
|
||||
android:layout_width="match_parent"
|
||||
|
37
app/src/main/res/layout/fragment_creator.xml
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
|
||||
android:id="@+id/creatorProgress"
|
||||
style="@style/Widget.MaterialProgressBar.ProgressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/creatorRecycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/creatorSeeMore"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginLeft="32dp"
|
||||
android:layout_marginRight="32dp"
|
||||
android:text="@string/creator_see_more" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
@ -101,7 +101,7 @@
|
||||
android:id="@+id/loginFormName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints="emailAddress"
|
||||
android:autofillHints="username|emailAddress"
|
||||
android:inputType="textEmailAddress"
|
||||
android:maxLines="1"
|
||||
tools:targetApi="o" />
|
||||
@ -121,6 +121,7 @@
|
||||
android:layout_marginRight="24dp"
|
||||
android:hint="@string/login_password_hint"
|
||||
app:errorEnabled="true"
|
||||
app:errorIconDrawable="@null"
|
||||
app:layout_constraintBottom_toTopOf="@+id/loginFormHostLayout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@ -216,6 +217,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:imeActionLabel="@string/login_sign_in"
|
||||
android:imeOptions="actionDone"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textAutoComplete|textNoSuggestions"
|
||||
android:maxLines="1"
|
||||
tools:ignore="LabelFor" />
|
||||
|
@ -133,7 +133,7 @@
|
||||
android:id="@+id/loginFormName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints="emailAddress"
|
||||
android:autofillHints="username|emailAddress"
|
||||
android:inputType="textEmailAddress"
|
||||
android:maxLines="1"
|
||||
tools:targetApi="o" />
|
||||
@ -153,6 +153,7 @@
|
||||
android:layout_marginRight="24dp"
|
||||
android:hint="@string/login_password_hint"
|
||||
app:errorEnabled="true"
|
||||
app:errorIconDrawable="@null"
|
||||
app:layout_constraintBottom_toTopOf="@+id/loginFormHostLayout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
@ -1,17 +1,16 @@
|
||||
<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:id="@+id/gradeHeaderContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingRight="14dp"
|
||||
android:paddingBottom="10dp"
|
||||
tools:context=".ui.modules.grade.details.GradeDetailsHeader"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingStart="16dp">
|
||||
tools:context=".ui.modules.grade.details.GradeDetailsHeader">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeHeaderSubject"
|
||||
@ -37,10 +36,10 @@
|
||||
tools:text="Average: 6,00" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeHeaderNumber"
|
||||
android:id="@+id/gradeHeaderPointsSum"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/gradeHeaderSubject"
|
||||
android:layout_below="@+id/gradeHeaderSubject"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginTop="5dp"
|
||||
@ -48,20 +47,34 @@
|
||||
android:layout_toRightOf="@+id/gradeHeaderAverage"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
tools:text="Points: 123/200 (61,5%)" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeHeaderNumber"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/gradeHeaderSubject"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_toEndOf="@+id/gradeHeaderPointsSum"
|
||||
android:layout_toRightOf="@+id/gradeHeaderPointsSum"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
tools:text="12 grades" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeHeaderNote"
|
||||
android:layout_width="wrap_content"
|
||||
android:minWidth="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="10dp"
|
||||
android:background="@drawable/background_header_note"
|
||||
android:gravity="center"
|
||||
android:minWidth="20dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:textColor="?colorOnPrimary"
|
||||
android:textSize="14sp"
|
||||
tools:text="255" />
|
||||
|
@ -13,16 +13,40 @@
|
||||
android:paddingBottom="7dp"
|
||||
tools:context=".ui.modules.attendance.AttendanceItem">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attendanceItemNumber"
|
||||
<LinearLayout
|
||||
android:id="@+id/attendanceItemNumberContainer"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLength="2"
|
||||
android:textSize="32sp"
|
||||
tools:text="5" />
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attendanceItemNumber"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLength="2"
|
||||
android:textSize="32sp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:text="5" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/attendanceItemExcuseCheckbox"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:text="@null"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/attendanceItemExcuseInfo"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:visibility="gone"
|
||||
app:srcCompat="@drawable/ic_excuse_waiting"
|
||||
app:tint="?attr/colorOnSurface" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attendanceItemSubject"
|
||||
@ -35,8 +59,8 @@
|
||||
android:layout_marginRight="40dp"
|
||||
android:layout_toStartOf="@id/attendanceItemAlert"
|
||||
android:layout_toLeftOf="@id/attendanceItemAlert"
|
||||
android:layout_toEndOf="@+id/attendanceItemNumber"
|
||||
android:layout_toRightOf="@+id/attendanceItemNumber"
|
||||
android:layout_toEndOf="@+id/attendanceItemNumberContainer"
|
||||
android:layout_toRightOf="@+id/attendanceItemNumberContainer"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="17sp"
|
||||
@ -48,7 +72,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignStart="@id/attendanceItemSubject"
|
||||
android:layout_alignLeft="@id/attendanceItemSubject"
|
||||
android:layout_alignBottom="@+id/attendanceItemNumber"
|
||||
android:layout_alignBottom="@+id/attendanceItemNumberContainer"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
|
34
app/src/main/res/layout/item_creator.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="56dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/creatorItemAvatar"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:contentDescription="@string/creator_avatar_description"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/creatorItemName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="bottom"
|
||||
android:textSize="16sp"
|
||||
tools:text="@tools:sample/lorem"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@id/creatorItemAvatar"
|
||||
android:layout_toRightOf="@id/creatorItemAvatar"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginEnd="16dp" />
|
||||
</RelativeLayout>
|
81
app/src/main/res/layout/item_timetable_small.xml
Normal file
@ -0,0 +1,81 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
tools:context=".ui.modules.timetable.TimetableItem">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableSmallItemNumber"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:gravity="center"
|
||||
android:maxLength="2"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="15sp"
|
||||
tools:text="5" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableSmallItemTimeStart"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@id/timetableSmallItemNumber"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_toEndOf="@id/timetableSmallItemNumber"
|
||||
android:layout_toRightOf="@id/timetableSmallItemNumber"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="15sp"
|
||||
tools:text="11:11" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableSmallItemSubject"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_toEndOf="@+id/timetableSmallItemTimeStart"
|
||||
android:layout_toRightOf="@+id/timetableSmallItemTimeStart"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="15sp"
|
||||
tools:text="Sieci komputerowe" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableSmallItemRoom"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_toEndOf="@+id/timetableSmallItemSubject"
|
||||
android:layout_toRightOf="@+id/timetableSmallItemSubject"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="15sp"
|
||||
tools:text="22" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableSmallItemTeacher"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_toEndOf="@id/timetableSmallItemRoom"
|
||||
android:layout_toRightOf="@id/timetableSmallItemRoom"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="15sp"
|
||||
tools:text="Agata Kowalska - Błaszczyk" />
|
||||
|
||||
</RelativeLayout>
|
11
app/src/main/res/menu/context_menu_excuse.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?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/excuseMenuSubmit"
|
||||
android:icon="@drawable/ic_all_done"
|
||||
android:title="@string/attendance_excuse_dialog_submit"
|
||||
app:iconTint="@color/material_on_surface_emphasis_medium"
|
||||
app:showAsAction="always" />
|
||||
</menu>
|
@ -41,4 +41,10 @@
|
||||
<item>Średnia ocen z 2 semestru</item>
|
||||
<item>Średnia ocen z całego roku</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="timetable_show_whole_class_entries">
|
||||
<item>Nie pokazuj</item>
|
||||
<item>Pokazuj wszystkie</item>
|
||||
<item>Pokazuj pomniejszone</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
@ -12,6 +12,7 @@
|
||||
<string name="settings_title">Ustawienia</string>
|
||||
<string name="more_title">Więcej</string>
|
||||
<string name="about_title">O aplikacji</string>
|
||||
<string name="creators_title">Twórcy</string>
|
||||
<string name="license_title">Licencje</string>
|
||||
<string name="message_title">Wiadomości</string>
|
||||
<string name="send_message_title">Nowa wiadomość</string>
|
||||
@ -66,10 +67,12 @@
|
||||
<string name="grade_switch_semester">Zmień semestr</string>
|
||||
<string name="grade_no_items">Brak ocen</string>
|
||||
<string name="grade_weight">Waga</string>
|
||||
<string name="grade_weight_value">Waga: %s</string>
|
||||
<string name="grade_comment">Komentarz</string>
|
||||
<string name="grade_no_new_items">Brak nowych ocen</string>
|
||||
<string name="grade_number_new_items">Ilość nowych ocen: %1$d</string>
|
||||
<string name="grade_average">Średnia: %1$.2f</string>
|
||||
<string name="grade_points_sum">Punkty: %s</string>
|
||||
<string name="grade_no_average">Brak średniej</string>
|
||||
<string name="grade_predicted">Przewidywana: %1$s</string>
|
||||
<string name="grade_final">Końcowa: %1$s</string>
|
||||
@ -139,6 +142,11 @@
|
||||
<item quantity="many">%1$d nieobecności</item>
|
||||
<item quantity="other">%1$d nieobecności</item>
|
||||
</plurals>
|
||||
<string name="attendance_excuse_dialog_reason">Powód nieobecności (opcjonalny)</string>
|
||||
<string name="attendance_excuse_dialog_submit">Wyślij</string>
|
||||
<string name="attendance_excuse_success">Usprawiedliwiono pomyślnie!</string>
|
||||
<string name="attendance_excuse_no_selection">Musisz wybrać co najmniej jedną nieobecność!</string>
|
||||
<string name="attendance_excuse_title">Usprawiedliw</string>
|
||||
|
||||
|
||||
<!--Attendance summary-->
|
||||
@ -268,6 +276,8 @@
|
||||
|
||||
<!--About-->
|
||||
<string name="about_version">Wersja aplikacji</string>
|
||||
<string name="about_creator">Twórcy</string>
|
||||
<string name="about_creator_summary">Lista programistów Wulkanowego</string>
|
||||
<string name="about_feedback">Zgłoś błąd</string>
|
||||
<string name="about_feedback_summary">Wyślij zgłoszenie o błędzie poprzez e-maila</string>
|
||||
<string name="about_faq">FAQ</string>
|
||||
@ -286,6 +296,11 @@
|
||||
<string name="license_dialog_title">Licencja</string>
|
||||
|
||||
|
||||
<!--Creators-->
|
||||
<string name="creator_avatar_description">Awatar</string>
|
||||
<string name="creator_see_more">Zobacz więcej na GitHub</string>
|
||||
|
||||
|
||||
<!--Generic-->
|
||||
<string name="all_content">Treść</string>
|
||||
<string name="all_retry">Ponów</string>
|
||||
@ -365,4 +380,5 @@
|
||||
<string name="error_unknown">Wystąpił nieoczekiwany błąd</string>
|
||||
<string name="error_feature_disabled">Funkcja wyłączona przez szkołę</string>
|
||||
<string name="error_feature_not_available">Funkcja niedostępna w tym trybie</string>
|
||||
<string name="pref_view_timetable_show_whole_class">Pokazuj lekcje całej klasy</string>
|
||||
</resources>
|
||||
|