1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-20 06:49:08 -05:00

Merge branch 'release/0.19.0'

This commit is contained in:
Mikołaj Pich 2020-06-14 22:44:36 +02:00
commit 9f87b92937
61 changed files with 2805 additions and 221 deletions

View File

@ -14,7 +14,7 @@ cache:
branches: branches:
only: only:
- develop - develop
- 0.18.3 - 0.19.0
android: android:
licenses: licenses:

View File

@ -17,8 +17,8 @@ android {
testApplicationId "io.github.tests.wulkanowy" testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 17 minSdkVersion 17
targetSdkVersion 29 targetSdkVersion 29
versionCode 62 versionCode 63
versionName "0.18.3" versionName "0.19.0"
multiDexEnabled true multiDexEnabled true
resValue "string", "app_name", "Wulkanowy" resValue "string", "app_name", "Wulkanowy"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -77,8 +77,8 @@ android {
} }
} }
viewBinding { buildFeatures {
enabled = true viewBinding = true
} }
lintOptions { lintOptions {
@ -124,14 +124,14 @@ configurations.all {
} }
dependencies { dependencies {
implementation "io.github.wulkanowy:sdk:0.18.3" implementation "io.github.wulkanowy:sdk:0.19.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.2.0" implementation "androidx.core:core-ktx:1.2.0"
implementation "androidx.activity:activity-ktx:1.1.0" implementation "androidx.activity:activity-ktx:1.1.0"
implementation "androidx.appcompat:appcompat:1.2.0-rc01" implementation "androidx.appcompat:appcompat:1.2.0-rc01"
implementation "androidx.appcompat:appcompat-resources:1.1.0" implementation "androidx.appcompat:appcompat-resources:1.1.0"
implementation "androidx.fragment:fragment-ktx:1.2.4" implementation "androidx.fragment:fragment-ktx:1.2.5"
implementation "androidx.annotation:annotation:1.1.0" implementation "androidx.annotation:annotation:1.1.0"
implementation "androidx.multidex:multidex:2.0.1" implementation "androidx.multidex:multidex:2.0.1"
@ -142,7 +142,7 @@ dependencies {
implementation "androidx.constraintlayout:constraintlayout:1.1.3" implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
implementation "com.google.android.material:material:1.1.0" implementation "com.google.android.material:material:1.1.0"
implementation "com.github.wulkanowy:material-chips-input:2.0.1" implementation "com.github.wulkanowy:material-chips-input:2.1.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1" implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
@ -180,12 +180,13 @@ dependencies {
implementation 'com.wdullaer:materialdatetimepicker:4.2.3' implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
implementation "io.coil-kt:coil:0.11.0" implementation "io.coil-kt:coil:0.11.0"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0" implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
playImplementation 'com.google.firebase:firebase-analytics:17.4.2' playImplementation 'com.google.firebase:firebase-analytics:17.4.3'
playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.0.7' playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.0.7'
playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.0.7" playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.0.7"
playImplementation 'com.google.firebase:firebase-messaging:20.2.0' playImplementation 'com.google.firebase:firebase-messaging:20.2.0'
playImplementation 'com.google.firebase:firebase-crashlytics:17.0.0' playImplementation 'com.google.firebase:firebase-crashlytics:17.0.1'
playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"

File diff suppressed because it is too large Load Diff

View File

@ -120,23 +120,23 @@ class Migration13Test : AbstractMigrationTest() {
assertEquals(2, first.diaryId) assertEquals(2, first.diaryId)
} }
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let { getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let { semesters ->
assertTrue { it.single { it.second }.second } assertTrue { semesters.single { it.second }.second }
assertEquals(1970, it[0].first.schoolYear) assertEquals(1970, semesters[0].first.schoolYear)
assertEquals(of(1970, 1, 1), it[0].first.end) assertEquals(of(1970, 1, 1), semesters[0].first.end)
assertEquals(of(1970, 1, 1), it[0].first.start) assertEquals(of(1970, 1, 1), semesters[0].first.start)
assertFalse(it[0].second) assertFalse(semesters[0].second)
assertFalse(it[1].second) assertFalse(semesters[1].second)
assertFalse(it[2].second) assertFalse(semesters[2].second)
assertTrue(it[3].second) assertTrue(semesters[3].second)
} }
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let { getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let { semesters ->
assertTrue { it.single { it.second }.second } assertTrue { semesters.single { it.second }.second }
assertFalse(it[0].second) assertFalse(semesters[0].second)
assertFalse(it[1].second) assertFalse(semesters[1].second)
assertFalse(it[2].second) assertFalse(semesters[2].second)
assertTrue(it[3].second) assertTrue(semesters[3].second)
} }
} }

View File

@ -10,6 +10,7 @@ import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.now import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.of import org.threeten.bp.LocalDate.of
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -35,9 +36,18 @@ class AttendanceLocalTest {
@Test @Test
fun saveAndReadTest() { fun saveAndReadTest() {
attendanceLocal.saveAttendance(listOf( attendanceLocal.saveAttendance(listOf(
Attendance(1, 2, 3, of(2018, 9, 10), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name), getAttendanceEntity(
Attendance(1, 2, 3, of(2018, 9, 14), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.WAITING.name), of(2018, 9, 10),
Attendance(1, 2, 3, of(2018, 9, 17), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name) SentExcuseStatus.ACCEPTED
),
getAttendanceEntity(
of(2018, 9, 14),
SentExcuseStatus.WAITING
),
getAttendanceEntity(
of(2018, 9, 17),
SentExcuseStatus.ACCEPTED
)
)) ))
val attendance = attendanceLocal val attendance = attendanceLocal
@ -50,4 +60,25 @@ class AttendanceLocalTest {
assertEquals(attendance[0].date, of(2018, 9, 10)) assertEquals(attendance[0].date, of(2018, 9, 10))
assertEquals(attendance[1].date, of(2018, 9, 14)) assertEquals(attendance[1].date, of(2018, 9, 14))
} }
private fun getAttendanceEntity(
date: LocalDate,
excuseStatus: SentExcuseStatus
) = Attendance(
studentId = 1,
diaryId = 2,
timeId = 3,
date = date,
number = 0,
subject = "",
name = "",
presence = false,
absence = false,
exemption = false,
lateness = false,
excused = false,
deleted = false,
excusable = false,
excuseStatus = excuseStatus.name
)
} }

View File

@ -5,7 +5,6 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before

View File

@ -2,7 +2,6 @@
package io.github.wulkanowy.utils package io.github.wulkanowy.utils
import android.content.Context
import timber.log.Timber import timber.log.Timber
open class TimberTreeNoOp : Timber.Tree() { open class TimberTreeNoOp : Timber.Tree() {

View File

@ -0,0 +1,94 @@
<!doctype html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>%SUBJECT% | Wulkanowy</title>
<style>
@page {
margin: 2.5cm;
size: A4;
}
body {
margin: 0;
font-family: sans-serif;
}
.title {
line-height: 1.5;
letter-spacing: 1pt;
font-size: 24pt;
font-weight: 200;
margin: 0 0 0.5cm;
}
.info {
margin: 0.5cm 0;
}
.info div {
font-size: 14pt;
font-weight: 400;
margin: 0.5cm 0;
}
h4 {
font-weight: 200;
text-transform: uppercase;
letter-spacing: 1pt;
font-size: 10pt;
margin: 0;
margin-bottom: 0.25cm;
font-family: sans-serif;
}
.content {
margin-top: 0.5cm;
font-size: 14pt;
font-weight: 400;
text-align: justify;
font-family: serif;
line-height: 1.5;
}
.content p {
page-break-after: auto;
page-break-inside: auto;
margin-bottom: 0.6cm;
}
.footer {
font-size: 11pt;
font-weight: 200;
display: flex;
align-items: center;
color: rgba(0, 0, 0, 0.5)
margin: 0;
margin-bottom: 0.5cm;
}
.footer .logo {
height: 0.5cm;
width: 0.5cm;
display: block;
margin-right: 0.2cm;
}
</style>
</head>
<body>
<h1 class="title">%SUBJECT%</h1>
<hr>
<div class="info">
%INFO%
</div>
<div class="footer">
<img src="wulkanowy-logo-black.svg" class="logo">
Wulkanowy Dzienniczek
</div>
<hr>
<div class="content">
<h4>Treść wiadomości</h4>
%CONTENT%
</div>
</body>
</html>

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 1024 1024"
xml:space="preserve"
width="1024"
height="1024"><metadata
id="metadata15"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs13" /><style
type="text/css"
id="style2">
.st0{fill:#D32F2F;}
.st1{fill:#AD2A2A;}
.st2{fill:#FFFFFF;}
</style><g
id="layer4"
style="display:none;fill:#808080"><rect
id="XMLID_57_"
x="0"
y="0"
class="st0"
width="3584"
height="1024"
style="display:inline;fill:#808080;stroke-width:1.02195609" /></g><g
id="layer3"
style="display:none;fill:#808080"><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="M 3046.8164,390.66602 3134.3164,542 v 91.33398 L 3524.9824,1024 H 3584 V 732.18359 L 3242.4824,390.66602 h -23.666 l -53.0352,94.63086 -94.6308,-94.63086 z"
id="path18992" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="m 2746.9824,390.66602 62,242.66796 L 3199.6484,1024 H 3584 V 940.68359 L 3033.9824,390.66602 h -21 l -21.9043,90.92773 -90.9277,-90.92773 h -18.5 l -25.4043,88.26367 -88.2637,-88.26367 z"
id="path18990" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="m 2620.8164,387.33398 c -18.6667,0 -35.1667,4.60982 -49.5,13.83204 -14.3333,9.11111 -25.4451,22.22287 -33.334,39.33398 -7.7778,17 -11.666,36.5549 -11.666,58.66602 v 25 c 0,34.44444 8.7216,61.83463 26.166,82.16796 L 2970.1484,1024 h 323.168 l -623.166,-623.16602 c -14.2222,-9 -30.6673,-13.5 -49.334,-13.5 z"
id="path18988" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="M 2293.4824,390.66602 V 633.33398 L 2684.1484,1024 h 423.336 l -633.334,-633.33398 h -20.334 v 139.66601 l -139.666,-139.66601 z"
id="path18984" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="M 1864.8164,390.66602 V 633.33398 L 2255.4824,1024 h 413.334 l -633.334,-633.33398 h -25.832 l -60.584,63.75 -63.75,-63.75 z"
id="path18978" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="M 1684.8164,390.66602 V 633.33398 L 2075.4824,1024 h 263.334 l -633.334,-633.33398 z"
id="path18976" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="m 1133.6504,390.66602 62,242.66796 L 1586.3164,1024 h 467.668 l -633.334,-633.33398 h -21 l -21.9043,90.92773 -90.9277,-90.92773 h -18.5 l -25.4043,88.26367 -88.2637,-88.26367 z"
id="path19059" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="m 1456.4824,390.66602 v 167.16796 c 0.5556,24.66667 8.5007,44 23.834,58 L 1888.4824,1024 h 372.168 l -633.334,-633.33398 h -20.666 V 520.5 l -129.834,-129.83398 z"
id="path18966" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="M 2146.3164,390.66602 2054.4824,633.33398 2445.1484,1024 h 354.002 l -633.334,-633.33398 z"
id="path18982" /><path
style="display:inline;fill:#808080;stroke-width:0.78179646"
d="M 637.15234,214.95703 487.75,364.35742 466.01562,386.0918 c 0.31273,0.31271 0.54872,0.54666 0.70508,0.85937 0.0782,0.23454 0.23432,0.54671 0.3125,0.78125 0.31272,0.54726 0.47071,1.17339 0.47071,1.79883 0.0782,0.54726 -0.0799,1.01725 -0.31446,1.48633 -0.23454,0.54725 -0.70285,1.40597 -1.09375,1.79687 l 150.8086,149.71485 -23.68946,23.6875 -12.74414,-12.74219 -13.44726,-13.44727 -78.80469,-78.80664 -11.17969,-11.17968 -7.5039,-7.50391 -35.41602,-35.17969 -3.08984,-0.98047 -4.33594,4.26367 v 0.46876 c 0,7.34888 0.38998,15.00865 -1.48633,22.20117 -0.85998,3.28355 -2.34444,6.25595 -4.14258,8.91406 -0.15636,0.15636 -0.23627,0.23426 -0.31445,0.39062 -1.87631,2.57993 -4.06471,4.84619 -6.48828,6.95704 -5.3944,4.53442 -11.25752,8.67896 -17.27734,12.50976 -0.15637,0.0782 -0.23427,0.1562 -0.39063,0.23438 -2.11085,1.40723 -4.3012,2.7354 -6.49023,4.06445 -8.91248,5.39439 -18.37192,10.08772 -28.37891,13.13672 -1.25087,0.31272 -2.42317,-0.001 -3.36133,-0.70508 l -6.01953,5.94141 c 1.25087,0.62543 2.03136,1.87776 1.875,3.51953 -10e-6,0.15636 -0.0762,0.23231 -0.0762,0.38867 0,0.0782 -0.0781,0.23628 -0.0781,0.31445 -1.32905,4.45624 -2.34505,8.98897 -3.2832,13.60156 -0.15636,0.70363 -0.23622,1.33154 -0.39258,2.03516 -0.85997,4.37806 -1.64209,8.83288 -2.3457,13.21094 0.23453,5.3944 0.39263,11.0234 0.31445,16.65234 v 0.39258 c -0.0782,7.66161 -0.78373,15.32114 -2.8164,22.51367 -2.26721,8.28704 -6.64376,15.63728 -10.55274,23.22071 -0.0782,0.15636 -0.15815,0.23426 -0.23633,0.39062 -1.25088,2.42357 -2.49924,4.92399 -3.59375,7.50391 -4.84714,11.33605 -7.42749,23.92328 -10.55468,35.88476 -0.23454,0.70362 -0.39046,1.48578 -0.625,2.26758 0,0.15636 -0.0801,0.23427 -0.0801,0.39063 -2.97082,11.10151 -6.09819,22.28173 -10.94532,32.75781 -1.40724,2.97082 -2.81531,5.86322 -4.3789,8.75586 -0.15636,0.23454 -0.23231,0.46858 -0.38867,0.70312 -0.62544,1.09451 -1.25152,2.26871 -1.87696,3.44141 -0.0782,0.15636 -0.15619,0.23426 -0.23437,0.39062 -3.51809,6.25438 -7.27098,12.43118 -10.78906,18.68555 -5.0035,8.8343 -8.99075,18.13635 -13.83789,27.04883 -0.0782,0.15636 -0.1562,0.23426 -0.23438,0.39062 -0.70362,1.32905 -1.48579,2.65728 -2.26758,3.98633 -5.0035,8.20887 -10.63256,16.0279 -16.57422,23.61133 -0.15635,0.15636 -0.23426,0.3124 -0.39062,0.46875 -0.7818,1.01634 -1.48578,1.95443 -2.26758,2.89258 -3.90898,4.92532 -7.97378,9.85009 -11.96094,14.77539 -0.0782,0.15637 -0.23432,0.23622 -0.3125,0.39258 -8.75612,10.71061 -17.35628,21.49761 -24.54883,33.30273 0,0.70362 -0.15602,1.33159 -0.46874,1.95703 -1.25087,2.42357 -2.65734,4.68971 -3.90821,7.11328 -0.0782,0.15636 0.62511,1.24989 0.46875,1.40625 L 429.86133,1024 H 1463.0215 L 661.85547,222.92969 c -0.93816,2.11087 -5.23681,1.40935 -7.34766,-0.23242 -1.71995,-1.32906 -3.12603,-3.05147 -4.45508,-4.84961 -0.62544,-0.31271 -1.25168,-0.62288 -1.64257,-0.85743 -2.89265,-1.40723 -6.09933,-1.48632 -9.30469,-1.48632 -0.7818,-0.0782 -1.40588,-0.23416 -1.95313,-0.54688 z m -206.12304,191.41992 0.11914,-0.11523 -0.23438,0.0781 z"
id="XMLID_64_" /></g><g
id="layer2"
style="display:inline;fill:#000000;fill-opacity:0.49803922"><path
id="XMLID_42_"
d="m 295.17362,965.05417 c 1.0692,3.47527 0.5346,7.21786 -1.3367,10.29214 l -25.7972,41.83679 c -2.5396,4.1436 -7.2178,6.8169 -12.297,6.8169 H 14.345318 C 3.1176178,1024 -3.6991822,1012.2376 2.3157178,1003.4158 L 157.76692,774.44928 c 0.9356,-1.33663 1.4704,-2.80694 1.8713,-4.27723 l 71.2428,-304.21933 c 0.8021,-3.60893 3.2081,-6.6832 6.6833,-8.55449 l 96.5054,-52.93096 c 3.4753,-1.8713 5.8812,-4.94557 6.6832,-8.68816 l 12.9654,-56.53988 c 2.6733,-11.76242 19.5151,-14.30205 26.1981,-4.00991 l 4.6783,7.48519 c 2.0049,3.20793 2.5396,7.21785 1.2031,10.82678 l -87.9511,254.22895 c -0.6683,2.00497 -0.9355,4.1436 -0.5346,6.28223 l 21.9209,121.63426 c 0.401,2.40595 0.1334,4.94556 -0.9357,7.21785 l -52.2625,117.357 c -1.203,2.80696 -1.4704,5.88123 -0.5347,8.68817 z M 1009.7413,1024 H 843.46322 c -4.8117,0 -9.2228,-2.4059 -11.8959,-6.1485 L 719.69042,860.52891 c -0.6683,-1.0693 -1.3366,-2.13861 -1.7375,-3.3416 l -55.4707,-162.00078 c -1.0692,-3.20793 -3.6088,-6.01489 -6.8169,-7.61886 l -135.8026,-68.56965 c -3.7426,-1.87127 -6.4159,-5.34655 -7.2179,-9.22281 l -20.0495,-99.44603 c -0.2674,-1.60396 -0.9357,-3.20793 -2.005,-4.67824 l -46.1141,-67.76766 c -2.5396,-3.74259 -2.9405,-8.28717 -1.0693,-12.2971 l 28.0694,-60.01513 c 2.1387,-4.54457 6.817,-7.61886 12.1634,-7.88619 l 52.129,-3.07427 c 3.0742,-0.1337 5.8812,-1.20296 8.1536,-3.07427 l 38.3615,-29.80707 c 7.2178,-5.61388 18.1784,-3.20794 22.0546,4.67824 l 132.1937,268.93201 c 0.5346,1.20297 0.9357,2.40595 1.2029,3.60894 l 16.3072,108.13418 c 0.4009,2.53963 1.4701,4.8119 3.2079,6.6832 l 263.31808,288.17958 c 7.7525,8.5545 1.203,22.0546 -10.8269,22.0546 z M 363.20852,182.58501 c 0,-30.60907 19.3812,-56.94088 47.1834,-69.23798 -2.005,-3.3416 -3.2079,-6.95052 -3.2079,-10.82678 0,-14.836705 17.109,-26.866465 38.0942,-26.866465 0.5346,0 0.9356,0 1.4704,0 8.688,-14.43572 25.2624,-24.19318 44.2426,-24.19318 1.3367,0 2.6733,0 4.01,0.1337 1.7377,0.13369 3.4753,-0.66833 4.4109,-2.00497 14.0347,-21.38624 49.5894,-36.62394 91.159,-36.62394 15.3712,0 29.9406,2.13863 42.906,5.74756 3.0744,-5.07924 9.8911,-8.5545 17.7773,-8.5545 8.9556,0 16.5744,4.54458 18.8466,10.82678 10.9606,-12.69809 29.5398,-20.98524 50.6587,-20.98524 33.6834,0 60.9508,21.25257 60.9508,47.45072 0,3.20793 -0.401,6.2822 -1.203,9.35647 -0.5346,2.13864 0.6683,4.27725 2.9407,5.07924 21.5199,7.88618 36.0893,22.85655 36.0893,39.965535 0,19.51495 -18.8466,36.22296 -45.4458,42.77249 -2.1387,0.53466 -3.4753,2.40595 -3.4753,4.41092 0,0.1337 0,0.26731 0,0.40098 0,15.10404 -14.9704,27.5348 -34.218,28.87144 0.1333,0.66833 0.1333,1.33663 0.1333,2.13862 0,29.00509 -55.2031,52.3963 -123.2382,52.3963 -14.7029,0 -28.7377,-1.06932 -41.7031,-3.07427 0,0.26733 0,0.40099 0,0.66832 0,12.02975 -15.5051,21.78723 -34.4854,21.78723 -1.0692,0 -2.0049,0 -2.9405,-0.13369 1.3367,2.9406 2.005,6.01487 2.005,9.22281 0,18.71296 -23.6586,33.81699 -52.9311,33.81699 -3.2079,0 -6.2821,-0.1337 -9.3563,-0.53466 -2.4061,-0.26731 -4.6783,1.20299 -5.2131,3.47529 -2.5396,9.35647 -10.693,16.17333 -20.4504,16.17333 -11.7625,0 -21.119,-10.0248 -21.119,-22.32189 0,-5.74755 2.005,-10.96045 5.3466,-14.83671 1.203,-1.33663 1.6039,-3.20793 0.8019,-4.81191 -1.8713,-3.47526 -2.6733,-7.08419 -2.6733,-10.96044 v 0 c 0,-2.13862 -1.7376,-3.87626 -3.8763,-4.4109 -36.2228,-8.01985 -63.4903,-38.22792 -63.4903,-74.3172 z m 306.8925,726.06294 c 0.5348,1.60398 0.6683,3.20796 0.6683,4.94558 l -7.7525,97.97577 c -0.5346,6.9505 -6.6832,12.4307 -14.1683,12.4307 h -250.219 c -5.3466,0 -10.2921,-3.0743 -12.6982,-7.4852 l -41.3021,-76.72312 c -0.2673,-0.401 -0.401,-0.80199 -0.5347,-1.20298 l -38.8962,-94.23313 c -1.4702,-3.3416 -1.203,-7.21785 0.4011,-10.42581 l 64.5596,-126.31249 c 1.604,-3.07427 1.8712,-6.6832 0.6683,-9.89114 l -31.5447,-87.41626 c -1.0693,-3.07428 -0.9356,-6.54955 0.4011,-9.49015 l 52.6636,-112.14412 c 5.3464,-11.22778 22.8565,-10.29212 26.5991,1.47031 l 16.4407,51.05965 50.124,134.19868 c 1.3367,3.7426 4.5446,6.6832 8.5545,8.01985 l 106.9312,36.49027 c 4.1435,1.47032 7.3516,4.54458 8.6881,8.42084 z"
style="fill:#000000;stroke-width:0.78179646;fill-opacity:0.49803922" /><g
aria-label="WULKANOWY"
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:0.49803922;stroke:none"
id="text4752" /></g></svg>

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,17 +1,15 @@
package io.github.wulkanowy.data package io.github.wulkanowy.data
import android.app.AlarmManager
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.AssetManager import android.content.res.AssetManager
import android.content.res.Resources import android.content.res.Resources
import androidx.core.content.getSystemService
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.WalledGardenInternetObservingStrategy
import com.chuckerteam.chucker.api.ChuckerCollector import com.chuckerteam.chucker.api.ChuckerCollector
import com.chuckerteam.chucker.api.ChuckerInterceptor import com.chuckerteam.chucker.api.ChuckerInterceptor
import com.chuckerteam.chucker.api.RetentionManager import com.chuckerteam.chucker.api.RetentionManager
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.WalledGardenInternetObservingStrategy
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase

View File

@ -68,6 +68,7 @@ import io.github.wulkanowy.data.db.migrations.Migration22
import io.github.wulkanowy.data.db.migrations.Migration23 import io.github.wulkanowy.data.db.migrations.Migration23
import io.github.wulkanowy.data.db.migrations.Migration24 import io.github.wulkanowy.data.db.migrations.Migration24
import io.github.wulkanowy.data.db.migrations.Migration25 import io.github.wulkanowy.data.db.migrations.Migration25
import io.github.wulkanowy.data.db.migrations.Migration26
import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration4 import io.github.wulkanowy.data.db.migrations.Migration4
import io.github.wulkanowy.data.db.migrations.Migration5 import io.github.wulkanowy.data.db.migrations.Migration5
@ -110,7 +111,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
companion object { companion object {
const val VERSION_SCHEMA = 25 const val VERSION_SCHEMA = 26
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> { fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
return arrayOf( return arrayOf(
@ -137,7 +138,8 @@ abstract class AppDatabase : RoomDatabase() {
Migration22(), Migration22(),
Migration23(), Migration23(),
Migration24(), Migration24(),
Migration25() Migration25(),
Migration26()
) )
} }

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.LocalDateTime
@Entity(tableName = "GradesSummary") @Entity(tableName = "GradesSummary")
data class GradeSummary( data class GradeSummary(
@ -36,4 +37,16 @@ data class GradeSummary(
) { ) {
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
var id: Long = 0 var id: Long = 0
@ColumnInfo(name = "is_predicted_grade_notified")
var isPredictedGradeNotified: Boolean = true
@ColumnInfo(name = "is_final_grade_notified")
var isFinalGradeNotified: Boolean = true
@ColumnInfo(name = "predicted_grade_last_change")
var predictedGradeLastChange: LocalDateTime = LocalDateTime.now()
@ColumnInfo(name = "final_grade_last_change")
var finalGradeLastChange: LocalDateTime = LocalDateTime.now()
} }

View File

@ -0,0 +1,14 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration26 : Migration(25, 26) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE GradesSummary ADD COLUMN is_predicted_grade_notified INTEGER NOT NULL DEFAULT 1")
database.execSQL("ALTER TABLE GradesSummary ADD COLUMN is_final_grade_notified INTEGER NOT NULL DEFAULT 1")
database.execSQL("ALTER TABLE GradesSummary ADD COLUMN predicted_grade_last_change INTEGER NOT NULL DEFAULT 0")
database.execSQL("ALTER TABLE GradesSummary ADD COLUMN final_grade_last_change INTEGER NOT NULL DEFAULT 0")
}
}

View File

@ -10,7 +10,7 @@ import javax.inject.Singleton
@Singleton @Singleton
class AppCreatorRepository @Inject constructor(private val assets: AssetManager) { class AppCreatorRepository @Inject constructor(private val assets: AssetManager) {
fun getAppCreators(): Single<List<Contributor>> { fun getAppCreators(): Single<List<Contributor>> {
return Single.fromCallable<List<Contributor>> { return Single.fromCallable {
Gson().fromJson( Gson().fromJson(
assets.open("contributors.json").bufferedReader().use { it.readText() }, assets.open("contributors.json").bufferedReader().use { it.readText() },
Array<Contributor>::class.java Array<Contributor>::class.java

View File

@ -13,7 +13,7 @@ class ExamLocal @Inject constructor(private val examDb: ExamDao) {
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Exam>> { fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Exam>> {
return examDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate) return examDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
.filter { !it.isEmpty() } .filter { it.isNotEmpty() }
} }
fun saveExams(exams: List<Exam>) { fun saveExams(exams: List<Exam>) {

View File

@ -27,6 +27,10 @@ class GradeLocal @Inject constructor(
gradeDb.updateAll(grades) gradeDb.updateAll(grades)
} }
fun updateGradesSummary(gradesSummary: List<GradeSummary>) {
gradeSummaryDb.updateAll(gradesSummary)
}
fun getGradesDetails(semester: Semester): Maybe<List<Grade>> { fun getGradesDetails(semester: Semester): Maybe<List<Grade>> {
return gradeDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() } return gradeDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() }
} }

View File

@ -9,6 +9,7 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Completable import io.reactivex.Completable
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDateTime
import java.net.UnknownHostException import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -43,7 +44,31 @@ class GradeRepository @Inject constructor(
local.getGradesSummary(semester).toSingle(emptyList()) local.getGradesSummary(semester).toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { old ->
local.deleteGradesSummary(old.uniqueSubtract(newSummary)) local.deleteGradesSummary(old.uniqueSubtract(newSummary))
local.saveGradesSummary(newSummary.uniqueSubtract(old)) local.saveGradesSummary(newSummary.uniqueSubtract(old)
.onEach { summary ->
val oldSummary = old.find { oldSummary -> oldSummary.subject == summary.subject }
summary.isPredictedGradeNotified = when {
summary.predictedGrade.isEmpty() -> true
notify && oldSummary?.predictedGrade != summary.predictedGrade -> false
else -> true
}
summary.isFinalGradeNotified = when {
summary.finalGrade.isEmpty() -> true
notify && oldSummary?.finalGrade != summary.finalGrade -> false
else -> true
}
summary.predictedGradeLastChange = when {
oldSummary == null -> LocalDateTime.now()
summary.predictedGrade != oldSummary.predictedGrade -> LocalDateTime.now()
else -> oldSummary.predictedGradeLastChange
}
summary.finalGradeLastChange = when {
oldSummary == null -> LocalDateTime.now()
summary.finalGrade != oldSummary.finalGrade -> LocalDateTime.now()
else -> oldSummary.finalGradeLastChange
}
})
} }
} }
}.flatMap { }.flatMap {
@ -63,6 +88,14 @@ class GradeRepository @Inject constructor(
return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isNotified } }.toSingle(emptyList()) return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isNotified } }.toSingle(emptyList())
} }
fun getNotNotifiedPredictedGrades(semester: Semester): Single<List<GradeSummary>> {
return local.getGradesSummary(semester).map { it.filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified } }.toSingle(emptyList())
}
fun getNotNotifiedFinalGrades(semester: Semester): Single<List<GradeSummary>> {
return local.getGradesSummary(semester).map { it.filter { gradeSummary -> !gradeSummary.isFinalGradeNotified } }.toSingle(emptyList())
}
fun updateGrade(grade: Grade): Completable { fun updateGrade(grade: Grade): Completable {
return Completable.fromCallable { local.updateGrades(listOf(grade)) } return Completable.fromCallable { local.updateGrades(listOf(grade)) }
} }
@ -70,4 +103,8 @@ class GradeRepository @Inject constructor(
fun updateGrades(grades: List<Grade>): Completable { fun updateGrades(grades: List<Grade>): Completable {
return Completable.fromCallable { local.updateGrades(grades) } return Completable.fromCallable { local.updateGrades(grades) }
} }
fun updateGradesSummary(gradesSummary: List<GradeSummary>): Completable {
return Completable.fromCallable { local.updateGradesSummary(gradesSummary) }
}
} }

View File

@ -12,7 +12,7 @@ import javax.inject.Singleton
class RecipientLocal @Inject constructor(private val recipientDb: RecipientDao) { class RecipientLocal @Inject constructor(private val recipientDb: RecipientDao) {
fun getRecipients(student: Student, role: Int, unit: ReportingUnit): Maybe<List<Recipient>> { fun getRecipients(student: Student, role: Int, unit: ReportingUnit): Maybe<List<Recipient>> {
return recipientDb.load(student.studentId, role, unit.realId).filter { !it.isEmpty() } return recipientDb.load(student.studentId, role, unit.realId).filter { it.isNotEmpty() }
} }
fun saveRecipients(recipients: List<Recipient>): List<Long> { fun saveRecipients(recipients: List<Recipient>): List<Long> {

View File

@ -11,7 +11,7 @@ import javax.inject.Singleton
class ReportingUnitLocal @Inject constructor(private val reportingUnitDb: ReportingUnitDao) { class ReportingUnitLocal @Inject constructor(private val reportingUnitDb: ReportingUnitDao) {
fun getReportingUnits(student: Student): Maybe<List<ReportingUnit>> { fun getReportingUnits(student: Student): Maybe<List<ReportingUnit>> {
return reportingUnitDb.load(student.studentId).filter { !it.isEmpty() } return reportingUnitDb.load(student.studentId).filter { it.isNotEmpty() }
} }
fun getReportingUnit(student: Student, unitId: Int): Maybe<ReportingUnit> { fun getReportingUnit(student: Student, unitId: Int): Maybe<ReportingUnit> {

View File

@ -14,7 +14,7 @@ class StudentRemote @Inject constructor(private val sdk: Sdk) {
private fun mapStudents(students: List<SdkStudent>, email: String, password: String): List<Student> { private fun mapStudents(students: List<SdkStudent>, email: String, password: String): List<Student> {
return students.map { student -> return students.map { student ->
Student( Student(
email = email, email = email.ifBlank { student.email },
password = password, password = password,
isParent = student.isParent, isParent = student.isParent,
symbol = student.symbol, symbol = student.symbol,

View File

@ -9,7 +9,7 @@ import javax.inject.Inject
class GradeStatisticsWork @Inject constructor(private val gradeStatisticsRepository: GradeStatisticsRepository) : Work { class GradeStatisticsWork @Inject constructor(private val gradeStatisticsRepository: GradeStatisticsRepository) : Work {
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return gradeStatisticsRepository.getGradesStatistics(student, semester, "Wszystkie", false, true) return gradeStatisticsRepository.getGradesStatistics(student, semester, "Wszystkie", false, forceRefresh = true)
.ignoreElement() .ignoreElement()
} }
} }

View File

@ -9,6 +9,7 @@ import androidx.core.app.NotificationCompat.PRIORITY_HIGH
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.grade.GradeRepository import io.github.wulkanowy.data.repositories.grade.GradeRepository
@ -30,17 +31,21 @@ class GradeWork @Inject constructor(
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return gradeRepository.getGrades(student, semester, true, preferencesRepository.isNotificationsEnable) return gradeRepository.getGrades(student, semester, true, preferencesRepository.isNotificationsEnable)
.flatMap { gradeRepository.getNotNotifiedGrades(semester) } .ignoreElement()
.flatMapCompletable { .concatWith(Completable.concatArray(gradeRepository.getNotNotifiedGrades(semester).flatMapCompletable {
if (it.isNotEmpty()) notify(it) if (it.isNotEmpty()) notifyDetails(it)
gradeRepository.updateGrades(it.onEach { grade -> grade.isNotified = true }) gradeRepository.updateGrades(it.onEach { grade -> grade.isNotified = true })
} }, gradeRepository.getNotNotifiedPredictedGrades(semester).flatMapCompletable {
if (it.isNotEmpty()) notifyPredicted(it)
gradeRepository.updateGradesSummary(it.onEach { grade -> grade.isPredictedGradeNotified = true })
}, gradeRepository.getNotNotifiedFinalGrades(semester).flatMapCompletable {
if (it.isNotEmpty()) notifyFinal(it)
gradeRepository.updateGradesSummary(it.onEach { grade -> grade.isFinalGradeNotified = true })
}))
} }
private fun notify(grades: List<Grade>) { private fun getNotificationBuilder(): NotificationCompat.Builder {
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewGradesChannel.CHANNEL_ID) return NotificationCompat.Builder(context, NewGradesChannel.CHANNEL_ID)
.setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items, grades.size, grades.size))
.setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items, grades.size, grades.size))
.setSmallIcon(R.drawable.ic_stat_grade) .setSmallIcon(R.drawable.ic_stat_grade)
.setAutoCancel(true) .setAutoCancel(true)
.setPriority(PRIORITY_HIGH) .setPriority(PRIORITY_HIGH)
@ -49,6 +54,12 @@ class GradeWork @Inject constructor(
.setContentIntent( .setContentIntent(
PendingIntent.getActivity(context, MainView.Section.GRADE.id, PendingIntent.getActivity(context, MainView.Section.GRADE.id,
MainActivity.getStartIntent(context, MainView.Section.GRADE, true), FLAG_UPDATE_CURRENT)) MainActivity.getStartIntent(context, MainView.Section.GRADE, true), FLAG_UPDATE_CURRENT))
}
private fun notifyDetails(grades: List<Grade>) {
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), getNotificationBuilder()
.setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items, grades.size, grades.size))
.setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items, grades.size, grades.size))
.setStyle(NotificationCompat.InboxStyle().run { .setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, grades.size, grades.size)) setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, grades.size, grades.size))
grades.forEach { addLine("${it.subject}: ${it.entry}") } grades.forEach { addLine("${it.subject}: ${it.entry}") }
@ -57,4 +68,30 @@ class GradeWork @Inject constructor(
.build() .build()
) )
} }
private fun notifyPredicted(gradesSummary: List<GradeSummary>) {
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), getNotificationBuilder()
.setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items_predicted, gradesSummary.size, gradesSummary.size))
.setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items_predicted, gradesSummary.size, gradesSummary.size))
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, gradesSummary.size, gradesSummary.size))
gradesSummary.forEach { addLine("${it.subject}: ${it.predictedGrade}") }
this
})
.build()
)
}
private fun notifyFinal(gradesSummary: List<GradeSummary>) {
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), getNotificationBuilder()
.setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items_final, gradesSummary.size, gradesSummary.size))
.setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items_final, gradesSummary.size, gradesSummary.size))
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, gradesSummary.size, gradesSummary.size))
gradesSummary.forEach { addLine("${it.subject}: ${it.finalGrade}") }
this
})
.build()
)
}
} }

