Compare commits
63 Commits
Author | SHA1 | Date | |
---|---|---|---|
5de2e9afbd | |||
cd99c6b2aa | |||
42aacb755c | |||
a880b3a9db | |||
7837fae2aa | |||
70e9f025bb | |||
ab1d9b358e | |||
2634c270b1 | |||
79bd2fccdf | |||
87107ec474 | |||
8aaa066142 | |||
5b7d465064 | |||
75c94865e4 | |||
f294e3d57c | |||
565114a2d2 | |||
be057dd63c | |||
e61c2bced8 | |||
3abfb9f819 | |||
7c86fabd7b | |||
59f2d4b0f3 | |||
8571586b0c | |||
e21de811e2 | |||
7b502ce9a8 | |||
33447d2ada | |||
7f6b2ec096 | |||
2c35117dfa | |||
5b2ca07506 | |||
18d6ec6961 | |||
96c1bb4c69 | |||
00f5b9431e | |||
9a87df7315 | |||
30e43501ac | |||
34738a4839 | |||
1cc2080cb9 | |||
0826e45a25 | |||
6925204019 | |||
bdbf1fe304 | |||
720a530a6c | |||
2f56f7e4a4 | |||
c3a6842027 | |||
731afbb00c | |||
fb3853dc70 | |||
ec5503678a | |||
4ab47fef46 | |||
8e17b1d72a | |||
ae9b616896 | |||
1999cd6eaf | |||
1e5269c22c | |||
19f495cba6 | |||
0c5d45717c | |||
1dbe470391 | |||
7142e05561 | |||
4640d114f6 | |||
ad3bb3a522 | |||
e9fa95f113 | |||
76a4bacb34 | |||
c5cadbd004 | |||
77e7e4e6e9 | |||
cc242e1e87 | |||
e0ec2f8160 | |||
eb1ce251a0 | |||
4d1de2d8ce | |||
0af7c00d12 |
1
.gitignore
vendored
@ -63,6 +63,7 @@ captures/
|
||||
.idea/dynamic.xml
|
||||
.idea/uiDesigner.xml
|
||||
.idea/runConfigurations.xml
|
||||
.idea/discord.xml
|
||||
|
||||
# Keystore files
|
||||
*.jks
|
||||
|
3
.idea/codeStyles/Project.xml
generated
@ -1,9 +1,6 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<AndroidXmlCodeStyleSettings>
|
||||
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
|
||||
</AndroidXmlCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
|
10
.travis.yml
@ -4,7 +4,7 @@ jdk: oraclejdk8
|
||||
env:
|
||||
global:
|
||||
- ANDROID_API_LEVEL=29
|
||||
- ANDROID_BUILD_TOOLS_VERSION=29.0.2
|
||||
- ANDROID_BUILD_TOOLS_VERSION=29.0.3
|
||||
|
||||
cache:
|
||||
directories:
|
||||
@ -14,7 +14,7 @@ cache:
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- 0.14.2
|
||||
- 0.16.0
|
||||
|
||||
android:
|
||||
licenses:
|
||||
@ -49,9 +49,9 @@ script:
|
||||
- ./gradlew dependencies --stacktrace --daemon
|
||||
- fossa --no-ansi || true
|
||||
#- ./gradlew lintPlayRelease -x fabricGenerateResourcesPlayRelease --stacktrace --daemon
|
||||
- ./gradlew testPlayDebugUnitTest -x fabricGenerateResourcesPlay --stacktrace --daemon
|
||||
- ./gradlew createFdroidDebugCoverageReport --stacktrace --daemon
|
||||
- ./gradlew jacocoTestReport --stacktrace --daemon
|
||||
- ./gradlew -Pcoverage testPlayDebugUnitTest -x fabricGenerateResourcesPlay --stacktrace --daemon
|
||||
- ./gradlew -Pcoverage createFdroidDebugCoverageReport --stacktrace --daemon
|
||||
- ./gradlew -Pcoverage jacocoTestReport --stacktrace --daemon
|
||||
- if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else
|
||||
git fetch --unshallow;
|
||||
./gradlew sonarqube -x test -x lint -x fabricGenerateResourcesPlayRelease -x fabricGenerateResourcesFdroidRelease -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} --stacktrace --daemon;
|
||||
|
@ -10,15 +10,15 @@ apply from: 'hooks.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion '29.0.2'
|
||||
buildToolsVersion '29.0.3'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.github.wulkanowy"
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 16
|
||||
minSdkVersion 17
|
||||
targetSdkVersion 29
|
||||
versionCode 51
|
||||
versionName "0.14.2"
|
||||
versionCode 53
|
||||
versionName "0.16.0"
|
||||
multiDexEnabled true
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
@ -61,7 +61,7 @@ android {
|
||||
buildConfigField "boolean", "CRASHLYTICS_ENABLED", project.hasProperty("enableCrashlytics") ? "true" : "false"
|
||||
applicationIdSuffix ".dev"
|
||||
versionNameSuffix "-dev"
|
||||
testCoverageEnabled = true
|
||||
testCoverageEnabled = project.hasProperty('coverage')
|
||||
ext.enableCrashlytics = project.hasProperty("enableCrashlytics")
|
||||
}
|
||||
}
|
||||
@ -110,9 +110,9 @@ play {
|
||||
}
|
||||
|
||||
ext {
|
||||
work_manager = "2.3.0-rc01"
|
||||
room = "2.2.3"
|
||||
dagger = "2.25.4"
|
||||
work_manager = "2.3.2"
|
||||
room = "2.2.4"
|
||||
dagger = "2.26"
|
||||
chucker = "2.0.4"
|
||||
mockk = "1.9.2"
|
||||
}
|
||||
@ -122,14 +122,14 @@ configurations.all {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "io.github.wulkanowy:sdk:0.14.2"
|
||||
implementation "io.github.wulkanowy:sdk:0.16.0"
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation "androidx.core:core-ktx:1.2.0-rc01"
|
||||
implementation "androidx.activity:activity-ktx:1.1.0-rc03"
|
||||
implementation "androidx.core:core-ktx:1.2.0"
|
||||
implementation "androidx.activity:activity-ktx:1.1.0"
|
||||
implementation "androidx.appcompat:appcompat:1.1.0"
|
||||
implementation "androidx.appcompat:appcompat-resources:1.1.0"
|
||||
implementation "androidx.fragment:fragment-ktx:1.2.0-rc05"
|
||||
implementation "androidx.fragment:fragment-ktx:1.2.2"
|
||||
implementation "androidx.annotation:annotation:1.1.0"
|
||||
implementation "androidx.multidex:multidex:2.0.1"
|
||||
|
||||
@ -139,7 +139,7 @@ dependencies {
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03"
|
||||
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
|
||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
|
||||
implementation "com.google.android.material:material:1.1.0-rc01"
|
||||
implementation "com.google.android.material:material:1.1.0"
|
||||
implementation "com.github.wulkanowy:material-chips-input:2.0.1"
|
||||
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
||||
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
|
||||
@ -167,16 +167,19 @@ dependencies {
|
||||
|
||||
implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6"
|
||||
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
|
||||
implementation "io.reactivex.rxjava2:rxjava:2.2.16"
|
||||
implementation "io.reactivex.rxjava2:rxjava:2.2.18"
|
||||
|
||||
implementation "com.google.code.gson:gson:2.8.6"
|
||||
implementation "com.jakewharton.threetenabp:threetenabp:1.2.1"
|
||||
implementation "com.jakewharton.threetenabp:threetenabp:1.2.2"
|
||||
implementation "com.jakewharton.timber:timber:4.7.1"
|
||||
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
||||
implementation "fr.bipi.treessence:treessence:0.3.2"
|
||||
implementation "com.mikepenz:aboutlibraries-core:7.1.0"
|
||||
implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
|
||||
|
||||
playImplementation "com.google.firebase:firebase-core:17.2.1"
|
||||
implementation("io.coil-kt:coil:0.9.5")
|
||||
|
||||
playImplementation "com.google.firebase:firebase-core:17.2.3"
|
||||
playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1"
|
||||
|
||||
releaseImplementation "fr.o80.chucker:library-no-op:$chucker"
|
||||
@ -186,8 +189,8 @@ dependencies {
|
||||
|
||||
testImplementation "junit:junit:4.13"
|
||||
testImplementation "io.mockk:mockk:$mockk"
|
||||
testImplementation "org.threeten:threetenbp:1.4.0"
|
||||
testImplementation "org.mockito:mockito-inline:3.2.4"
|
||||
testImplementation "org.threeten:threetenbp:1.4.1"
|
||||
testImplementation "org.mockito:mockito-inline:3.3.1"
|
||||
|
||||
androidTestImplementation "androidx.test:core:1.2.0"
|
||||
androidTestImplementation "androidx.test:runner:1.2.0"
|
||||
@ -195,7 +198,7 @@ dependencies {
|
||||
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
||||
androidTestImplementation "androidx.room:room-testing:$room"
|
||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
androidTestImplementation "org.mockito:mockito-android:3.2.4"
|
||||
androidTestImplementation "org.mockito:mockito-android:3.3.1"
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
@ -1,7 +1,7 @@
|
||||
apply plugin: "jacoco"
|
||||
|
||||
jacoco {
|
||||
toolVersion "0.8.4"
|
||||
toolVersion "0.8.5"
|
||||
reportsDir = file("$buildDir/reports")
|
||||
}
|
||||
|
||||
|
1634
app/schemas/io.github.wulkanowy.data.db.AppDatabase/20.json
Normal file
1652
app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json
Normal file
1658
app/schemas/io.github.wulkanowy.data.db.AppDatabase/22.json
Normal file
@ -8,7 +8,6 @@ import io.github.wulkanowy.data.db.entities.Semester
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import org.threeten.bp.LocalDate.of
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
@ -104,45 +103,45 @@ class Migration13Test : AbstractMigrationTest() {
|
||||
val db = helper.runMigrationsAndValidate(dbName, 13, true, Migration13())
|
||||
|
||||
val semesters1 = getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 1 AND class_id = 5")
|
||||
assertTrue { semesters1.single { it.isCurrent }.isCurrent }
|
||||
assertTrue { semesters1.single { it.second }.second }
|
||||
semesters1[0].run {
|
||||
assertFalse(isCurrent)
|
||||
assertEquals(1, semesterId)
|
||||
assertEquals(1, diaryId)
|
||||
assertFalse(second)
|
||||
assertEquals(1, first.semesterId)
|
||||
assertEquals(1, first.diaryId)
|
||||
}
|
||||
semesters1[2].run {
|
||||
assertFalse(isCurrent)
|
||||
assertEquals(3, semesterId)
|
||||
assertEquals(2, diaryId)
|
||||
assertFalse(second)
|
||||
assertEquals(3, first.semesterId)
|
||||
assertEquals(2, first.diaryId)
|
||||
}
|
||||
semesters1[3].run {
|
||||
assertTrue(isCurrent)
|
||||
assertEquals(4, semesterId)
|
||||
assertEquals(2, diaryId)
|
||||
assertTrue(second)
|
||||
assertEquals(4, first.semesterId)
|
||||
assertEquals(2, first.diaryId)
|
||||
}
|
||||
|
||||
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let {
|
||||
assertTrue { it.single { it.isCurrent }.isCurrent }
|
||||
assertEquals(1970, it[0].schoolYear)
|
||||
assertEquals(of(1970, 1, 1), it[0].end)
|
||||
assertEquals(of(1970, 1, 1), it[0].start)
|
||||
assertFalse(it[0].isCurrent)
|
||||
assertFalse(it[1].isCurrent)
|
||||
assertFalse(it[2].isCurrent)
|
||||
assertTrue(it[3].isCurrent)
|
||||
assertTrue { it.single { it.second }.second }
|
||||
assertEquals(1970, it[0].first.schoolYear)
|
||||
assertEquals(of(1970, 1, 1), it[0].first.end)
|
||||
assertEquals(of(1970, 1, 1), it[0].first.start)
|
||||
assertFalse(it[0].second)
|
||||
assertFalse(it[1].second)
|
||||
assertFalse(it[2].second)
|
||||
assertTrue(it[3].second)
|
||||
}
|
||||
|
||||
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let {
|
||||
assertTrue { it.single { it.isCurrent }.isCurrent }
|
||||
assertFalse(it[0].isCurrent)
|
||||
assertFalse(it[1].isCurrent)
|
||||
assertFalse(it[2].isCurrent)
|
||||
assertTrue(it[3].isCurrent)
|
||||
assertTrue { it.single { it.second }.second }
|
||||
assertFalse(it[0].second)
|
||||
assertFalse(it[1].second)
|
||||
assertFalse(it[2].second)
|
||||
assertTrue(it[3].second)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSemesters(db: SupportSQLiteDatabase, query: String): List<Semester> {
|
||||
val semesters = mutableListOf<Semester>()
|
||||
private fun getSemesters(db: SupportSQLiteDatabase, query: String): List<Pair<Semester, Boolean>> {
|
||||
val semesters = mutableListOf<Pair<Semester, Boolean>>()
|
||||
|
||||
val cursor = db.query(query)
|
||||
if (cursor.moveToFirst()) {
|
||||
@ -153,13 +152,12 @@ class Migration13Test : AbstractMigrationTest() {
|
||||
diaryName = cursor.getString(3),
|
||||
semesterId = cursor.getInt(4),
|
||||
semesterName = cursor.getInt(5),
|
||||
isCurrent = cursor.getInt(6) == 1,
|
||||
classId = cursor.getInt(7),
|
||||
unitId = cursor.getInt(8),
|
||||
schoolYear = cursor.getInt(9),
|
||||
start = Converters().timestampToDate(cursor.getLong(10))!!,
|
||||
end = Converters().timestampToDate(cursor.getLong(11))!!
|
||||
))
|
||||
) to (cursor.getInt(6) == 1))
|
||||
} while (cursor.moveToNext())
|
||||
}
|
||||
return semesters.toList()
|
||||
|
@ -10,8 +10,8 @@ import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import org.threeten.bp.LocalDate.of
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@ -35,19 +35,19 @@ class AttendanceLocalTest {
|
||||
@Test
|
||||
fun saveAndReadTest() {
|
||||
attendanceLocal.saveAttendance(listOf(
|
||||
Attendance(1, 2, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false),
|
||||
Attendance(1, 2, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false),
|
||||
Attendance(1, 2, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false)
|
||||
Attendance(1, 2, 3, of(2018, 9, 10), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name),
|
||||
Attendance(1, 2, 3, of(2018, 9, 14), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.WAITING.name),
|
||||
Attendance(1, 2, 3, of(2018, 9, 17), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name)
|
||||
))
|
||||
|
||||
val attendance = attendanceLocal
|
||||
.getAttendance(Semester(1, 2, "", 1, 3, 2019, true, now(), now(), 1, 1),
|
||||
LocalDate.of(2018, 9, 10),
|
||||
LocalDate.of(2018, 9, 14)
|
||||
)
|
||||
.blockingGet()
|
||||
.getAttendance(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1),
|
||||
of(2018, 9, 10),
|
||||
of(2018, 9, 14)
|
||||
)
|
||||
.blockingGet()
|
||||
assertEquals(2, attendance.size)
|
||||
assertEquals(attendance[0].date, LocalDate.of(2018, 9, 10))
|
||||
assertEquals(attendance[1].date, LocalDate.of(2018, 9, 14))
|
||||
assertEquals(attendance[0].date, of(2018, 9, 10))
|
||||
assertEquals(attendance[1].date, of(2018, 9, 14))
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import org.threeten.bp.LocalDate.of
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@ -35,20 +37,20 @@ class CompletedLessonsLocalTest {
|
||||
@Test
|
||||
fun saveAndReadTest() {
|
||||
completedLessonsLocal.saveCompletedLessons(listOf(
|
||||
getCompletedLesson(LocalDate.of(2018, 9, 10), 1),
|
||||
getCompletedLesson(LocalDate.of(2018, 9, 14), 2),
|
||||
getCompletedLesson(LocalDate.of(2018, 9, 17), 3)
|
||||
getCompletedLesson(of(2018, 9, 10), 1),
|
||||
getCompletedLesson(of(2018, 9, 14), 2),
|
||||
getCompletedLesson(of(2018, 9, 17), 3)
|
||||
))
|
||||
|
||||
val completed = completedLessonsLocal
|
||||
.getCompletedLessons(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
LocalDate.of(2018, 9, 10),
|
||||
LocalDate.of(2018, 9, 14)
|
||||
.getCompletedLessons(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1),
|
||||
of(2018, 9, 10),
|
||||
of(2018, 9, 14)
|
||||
)
|
||||
.blockingGet()
|
||||
assertEquals(2, completed.size)
|
||||
assertEquals(completed[0].date, LocalDate.of(2018, 9, 10))
|
||||
assertEquals(completed[1].date, LocalDate.of(2018, 9, 14))
|
||||
assertEquals(completed[0].date, of(2018, 9, 10))
|
||||
assertEquals(completed[1].date, of(2018, 9, 14))
|
||||
}
|
||||
|
||||
private fun getCompletedLesson(date: LocalDate, number: Int): CompletedLesson {
|
||||
|
@ -10,7 +10,8 @@ import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import org.threeten.bp.LocalDate.of
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@ -34,19 +35,19 @@ class ExamLocalTest {
|
||||
@Test
|
||||
fun saveAndReadTest() {
|
||||
examLocal.saveExams(listOf(
|
||||
Exam(1, 2, LocalDate.of(2018, 9, 10), LocalDate.now(), "", "", "", "", "", ""),
|
||||
Exam(1, 2, LocalDate.of(2018, 9, 14), LocalDate.now(), "", "", "", "", "", ""),
|
||||
Exam(1, 2, LocalDate.of(2018, 9, 17), LocalDate.now(), "", "", "", "", "", "")
|
||||
Exam(1, 2, of(2018, 9, 10), now(), "", "", "", "", "", ""),
|
||||
Exam(1, 2, of(2018, 9, 14), now(), "", "", "", "", "", ""),
|
||||
Exam(1, 2, of(2018, 9, 17), now(), "", "", "", "", "", "")
|
||||
))
|
||||
|
||||
val exams = examLocal
|
||||
.getExams(Semester(1, 2, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
LocalDate.of(2018, 9, 10),
|
||||
LocalDate.of(2018, 9, 14)
|
||||
)
|
||||
.blockingGet()
|
||||
.getExams(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1),
|
||||
of(2018, 9, 10),
|
||||
of(2018, 9, 14)
|
||||
)
|
||||
.blockingGet()
|
||||
assertEquals(2, exams.size)
|
||||
assertEquals(exams[0].date, LocalDate.of(2018, 9, 10))
|
||||
assertEquals(exams[1].date, LocalDate.of(2018, 9, 14))
|
||||
assertEquals(exams[0].date, of(2018, 9, 10))
|
||||
assertEquals(exams[1].date, of(2018, 9, 14))
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ class GradeLocalTest {
|
||||
createGradeLocal(3, 5.0, LocalDate.of(2019, 2, 28), "", 2)
|
||||
))
|
||||
|
||||
val semester = Semester(1, 2, "", 2019, 2, 1, true, now(), now(), 1, 1)
|
||||
val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1)
|
||||
|
||||
val grades = gradeLocal
|
||||
.getGrades(semester)
|
||||
|
@ -6,7 +6,6 @@ import androidx.test.core.app.ApplicationProvider.getApplicationContext
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SdkSuppress
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
import io.github.wulkanowy.data.SdkHelper
|
||||
import io.github.wulkanowy.data.db.AppDatabase
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
@ -15,9 +14,6 @@ import io.github.wulkanowy.sdk.Sdk
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.impl.annotations.SpyK
|
||||
import io.mockk.just
|
||||
import io.mockk.runs
|
||||
import io.reactivex.Single
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
|
@ -11,7 +11,7 @@ import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDate.now
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@ -40,10 +40,7 @@ class GradeStatisticsLocalTest {
|
||||
getGradeStatistics("Fizyka", 1, 2)
|
||||
))
|
||||
|
||||
val stats = gradeStatisticsLocal.getGradesStatistics(
|
||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false,
|
||||
"Matematyka"
|
||||
).blockingGet()
|
||||
val stats = gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Matematyka").blockingGet()
|
||||
assertEquals(1, stats.size)
|
||||
assertEquals(stats[0].subject, "Matematyka")
|
||||
}
|
||||
@ -56,12 +53,11 @@ class GradeStatisticsLocalTest {
|
||||
getGradeStatistics("Fizyka", 1, 2)
|
||||
))
|
||||
|
||||
val stats = gradeStatisticsLocal.getGradesStatistics(
|
||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1), false,
|
||||
"Wszystkie"
|
||||
).blockingGet()
|
||||
assertEquals(1, stats.size)
|
||||
val stats = gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Wszystkie").blockingGet()
|
||||
assertEquals(3, stats.size)
|
||||
assertEquals(stats[0].subject, "Wszystkie")
|
||||
assertEquals(stats[1].subject, "Matematyka")
|
||||
assertEquals(stats[2].subject, "Chemia")
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -72,11 +68,8 @@ class GradeStatisticsLocalTest {
|
||||
getGradePointsStatistics("Fizyka", 1, 2)
|
||||
))
|
||||
|
||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
|
||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
"Matematyka"
|
||||
).blockingGet()
|
||||
with(stats) {
|
||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka").blockingGet()
|
||||
with(stats[0]) {
|
||||
assertEquals(subject, "Matematyka")
|
||||
assertEquals(others, 5.0)
|
||||
assertEquals(student, 5.0)
|
||||
@ -87,10 +80,7 @@ class GradeStatisticsLocalTest {
|
||||
fun saveAndRead_subjectEmpty() {
|
||||
gradeStatisticsLocal.saveGradesPointsStatistics(listOf())
|
||||
|
||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
|
||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
"Matematyka"
|
||||
).blockingGet()
|
||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka").blockingGet()
|
||||
assertEquals(null, stats)
|
||||
}
|
||||
|
||||
@ -98,13 +88,14 @@ class GradeStatisticsLocalTest {
|
||||
fun saveAndRead_allEmpty() {
|
||||
gradeStatisticsLocal.saveGradesPointsStatistics(listOf())
|
||||
|
||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
|
||||
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
"Wszystkie"
|
||||
).blockingGet()
|
||||
val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Wszystkie").blockingGet()
|
||||
assertEquals(null, stats)
|
||||
}
|
||||
|
||||
private fun getSemester(): Semester {
|
||||
return Semester(2, 2, "", 2019, 1, 2, now(), now(), 1, 1)
|
||||
}
|
||||
|
||||
private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradeStatistics {
|
||||
return GradeStatistics(studentId, semesterId, subject, 5, 5, false)
|
||||
}
|
||||
|
@ -6,11 +6,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.github.wulkanowy.data.db.AppDatabase
|
||||
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 org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDateTime.now
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@ -36,7 +38,7 @@ class LuckyNumberLocalTest {
|
||||
fun saveAndReadTest() {
|
||||
luckyNumberLocal.saveLuckyNumber(LuckyNumber(1, LocalDate.of(2019, 1, 20), 14))
|
||||
|
||||
val luckyNumber = luckyNumberLocal.getLuckyNumber(Semester(1, 1, "", 1, 3, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
val luckyNumber = luckyNumberLocal.getLuckyNumber(Student("", "", "", "", "", "", false, "", "", "", 1, 1, "", "", "", "", "", 1, false, now()),
|
||||
LocalDate.of(2019, 1, 20)
|
||||
).blockingGet()
|
||||
|
||||
|
@ -42,7 +42,7 @@ class RecipientLocalTest {
|
||||
))
|
||||
|
||||
val recipients = recipientLocal.getRecipients(
|
||||
Student("fakelog.cf", "AUTO", "", "", "", "", false, "", "", "", 1, 0, "", "", "", "", 1, true, LocalDateTime.now()),
|
||||
Student("fakelog.cf", "AUTO", "", "", "", "", false, "", "", "", 1, 0, "", "", "", "", "", 1, true, LocalDateTime.now()),
|
||||
2,
|
||||
ReportingUnit(1, 4, "", 0, "", emptyList())
|
||||
).blockingGet()
|
||||
|
@ -39,7 +39,7 @@ class StudentLocalTest {
|
||||
|
||||
@Test
|
||||
fun saveAndReadTest() {
|
||||
studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", scrapperBaseUrl = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "", loginMode = "API", certificateKey = "", privateKey = "", mobileBaseUrl = "", userLoginId = 0, isParent = false)))
|
||||
studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", scrapperBaseUrl = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolShortName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "", loginMode = "API", certificateKey = "", privateKey = "", mobileBaseUrl = "", userLoginId = 0, isParent = false)))
|
||||
.blockingGet()
|
||||
|
||||
val student = studentLocal.getCurrentStudent(true).blockingGet()
|
||||
|
@ -2,8 +2,8 @@ package io.github.wulkanowy.data.repositories.timetable
|
||||
|
||||
import org.threeten.bp.LocalDateTime
|
||||
import org.threeten.bp.LocalDateTime.now
|
||||
import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote
|
||||
import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal
|
||||
import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote
|
||||
|
||||
fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableLocal {
|
||||
return TimetableLocal(
|
||||
@ -21,6 +21,7 @@ fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", s
|
||||
teacher = teacher,
|
||||
teacherOld = "",
|
||||
info = "",
|
||||
isStudentPlan = true,
|
||||
changes = changes,
|
||||
canceled = false
|
||||
)
|
||||
|
@ -41,7 +41,7 @@ class TimetableLocalTest {
|
||||
))
|
||||
|
||||
val exams = timetableDb.getTimetable(
|
||||
Semester(1, 2, "", 1, 1, 2019, true, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
Semester(1, 2, "", 1, 1, 2019, LocalDate.now(), LocalDate.now(), 1, 1),
|
||||
LocalDate.of(2018, 9, 10),
|
||||
LocalDate.of(2018, 9, 14)
|
||||
).blockingGet()
|
||||
|
12
app/src/debug/res/drawable-anydpi-v24/ic_stat_grade.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="30.434782"
|
||||
android:viewportHeight="30.434782">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 8.131 2.131 C 7.601 2.131 7.092 2.342 6.717 2.717 C 6.342 3.092 6.131 3.601 6.131 4.131 L 6.131 18.131 C 6.131 18.661 6.342 19.17 6.717 19.545 C 7.092 19.92 7.601 20.131 8.131 20.131 L 16.918 20.131 C 17.252 19.39 17.712 18.714 18.277 18.131 L 8.131 18.131 L 8.131 4.131 L 22.131 4.131 L 22.131 16.1 C 22.516 16.034 22.906 16.001 23.297 16 C 23.576 16.002 23.854 16.02 24.131 16.055 L 24.131 4.131 C 24.131 3.601 23.92 3.092 23.545 2.717 C 23.17 2.342 22.661 2.131 22.131 2.131 L 8.131 2.131 Z M 2.131 6.131 L 2.131 22.131 C 2.131 22.661 2.342 23.17 2.717 23.545 C 3.092 23.92 3.601 24.131 4.131 24.131 L 16.391 24.131 C 16.329 23.757 16.297 23.379 16.297 23 C 16.299 22.709 16.319 22.419 16.357 22.131 L 4.131 22.131 L 4.131 6.131 L 2.131 6.131 Z M 14.131 6.131 C 13.601 6.131 13.092 6.342 12.717 6.717 C 12.342 7.092 12.131 7.601 12.131 8.131 L 12.131 14.131 C 12.131 15.231 13.031 16.131 14.131 16.131 L 16.131 16.131 C 16.661 16.131 17.17 15.92 17.545 15.545 C 17.92 15.17 18.131 14.661 18.131 14.131 L 18.131 12.131 C 18.131 11.601 17.92 11.092 17.545 10.717 C 17.17 10.342 16.661 10.131 16.131 10.131 L 14.131 10.131 L 14.131 8.131 L 18.131 8.131 L 18.131 6.131 L 14.131 6.131 Z M 14.131 12.131 L 16.131 12.131 L 16.131 14.131 L 14.131 14.131 L 14.131 12.131 Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 20.174 28 L 20.174 18.521 L 23.091 18.521 Q 24.341 18.521 25.324 19.087 Q 26.314 19.647 26.867 20.689 Q 27.421 21.724 27.421 23.046 L 27.421 23.482 Q 27.421 24.803 26.874 25.832 Q 26.333 26.861 25.344 27.427 Q 24.354 27.993 23.111 28 Z M 22.128 20.103 L 22.128 26.431 L 23.072 26.431 Q 24.217 26.431 24.823 25.682 Q 25.428 24.934 25.441 23.54 L 25.441 23.039 Q 25.441 21.594 24.842 20.852 Q 24.243 20.103 23.091 20.103 Z" />
|
||||
</vector>
|
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="28.26087"
|
||||
android:viewportHeight="28.26087">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 10.734 3.043 C 9.234 3.043 8.043 4.293 8.043 5.793 C 8.043 7.683 9.743 9.223 13.043 12.223 C 16.343 9.223 18.043 7.684 18.043 5.734 C 18.043 4.234 16.793 3.043 15.293 3.043 C 14.433 3.043 13.613 3.403 13.043 4.043 C 12.473 3.403 11.654 3.043 10.734 3.043 Z M 5.734 8.043 C 4.234 8.043 3.043 9.293 3.043 10.793 C 3.043 11.653 3.403 12.473 4.043 13.043 C 3.403 13.613 3.043 14.434 3.043 15.354 C 3.043 16.854 4.293 18.043 5.793 18.043 C 7.683 18.043 9.223 16.343 12.223 13.043 C 9.223 9.743 7.684 8.043 5.734 8.043 Z M 20.293 8.043 C 18.403 8.043 16.863 9.743 13.873 13.043 C 15.043 14.334 15.987 15.373 16.824 16.168 C 17.476 15.484 18.26 14.94 19.129 14.567 C 19.997 14.195 20.932 14.002 21.877 14 C 22.181 14.002 22.484 14.025 22.785 14.066 C 22.615 13.68 22.365 13.329 22.043 13.043 C 22.683 12.473 23.043 11.654 23.043 10.734 C 23.043 9.234 21.793 8.043 20.293 8.043 Z M 13.043 13.863 C 9.743 16.863 8.043 18.404 8.043 20.354 C 8.043 21.854 9.293 23.043 10.793 23.043 C 11.653 23.043 12.473 22.683 13.043 22.043 C 13.576 22.642 14.333 22.982 15.182 23.025 C 14.981 22.369 14.879 21.686 14.877 21 C 14.88 19.52 15.351 18.078 16.225 16.883 C 15.422 16.032 14.368 15.067 13.043 13.863 Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 18.754 26 L 18.754 16.521 L 21.671 16.521 Q 22.921 16.521 23.904 17.087 Q 24.893 17.647 25.447 18.689 Q 26 19.724 26 21.046 L 26 21.482 Q 26 22.803 25.453 23.832 Q 24.913 24.861 23.923 25.427 Q 22.934 25.993 21.69 26 Z M 20.707 18.103 L 20.707 24.431 L 21.651 24.431 Q 22.797 24.431 23.402 23.682 Q 24.008 22.934 24.021 21.54 L 24.021 21.039 Q 24.021 19.594 23.422 18.852 Q 22.823 18.103 21.671 18.103 Z" />
|
||||
</vector>
|
12
app/src/debug/res/drawable-anydpi-v24/ic_stat_message.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="28.26087"
|
||||
android:viewportHeight="28.26087">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 5.043 3.043 C 3.943 3.043 3.053 3.943 3.053 5.043 L 3.043 23.043 L 7.043 19.043 L 15.16 19.043 C 15.646 17.378 16.733 15.952 18.209 15.043 L 7.043 15.043 L 7.043 13.043 L 19.043 13.043 L 19.043 14.609 C 19.935 14.211 20.9 14.003 21.877 14 C 22.268 14.003 22.658 14.038 23.043 14.105 L 23.043 5.043 C 23.043 3.943 22.143 3.043 21.043 3.043 L 5.043 3.043 Z M 7.043 7.043 L 19.043 7.043 L 19.043 9.043 L 7.043 9.043 L 7.043 7.043 Z M 7.043 10.043 L 19.043 10.043 L 19.043 12.043 L 7.043 12.043 L 7.043 10.043 Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 18.754 26 L 18.754 16.521 L 21.671 16.521 Q 22.921 16.521 23.904 17.087 Q 24.893 17.647 25.447 18.689 Q 26 19.724 26 21.046 L 26 21.482 Q 26 22.803 25.453 23.832 Q 24.913 24.861 23.923 25.427 Q 22.934 25.993 21.69 26 Z M 20.707 18.103 L 20.707 24.431 L 21.651 24.431 Q 22.797 24.431 23.402 23.682 Q 24.008 22.934 24.021 21.54 L 24.021 21.039 Q 24.021 19.594 23.422 18.852 Q 22.823 18.103 21.671 18.103 Z" />
|
||||
</vector>
|
12
app/src/debug/res/drawable-anydpi-v24/ic_stat_note.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="28.26087"
|
||||
android:viewportHeight="28.26087">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 3.043 3.043 L 3.043 12.043 C 3.043 13.043 4.043 14.043 5.043 14.043 L 7.244 14.043 C 7.644 16.043 8.943 17.743 12.043 18.043 L 12.043 20.143 C 9.843 20.343 9.043 21.444 9.043 22.744 L 9.043 23.043 L 15.188 23.043 C 14.983 22.381 14.879 21.693 14.877 21 C 14.877 20.763 14.888 20.527 14.912 20.291 C 14.649 20.226 14.364 20.172 14.043 20.143 L 14.043 18.043 C 14.68 17.981 15.224 17.848 15.717 17.678 C 16.382 16.446 17.401 15.442 18.643 14.795 C 18.725 14.551 18.792 14.299 18.844 14.043 L 21.043 14.043 C 22.043 14.043 23.043 13.043 23.043 12.043 L 23.043 3.043 L 19.043 3.043 C 18.143 3.043 17.043 4.043 17.043 5.043 L 9.043 5.043 C 9.043 4.043 7.943 3.043 7.043 3.043 L 3.043 3.043 Z M 5.043 5.043 L 7.043 5.043 L 7.043 12.043 L 5.043 12.043 L 5.043 5.043 Z M 19.043 5.043 L 21.043 5.043 L 21.043 12.043 L 19.043 12.043 L 19.043 5.043 Z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M 18.754 26 L 18.754 16.521 L 21.671 16.521 Q 22.921 16.521 23.904 17.087 Q 24.893 17.647 25.447 18.689 Q 26 19.724 26 21.046 L 26 21.482 Q 26 22.803 25.453 23.832 Q 24.913 24.861 23.923 25.427 Q 22.934 25.993 21.69 26 Z M 20.707 18.103 L 20.707 24.431 L 21.651 24.431 Q 22.797 24.431 23.402 23.682 Q 24.008 22.934 24.021 21.54 L 24.021 21.039 Q 24.021 19.594 23.422 18.852 Q 22.823 18.103 21.671 18.103 Z" />
|
||||
</vector>
|
BIN
app/src/debug/res/drawable-hdpi/ic_stat_grade.png
Normal file
After Width: | Height: | Size: 478 B |
BIN
app/src/debug/res/drawable-hdpi/ic_stat_luckynumber.png
Normal file
After Width: | Height: | Size: 652 B |
BIN
app/src/debug/res/drawable-hdpi/ic_stat_message.png
Normal file
After Width: | Height: | Size: 384 B |
BIN
app/src/debug/res/drawable-hdpi/ic_stat_note.png
Normal file
After Width: | Height: | Size: 460 B |
BIN
app/src/debug/res/drawable-mdpi/ic_stat_grade.png
Normal file
After Width: | Height: | Size: 355 B |
BIN
app/src/debug/res/drawable-mdpi/ic_stat_luckynumber.png
Normal file
After Width: | Height: | Size: 451 B |
BIN
app/src/debug/res/drawable-mdpi/ic_stat_message.png
Normal file
After Width: | Height: | Size: 297 B |
BIN
app/src/debug/res/drawable-mdpi/ic_stat_note.png
Normal file
After Width: | Height: | Size: 339 B |
BIN
app/src/debug/res/drawable-xhdpi/ic_stat_grade.png
Normal file
After Width: | Height: | Size: 588 B |
BIN
app/src/debug/res/drawable-xhdpi/ic_stat_luckynumber.png
Normal file
After Width: | Height: | Size: 897 B |
BIN
app/src/debug/res/drawable-xhdpi/ic_stat_message.png
Normal file
After Width: | Height: | Size: 508 B |
BIN
app/src/debug/res/drawable-xhdpi/ic_stat_note.png
Normal file
After Width: | Height: | Size: 586 B |
BIN
app/src/debug/res/drawable-xxhdpi/ic_stat_grade.png
Normal file
After Width: | Height: | Size: 781 B |
BIN
app/src/debug/res/drawable-xxhdpi/ic_stat_luckynumber.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/debug/res/drawable-xxhdpi/ic_stat_message.png
Normal file
After Width: | Height: | Size: 692 B |
BIN
app/src/debug/res/drawable-xxhdpi/ic_stat_note.png
Normal file
After Width: | Height: | Size: 805 B |
@ -5,13 +5,12 @@ package io.github.wulkanowy.utils
|
||||
import android.content.Context
|
||||
import timber.log.Timber
|
||||
|
||||
fun initCrashlytics(context: Context, appInfo: AppInfo) {
|
||||
// do nothing
|
||||
fun initCrashlytics(context: Context, appInfo: AppInfo) {}
|
||||
|
||||
open class TimberTreeNoOp : Timber.Tree() {
|
||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {}
|
||||
}
|
||||
|
||||
class CrashlyticsTree : Timber.Tree() {
|
||||
class CrashlyticsTree : TimberTreeNoOp()
|
||||
|
||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
class CrashlyticsExceptionTree : TimberTreeNoOp()
|
||||
|
@ -43,8 +43,8 @@
|
||||
android:name=".ui.modules.message.send.SendMessageActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/send_message_title"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar" />
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||
android:excludeFromRecents="true"
|
||||
@ -96,6 +96,16 @@
|
||||
android:exported="false"
|
||||
tools:node="remove" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
|
||||
<meta-data
|
||||
android:name="io.fabric.ApiKey"
|
||||
android:value="${fabric_api_key}" />
|
||||
|
38
app/src/main/assets/contributors.json
Normal file
@ -0,0 +1,38 @@
|
||||
[
|
||||
{
|
||||
"displayName": "Mikołaj Pich",
|
||||
"githubUsername": "mklkj"
|
||||
},
|
||||
{
|
||||
"displayName": "Rafał Borcz",
|
||||
"githubUsername": "Faierbel"
|
||||
},
|
||||
{
|
||||
"displayName": "Dominik Korsa",
|
||||
"githubUsername": "dominik-korsa"
|
||||
},
|
||||
{
|
||||
"displayName": "Kacper Ziubryniewicz",
|
||||
"githubUsername": "kapi2289"
|
||||
},
|
||||
{
|
||||
"displayName": "doteq",
|
||||
"githubUsername": "doteq"
|
||||
},
|
||||
{
|
||||
"displayName": "Paweł Krzyś",
|
||||
"githubUsername": "pavuloff"
|
||||
},
|
||||
{
|
||||
"displayName": "Piotr Romanowski",
|
||||
"githubUsername": "v0idzz"
|
||||
},
|
||||
{
|
||||
"displayName": "Dinolek",
|
||||
"githubUsername": "Dinolek"
|
||||
},
|
||||
{
|
||||
"displayName": "Mateusz Idziejczak",
|
||||
"githubUsername": "PanTajemnic"
|
||||
}
|
||||
]
|
@ -1,6 +1,7 @@
|
||||
package io.github.wulkanowy
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log.DEBUG
|
||||
import android.util.Log.INFO
|
||||
import android.util.Log.VERBOSE
|
||||
import androidx.multidex.MultiDex
|
||||
@ -11,11 +12,13 @@ import dagger.android.AndroidInjector
|
||||
import dagger.android.support.DaggerApplication
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.utils.Log
|
||||
import fr.bipi.tressence.file.FileLoggerTree
|
||||
import io.github.wulkanowy.di.DaggerAppComponent
|
||||
import io.github.wulkanowy.services.sync.SyncWorkerFactory
|
||||
import io.github.wulkanowy.ui.base.ThemeManager
|
||||
import io.github.wulkanowy.utils.ActivityLifecycleLogger
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.CrashlyticsExceptionTree
|
||||
import io.github.wulkanowy.utils.CrashlyticsTree
|
||||
import io.github.wulkanowy.utils.DebugLogTree
|
||||
import io.github.wulkanowy.utils.initCrashlytics
|
||||
@ -54,9 +57,17 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider {
|
||||
|
||||
private fun initLogging() {
|
||||
if (appInfo.isDebug) {
|
||||
Timber.plant(DebugLogTree())
|
||||
FlexibleAdapter.enableLogs(Log.Level.DEBUG)
|
||||
Timber.plant(DebugLogTree())
|
||||
Timber.plant(FileLoggerTree.Builder()
|
||||
.withFileName("wulkanowy.%g.log")
|
||||
.withDirName(applicationContext.filesDir.absolutePath)
|
||||
.withFileLimit(10)
|
||||
.withMinPriority(DEBUG)
|
||||
.build()
|
||||
)
|
||||
} else {
|
||||
Timber.plant(CrashlyticsExceptionTree())
|
||||
Timber.plant(CrashlyticsTree())
|
||||
}
|
||||
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
|
||||
|
@ -2,6 +2,7 @@ package io.github.wulkanowy.data
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.AssetManager
|
||||
import android.content.res.Resources
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
@ -58,6 +59,10 @@ internal class RepositoryModule {
|
||||
@Provides
|
||||
fun provideResources(context: Context): Resources = context.resources
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAssets(context: Context): AssetManager = context.assets
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideSharedPref(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
@ -60,6 +60,9 @@ import io.github.wulkanowy.data.db.migrations.Migration17
|
||||
import io.github.wulkanowy.data.db.migrations.Migration18
|
||||
import io.github.wulkanowy.data.db.migrations.Migration19
|
||||
import io.github.wulkanowy.data.db.migrations.Migration2
|
||||
import io.github.wulkanowy.data.db.migrations.Migration20
|
||||
import io.github.wulkanowy.data.db.migrations.Migration21
|
||||
import io.github.wulkanowy.data.db.migrations.Migration22
|
||||
import io.github.wulkanowy.data.db.migrations.Migration3
|
||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||
@ -101,7 +104,7 @@ import javax.inject.Singleton
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 19
|
||||
const val VERSION_SCHEMA = 22
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
|
||||
return arrayOf(
|
||||
@ -122,7 +125,10 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
Migration16(),
|
||||
Migration17(),
|
||||
Migration18(),
|
||||
Migration19(sharedPrefProvider)
|
||||
Migration19(sharedPrefProvider),
|
||||
Migration20(),
|
||||
Migration21(),
|
||||
Migration22()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ import javax.inject.Singleton
|
||||
interface GradePointsStatisticsDao : BaseDao<GradePointsStatistics> {
|
||||
|
||||
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName")
|
||||
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): Maybe<GradePointsStatistics>
|
||||
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): Maybe<List<GradePointsStatistics>>
|
||||
|
||||
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId")
|
||||
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<GradePointsStatistics>>
|
||||
|
@ -4,6 +4,7 @@ import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
|
||||
@Dao
|
||||
interface MessagesDao : BaseDao<Message> {
|
||||
@ -12,7 +13,7 @@ interface MessagesDao : BaseDao<Message> {
|
||||
fun loadAll(studentId: Int, folder: Int): Maybe<List<Message>>
|
||||
|
||||
@Query("SELECT * FROM Messages WHERE id = :id")
|
||||
fun load(id: Long): Maybe<Message>
|
||||
fun load(id: Long): Single<Message>
|
||||
|
||||
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC")
|
||||
fun loadDeleted(studentId: Int): Maybe<List<Message>>
|
||||
|
@ -15,6 +15,9 @@ data class Attendance(
|
||||
@ColumnInfo(name = "diary_id")
|
||||
val diaryId: Int,
|
||||
|
||||
@ColumnInfo(name = "time_id")
|
||||
val timeId: Int,
|
||||
|
||||
val date: LocalDate,
|
||||
|
||||
val number: Int,
|
||||
@ -33,7 +36,13 @@ data class Attendance(
|
||||
|
||||
val excused: Boolean,
|
||||
|
||||
val deleted: Boolean
|
||||
val deleted: Boolean,
|
||||
|
||||
val excusable: Boolean,
|
||||
|
||||
@ColumnInfo(name = "excuse_status")
|
||||
val excuseStatus: String?
|
||||
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
|
@ -27,9 +27,6 @@ data class Semester(
|
||||
@ColumnInfo(name = "semester_name")
|
||||
val semesterName: Int,
|
||||
|
||||
@ColumnInfo(name = "is_current")
|
||||
val isCurrent: Boolean,
|
||||
|
||||
val start: LocalDate,
|
||||
|
||||
val end: LocalDate,
|
||||
@ -43,4 +40,8 @@ data class Semester(
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
||||
|
||||
@ColumnInfo(name = "is_current")
|
||||
var current: Boolean = false
|
||||
}
|
||||
|
@ -49,6 +49,9 @@ data class Student(
|
||||
@ColumnInfo(name = "school_id")
|
||||
val schoolSymbol: String,
|
||||
|
||||
@ColumnInfo(name ="school_short")
|
||||
val schoolShortName: String,
|
||||
|
||||
@ColumnInfo(name = "school_name")
|
||||
val schoolName: String,
|
||||
|
||||
|
@ -40,6 +40,9 @@ data class Timetable(
|
||||
|
||||
val info: String,
|
||||
|
||||
@ColumnInfo(name = "student_plan")
|
||||
val isStudentPlan: Boolean,
|
||||
|
||||
val changes: Boolean,
|
||||
|
||||
val canceled: Boolean
|
||||
|
@ -0,0 +1,42 @@
|
||||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration20 : Migration(19, 20) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
migrateTimetable(database)
|
||||
truncateSubjects(database)
|
||||
}
|
||||
|
||||
private fun migrateTimetable(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE Timetable")
|
||||
database.execSQL("""
|
||||
CREATE TABLE IF NOT EXISTS `Timetable` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`student_id` INTEGER NOT NULL,
|
||||
`diary_id` INTEGER NOT NULL,
|
||||
`number` INTEGER NOT NULL,
|
||||
`start` INTEGER NOT NULL,
|
||||
`end` INTEGER NOT NULL,
|
||||
`date` INTEGER NOT NULL,
|
||||
`subject` TEXT NOT NULL,
|
||||
`subjectOld` TEXT NOT NULL,
|
||||
`group` TEXT NOT NULL,
|
||||
`room` TEXT NOT NULL,
|
||||
`roomOld` TEXT NOT NULL,
|
||||
`teacher` TEXT NOT NULL,
|
||||
`teacherOld` TEXT NOT NULL,
|
||||
`info` TEXT NOT NULL,
|
||||
`student_plan` INTEGER NOT NULL,
|
||||
`changes` INTEGER NOT NULL,
|
||||
`canceled` INTEGER NOT NULL
|
||||
)
|
||||
""")
|
||||
}
|
||||
|
||||
private fun truncateSubjects(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DELETE FROM Subjects")
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration21 : Migration(20, 21) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE Attendance ADD COLUMN excusable INTEGER NOT NULL DEFAULT 0")
|
||||
database.execSQL("ALTER TABLE Attendance ADD COLUMN time_id INTEGER NOT NULL DEFAULT 0")
|
||||
database.execSQL("ALTER TABLE Attendance ADD COLUMN excuse_status TEXT DEFAULT NULL")
|
||||
|
||||
database.execSQL("DELETE FROM Semesters")
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration22 : Migration(21, 22) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE Students ADD COLUMN school_short TEXT NOT NULL DEFAULT ''")
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package io.github.wulkanowy.data.pojos
|
||||
|
||||
class AppCreator(val displayName: String, val githubUsername: String)
|
@ -0,0 +1,14 @@
|
||||
package io.github.wulkanowy.data.pojos
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
|
||||
|
||||
data class GradeStatisticsItem(
|
||||
|
||||
val type: ViewType,
|
||||
|
||||
val partial: List<GradeStatistics>,
|
||||
|
||||
val points: GradePointsStatistics?
|
||||
)
|
@ -0,0 +1,20 @@
|
||||
package io.github.wulkanowy.data.repositories.appcreator
|
||||
|
||||
import android.content.res.AssetManager
|
||||
import com.google.gson.Gson
|
||||
import io.github.wulkanowy.data.pojos.AppCreator
|
||||
import io.reactivex.Single
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AppCreatorRepository @Inject constructor(private val assets: AssetManager) {
|
||||
fun getAppCreators(): Single<List<AppCreator>> {
|
||||
return Single.fromCallable<List<AppCreator>> {
|
||||
Gson().fromJson(
|
||||
assets.open("contributors.json").bufferedReader().use { it.readText() },
|
||||
Array<AppCreator>::class.java
|
||||
).toList()
|
||||
}
|
||||
}
|
||||
}
|
@ -3,8 +3,11 @@ package io.github.wulkanowy.data.repositories.attendance
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.pojo.Absent
|
||||
import io.reactivex.Single
|
||||
import org.threeten.bp.LocalDate
|
||||
import org.threeten.bp.LocalDateTime
|
||||
import org.threeten.bp.LocalTime
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@ -19,6 +22,7 @@ class AttendanceRemote @Inject constructor(private val sdk: Sdk) {
|
||||
studentId = semester.studentId,
|
||||
diaryId = semester.diaryId,
|
||||
date = it.date,
|
||||
timeId = it.timeId,
|
||||
number = it.number,
|
||||
subject = it.subject,
|
||||
name = it.name,
|
||||
@ -27,9 +31,20 @@ class AttendanceRemote @Inject constructor(private val sdk: Sdk) {
|
||||
exemption = it.exemption,
|
||||
lateness = it.lateness,
|
||||
excused = it.excused,
|
||||
deleted = it.deleted
|
||||
deleted = it.deleted,
|
||||
excusable = it.excusable,
|
||||
excuseStatus = it.excuseStatus?.name
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun excuseAbsence(semester: Semester, absenceList: List<Attendance>, reason: String?): Single<Boolean> {
|
||||
return sdk.switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance ->
|
||||
Absent(
|
||||
date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)),
|
||||
timeId = attendance.timeId
|
||||
)
|
||||
}, reason)
|
||||
}
|
||||
}
|
||||
|
@ -41,4 +41,8 @@ class AttendanceRepository @Inject constructor(
|
||||
}).map { list -> list.filter { it.date in startDate..endDate } }
|
||||
}
|
||||
}
|
||||
|
||||
fun excuseForAbsence(semester: Semester, attendanceList: List<Attendance>, reason: String? = null): Single<Boolean> {
|
||||
return remote.excuseAbsence(semester, attendanceList, reason)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package io.github.wulkanowy.data.repositories.attendance
|
||||
|
||||
enum class SentExcuseStatus(val id: Int = 0) {
|
||||
WAITING,
|
||||
ACCEPTED,
|
||||
DENIED
|
||||
}
|
@ -29,23 +29,17 @@ class GradeStatisticsLocal @Inject constructor(
|
||||
list.groupBy { it.grade }.map {
|
||||
GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key,
|
||||
it.value.fold(0) { acc, e -> acc + e.amount }, false)
|
||||
}
|
||||
} + list
|
||||
}
|
||||
else -> gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)
|
||||
}.filter { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
fun getGradesPointsStatistics(semester: Semester, subjectName: String): Maybe<GradePointsStatistics> {
|
||||
fun getGradesPointsStatistics(semester: Semester, subjectName: String): Maybe<List<GradePointsStatistics>> {
|
||||
return when (subjectName) {
|
||||
"Wszystkie" -> gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId).flatMap { list ->
|
||||
if (list.isEmpty()) return@flatMap Maybe.empty<GradePointsStatistics>()
|
||||
Maybe.just(GradePointsStatistics(semester.studentId, semester.semesterId, subjectName,
|
||||
list.fold(.0) { acc, e -> acc + e.others },
|
||||
list.fold(.0) { acc, e -> acc + e.student })
|
||||
)
|
||||
}
|
||||
"Wszystkie" -> gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId)
|
||||
else -> gradePointsStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName)
|
||||
}
|
||||
}.filter { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
fun saveGradesStatistics(gradesStatistics: List<GradeStatistics>) {
|
||||
|
@ -5,8 +5,9 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter
|
||||
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||
import io.github.wulkanowy.data.db.entities.GradeStatistics
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.pojos.GradeStatisticsItem
|
||||
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
@ -19,8 +20,8 @@ class GradeStatisticsRepository @Inject constructor(
|
||||
private val remote: GradeStatisticsRemote
|
||||
) {
|
||||
|
||||
fun getGradesStatistics(semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single<List<GradeStatistics>> {
|
||||
return local.getGradesStatistics(semester, isSemester, subjectName).filter { !forceRefresh }
|
||||
fun getGradesStatistics(semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single<List<GradeStatisticsItem>> {
|
||||
return local.getGradesStatistics(semester, isSemester, subjectName).map { it.mapToStatisticItems() }.filter { !forceRefresh }
|
||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||
.flatMap {
|
||||
if (it) remote.getGradeStatistics(semester, isSemester)
|
||||
@ -31,21 +32,43 @@ class GradeStatisticsRepository @Inject constructor(
|
||||
local.deleteGradesStatistics(old.uniqueSubtract(new))
|
||||
local.saveGradesStatistics(new.uniqueSubtract(old))
|
||||
}
|
||||
}.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).toSingle(emptyList()) })
|
||||
}.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).map { it.mapToStatisticItems() }.toSingle(emptyList()) })
|
||||
}
|
||||
|
||||
fun getGradesPointsStatistics(semester: Semester, subjectName: String, forceRefresh: Boolean): Maybe<GradePointsStatistics> {
|
||||
return local.getGradesPointsStatistics(semester, subjectName).filter { !forceRefresh }
|
||||
fun getGradesPointsStatistics(semester: Semester, subjectName: String, forceRefresh: Boolean): Single<List<GradeStatisticsItem>> {
|
||||
return local.getGradesPointsStatistics(semester, subjectName).map { it.mapToStatisticsItem() }.filter { !forceRefresh }
|
||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||
.flatMapMaybe {
|
||||
if (it) remote.getGradePointsStatistics(semester).toMaybe()
|
||||
else Maybe.error(UnknownHostException())
|
||||
.flatMap {
|
||||
if (it) remote.getGradePointsStatistics(semester)
|
||||
else Single.error(UnknownHostException())
|
||||
}.flatMap { new ->
|
||||
local.getGradesPointsStatistics(semester).defaultIfEmpty(emptyList())
|
||||
local.getGradesPointsStatistics(semester).toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteGradesPointsStatistics(old.uniqueSubtract(new))
|
||||
local.saveGradesPointsStatistics(new.uniqueSubtract(old))
|
||||
}
|
||||
}.flatMap { local.getGradesPointsStatistics(semester, subjectName) })
|
||||
}.flatMap { local.getGradesPointsStatistics(semester, subjectName).map { it.mapToStatisticsItem() }.toSingle(emptyList()) })
|
||||
}
|
||||
|
||||
private fun List<GradeStatistics>.mapToStatisticItems(): List<GradeStatisticsItem> {
|
||||
return groupBy { it.subject }.map {
|
||||
GradeStatisticsItem(
|
||||
type = ViewType.PARTIAL,
|
||||
partial = it.value
|
||||
.sortedByDescending { item -> item.grade }
|
||||
.filter { item -> item.amount != 0 },
|
||||
points = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<GradePointsStatistics>.mapToStatisticsItem(): List<GradeStatisticsItem> {
|
||||
return map {
|
||||
GradeStatisticsItem(
|
||||
type = ViewType.POINTS,
|
||||
partial = emptyList(),
|
||||
points = it
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
package io.github.wulkanowy.data.repositories.logger
|
||||
|
||||
import android.content.Context
|
||||
import io.reactivex.Single
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import javax.inject.Inject
|
||||
|
||||
class LoggerRepository @Inject constructor(private val context: Context) {
|
||||
|
||||
fun getLastLogLines(): Single<List<String>> {
|
||||
return getLastModified()
|
||||
.map { it.readText() }
|
||||
.map { it.split("\n") }
|
||||
}
|
||||
|
||||
fun getLogFiles(): Single<List<File>> {
|
||||
return Single.fromCallable {
|
||||
File(context.filesDir.absolutePath).listFiles(File::isFile)?.filter {
|
||||
it.name.endsWith(".log")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLastModified(): Single<File> {
|
||||
return Single.fromCallable {
|
||||
var lastModifiedTime = Long.MIN_VALUE
|
||||
var chosenFile: File? = null
|
||||
File(context.filesDir.absolutePath).listFiles(File::isFile)?.forEach { file ->
|
||||
if (file.lastModified() > lastModifiedTime) {
|
||||
lastModifiedTime = file.lastModified()
|
||||
chosenFile = file
|
||||
}
|
||||
}
|
||||
if (chosenFile == null) throw FileNotFoundException("Log file not found")
|
||||
chosenFile
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ package io.github.wulkanowy.data.repositories.luckynumber
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
||||
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.reactivex.Maybe
|
||||
import org.threeten.bp.LocalDate
|
||||
import javax.inject.Inject
|
||||
@ -23,7 +23,7 @@ class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumbe
|
||||
luckyNumberDb.deleteAll(listOf(luckyNumber))
|
||||
}
|
||||
|
||||
fun getLuckyNumber(semester: Semester, date: LocalDate): Maybe<LuckyNumber> {
|
||||
return luckyNumberDb.load(semester.studentId, date)
|
||||
fun getLuckyNumber(student: Student, date: LocalDate): Maybe<LuckyNumber> {
|
||||
return luckyNumberDb.load(student.studentId, date)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package io.github.wulkanowy.data.repositories.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.sdk.Sdk
|
||||
import io.reactivex.Maybe
|
||||
import org.threeten.bp.LocalDate
|
||||
@ -11,14 +11,13 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class LuckyNumberRemote @Inject constructor(private val sdk: Sdk) {
|
||||
|
||||
fun getLuckyNumber(semester: Semester): Maybe<LuckyNumber> {
|
||||
return sdk.getLuckyNumber()
|
||||
.map {
|
||||
LuckyNumber(
|
||||
studentId = semester.studentId,
|
||||
date = LocalDate.now(),
|
||||
luckyNumber = it
|
||||
)
|
||||
}
|
||||
fun getLuckyNumber(student: Student): Maybe<LuckyNumber> {
|
||||
return sdk.getLuckyNumber(student.schoolShortName).map {
|
||||
LuckyNumber(
|
||||
studentId = student.studentId,
|
||||
date = LocalDate.now(),
|
||||
luckyNumber = it
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package io.github.wulkanowy.data.repositories.luckynumber
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Maybe
|
||||
import org.threeten.bp.LocalDate
|
||||
@ -18,14 +18,14 @@ class LuckyNumberRepository @Inject constructor(
|
||||
private val remote: LuckyNumberRemote
|
||||
) {
|
||||
|
||||
fun getLuckyNumber(semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Maybe<LuckyNumber> {
|
||||
return local.getLuckyNumber(semester, LocalDate.now()).filter { !forceRefresh }
|
||||
fun getLuckyNumber(student: Student, forceRefresh: Boolean = false, notify: Boolean = false): Maybe<LuckyNumber> {
|
||||
return local.getLuckyNumber(student, LocalDate.now()).filter { !forceRefresh }
|
||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||
.flatMapMaybe {
|
||||
if (it) remote.getLuckyNumber(semester)
|
||||
if (it) remote.getLuckyNumber(student)
|
||||
else Maybe.error(UnknownHostException())
|
||||
}.flatMap { new ->
|
||||
local.getLuckyNumber(semester, LocalDate.now())
|
||||
local.getLuckyNumber(student, LocalDate.now())
|
||||
.doOnSuccess { old ->
|
||||
if (new != old) {
|
||||
local.deleteLuckyNumber(old)
|
||||
@ -39,13 +39,13 @@ class LuckyNumberRepository @Inject constructor(
|
||||
if (notify) isNotified = false
|
||||
})
|
||||
}
|
||||
}.flatMap({ local.getLuckyNumber(semester, LocalDate.now()) }, { Maybe.error(it) },
|
||||
{ local.getLuckyNumber(semester, LocalDate.now()) })
|
||||
}.flatMap({ local.getLuckyNumber(student, LocalDate.now()) }, { Maybe.error(it) },
|
||||
{ local.getLuckyNumber(student, LocalDate.now()) })
|
||||
)
|
||||
}
|
||||
|
||||
fun getNotNotifiedLuckyNumber(semester: Semester): Maybe<LuckyNumber> {
|
||||
return local.getLuckyNumber(semester, LocalDate.now()).filter { !it.isNotified }
|
||||
fun getNotNotifiedLuckyNumber(student: Student): Maybe<LuckyNumber> {
|
||||
return local.getLuckyNumber(student, LocalDate.now()).filter { !it.isNotified }
|
||||
}
|
||||
|
||||
fun updateLuckyNumber(luckyNumber: LuckyNumber): Completable {
|
||||
|
@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@ -23,7 +24,7 @@ class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) {
|
||||
messagesDb.deleteAll(messages)
|
||||
}
|
||||
|
||||
fun getMessage(id: Long): Maybe<Message> {
|
||||
fun getMessage(id: Long): Single<Message> {
|
||||
return messagesDb.load(id)
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import timber.log.Timber
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@ -51,21 +52,26 @@ class MessageRepository @Inject constructor(
|
||||
return Single.just(sdkHelper.init(student))
|
||||
.flatMap { _ ->
|
||||
local.getMessage(messageDbId)
|
||||
.filter { it.content.isNotEmpty() }
|
||||
.filter {
|
||||
it.content.isNotEmpty().also { status ->
|
||||
Timber.d("Message content in db empty: ${!status}")
|
||||
}
|
||||
}
|
||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||
.flatMap {
|
||||
if (it) local.getMessage(messageDbId).toSingle()
|
||||
if (it) local.getMessage(messageDbId)
|
||||
else Single.error(UnknownHostException())
|
||||
}
|
||||
.flatMap { dbMessage ->
|
||||
remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess {
|
||||
local.updateMessages(listOf(dbMessage.copy(unread = false).apply {
|
||||
local.updateMessages(listOf(dbMessage.copy(unread = !markAsRead).apply {
|
||||
id = dbMessage.id
|
||||
content = content.ifBlank { it }
|
||||
}))
|
||||
Timber.d("Message $messageDbId with blank content: ${dbMessage.content.isBlank()}, marked as read")
|
||||
}
|
||||
}.flatMap {
|
||||
local.getMessage(messageDbId).toSingle()
|
||||
local.getMessage(messageDbId)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -65,6 +65,9 @@ class PreferencesRepository @Inject constructor(
|
||||
val fillMessageContent: Boolean
|
||||
get() = getBoolean(R.string.pref_key_fill_message_content, R.bool.pref_default_fill_message_content)
|
||||
|
||||
val showWholeClassPlan: String
|
||||
get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class)
|
||||
|
||||
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
|
||||
|
||||
private fun getString(id: String, default: Int) = sharedPref.getString(id, context.getString(default)) ?: context.getString(default)
|
||||
|
@ -0,0 +1,19 @@
|
||||
package io.github.wulkanowy.data.repositories.recover
|
||||
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.reactivex.Single
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class RecoverRemote @Inject constructor(private val sdk: Sdk) {
|
||||
|
||||
fun getReCaptchaSiteKey(host: String, symbol: String): Single<Pair<String, String>> {
|
||||
return sdk.getPasswordResetCaptchaCode(host, symbol)
|
||||
}
|
||||
|
||||
fun sendRecoverRequest(url: String, symbol: String, email: String, reCaptchaResponse: String): Single<String> {
|
||||
return sdk.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
package io.github.wulkanowy.data.repositories.recover
|
||||
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
|
||||
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
|
||||
import io.reactivex.Single
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class RecoverRepository @Inject constructor(private val settings: InternetObservingSettings, private val remote: RecoverRemote) {
|
||||
|
||||
fun getReCaptchaSiteKey(host: String, symbol: String): Single<Pair<String, String>> {
|
||||
return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
|
||||
if (it) remote.getReCaptchaSiteKey(host, symbol)
|
||||
else Single.error(UnknownHostException())
|
||||
}
|
||||
}
|
||||
|
||||
fun sendRecoverRequest(url: String, symbol: String, email: String, reCaptchaResponse: String): Single<String> {
|
||||
return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
|
||||
if (it) remote.sendRecoverRequest(url, symbol, email, reCaptchaResponse)
|
||||
else Single.error(UnknownHostException())
|
||||
}
|
||||
}
|
||||
}
|
@ -19,6 +19,6 @@ class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) {
|
||||
}
|
||||
|
||||
fun getSemesters(student: Student): Maybe<List<Semester>> {
|
||||
return semesterDb.loadAll(student.studentId, student.classId).filter { !it.isEmpty() }
|
||||
return semesterDb.loadAll(student.studentId, student.classId).filter { it.isNotEmpty() }
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ class SemesterRemote @Inject constructor(private val sdk: Sdk) {
|
||||
schoolYear = it.schoolYear,
|
||||
semesterId = it.semesterId,
|
||||
semesterName = it.semesterNumber,
|
||||
isCurrent = it.current,
|
||||
start = it.start,
|
||||
end = it.end,
|
||||
classId = it.classId,
|
||||
|
@ -5,10 +5,11 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter
|
||||
import io.github.wulkanowy.data.SdkHelper
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.utils.getCurrentOrLast
|
||||
import io.github.wulkanowy.utils.isCurrent
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import timber.log.Timber
|
||||
import java.net.UnknownHostException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@ -21,28 +22,30 @@ class SemesterRepository @Inject constructor(
|
||||
private val sdkHelper: SdkHelper
|
||||
) {
|
||||
|
||||
fun getSemesters(student: Student, forceRefresh: Boolean = false): Single<List<Semester>> {
|
||||
fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false): Single<List<Semester>> {
|
||||
return Maybe.just(sdkHelper.init(student))
|
||||
.flatMap { local.getSemesters(student).filter { !forceRefresh } }
|
||||
.flatMap {
|
||||
local.getSemesters(student).filter { !forceRefresh }.filter {
|
||||
if (refreshOnNoCurrent) {
|
||||
it.any { semester -> semester.isCurrent }
|
||||
} else true
|
||||
}
|
||||
}
|
||||
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
|
||||
.flatMap {
|
||||
if (it) remote.getSemesters(student) else Single.error(UnknownHostException())
|
||||
if (it) remote.getSemesters(student)
|
||||
else Single.error(UnknownHostException())
|
||||
}.flatMap { new ->
|
||||
val currentSemesters = new.filter { it.isCurrent }
|
||||
if (currentSemesters.size == 1) {
|
||||
local.getSemesters(student).toSingle(emptyList())
|
||||
.doOnSuccess { old ->
|
||||
local.deleteSemesters(old.uniqueSubtract(new))
|
||||
local.saveSemesters(new.uniqueSubtract(old))
|
||||
}
|
||||
} else {
|
||||
Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}")
|
||||
throw IllegalArgumentException("Current semester can be only one.")
|
||||
if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!")
|
||||
|
||||
local.getSemesters(student).toSingle(emptyList()).doOnSuccess { old ->
|
||||
local.deleteSemesters(old.uniqueSubtract(new))
|
||||
local.saveSemesters(new.uniqueSubtract(old))
|
||||
}
|
||||
}.flatMap { local.getSemesters(student).toSingle(emptyList()) })
|
||||
}
|
||||
|
||||
fun getCurrentSemester(student: Student, forceRefresh: Boolean = false): Single<Semester> {
|
||||
return getSemesters(student, forceRefresh).map { item -> item.single { it.isCurrent } }
|
||||
return getSemesters(student, forceRefresh).map { it.getCurrentOrLast() }
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ class StudentRemote @Inject constructor(private val sdk: Sdk) {
|
||||
userLoginId = student.userLoginId,
|
||||
studentName = student.studentName,
|
||||
schoolSymbol = student.schoolSymbol,
|
||||
schoolShortName = student.schoolShortName,
|
||||
schoolName = student.schoolName,
|
||||
className = student.className,
|
||||
classId = student.classId,
|
||||
|
@ -29,7 +29,7 @@ class StudentRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun getStudentsScrapper(email: String, password: String, endpoint: String, symbol: String = ""): Single<List<Student>> {
|
||||
fun getStudentsScrapper(email: String, password: String, endpoint: String, symbol: String): Single<List<Student>> {
|
||||
return ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
|
||||
if (it) remote.getStudentsScrapper(email, password, endpoint, symbol)
|
||||
else Single.error(UnknownHostException("No internet connection"))
|
||||
|
@ -30,6 +30,7 @@ class TimetableRemote @Inject constructor(private val sdk: Sdk) {
|
||||
teacher = it.teacher,
|
||||
teacherOld = it.teacherOld,
|
||||
info = it.info,
|
||||
isStudentPlan = it.studentPlan,
|
||||
changes = it.changes,
|
||||
canceled = it.canceled
|
||||
)
|
||||
|
@ -9,6 +9,12 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import dagger.multibindings.IntoSet
|
||||
import io.github.wulkanowy.services.sync.channels.Channel
|
||||
import io.github.wulkanowy.services.sync.channels.DebugChannel
|
||||
import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewGradesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
|
||||
import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork
|
||||
import io.github.wulkanowy.services.sync.works.AttendanceWork
|
||||
import io.github.wulkanowy.services.sync.works.CompletedLessonWork
|
||||
@ -29,11 +35,10 @@ import javax.inject.Singleton
|
||||
|
||||
@Suppress("unused")
|
||||
@AssistedModule
|
||||
@Module(includes = [AssistedInject_ServicesModule::class, ServicesModule.Static::class])
|
||||
@Module(includes = [AssistedInject_ServicesModule::class])
|
||||
abstract class ServicesModule {
|
||||
|
||||
@Module
|
||||
object Static {
|
||||
companion object {
|
||||
|
||||
@Provides
|
||||
fun provideWorkManager(context: Context) = WorkManager.getInstance(context)
|
||||
@ -101,4 +106,24 @@ abstract class ServicesModule {
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideGradeStatistics(work: GradeStatisticsWork): Work
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideDebugChannel(channel: DebugChannel): Channel
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideLuckyNumberChannel(channel: LuckyNumberChannel): Channel
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideNewGradesChannel(channel: NewGradesChannel): Channel
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideNewMessageChannel(channel: NewMessagesChannel): Channel
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun provideNewNotesChannel(channel: NewNotesChannel): Channel
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package io.github.wulkanowy.services.sync
|
||||
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.Build.VERSION_CODES.O
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.work.BackoffPolicy.EXPONENTIAL
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.ExistingPeriodicWorkPolicy.KEEP
|
||||
@ -13,8 +14,7 @@ import androidx.work.WorkManager
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.DebugChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.Channel
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.isHolidays
|
||||
import org.threeten.bp.LocalDate.now
|
||||
@ -27,9 +27,9 @@ import javax.inject.Singleton
|
||||
class SyncManager @Inject constructor(
|
||||
private val workManager: WorkManager,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
channels: Set<@JvmSuppressWildcards Channel>,
|
||||
notificationManager: NotificationManagerCompat,
|
||||
sharedPrefProvider: SharedPrefProvider,
|
||||
newEntriesChannel: NewEntriesChannel,
|
||||
debugChannel: DebugChannel,
|
||||
appInfo: AppInfo
|
||||
) {
|
||||
|
||||
@ -37,8 +37,8 @@ class SyncManager @Inject constructor(
|
||||
if (now().isHolidays) stopSyncWorker()
|
||||
|
||||
if (SDK_INT > O) {
|
||||
newEntriesChannel.create()
|
||||
if (appInfo.isDebug) debugChannel.create()
|
||||
channels.forEach { it.create() }
|
||||
notificationManager.deleteNotificationChannel("new_entries_channel")
|
||||
}
|
||||
|
||||
if (sharedPrefProvider.getLong(APP_VERSION_CODE_KEY, -1L) != appInfo.versionCode.toLong()) {
|
||||
|
@ -0,0 +1,6 @@
|
||||
package io.github.wulkanowy.services.sync.channels
|
||||
|
||||
interface Channel {
|
||||
|
||||
fun create()
|
||||
}
|
@ -7,19 +7,22 @@ import android.app.NotificationManager.IMPORTANCE_DEFAULT
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import javax.inject.Inject
|
||||
|
||||
@TargetApi(26)
|
||||
class DebugChannel @Inject constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
private val context: Context
|
||||
) {
|
||||
private val context: Context,
|
||||
private val appInfo: AppInfo
|
||||
) : Channel {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "debug_channel"
|
||||
}
|
||||
|
||||
fun create() {
|
||||
override fun create() {
|
||||
if (appInfo.isDebug) return
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_debug), IMPORTANCE_DEFAULT)
|
||||
.apply {
|
||||
|
@ -0,0 +1,31 @@
|
||||
package io.github.wulkanowy.services.sync.channels
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import io.github.wulkanowy.R
|
||||
import javax.inject.Inject
|
||||
|
||||
@TargetApi(26)
|
||||
class LuckyNumberChannel @Inject constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
private val context: Context
|
||||
) : Channel {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "lucky_number_channel"
|
||||
}
|
||||
|
||||
override fun create() {
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_lucky_number), NotificationManager.IMPORTANCE_HIGH)
|
||||
.apply {
|
||||
enableLights(true)
|
||||
enableVibration(true)
|
||||
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package io.github.wulkanowy.services.sync.channels
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import io.github.wulkanowy.R
|
||||
import javax.inject.Inject
|
||||
|
||||
@TargetApi(26)
|
||||
class NewGradesChannel @Inject constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
private val context: Context
|
||||
) : Channel {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "new_grade_channel"
|
||||
}
|
||||
|
||||
override fun create() {
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_grades), NotificationManager.IMPORTANCE_HIGH)
|
||||
.apply {
|
||||
enableLights(true)
|
||||
enableVibration(true)
|
||||
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package io.github.wulkanowy.services.sync.channels
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import io.github.wulkanowy.R
|
||||
import javax.inject.Inject
|
||||
|
||||
@TargetApi(26)
|
||||
class NewMessagesChannel @Inject constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
private val context: Context
|
||||
) : Channel {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "new_message_channel"
|
||||
}
|
||||
|
||||
override fun create() {
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_message), NotificationManager.IMPORTANCE_HIGH)
|
||||
.apply {
|
||||
enableLights(true)
|
||||
enableVibration(true)
|
||||
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
})
|
||||
}
|
||||
}
|
@ -1,31 +1,31 @@
|
||||
package io.github.wulkanowy.services.sync.channels
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Notification.VISIBILITY_PUBLIC
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager.IMPORTANCE_HIGH
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import io.github.wulkanowy.R
|
||||
import javax.inject.Inject
|
||||
|
||||
@TargetApi(26)
|
||||
class NewEntriesChannel @Inject constructor(
|
||||
class NewNotesChannel @Inject constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
private val context: Context
|
||||
) {
|
||||
) : Channel {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "new_entries_channel"
|
||||
const val CHANNEL_ID = "new_notes_channel"
|
||||
}
|
||||
|
||||
fun create() {
|
||||
override fun create() {
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_entries), IMPORTANCE_HIGH)
|
||||
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_notes), NotificationManager.IMPORTANCE_HIGH)
|
||||
.apply {
|
||||
enableLights(true)
|
||||
enableVibration(true)
|
||||
lockscreenVisibility = VISIBILITY_PUBLIC
|
||||
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
})
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.grade.GradeRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewGradesChannel
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
@ -38,7 +38,7 @@ class GradeWork @Inject constructor(
|
||||
}
|
||||
|
||||
private fun notify(grades: List<Grade>) {
|
||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
|
||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), 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)
|
||||
|
@ -13,7 +13,7 @@ import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
@ -29,8 +29,8 @@ class LuckyNumberWork @Inject constructor(
|
||||
) : Work {
|
||||
|
||||
override fun create(student: Student, semester: Semester): Completable {
|
||||
return luckyNumberRepository.getLuckyNumber(semester, true, preferencesRepository.isNotificationsEnable)
|
||||
.flatMap { luckyNumberRepository.getNotNotifiedLuckyNumber(semester) }
|
||||
return luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable)
|
||||
.flatMap { luckyNumberRepository.getNotNotifiedLuckyNumber(student) }
|
||||
.flatMapCompletable {
|
||||
notify(it)
|
||||
luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true })
|
||||
@ -38,7 +38,7 @@ class LuckyNumberWork @Inject constructor(
|
||||
}
|
||||
|
||||
private fun notify(luckyNumber: LuckyNumber) {
|
||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
|
||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, LuckyNumberChannel.CHANNEL_ID)
|
||||
.setContentTitle(context.getString(R.string.lucky_number_notify_new_item_title))
|
||||
.setContentText(context.getString(R.string.lucky_number_notify_new_item, luckyNumber.luckyNumber))
|
||||
.setSmallIcon(R.drawable.ic_stat_luckynumber)
|
||||
|
@ -14,7 +14,7 @@ import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
|
||||
import io.github.wulkanowy.data.repositories.message.MessageRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
@ -39,7 +39,7 @@ class MessageWork @Inject constructor(
|
||||
}
|
||||
|
||||
private fun notify(messages: List<Message>) {
|
||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
|
||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewMessagesChannel.CHANNEL_ID)
|
||||
.setContentTitle(context.resources.getQuantityString(R.plurals.message_new_items, messages.size, messages.size))
|
||||
.setContentText(context.resources.getQuantityString(R.plurals.message_notify_new_items, messages.size, messages.size))
|
||||
.setSmallIcon(R.drawable.ic_stat_message)
|
||||
|
@ -13,7 +13,7 @@ import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.note.NoteRepository
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
|
||||
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
@ -38,7 +38,7 @@ class NoteWork @Inject constructor(
|
||||
}
|
||||
|
||||
private fun notify(notes: List<Note>) {
|
||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
|
||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewNotesChannel.CHANNEL_ID)
|
||||
.setContentTitle(context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size))
|
||||
.setContentText(context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size))
|
||||
.setSmallIcon(R.drawable.ic_stat_note)
|
||||
|
@ -4,6 +4,7 @@ import android.content.Intent
|
||||
import android.widget.RemoteViewsService
|
||||
import dagger.android.AndroidInjection
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
|
||||
@ -22,6 +23,9 @@ class TimetableWidgetService : RemoteViewsService() {
|
||||
@Inject
|
||||
lateinit var semesterRepo: SemesterRepository
|
||||
|
||||
@Inject
|
||||
lateinit var prefRepository: PreferencesRepository
|
||||
|
||||
@Inject
|
||||
lateinit var sharedPref: SharedPrefProvider
|
||||
|
||||
@ -30,6 +34,6 @@ class TimetableWidgetService : RemoteViewsService() {
|
||||
|
||||
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
|
||||
AndroidInjection.inject(this)
|
||||
return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, sharedPref, schedulers, applicationContext, intent)
|
||||
return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, prefRepository, sharedPref, schedulers, applicationContext, intent)
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,9 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment
|
||||
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
|
||||
import io.github.wulkanowy.ui.modules.about.logviewer.LogViewerFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
@ -42,6 +44,11 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
||||
Triple(getString(R.string.about_version), "${appInfo.versionName} (${appInfo.versionCode})", getCompatDrawable(R.drawable.ic_all_about))
|
||||
}
|
||||
|
||||
override val creatorsRes: Triple<String, String, Drawable?>?
|
||||
get() = context?.run {
|
||||
Triple(getString(R.string.about_contributor), getString(R.string.about_contributor_summary), getCompatDrawable(R.drawable.ic_about_creator))
|
||||
}
|
||||
|
||||
override val feedbackRes: Triple<String, String, Drawable?>?
|
||||
get() = context?.run {
|
||||
Triple(getString(R.string.about_feedback), getString(R.string.about_feedback_summary), getCompatDrawable(R.drawable.ic_about_feedback))
|
||||
@ -104,6 +111,10 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
||||
}
|
||||
}
|
||||
|
||||
override fun openLogViewer() {
|
||||
if (appInfo.isDebug) (activity as? MainActivity)?.pushView(LogViewerFragment.newInstance())
|
||||
}
|
||||
|
||||
override fun openDiscordInvite() {
|
||||
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
|
||||
}
|
||||
@ -143,6 +154,10 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
|
||||
(activity as? MainActivity)?.pushView(LicenseFragment.newInstance())
|
||||
}
|
||||
|
||||
override fun openCreators() {
|
||||
(activity as? MainActivity)?.pushView(ContributorFragment.newInstance())
|
||||
}
|
||||
|
||||
override fun openPrivacyPolicy() {
|
||||
context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage)
|
||||
}
|
||||
|
@ -27,6 +27,11 @@ class AboutPresenter @Inject constructor(
|
||||
if (item !is AboutItem) return
|
||||
view?.run {
|
||||
when (item.title) {
|
||||
versionRes?.first -> {
|
||||
Timber.i("Opening log viewer")
|
||||
openLogViewer()
|
||||
analytics.logEvent("about_open", "name" to "log_viewer")
|
||||
}
|
||||
feedbackRes?.first -> {
|
||||
Timber.i("Opening email client")
|
||||
openEmailClient()
|
||||
@ -52,6 +57,11 @@ class AboutPresenter @Inject constructor(
|
||||
openLicenses()
|
||||
analytics.logEvent("about_open", "name" to "licenses")
|
||||
}
|
||||
creatorsRes?.first -> {
|
||||
Timber.i("Opening creators view")
|
||||
openCreators()
|
||||
analytics.logEvent("about_open", "name" to "creators")
|
||||
}
|
||||
privacyRes?.first -> {
|
||||
Timber.i("Opening privacy page ")
|
||||
openPrivacyPolicy()
|
||||
@ -65,6 +75,7 @@ class AboutPresenter @Inject constructor(
|
||||
view?.run {
|
||||
updateData(AboutScrollableHeader(), listOfNotNull(
|
||||
versionRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
creatorsRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
feedbackRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
faqRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
discordRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
|
||||
|
@ -7,6 +7,8 @@ interface AboutView : BaseView {
|
||||
|
||||
val versionRes: Triple<String, String, Drawable?>?
|
||||
|
||||
val creatorsRes: Triple<String, String, Drawable?>?
|
||||
|
||||
val feedbackRes: Triple<String, String, Drawable?>?
|
||||
|
||||
val faqRes: Triple<String, String, Drawable?>?
|
||||
@ -23,6 +25,8 @@ interface AboutView : BaseView {
|
||||
|
||||
fun updateData(header: AboutScrollableHeader, items: List<AboutItem>)
|
||||
|
||||
fun openLogViewer()
|
||||
|
||||
fun openDiscordInvite()
|
||||
|
||||
fun openEmailClient()
|
||||
@ -33,5 +37,7 @@ interface AboutView : BaseView {
|
||||
|
||||
fun openLicenses()
|
||||
|
||||
fun openCreators()
|
||||
|
||||
fun openPrivacyPolicy()
|
||||
}
|
||||
|
@ -0,0 +1,76 @@
|
||||
package io.github.wulkanowy.ui.modules.about.contributor
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||
import kotlinx.android.synthetic.main.fragment_creator.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class ContributorFragment : BaseFragment(), ContributorView, MainView.TitledView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: ContributorPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var creatorsAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
||||
|
||||
override val titleStringId get() = R.string.contributors_title
|
||||
|
||||
companion object {
|
||||
fun newInstance() = ContributorFragment()
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_creator, container, false)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
presenter.onAttachView(this)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
with(creatorRecycler) {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||
adapter = creatorsAdapter
|
||||
addItemDecoration(FlexibleItemDecoration(context)
|
||||
.withDefaultDivider()
|
||||
.withDrawDividerOnLastItem(false))
|
||||
}
|
||||
creatorsAdapter.setOnItemClickListener(presenter::onItemSelected)
|
||||
creatorSeeMore.setOnClickListener { presenter.onSeeMoreClick() }
|
||||
}
|
||||
|
||||
override fun updateData(data: List<ContributorItem>) {
|
||||
creatorsAdapter.updateDataSet(data)
|
||||
}
|
||||
|
||||
override fun openUserGithubPage(username: String) {
|
||||
context?.openInternetBrowser("https://github.com/${username}", ::showMessage)
|
||||
}
|
||||
|
||||
override fun openGithubContributorsPage() {
|
||||
context?.openInternetBrowser("https://github.com/wulkanowy/wulkanowy/graphs/contributors", ::showMessage)
|
||||
}
|
||||
|
||||
override fun showProgress(show: Boolean) {
|
||||
creatorProgress.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package io.github.wulkanowy.ui.modules.about.contributor
|
||||
|
||||
import android.view.View
|
||||
import coil.api.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.pojos.AppCreator
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.item_contributor.*
|
||||
|
||||
class ContributorItem(val creator: AppCreator) :
|
||||
AbstractFlexibleItem<ContributorItem.ViewHolder>() {
|
||||
|
||||
override fun getLayoutRes() = R.layout.item_contributor
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
with(holder) {
|
||||
creatorItemName.text = creator.displayName
|
||||
|
||||
creatorItemAvatar.load("https://github.com/${creator.githubUsername}.png") {
|
||||
transformations(RoundedCornersTransformation(8f))
|
||||
crossfade(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ContributorItem
|
||||
|
||||
if (creator != other.creator) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode() = creator.hashCode()
|
||||
|
||||
class ViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) : FlexibleViewHolder(view, adapter),
|
||||
LayoutContainer {
|
||||
|
||||
override val containerView: View? get() = contentView
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package io.github.wulkanowy.ui.modules.about.contributor
|
||||
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository
|
||||
import io.github.wulkanowy.data.repositories.student.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.utils.SchedulersProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
class ContributorPresenter @Inject constructor(
|
||||
schedulers: SchedulersProvider,
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
private val appCreatorRepository: AppCreatorRepository
|
||||
) : BasePresenter<ContributorView>(errorHandler, studentRepository, schedulers) {
|
||||
|
||||
override fun onAttachView(view: ContributorView) {
|
||||
super.onAttachView(view)
|
||||
view.initView()
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onItemSelected(item: AbstractFlexibleItem<*>) {
|
||||
if (item !is ContributorItem) return
|
||||
view?.openUserGithubPage(item.creator.githubUsername)
|
||||
}
|
||||
|
||||
fun onSeeMoreClick() {
|
||||
view?.openGithubContributorsPage()
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
disposable.add(appCreatorRepository.getAppCreators()
|
||||
.map { it.map { creator -> ContributorItem(creator) } }
|
||||
.subscribeOn(schedulers.backgroundThread)
|
||||
.observeOn(schedulers.mainThread)
|
||||
.doFinally { view?.showProgress(false) }
|
||||
.subscribe({ view?.run { updateData(it) } }, { errorHandler.dispatch(it) }))
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package io.github.wulkanowy.ui.modules.about.contributor
|
||||
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface ContributorView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<ContributorItem>)
|
||||
|
||||
fun openUserGithubPage(username: String)
|
||||
|
||||
fun openGithubContributorsPage()
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package io.github.wulkanowy.ui.modules.about.logviewer
|
||||
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class LogViewerAdapter : RecyclerView.Adapter<LogViewerAdapter.ViewHolder>() {
|
||||
|
||||
var lines = emptyList<String>()
|
||||
|
||||
class ViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView)
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(TextView(parent.context))
|
||||
}
|
||||
|
||||
override fun getItemCount() = lines.size
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.textView.text = lines[position]
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package io.github.wulkanowy.ui.modules.about.logviewer
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.Intent.EXTRA_EMAIL
|
||||
import android.content.Intent.EXTRA_STREAM
|
||||
import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
import android.net.Uri
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.Build.VERSION_CODES.LOLLIPOP
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.github.wulkanowy.BuildConfig.APPLICATION_ID
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import kotlinx.android.synthetic.main.fragment_logviewer.*
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
class LogViewerFragment : BaseFragment(), LogViewerView, MainView.TitledView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: LogViewerPresenter
|
||||
|
||||
private val logAdapter = LogViewerAdapter()
|
||||
|
||||
override val titleStringId: Int
|
||||
get() = R.string.logviewer_title
|
||||
|
||||
companion object {
|
||||
fun newInstance() = LogViewerFragment()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_logviewer, container, false)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
messageContainer = logViewerRecycler
|
||||
presenter.onAttachView(this)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_logviewer, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.logViewerMenuShare) presenter.onShareLogsSelected()
|
||||
else false
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
with(logViewerRecycler) {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = logAdapter
|
||||
}
|
||||
|
||||
logViewRefreshButton.setOnClickListener { presenter.onRefreshClick() }
|
||||
}
|
||||
|
||||
override fun setLines(lines: List<String>) {
|
||||
logAdapter.lines = lines
|
||||
logAdapter.notifyDataSetChanged()
|
||||
logViewerRecycler.scrollToPosition(lines.size - 1)
|
||||
}
|
||||
|
||||
override fun shareLogs(files: List<File>) {
|
||||
val intent = Intent(Intent.ACTION_SEND_MULTIPLE).apply {
|
||||
type = "text/plain"
|
||||
putExtra(EXTRA_EMAIL, arrayOf("wulkanowyinc@gmail.com"))
|
||||
addFlags(FLAG_GRANT_READ_URI_PERMISSION)
|
||||
putParcelableArrayListExtra(EXTRA_STREAM, ArrayList(files.map {
|
||||
if (SDK_INT < LOLLIPOP) Uri.fromFile(it)
|
||||
else FileProvider.getUriForFile(requireContext(), "$APPLICATION_ID.fileprovider", it)
|
||||
}))
|
||||
}
|
||||
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.logviewer_share)))
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|