View File

@ -25,9 +25,9 @@ class LogViewerPresenter @Inject constructor(
disposable.add(loggerRepository.getLogFiles() disposable.add(loggerRepository.getLogFiles()
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.subscribe({ .subscribe({ files ->
Timber.i("Loading logs files result: ${it.joinToString { it.name }}") Timber.i("Loading logs files result: ${files.joinToString { it.name }}")
view?.shareLogs(it) view?.shareLogs(files)
}, { }, {
Timber.i("Loading logs files result: An exception occurred") Timber.i("Loading logs files result: An exception occurred")
errorHandler.dispatch(it) errorHandler.dispatch(it)

View File

@ -0,0 +1,3 @@
package io.github.wulkanowy.ui.modules.account
data class Account(val email: String, val isParent: Boolean)

View File

@ -3,33 +3,72 @@ package io.github.wulkanowy.ui.modules.account
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.PorterDuff import android.graphics.PorterDuff
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.databinding.HeaderAccountBinding
import io.github.wulkanowy.databinding.ItemAccountBinding import io.github.wulkanowy.databinding.ItemAccountBinding
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.getThemeAttrColor
import javax.inject.Inject import javax.inject.Inject
class AccountAdapter @Inject constructor() : RecyclerView.Adapter<AccountAdapter.ItemViewHolder>() { class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var items = emptyList<Student>() var items = emptyList<AccountItem<*>>()
var onClickListener: (Student) -> Unit = {} var onClickListener: (Student) -> Unit = {}
override fun getItemCount() = items.size override fun getItemCount() = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( override fun getItemViewType(position: Int) = items[position].viewType.id
ItemAccountBinding.inflate(LayoutInflater.from(parent.context), parent, false)
) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
AccountItem.ViewType.HEADER.id -> HeaderViewHolder(HeaderAccountBinding.inflate(inflater, parent, false))
AccountItem.ViewType.ITEM.id -> ItemViewHolder(ItemAccountBinding.inflate(inflater, parent, false))
else -> throw IllegalStateException()
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is HeaderViewHolder -> bindHeaderViewHolder(holder.binding, items[position].value as Account)
is ItemViewHolder -> bindItemViewHolder(holder.binding, items[position].value as Student)
}
}
private fun bindHeaderViewHolder(binding: HeaderAccountBinding, account: Account) {
with(binding) {
accountHeaderEmail.text = account.email
accountHeaderType.setText(if (account.isParent) R.string.account_type_parent else R.string.account_type_student)
}
}
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { private fun bindItemViewHolder(binding: ItemAccountBinding, student: Student) {
val student = items[position] with(binding) {
with(holder.binding) {
accountItemName.text = "${student.studentName} ${student.className}" accountItemName.text = "${student.studentName} ${student.className}"
accountItemSchool.text = student.schoolName accountItemSchool.text = student.schoolName
with(accountItemLoginMode) {
visibility = when (Sdk.Mode.valueOf(student.loginMode)) {
Sdk.Mode.API -> {
setText(R.string.account_login_mobile_api)
VISIBLE
}
Sdk.Mode.HYBRID -> {
setText(R.string.account_login_hybrid)
VISIBLE
}
Sdk.Mode.SCRAPPER -> {
GONE
}
}
}
with(accountItemImage) { with(accountItemImage) {
val colorImage = if (student.isCurrent) context.getThemeAttrColor(R.attr.colorPrimary) val colorImage = if (student.isCurrent) context.getThemeAttrColor(R.attr.colorPrimary)
@ -42,5 +81,8 @@ class AccountAdapter @Inject constructor() : RecyclerView.Adapter<AccountAdapter
} }
} }
class HeaderViewHolder(val binding: HeaderAccountBinding) :
RecyclerView.ViewHolder(binding.root)
class ItemViewHolder(val binding: ItemAccountBinding) : RecyclerView.ViewHolder(binding.root) class ItemViewHolder(val binding: ItemAccountBinding) : RecyclerView.ViewHolder(binding.root)
} }

View File

@ -9,7 +9,6 @@ import android.widget.Toast.LENGTH_LONG
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.databinding.DialogAccountBinding import io.github.wulkanowy.databinding.DialogAccountBinding
import io.github.wulkanowy.ui.base.BaseDialogFragment import io.github.wulkanowy.ui.base.BaseDialogFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.login.LoginActivity
@ -54,7 +53,7 @@ class AccountDialog : BaseDialogFragment<DialogAccountBinding>(), AccountView {
} }
} }
override fun updateData(data: List<Student>) { override fun updateData(data: List<AccountItem<*>>) {
with(accountAdapter) { with(accountAdapter) {
items = data items = data
notifyDataSetChanged() notifyDataSetChanged()

View File

@ -0,0 +1,9 @@
package io.github.wulkanowy.ui.modules.account
data class AccountItem<out T>(val value: T, val viewType: ViewType) {
enum class ViewType(val id: Int) {
HEADER(1),
ITEM(2)
}
}

View File

@ -83,11 +83,20 @@ class AccountPresenter @Inject constructor(
} }
} }
private fun createAccountItems(items: List<Student>): List<AccountItem<*>> {
return items.groupBy { Account(it.email, it.isParent) }.map { (account, students) ->
listOf(AccountItem(account, AccountItem.ViewType.HEADER)) + students.map { student ->
AccountItem(student, AccountItem.ViewType.ITEM)
}
}.flatten()
}
private fun loadData() { private fun loadData() {
Timber.i("Loading account data started") Timber.i("Loading account data started")
disposable.add(studentRepository.getSavedStudents(false) disposable.add(studentRepository.getSavedStudents(false)
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.map { createAccountItems(it) }
.subscribe({ .subscribe({
Timber.i("Loading account result: Success") Timber.i("Loading account result: Success")
view?.updateData(it) view?.updateData(it)

View File

@ -1,13 +1,12 @@
package io.github.wulkanowy.ui.modules.account package io.github.wulkanowy.ui.modules.account
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.ui.base.BaseView import io.github.wulkanowy.ui.base.BaseView
interface AccountView : BaseView { interface AccountView : BaseView {
fun initView() fun initView()
fun updateData(data: List<Student>) fun updateData(data: List<AccountItem<*>>)
fun dismissView() fun dismissView()

View File

@ -20,7 +20,6 @@ import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.now import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.ofEpochDay import org.threeten.bp.LocalDate.ofEpochDay
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject import javax.inject.Inject
class AttendancePresenter @Inject constructor( class AttendancePresenter @Inject constructor(

View File

@ -98,7 +98,7 @@ class GradeAverageProvider @Inject constructor(
} }
private fun List<GradeSummary>.emulateEmptySummaries(student: Student, semester: Semester, grades: List<Pair<String, List<Grade>>>, calcAverage: Boolean): List<GradeSummary> { private fun List<GradeSummary>.emulateEmptySummaries(student: Student, semester: Semester, grades: List<Pair<String, List<Grade>>>, calcAverage: Boolean): List<GradeSummary> {
if (isNotEmpty() && size == grades.size) return this if (isNotEmpty() && size > grades.size) return this
return grades.mapIndexed { i, (subject, details) -> return grades.mapIndexed { i, (subject, details) ->
singleOrNull { it.subject == subject }?.let { return@mapIndexed it } singleOrNull { it.subject == subject }?.let { return@mapIndexed it }

View File

@ -63,7 +63,7 @@ class MessagePreviewAdapter @Inject constructor() :
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun bindMessage(holder: MessageViewHolder, message: Message) { private fun bindMessage(holder: MessageViewHolder, message: Message) {
with(holder.binding) { with(holder.binding) {
messagePreviewSubject.text = if (message.subject.isNotBlank()) message.subject else root.context.getString(R.string.message_no_subject) messagePreviewSubject.text = message.subject.ifBlank { root.context.getString(R.string.message_no_subject) }
messagePreviewDate.text = root.context.getString(R.string.message_date, message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) messagePreviewDate.text = root.context.getString(R.string.message_date, message.date.toFormattedString("yyyy-MM-dd HH:mm:ss"))
messagePreviewContent.text = message.content messagePreviewContent.text = message.content
messagePreviewAuthor.text = if (message.folderId == MessageFolder.SENT.id) "${root.context.getString(R.string.message_to)} ${message.recipient}" messagePreviewAuthor.text = if (message.folderId == MessageFolder.SENT.id) "${root.context.getString(R.string.message_to)} ${message.recipient}"

View File

@ -1,12 +1,20 @@
package io.github.wulkanowy.ui.modules.message.preview package io.github.wulkanowy.ui.modules.message.preview
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.print.PrintAttributes
import android.print.PrintManager
import android.view.Menu import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.View.GONE import android.view.View.GONE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
@ -17,6 +25,8 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.shareText
import javax.inject.Inject import javax.inject.Inject
class MessagePreviewFragment : class MessagePreviewFragment :
@ -29,18 +39,31 @@ class MessagePreviewFragment :
@Inject @Inject
lateinit var previewAdapter: MessagePreviewAdapter lateinit var previewAdapter: MessagePreviewAdapter
@Inject
lateinit var appInfo: AppInfo
private var menuReplyButton: MenuItem? = null private var menuReplyButton: MenuItem? = null
private var menuForwardButton: MenuItem? = null private var menuForwardButton: MenuItem? = null
private var menuDeleteButton: MenuItem? = null private var menuDeleteButton: MenuItem? = null
private var menuShareButton: MenuItem? = null
private var menuPrintButton: MenuItem? = null
override val titleStringId: Int override val titleStringId: Int
get() = R.string.message_title get() = R.string.message_title
override val deleteMessageSuccessString: String override val deleteMessageSuccessString: String
get() = getString(R.string.message_delete_success) get() = getString(R.string.message_delete_success)
override val messageNoSubjectString: String
get() = getString(R.string.message_no_subject)
override val printHTML: String
get() = requireContext().assets.open("message-print-page.html").bufferedReader().use { it.readText() }
companion object { companion object {
const val MESSAGE_ID_KEY = "message_id" const val MESSAGE_ID_KEY = "message_id"
@ -77,6 +100,8 @@ class MessagePreviewFragment :
menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply) menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply)
menuForwardButton = menu.findItem(R.id.messagePreviewMenuForward) menuForwardButton = menu.findItem(R.id.messagePreviewMenuForward)
menuDeleteButton = menu.findItem(R.id.messagePreviewMenuDelete) menuDeleteButton = menu.findItem(R.id.messagePreviewMenuDelete)
menuShareButton = menu.findItem(R.id.messagePreviewMenuShare)
menuPrintButton = menu.findItem(R.id.messagePreviewMenuPrint)
presenter.onCreateOptionsMenu() presenter.onCreateOptionsMenu()
} }
@ -85,6 +110,8 @@ class MessagePreviewFragment :
R.id.messagePreviewMenuReply -> presenter.onReply() R.id.messagePreviewMenuReply -> presenter.onReply()
R.id.messagePreviewMenuForward -> presenter.onForward() R.id.messagePreviewMenuForward -> presenter.onForward()
R.id.messagePreviewMenuDelete -> presenter.onMessageDelete() R.id.messagePreviewMenuDelete -> presenter.onMessageDelete()
R.id.messagePreviewMenuShare -> presenter.onShare()
R.id.messagePreviewMenuPrint -> presenter.onPrint()
else -> false else -> false
} }
} }
@ -108,6 +135,8 @@ class MessagePreviewFragment :
menuReplyButton?.isVisible = show menuReplyButton?.isVisible = show
menuForwardButton?.isVisible = show menuForwardButton?.isVisible = show
menuDeleteButton?.isVisible = show menuDeleteButton?.isVisible = show
menuShareButton?.isVisible = show
menuPrintButton?.isVisible = show && appInfo.systemVersion >= Build.VERSION_CODES.LOLLIPOP
} }
override fun setDeletedOptionsLabels() { override fun setDeletedOptionsLabels() {
@ -138,6 +167,38 @@ class MessagePreviewFragment :
context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message)) } context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message)) }
} }
override fun shareText(text: String, subject: String) {
context?.shareText(text, subject)
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun printDocument(html: String, jobName: String) {
val webView = WebView(activity)
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = false
override fun onPageFinished(view: WebView, url: String) {
createWebPrintJob(view, jobName)
}
}
webView.loadDataWithBaseURL("file:///android_asset/", html, "text/HTML", "UTF-8", null)
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun createWebPrintJob(webView: WebView, jobName: String) {
activity?.getSystemService<PrintManager>()?.let { printManager ->
val printAdapter = webView.createPrintDocumentAdapter(jobName)
printManager.print(
jobName,
printAdapter,
PrintAttributes.Builder().build()
)
}
}
override fun popView() { override fun popView() {
(activity as MainActivity).popView() (activity as MainActivity).popView()
} }

View File

@ -1,12 +1,17 @@
package io.github.wulkanowy.ui.modules.message.preview package io.github.wulkanowy.ui.modules.message.preview
import android.annotation.SuppressLint
import android.os.Build
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.repositories.message.MessageRepository import io.github.wulkanowy.data.repositories.message.MessageRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.toFormattedString
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -15,11 +20,14 @@ class MessagePreviewPresenter @Inject constructor(
errorHandler: ErrorHandler, errorHandler: ErrorHandler,
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val messageRepository: MessageRepository, private val messageRepository: MessageRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper,
private var appInfo: AppInfo
) : BasePresenter<MessagePreviewView>(errorHandler, studentRepository, schedulers) { ) : BasePresenter<MessagePreviewView>(errorHandler, studentRepository, schedulers) {
var message: Message? = null var message: Message? = null
var attachments: List<MessageAttachment>? = null
private lateinit var lastError: Throwable private lateinit var lastError: Throwable
private var retryCallback: () -> Unit = {} private var retryCallback: () -> Unit = {}
@ -56,6 +64,7 @@ class MessagePreviewPresenter @Inject constructor(
.subscribe({ message -> .subscribe({ message ->
Timber.i("Loading message ${message.message.messageId} preview result: Success ") Timber.i("Loading message ${message.message.messageId} preview result: Success ")
this@MessagePreviewPresenter.message = message.message this@MessagePreviewPresenter.message = message.message
this@MessagePreviewPresenter.attachments = message.attachments
view?.apply { view?.apply {
setMessageWithAttachment(message) setMessageWithAttachment(message)
initOptions() initOptions()
@ -87,6 +96,60 @@ class MessagePreviewPresenter @Inject constructor(
} else false } else false
} }
fun onShare(): Boolean {
message?.let {
var text = "Temat: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}\n" + when (it.sender.isNotEmpty()) {
true -> "Od: ${it.sender}\n"
false -> "Do: ${it.recipient}\n"
} + "Data: ${it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${it.content}"
attachments?.let { attachments ->
if (attachments.isNotEmpty()) {
text += "\n\nZałączniki:"
attachments.forEach { attachment ->
text += "\n${attachment.filename}: ${attachment.url}"
}
}
}
view?.shareText(text, "FW: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}")
return true
}
return false
}
@SuppressLint("NewApi")
fun onPrint(): Boolean {
if (appInfo.systemVersion < Build.VERSION_CODES.LOLLIPOP) return false
message?.let {
val dateString = it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")
val infoContent = "<div><h4>Data wysłania</h4>$dateString</div>" + when {
it.sender.isNotEmpty() -> "<div><h4>Od</h4>${it.sender}</div>"
else -> "<div><h4>Do</h4>${it.recipient}</div>"
}
val messageContent = "<p>${it.content}</p>"
.replace(Regex("[\\n\\r]{2,}"), "</p><p>")
.replace(Regex("[\\n\\r]"), "<br>")
val jobName = "Wiadomość " + when {
it.sender.isNotEmpty() -> "od ${it.sender}"
else -> "do ${it.recipient}"
} + " $dateString: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }} | Wulkanowy"
view?.apply {
val html = printHTML
.replace("%SUBJECT%", it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() })
.replace("%CONTENT%", messageContent)
.replace("%INFO%", infoContent)
printDocument(html, jobName)
}
return true
}
return false
}
private fun deleteMessage() { private fun deleteMessage() {
message?.let { message -> message?.let { message ->
disposable.add(studentRepository.getCurrentStudent() disposable.add(studentRepository.getCurrentStudent()

View File

@ -1,5 +1,7 @@
package io.github.wulkanowy.ui.modules.message.preview package io.github.wulkanowy.ui.modules.message.preview
import android.os.Build
import androidx.annotation.RequiresApi
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.ui.base.BaseView import io.github.wulkanowy.ui.base.BaseView
@ -8,6 +10,10 @@ interface MessagePreviewView : BaseView {
val deleteMessageSuccessString: String val deleteMessageSuccessString: String
val messageNoSubjectString: String
val printHTML: String
fun initView() fun initView()
fun setMessageWithAttachment(item: MessageWithAttachment) fun setMessageWithAttachment(item: MessageWithAttachment)
@ -34,5 +40,10 @@ interface MessagePreviewView : BaseView {
fun openMessageForward(message: Message?) fun openMessageForward(message: Message?)
fun shareText(text: String, subject: String)
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun printDocument(html: String, jobName: String)
fun popView() fun popView()
} }

View File

@ -4,10 +4,9 @@ import android.graphics.Typeface
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.NO_POSITION import androidx.recyclerview.widget.RecyclerView.NO_POSITION
import androidx.recyclerview.widget.SortedList
import androidx.recyclerview.widget.SortedListAdapterCallback
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.repositories.message.MessageFolder import io.github.wulkanowy.data.repositories.message.MessageFolder
@ -20,39 +19,23 @@ class MessageTabAdapter @Inject constructor() :
var onClickListener: (Message, position: Int) -> Unit = { _, _ -> } var onClickListener: (Message, position: Int) -> Unit = { _, _ -> }
private val items = SortedList(Message::class.java, object : private var items = mutableListOf<Message>()
SortedListAdapterCallback<Message>(this) {
override fun compare(item1: Message, item2: Message): Int { fun setDataItems(data: List<Message>) {
return item2.date.compareTo(item1.date) val diffResult = DiffUtil.calculateDiff(MessageTabDiffUtil(items, data))
} items = data.toMutableList()
diffResult.dispatchUpdatesTo(this)
override fun areContentsTheSame(oldItem: Message?, newItem: Message?): Boolean {
return oldItem == newItem
}
override fun areItemsTheSame(item1: Message, item2: Message): Boolean {
return item1 == item2
}
})
fun replaceAll(models: List<Message>) {
items.beginBatchedUpdates()
for (i in items.size() - 1 downTo 0) {
val model = items.get(i)
if (model !in models) {
items.remove(model)
}
}
items.addAll(models)
items.endBatchedUpdates()
} }
fun updateItem(position: Int, item: Message) { fun updateItem(position: Int, item: Message) {
items.updateItemAt(position, item) val currentItem = items[position]
items[position] = item
if (item != currentItem) {
notifyItemChanged(position)
}
} }
override fun getItemCount() = items.size() override fun getItemCount() = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
ItemMessageBinding.inflate(LayoutInflater.from(parent.context), parent, false) ItemMessageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
@ -85,4 +68,19 @@ class MessageTabAdapter @Inject constructor() :
} }
class ItemViewHolder(val binding: ItemMessageBinding) : RecyclerView.ViewHolder(binding.root) class ItemViewHolder(val binding: ItemMessageBinding) : RecyclerView.ViewHolder(binding.root)
private class MessageTabDiffUtil(private val old: List<Message>, private val new: List<Message>) :
DiffUtil.Callback() {
override fun getOldListSize(): Int = old.size
override fun getNewListSize(): Int = new.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return old[oldItemPosition].id == new[newItemPosition].id
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return old[oldItemPosition] == new[newItemPosition]
}
}
} }

View File

@ -90,7 +90,7 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
} }
override fun updateData(data: List<Message>) { override fun updateData(data: List<Message>) {
tabAdapter.replaceAll(data) tabAdapter.setDataItems(data)
} }
override fun updateItem(item: Message, position: Int) { override fun updateItem(item: Message, position: Int) {

View File

@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.message.tab package io.github.wulkanowy.ui.modules.message.tab
import android.annotation.SuppressLint
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.repositories.message.MessageFolder import io.github.wulkanowy.data.repositories.message.MessageFolder
import io.github.wulkanowy.data.repositories.message.MessageRepository import io.github.wulkanowy.data.repositories.message.MessageRepository
@ -11,8 +10,13 @@ import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
import io.reactivex.subjects.PublishSubject
import me.xdrop.fuzzywuzzy.FuzzySearch
import timber.log.Timber import timber.log.Timber
import java.util.Locale
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.pow
class MessageTabPresenter @Inject constructor( class MessageTabPresenter @Inject constructor(
schedulers: SchedulersProvider, schedulers: SchedulersProvider,
@ -31,9 +35,12 @@ class MessageTabPresenter @Inject constructor(
private var messages = emptyList<Message>() private var messages = emptyList<Message>()
private val searchQuery = PublishSubject.create<String>()
fun onAttachView(view: MessageTabView, folder: MessageFolder) { fun onAttachView(view: MessageTabView, folder: MessageFolder) {
super.onAttachView(view) super.onAttachView(view)
view.initView() view.initView()
initializeSearchStream()
errorHandler.showErrorMessage = ::showErrorViewOnError errorHandler.showErrorMessage = ::showErrorViewOnError
this.folder = folder this.folder = folder
} }
@ -76,9 +83,7 @@ class MessageTabPresenter @Inject constructor(
private fun loadData(forceRefresh: Boolean) { private fun loadData(forceRefresh: Boolean) {
Timber.i("Loading $folder message data started") Timber.i("Loading $folder message data started")
disposable.apply { disposable.add(studentRepository.getCurrentStudent()
clear()
add(studentRepository.getCurrentStudent()
.flatMap { student -> .flatMap { student ->
semesterRepository.getCurrentSemester(student) semesterRepository.getCurrentSemester(student)
.flatMap { messageRepository.getMessages(student, it, folder, forceRefresh) } .flatMap { messageRepository.getMessages(student, it, folder, forceRefresh) }
@ -96,7 +101,7 @@ class MessageTabPresenter @Inject constructor(
.subscribe({ .subscribe({
Timber.i("Loading $folder message result: Success") Timber.i("Loading $folder message result: Success")
messages = it messages = it
onSearchQueryTextChange(lastSearchQuery) view?.updateData(getFilteredData(lastSearchQuery))
analytics.logEvent( analytics.logEvent(
"load_data", "load_data",
"type" to "messages", "type" to "messages",
@ -108,7 +113,6 @@ class MessageTabPresenter @Inject constructor(
errorHandler.dispatch(it) errorHandler.dispatch(it)
}) })
} }
}
private fun showErrorViewOnError(message: String, error: Throwable) { private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run { view?.run {
@ -121,25 +125,36 @@ class MessageTabPresenter @Inject constructor(
} }
} }
@SuppressLint("DefaultLocale")
fun onSearchQueryTextChange(query: String) { fun onSearchQueryTextChange(query: String) {
if (query != searchQuery.toString())
searchQuery.onNext(query)
}
private fun initializeSearchStream() {
disposable.add(searchQuery
.debounce(250, TimeUnit.MILLISECONDS)
.map { query ->
lastSearchQuery = query lastSearchQuery = query
getFilteredData(query)
val lowerCaseQuery = query.toLowerCase()
val filteredList = mutableListOf<Message>()
messages.forEach {
if (lowerCaseQuery in it.subject.toLowerCase() ||
lowerCaseQuery in it.sender.toLowerCase() ||
lowerCaseQuery in it.recipient.toLowerCase() ||
lowerCaseQuery in it.date.toFormattedString()
) {
filteredList.add(it)
} }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.d("Applying filter. Full list: ${messages.size}, filtered: ${it.size}")
updateData(it)
}) { Timber.e(it) })
} }
Timber.d("Applying filter. Full list: ${messages.size}, filtered: ${filteredList.size}") private fun getFilteredData(query: String): List<Message> {
return if (query.trim().isEmpty()) {
updateData(filteredList) messages.sortedByDescending { it.date }
} else {
messages
.map { it to calculateMatchRatio(it, query) }
.sortedByDescending { it.second }
.filter { it.second > 5000 }
.map { it.first }
}
} }
private fun updateData(data: List<Message>) { private fun updateData(data: List<Message>) {
@ -151,4 +166,42 @@ class MessageTabPresenter @Inject constructor(
resetListPosition() resetListPosition()
} }
} }
private fun calculateMatchRatio(message: Message, query: String): Int {
val subjectRatio = FuzzySearch.tokenSortPartialRatio(
query.toLowerCase(Locale.getDefault()),
message.subject
)
val senderOrRecipientRatio = FuzzySearch.tokenSortPartialRatio(
query.toLowerCase(Locale.getDefault()),
if (message.sender.isNotEmpty()) message.sender.toLowerCase(Locale.getDefault())
else message.recipient.toLowerCase(Locale.getDefault())
)
val dateRatio = listOf(
FuzzySearch.ratio(
query.toLowerCase(Locale.getDefault()),
message.date.toFormattedString("dd.MM").toLowerCase(Locale.getDefault())
),
FuzzySearch.ratio(
query.toLowerCase(Locale.getDefault()),
message.date.toFormattedString("dd.MM.yyyy").toLowerCase(Locale.getDefault())
),
FuzzySearch.ratio(
query.toLowerCase(Locale.getDefault()),
message.date.toFormattedString("d MMMM").toLowerCase(Locale.getDefault())
),
FuzzySearch.ratio(
query.toLowerCase(Locale.getDefault()),
message.date.toFormattedString("d MMMM yyyy").toLowerCase(Locale.getDefault())
)
).max() ?: 0
return (subjectRatio.toDouble().pow(2)
+ senderOrRecipientRatio.toDouble().pow(2)
+ dateRatio.toDouble().pow(2) * 2
).toInt()
}
} }

View File

@ -43,7 +43,7 @@ class CompletedLessonsPresenter @Inject constructor(
completedLessonsErrorHandler.showErrorMessage = ::showErrorViewOnError completedLessonsErrorHandler.showErrorMessage = ::showErrorViewOnError
completedLessonsErrorHandler.onFeatureDisabled = { completedLessonsErrorHandler.onFeatureDisabled = {
this.view?.showFeatureDisabled() this.view?.showFeatureDisabled()
this.view?.showEmpty(true); this.view?.showEmpty(true)
Timber.i("Completed lessons feature disabled by school") Timber.i("Completed lessons feature disabled by school")
} }
loadData(ofEpochDay(date ?: baseDate.toEpochDay())) loadData(ofEpochDay(date ?: baseDate.toEpochDay()))

View File

@ -71,4 +71,17 @@ fun Context.openDialer(phone: String) {
startActivity(intent) startActivity(intent)
} }
fun Context.shareText(text: String, subject: String?) {
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, text)
if (subject != null) {
putExtra(Intent.EXTRA_SUBJECT, subject)
}
type = "text/plain"
}
val shareIntent = Intent.createChooser(sendIntent, null)
startActivity(shareIntent)
}
fun Context.dpToPx(dp: Float) = dp * resources.displayMetrics.densityDpi / DENSITY_DEFAULT fun Context.dpToPx(dp: Float) = dp * resources.displayMetrics.densityDpi / DENSITY_DEFAULT

View File

@ -52,4 +52,4 @@ class LifecycleAwareVariableActivity<T : Any> : ReadWriteProperty<AppCompatActiv
@Suppress("unused") @Suppress("unused")
fun <T : Any> Fragment.lifecycleAwareVariable() = LifecycleAwareVariable<T>() fun <T : Any> Fragment.lifecycleAwareVariable() = LifecycleAwareVariable<T>()
fun <T : Any> AppCompatActivity.lifecycleAwareVariable() = LifecycleAwareVariableActivity<T>() fun <T : Any> lifecycleAwareVariable() = LifecycleAwareVariableActivity<T>()

View File

@ -23,6 +23,8 @@ fun Sdk.init(student: Student): Sdk {
certKey = student.certificateKey certKey = student.certificateKey
privateKey = student.privateKey privateKey = student.privateKey
emptyCookieJarInterceptor = true
Timber.d("Sdk in ${student.loginMode} mode reinitialized") Timber.d("Sdk in ${student.loginMode} mode reinitialized")
return this return this

View File

@ -1,6 +1,8 @@
Wersja 0.18.3 Wersja 0.19.0
- poprawiliśmy liczenie średniej i dodaliśmy nowy sposób jej liczenia w ustawieniach - naprawiliśmy pokazywanie brakujących przedmiotów na liście podsumowania ocen
- naprawiliśmy usuwanie wiadomości - ulepszyliśmy wygląd menadżera kont
- naprawiliśmy wysyłanie wiadomości na Lubelskim Portalu Oświatowym - ulepszyliśmy wyszukiwarkę wiadomości
- dodaliśmy powiadomienia o proponowanych i końcowych ocenach
- dodaliśmy opcję udostępniania i drukowania wiadomości
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M19,8h-1L18,3L6,3v5L5,8c-1.66,0 -3,1.34 -3,3v6h4v4h12v-4h4v-6c0,-1.66 -1.34,-3 -3,-3zM8,5h8v3L8,8L8,5zM16,17v2L8,19v-4h8v2zM18,15v-2L6,13v2L4,15v-4c0,-0.55 0.45,-1 1,-1h14c0.55,0 1,0.45 1,1v4h-2z" />
<path
android:fillColor="@android:color/white"
android:pathData="M18,11.5m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0" />
</vector>

View 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"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92s2.92,-1.31 2.92,-2.92c0,-1.61 -1.31,-2.92 -2.92,-2.92zM18,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM6,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM18,20.02c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z" />
</vector>

View File

@ -5,10 +5,15 @@
android:layout_width="300dp" android:layout_width="300dp"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView <TextView
android:id="@+id/accountDialogTitle" android:id="@+id/accountDialogTitle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="64dp" android:layout_height="wrap_content"
android:paddingStart="24dp" android:paddingStart="24dp"
android:paddingLeft="24dp" android:paddingLeft="24dp"
android:paddingEnd="24dp" android:paddingEnd="24dp"
@ -21,29 +26,39 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/accountDialogRecycler" android:id="@+id/accountDialogRecycler"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="0dp"
android:layout_below="@id/accountDialogTitle" android:layout_marginTop="8dp"
android:layout_weight="1"
android:overScrollMode="never" android:overScrollMode="never"
tools:itemCount="3" tools:itemCount="3"
tools:listitem="@layout/item_account" /> tools:listitem="@layout/item_account" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/accountDialogAdd" android:id="@+id/accountDialogAdd"
style="@style/Widget.MaterialComponents.Button.TextButton" style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/accountDialogRecycler"
android:layout_margin="8dp" android:layout_margin="8dp"
android:text="@string/account_add_new" android:text="@string/account_add_new"
android:textColor="?android:textColorPrimary" /> android:textColor="?android:textColorPrimary" />
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/accountDialogRemove" android:id="@+id/accountDialogRemove"
style="@style/Widget.MaterialComponents.Button.TextButton" style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/accountDialogRecycler"
android:layout_alignParentEnd="true"
android:layout_margin="8dp" android:layout_margin="8dp"
android:text="@string/account_logout" /> android:text="@string/account_logout" />
</LinearLayout>
</LinearLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
android:paddingHorizontal="24dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
tools:context=".ui.modules.account.AccountAdapter">
<TextView
android:id="@+id/accountHeaderEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?android:textColorPrimary"
android:textSize="14sp"
tools:text="jan@fakelog.cf" />
<TextView
android:id="@+id/accountHeaderType"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
tools:text="Konto ucznia" />
</LinearLayout>

View File

@ -3,20 +3,17 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="56dp" android:layout_height="wrap_content"
android:background="?selectableItemBackground" android:background="?selectableItemBackground"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingStart="24dp" android:paddingVertical="8dp"
android:paddingLeft="24dp" android:paddingHorizontal="16dp"
android:paddingEnd="24dp"
android:paddingRight="24dp"
tools:context=".ui.modules.account.AccountAdapter"> tools:context=".ui.modules.account.AccountAdapter">
<ImageView <ImageView
android:id="@+id/accountItemImage" android:id="@+id/accountItemImage"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_all_account" app:srcCompat="@drawable/ic_all_account"
@ -27,7 +24,7 @@
android:id="@+id/accountItemName" android:id="@+id/accountItemName"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="20dp" android:layout_marginStart="16dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:textSize="16sp" android:textSize="16sp"
@ -40,7 +37,7 @@
android:id="@+id/accountItemSchool" android:id="@+id/accountItemSchool"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="20dp" android:layout_marginStart="16dp"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
@ -50,4 +47,21 @@
app:layout_constraintStart_toEndOf="@id/accountItemImage" app:layout_constraintStart_toEndOf="@id/accountItemImage"
app:layout_constraintTop_toBottomOf="@id/accountItemName" app:layout_constraintTop_toBottomOf="@id/accountItemName"
tools:text="@tools:sample/lorem/random" /> tools:text="@tools:sample/lorem/random" />
<TextView
android:id="@+id/accountItemLoginMode"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="3dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/accountItemImage"
app:layout_constraintTop_toBottomOf="@id/accountItemSchool"
tools:text="Tryb API mobilne"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -22,4 +22,18 @@
android:title="@string/message_delete" android:title="@string/message_delete"
app:iconTint="@color/material_on_surface_emphasis_medium" app:iconTint="@color/material_on_surface_emphasis_medium"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item
android:id="@+id/messagePreviewMenuShare"
android:icon="@drawable/ic_menu_message_share"
android:orderInCategory="1"
android:title="@string/message_share"
app:iconTint="@color/material_on_surface_emphasis_medium"
app:showAsAction="ifRoom" />
<item
android:id="@+id/messagePreviewMenuPrint"
android:icon="@drawable/ic_menu_message_print"
android:orderInCategory="1"
android:title="@string/message_print"
app:iconTint="@color/material_on_surface_emphasis_medium"
app:showAsAction="ifRoom" />
</menu> </menu>

View File

@ -36,8 +36,8 @@
</string-array> </string-array>
<string-array name="grade_average_mode_entries"> <string-array name="grade_average_mode_entries">
<item>Durchschnittsnote für das 2. Semester</item> <item>Durchschnittsnote für das 2. Semester</item>
<item>Average of grades from both semesters</item> <item>Durchschnitt der Noten aus beiden Semestern</item>
<item>Durchschnitt der Bewertungen für das ganze Jahr</item> <item>Durchschnitt der Noten aus dem ganzen Jahr</item>
</string-array> </string-array>
<string-array name="timetable_show_whole_class_entries"> <string-array name="timetable_show_whole_class_entries">
<item>Nicht zeigen</item> <item>Nicht zeigen</item>

View File

@ -103,10 +103,26 @@
<item quantity="one">Neue Note</item> <item quantity="one">Neue Note</item>
<item quantity="other">Neue Noten</item> <item quantity="other">Neue Noten</item>
</plurals> </plurals>
<plurals name="grade_new_items_predicted">
<item quantity="one">New predicted grade</item>
<item quantity="other">New predicted grades</item>
</plurals>
<plurals name="grade_new_items_final">
<item quantity="one">New final grade</item>
<item quantity="other">New final grades</item>
</plurals>
<plurals name="grade_notify_new_items"> <plurals name="grade_notify_new_items">
<item quantity="one">Du hast %1$d Note bekommen</item> <item quantity="one">Du hast %1$d Note bekommen</item>
<item quantity="other">Du hast %1$d Noten bekommen</item> <item quantity="other">Du hast %1$d Noten bekommen</item>
</plurals> </plurals>
<plurals name="grade_notify_new_items_predicted">
<item quantity="one">You received %1$d predicted grade</item>
<item quantity="other">You received %1$d predicted grades</item>
</plurals>
<plurals name="grade_notify_new_items_final">
<item quantity="one">You received %1$d final grade</item>
<item quantity="other">You received %1$d final grades</item>
</plurals>
<!--Timetable--> <!--Timetable-->
<string name="timetable_lesson">Lektion</string> <string name="timetable_lesson">Lektion</string>
<string name="timetable_room">Klassenzimmer</string> <string name="timetable_room">Klassenzimmer</string>
@ -172,6 +188,8 @@
<string name="message_move_to_bin">In den Korb wandern</string> <string name="message_move_to_bin">In den Korb wandern</string>
<string name="message_delete_forever">Dauerhaft löschen</string> <string name="message_delete_forever">Dauerhaft löschen</string>
<string name="message_delete_success">Nachricht erfolgreich gelöscht</string> <string name="message_delete_success">Nachricht erfolgreich gelöscht</string>
<string name="message_share">Share</string>
<string name="message_print">Print</string>
<string name="message_subject">Thema</string> <string name="message_subject">Thema</string>
<string name="message_content">Inhalt</string> <string name="message_content">Inhalt</string>
<string name="message_send_successful">Nachricht erfolgreich gesendet</string> <string name="message_send_successful">Nachricht erfolgreich gesendet</string>
@ -214,7 +232,7 @@
<string name="lucky_number_header">Die heutige Glücksnummer ist </string> <string name="lucky_number_header">Die heutige Glücksnummer ist </string>
<string name="lucky_number_empty">Keine Information über die Glücksnummer.</string> <string name="lucky_number_empty">Keine Information über die Glücksnummer.</string>
<string name="lucky_number_notify_new_item_title">Glücksnummer für heute</string> <string name="lucky_number_notify_new_item_title">Glücksnummer für heute</string>
<string name="lucky_number_notify_new_item">Die heutige Glücksnummer ist: </string> <string name="lucky_number_notify_new_item">Die heutige Glücksnummer ist: %d</string>
<!--Mobile devices--> <!--Mobile devices-->
<string name="mobile_devices_title">Mobile Geräte</string> <string name="mobile_devices_title">Mobile Geräte</string>
<string name="mobile_devices_no_items">Keine Geräte</string> <string name="mobile_devices_no_items">Keine Geräte</string>
@ -245,6 +263,10 @@
<string name="account_logout">Abmelden</string> <string name="account_logout">Abmelden</string>
<string name="account_confirm">Wollen Sie sich von einem aktiven Studenten abmelden?</string> <string name="account_confirm">Wollen Sie sich von einem aktiven Studenten abmelden?</string>
<string name="account_logout_student">Abmeldung von Student</string> <string name="account_logout_student">Abmeldung von Student</string>
<string name="account_type_student">Student account</string>
<string name="account_type_parent">Parent account</string>
<string name="account_login_mobile_api">Mobile API mode</string>
<string name="account_login_hybrid">Hybrid mode</string>
<!--About--> <!--About-->
<string name="about_version">Version der App</string> <string name="about_version">Version der App</string>
<string name="about_contributor">Mitarbeiter</string> <string name="about_contributor">Mitarbeiter</string>
@ -270,8 +292,8 @@
<string name="logviewer_share">Logs teilen</string> <string name="logviewer_share">Logs teilen</string>
<string name="logviewer_refresh">Aktualisieren</string> <string name="logviewer_refresh">Aktualisieren</string>
<!--Error dialog--> <!--Error dialog-->
<string name="dialog_error_check_update">Check for updates</string> <string name="dialog_error_check_update">Auf Updates prüfen</string>
<string name="dialog_error_check_update_message">Before reporting a bug, check first if an update with the bug fix is available</string> <string name="dialog_error_check_update_message">Bevor Sie einen Fehler melden, prüfen Sie zuerst, ob ein Update mit der Fehlerbehebung verfügbar ist</string>
<!--Generic--> <!--Generic-->
<string name="all_content">Inhalt</string> <string name="all_content">Inhalt</string>
<string name="all_retry">Wiederhol</string> <string name="all_retry">Wiederhol</string>
@ -289,7 +311,7 @@
<string name="all_prev">Zurück</string> <string name="all_prev">Zurück</string>
<string name="all_next">Nächste</string> <string name="all_next">Nächste</string>
<string name="all_search">Suchen</string> <string name="all_search">Suchen</string>
<string name="all_search_hint">Suchen...</string> <string name="all_search_hint">Suchen</string>
<!--Timetable Widget--> <!--Timetable Widget-->
<string name="widget_timetable_no_items">Keine Lektionen</string> <string name="widget_timetable_no_items">Keine Lektionen</string>
<string name="widget_timetable_theme_title">Thema wählen</string> <string name="widget_timetable_theme_title">Thema wählen</string>

View File

@ -107,12 +107,36 @@
<item quantity="many">Nowe oceny</item> <item quantity="many">Nowe oceny</item>
<item quantity="other">Nowe oceny</item> <item quantity="other">Nowe oceny</item>
</plurals> </plurals>
<plurals name="grade_new_items_predicted">
<item quantity="one">Nowa ocena przewidywana</item>
<item quantity="few">Nowe oceny przewidywane</item>
<item quantity="many">Nowe oceny przewidywane</item>
<item quantity="other">Nowe oceny przewidywane</item>
</plurals>
<plurals name="grade_new_items_final">
<item quantity="one">Nowa ocena końcowa</item>
<item quantity="few">Nowe oceny końcowe</item>
<item quantity="many">Nowe oceny końcowe</item>
<item quantity="other">Nowe oceny końcowe</item>
</plurals>
<plurals name="grade_notify_new_items"> <plurals name="grade_notify_new_items">
<item quantity="one">Masz %1$d nową ocenę</item> <item quantity="one">Masz %1$d nową ocenę</item>
<item quantity="few">Masz %1$d nowe oceny</item> <item quantity="few">Masz %1$d nowe oceny</item>
<item quantity="many">Masz %1$d nowych ocen</item> <item quantity="many">Masz %1$d nowych ocen</item>
<item quantity="other">Masz %1$d nowych ocen</item> <item quantity="other">Masz %1$d nowych ocen</item>
</plurals> </plurals>
<plurals name="grade_notify_new_items_predicted">
<item quantity="one">Masz %1$d nową przewidywaną ocenę</item>
<item quantity="few">Masz %1$d nowe przewidywane oceny</item>
<item quantity="many">Masz %1$d nowych przewidywanych ocen</item>
<item quantity="other">Masz %1$d nowych przewidywanych ocen</item>
</plurals>
<plurals name="grade_notify_new_items_final">
<item quantity="one">Masz %1$d nową końcową ocenę</item>
<item quantity="few">Masz %1$d nowe końcowe oceny</item>
<item quantity="many">Masz %1$d nowych końcowych ocen</item>
<item quantity="other">Masz %1$d nowych końcowych ocen</item>
</plurals>
<!--Timetable--> <!--Timetable-->
<string name="timetable_lesson">Lekcja</string> <string name="timetable_lesson">Lekcja</string>
<string name="timetable_room">Sala</string> <string name="timetable_room">Sala</string>
@ -180,6 +204,8 @@
<string name="message_move_to_bin">Przenieś do kosza</string> <string name="message_move_to_bin">Przenieś do kosza</string>
<string name="message_delete_forever">Usuń trwale</string> <string name="message_delete_forever">Usuń trwale</string>
<string name="message_delete_success">Wiadomość usunięta pomyślnie</string> <string name="message_delete_success">Wiadomość usunięta pomyślnie</string>
<string name="message_share">Udostępnij</string>
<string name="message_print">Drukuj</string>
<string name="message_subject">Temat</string> <string name="message_subject">Temat</string>
<string name="message_content">Treść</string> <string name="message_content">Treść</string>
<string name="message_send_successful">Wiadomość wysłana pomyślnie</string> <string name="message_send_successful">Wiadomość wysłana pomyślnie</string>
@ -265,6 +291,10 @@
<string name="account_logout">Wyloguj</string> <string name="account_logout">Wyloguj</string>
<string name="account_confirm">Czy chcesz wylogować aktualnego ucznia?</string> <string name="account_confirm">Czy chcesz wylogować aktualnego ucznia?</string>
<string name="account_logout_student">Wylogowanie ucznia</string> <string name="account_logout_student">Wylogowanie ucznia</string>
<string name="account_type_student">Konto ucznia</string>
<string name="account_type_parent">Konto rodzica</string>
<string name="account_login_mobile_api">Tryb API mobilne</string>
<string name="account_login_hybrid">Tryb hybrydowy</string>
<!--About--> <!--About-->
<string name="about_version">Wersja aplikacji</string> <string name="about_version">Wersja aplikacji</string>
<string name="about_contributor">Twórcy</string> <string name="about_contributor">Twórcy</string>
@ -309,7 +339,7 @@
<string name="all_prev">Poprzedni</string> <string name="all_prev">Poprzedni</string>
<string name="all_next">Następny</string> <string name="all_next">Następny</string>
<string name="all_search">Szukaj</string> <string name="all_search">Szukaj</string>
<string name="all_search_hint">Szukaj...</string> <string name="all_search_hint">Szukaj</string>
<!--Timetable Widget--> <!--Timetable Widget-->
<string name="widget_timetable_no_items">Brak lekcji</string> <string name="widget_timetable_no_items">Brak lekcji</string>
<string name="widget_timetable_theme_title">Wybierz motyw</string> <string name="widget_timetable_theme_title">Wybierz motyw</string>

View File

@ -37,7 +37,7 @@
<string-array name="grade_average_mode_entries"> <string-array name="grade_average_mode_entries">
<item>Средняя оценка со 2 семестра</item> <item>Средняя оценка со 2 семестра</item>
<item>Average of grades from both semesters</item> <item>Average of grades from both semesters</item>
<item>Средняя оценка с целого года</item> <item>Average of grades from the whole year</item>
</string-array> </string-array>
<string-array name="timetable_show_whole_class_entries"> <string-array name="timetable_show_whole_class_entries">
<item>Не показывать</item> <item>Не показывать</item>

View File

@ -107,12 +107,36 @@
<item quantity="many">Новые оценки</item> <item quantity="many">Новые оценки</item>
<item quantity="other">Новые оценки</item> <item quantity="other">Новые оценки</item>
</plurals> </plurals>
<plurals name="grade_new_items_predicted">
<item quantity="one">New predicted grade</item>
<item quantity="few">New predicted grades</item>
<item quantity="many">New predicted grades</item>
<item quantity="other">New predicted grades</item>
</plurals>
<plurals name="grade_new_items_final">
<item quantity="one">New final grade</item>
<item quantity="few">New final grades</item>
<item quantity="many">New final grades</item>
<item quantity="other">New final grades</item>
</plurals>
<plurals name="grade_notify_new_items"> <plurals name="grade_notify_new_items">
<item quantity="one">Вы получили %1$d оценку</item> <item quantity="one">Вы получили %1$d оценку</item>
<item quantity="few">Вы получили %1$d оценки</item> <item quantity="few">Вы получили %1$d оценки</item>
<item quantity="many">Вы получили %1$d оценок</item> <item quantity="many">Вы получили %1$d оценок</item>
<item quantity="other">Вы получили %1$d оценок</item> <item quantity="other">Вы получили %1$d оценок</item>
</plurals> </plurals>
<plurals name="grade_notify_new_items_predicted">
<item quantity="one">You received %1$d predicted grade</item>
<item quantity="few">You received %1$d predicted grades</item>
<item quantity="many">You received %1$d predicted grades</item>
<item quantity="other">You received %1$d predicted grades</item>
</plurals>
<plurals name="grade_notify_new_items_final">
<item quantity="one">You received %1$d final grade</item>
<item quantity="few">You received %1$d final grades</item>
<item quantity="many">You received %1$d final grades</item>
<item quantity="other">You received %1$d final grades</item>
</plurals>
<!--Timetable--> <!--Timetable-->
<string name="timetable_lesson">Урок</string> <string name="timetable_lesson">Урок</string>
<string name="timetable_room">Аудитория</string> <string name="timetable_room">Аудитория</string>
@ -180,6 +204,8 @@
<string name="message_move_to_bin">Перенести в корзину</string> <string name="message_move_to_bin">Перенести в корзину</string>
<string name="message_delete_forever">Удалить навсегда</string> <string name="message_delete_forever">Удалить навсегда</string>
<string name="message_delete_success">Сообщение успешно удалено</string> <string name="message_delete_success">Сообщение успешно удалено</string>
<string name="message_share">Share</string>
<string name="message_print">Print</string>
<string name="message_subject">Тема</string> <string name="message_subject">Тема</string>
<string name="message_content">Текст</string> <string name="message_content">Текст</string>
<string name="message_send_successful">Сообщение успешно отправлено</string> <string name="message_send_successful">Сообщение успешно отправлено</string>
@ -265,6 +291,10 @@
<string name="account_logout">Выйти</string> <string name="account_logout">Выйти</string>
<string name="account_confirm">Вы точно хотите выйти из данного аккаунта?</string> <string name="account_confirm">Вы точно хотите выйти из данного аккаунта?</string>
<string name="account_logout_student">Выйти</string> <string name="account_logout_student">Выйти</string>
<string name="account_type_student">Student account</string>
<string name="account_type_parent">Parent account</string>
<string name="account_login_mobile_api">Mobile API mode</string>
<string name="account_login_hybrid">Hybrid mode</string>
<!--About--> <!--About-->
<string name="about_version">Версия приложения</string> <string name="about_version">Версия приложения</string>
<string name="about_contributor">Разработчики</string> <string name="about_contributor">Разработчики</string>
@ -309,7 +339,7 @@
<string name="all_prev">Предыдущий</string> <string name="all_prev">Предыдущий</string>
<string name="all_next">Следующий</string> <string name="all_next">Следующий</string>
<string name="all_search">Поиск</string> <string name="all_search">Поиск</string>
<string name="all_search_hint">Поиск...</string> <string name="all_search_hint">Поиск</string>
<!--Timetable Widget--> <!--Timetable Widget-->
<string name="widget_timetable_no_items">Нет уроков</string> <string name="widget_timetable_no_items">Нет уроков</string>
<string name="widget_timetable_theme_title">Выбрать тему</string> <string name="widget_timetable_theme_title">Выбрать тему</string>

View File

@ -37,7 +37,7 @@
<string-array name="grade_average_mode_entries"> <string-array name="grade_average_mode_entries">
<item>Середня оцінка з 2 семестру</item> <item>Середня оцінка з 2 семестру</item>
<item>Average of grades from both semesters</item> <item>Average of grades from both semesters</item>
<item>Середня оцінка за весь рік</item> <item>Average of grades from the whole year</item>
</string-array> </string-array>
<string-array name="timetable_show_whole_class_entries"> <string-array name="timetable_show_whole_class_entries">
<item>Не показувати</item> <item>Не показувати</item>

View File

@ -107,12 +107,36 @@
<item quantity="many">Нові оцінки</item> <item quantity="many">Нові оцінки</item>
<item quantity="other">Нові оцінки</item> <item quantity="other">Нові оцінки</item>
</plurals> </plurals>
<plurals name="grade_new_items_predicted">
<item quantity="one">New predicted grade</item>
<item quantity="few">New predicted grades</item>
<item quantity="many">New predicted grades</item>
<item quantity="other">New predicted grades</item>
</plurals>
<plurals name="grade_new_items_final">
<item quantity="one">New final grade</item>
<item quantity="few">New final grades</item>
<item quantity="many">New final grades</item>
<item quantity="other">New final grades</item>
</plurals>
<plurals name="grade_notify_new_items"> <plurals name="grade_notify_new_items">
<item quantity="one">Ви отримали %1$d оцінку</item> <item quantity="one">Ви отримали %1$d оцінку</item>
<item quantity="few">Ви отримали %1$d оцінки</item> <item quantity="few">Ви отримали %1$d оцінки</item>
<item quantity="many">Ви отримали %1$d оцінок</item> <item quantity="many">Ви отримали %1$d оцінок</item>
<item quantity="other">Ви отримали %1$d оцінок</item> <item quantity="other">Ви отримали %1$d оцінок</item>
</plurals> </plurals>
<plurals name="grade_notify_new_items_predicted">
<item quantity="one">You received %1$d predicted grade</item>
<item quantity="few">You received %1$d predicted grades</item>
<item quantity="many">You received %1$d predicted grades</item>
<item quantity="other">You received %1$d predicted grades</item>
</plurals>
<plurals name="grade_notify_new_items_final">
<item quantity="one">You received %1$d final grade</item>
<item quantity="few">You received %1$d final grades</item>
<item quantity="many">You received %1$d final grades</item>
<item quantity="other">You received %1$d final grades</item>
</plurals>
<!--Timetable--> <!--Timetable-->
<string name="timetable_lesson">Урок</string> <string name="timetable_lesson">Урок</string>
<string name="timetable_room">Аудиторія</string> <string name="timetable_room">Аудиторія</string>
@ -180,6 +204,8 @@
<string name="message_move_to_bin">Перемістити у кошик</string> <string name="message_move_to_bin">Перемістити у кошик</string>
<string name="message_delete_forever">Видалити назавжди</string> <string name="message_delete_forever">Видалити назавжди</string>
<string name="message_delete_success">Повідомлення було успішно видалено</string> <string name="message_delete_success">Повідомлення було успішно видалено</string>
<string name="message_share">Share</string>
<string name="message_print">Print</string>
<string name="message_subject">Тема</string> <string name="message_subject">Тема</string>
<string name="message_content">Зміст</string> <string name="message_content">Зміст</string>
<string name="message_send_successful">Повідомлення було успішно відправлено</string> <string name="message_send_successful">Повідомлення було успішно відправлено</string>
@ -265,6 +291,10 @@
<string name="account_logout">Вийти</string> <string name="account_logout">Вийти</string>
<string name="account_confirm">Ви впевнені, що хочете вийти з цього аккаунту?</string> <string name="account_confirm">Ви впевнені, що хочете вийти з цього аккаунту?</string>
<string name="account_logout_student">Вийти з аккаунту учня</string> <string name="account_logout_student">Вийти з аккаунту учня</string>
<string name="account_type_student">Student account</string>
<string name="account_type_parent">Parent account</string>
<string name="account_login_mobile_api">Mobile API mode</string>
<string name="account_login_hybrid">Hybrid mode</string>
<!--About--> <!--About-->
<string name="about_version">Версія додатка</string> <string name="about_version">Версія додатка</string>
<string name="about_contributor">Розробники</string> <string name="about_contributor">Розробники</string>
@ -309,7 +339,7 @@
<string name="all_prev">Попередній</string> <string name="all_prev">Попередній</string>
<string name="all_next">Наступний</string> <string name="all_next">Наступний</string>
<string name="all_search">Пошук</string> <string name="all_search">Пошук</string>
<string name="all_search_hint">Пошук...</string> <string name="all_search_hint">Пошук</string>
<!--Timetable Widget--> <!--Timetable Widget-->
<string name="widget_timetable_no_items">Брак уроків</string> <string name="widget_timetable_no_items">Брак уроків</string>
<string name="widget_timetable_theme_title">Увібрати тему</string> <string name="widget_timetable_theme_title">Увібрати тему</string>

View File

@ -113,10 +113,26 @@
<item quantity="one">New grade</item> <item quantity="one">New grade</item>
<item quantity="other">New grades</item> <item quantity="other">New grades</item>
</plurals> </plurals>
<plurals name="grade_new_items_predicted">
<item quantity="one">New predicted grade</item>
<item quantity="other">New predicted grades</item>
</plurals>
<plurals name="grade_new_items_final">
<item quantity="one">New final grade</item>
<item quantity="other">New final grades</item>
</plurals>
<plurals name="grade_notify_new_items"> <plurals name="grade_notify_new_items">
<item quantity="one">You received %1$d grade</item> <item quantity="one">You received %1$d grade</item>
<item quantity="other">You received %1$d grades</item> <item quantity="other">You received %1$d grades</item>
</plurals> </plurals>
<plurals name="grade_notify_new_items_predicted">
<item quantity="one">You received %1$d predicted grade</item>
<item quantity="other">You received %1$d predicted grades</item>
</plurals>
<plurals name="grade_notify_new_items_final">
<item quantity="one">You received %1$d final grade</item>
<item quantity="other">You received %1$d final grades</item>
</plurals>
<!--Timetable--> <!--Timetable-->
@ -194,6 +210,8 @@
<string name="message_move_to_bin">Move to trash</string> <string name="message_move_to_bin">Move to trash</string>
<string name="message_delete_forever">Delete permanently</string> <string name="message_delete_forever">Delete permanently</string>
<string name="message_delete_success">Message deleted successfully</string> <string name="message_delete_success">Message deleted successfully</string>
<string name="message_share">Share</string>
<string name="message_print">Print</string>
<string name="message_subject">Subject</string> <string name="message_subject">Subject</string>
<string name="message_content">Content</string> <string name="message_content">Content</string>
<string name="message_send_successful">Message sent successfully</string> <string name="message_send_successful">Message sent successfully</string>
@ -282,6 +300,10 @@
<string name="account_logout">Logout</string> <string name="account_logout">Logout</string>
<string name="account_confirm">Do you want to log out of an active student?</string> <string name="account_confirm">Do you want to log out of an active student?</string>
<string name="account_logout_student">Student logout</string> <string name="account_logout_student">Student logout</string>
<string name="account_type_student">Student account</string>
<string name="account_type_parent">Parent account</string>
<string name="account_login_mobile_api">Mobile API mode</string>
<string name="account_login_hybrid">Hybrid mode</string>
<!--About--> <!--About-->
@ -339,7 +361,7 @@
<string name="all_prev">Prev</string> <string name="all_prev">Prev</string>
<string name="all_next">Next</string> <string name="all_next">Next</string>
<string name="all_search">Search</string> <string name="all_search">Search</string>
<string name="all_search_hint">Search...</string> <string name="all_search_hint">Search</string>
<!--Timetable Widget--> <!--Timetable Widget-->

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy package io.github.wulkanowy
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
@ -72,3 +73,25 @@ fun getTimetableEntity(
teacher = "", teacher = "",
teacherOld = "" teacherOld = ""
) )
fun getMessageEntity(
messageId: Int,
content: String,
unread: Boolean
) = Message(
studentId = 1,
realId = 1,
messageId = messageId,
sender = "",
senderId = 1,
recipient = "",
subject = "",
content = content,
date = now(),
folderId = 1,
unread = unread,
unreadBy = 1,
readBy = 1,
removed = false,
hasAttachments = false
)

View File

@ -2,10 +2,10 @@ package io.github.wulkanowy.data.repositories.message
import androidx.room.EmptyResultSetException import androidx.room.EmptyResultSetException
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.UnitTestInternetObservingStrategy import io.github.wulkanowy.data.repositories.UnitTestInternetObservingStrategy
import io.github.wulkanowy.getMessageEntity
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.observers.TestObserver import io.reactivex.observers.TestObserver
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
@ -15,7 +15,6 @@ import org.mockito.Mock
import org.mockito.Mockito.`when` import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import org.threeten.bp.LocalDateTime.now
import java.net.UnknownHostException import java.net.UnknownHostException
class MessageRepositoryTest { class MessageRepositoryTest {
@ -44,7 +43,7 @@ class MessageRepositoryTest {
@Test @Test
fun `throw error when message is not in the db`() { fun `throw error when message is not in the db`() {
val testMessage = Message(1, 1, 1, "", 1, "", "", "", now(), 1, false, 1, 1, false, false) val testMessage = getMessageEntity(1, "", false)
`when`(local.getMessageWithAttachment(student, testMessage)).thenReturn(Single.error(EmptyResultSetException("No message in database"))) `when`(local.getMessageWithAttachment(student, testMessage)).thenReturn(Single.error(EmptyResultSetException("No message in database")))
val message = repo.getMessage(student, testMessage) val message = repo.getMessage(student, testMessage)
@ -55,7 +54,7 @@ class MessageRepositoryTest {
@Test @Test
fun `get message when content already in db`() { fun `get message when content already in db`() {
val testMessage = Message(1, 1, 123, "", 1, "", "", "Test", now(), 1, false, 1, 1, false, false) val testMessage = getMessageEntity(123, "Test", false)
val messageWithAttachment = MessageWithAttachment(testMessage, emptyList()) val messageWithAttachment = MessageWithAttachment(testMessage, emptyList())
`when`(local.getMessageWithAttachment(student, testMessage)).thenReturn(Single.just(messageWithAttachment)) `when`(local.getMessageWithAttachment(student, testMessage)).thenReturn(Single.just(messageWithAttachment))
@ -67,7 +66,7 @@ class MessageRepositoryTest {
@Test @Test
fun `get message when content in db is empty`() { fun `get message when content in db is empty`() {
val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, true, 1, 1, false, false) val testMessage = getMessageEntity(123, "", true)
val testMessageWithContent = testMessage.copy(content = "Test") val testMessageWithContent = testMessage.copy(content = "Test")
val mWa = MessageWithAttachment(testMessage, emptyList()) val mWa = MessageWithAttachment(testMessage, emptyList())
@ -86,7 +85,7 @@ class MessageRepositoryTest {
@Test @Test
fun `get message when content in db is empty and there is no internet connection`() { fun `get message when content in db is empty and there is no internet connection`() {
val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, false, 1, 1, false, false) val testMessage = getMessageEntity(123, "", false)
val messageWithAttachment = MessageWithAttachment(testMessage, emptyList()) val messageWithAttachment = MessageWithAttachment(testMessage, emptyList())
testObservingStrategy.isInternetConnection = false testObservingStrategy.isInternetConnection = false
@ -100,7 +99,7 @@ class MessageRepositoryTest {
@Test @Test
fun `get message when content in db is empty, unread and there is no internet connection`() { fun `get message when content in db is empty, unread and there is no internet connection`() {
val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, true, 1, 1, false, false) val testMessage = getMessageEntity(123, "", true)
val messageWithAttachment = MessageWithAttachment(testMessage, emptyList()) val messageWithAttachment = MessageWithAttachment(testMessage, emptyList())
testObservingStrategy.isInternetConnection = false testObservingStrategy.isInternetConnection = false

View File

@ -1,6 +1,6 @@
buildscript { buildscript {
ext.kotlin_version = '1.3.72' ext.kotlin_version = '1.3.72'
ext.about_libraries = '8.1.6' ext.about_libraries = '8.2.0'
repositories { repositories {
mavenCentral() mavenCentral()
google() google()
@ -13,7 +13,7 @@ buildscript {
classpath 'com.google.gms:google-services:4.3.3' classpath 'com.google.gms:google-services:4.3.3'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.1.1' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.1.1'
classpath "com.github.triplet.gradle:play-publisher:2.7.5" classpath "com.github.triplet.gradle:play-publisher:2.7.5"
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8" classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.0"
classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0" classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${about_libraries}" classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${about_libraries}"
} }