diff --git a/.gitignore b/.gitignore index d3fb6e4e9..5daeb6b97 100644 --- a/.gitignore +++ b/.gitignore @@ -113,3 +113,6 @@ Thumbs.db !/gradle/wrapper/gradle-wrapper.jar .idea/jarRepositories.xml + + +app/src/release/agconnect-services.json diff --git a/.travis.yml b/.travis.yml index e8366be2a..26abd2025 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,8 @@ jdk: oraclejdk8 env: global: - - ANDROID_API_LEVEL=29 - - ANDROID_BUILD_TOOLS_VERSION=29.0.3 + - ANDROID_API_LEVEL=30 + - ANDROID_BUILD_TOOLS_VERSION=30.0.2 cache: directories: @@ -14,7 +14,7 @@ cache: branches: only: - develop - - 0.20.1 + - 0.22.2 android: licenses: @@ -28,32 +28,37 @@ android: - build-tools-$ANDROID_BUILD_TOOLS_VERSION # The SDK version used to compile your project - android-$ANDROID_API_LEVEL - # Additional components + # Additional components - extra-google-google_play_services - extra-google-m2repository - extra-android-m2repository - addon-google_apis-google-$ANDROID_API_LEVEL - # Android emulator + # Android emulator - android-22 - sys-img-armeabi-v7a-android-22 +before_install: + - yes | sdkmanager "platforms;android-30" + - yes | sdkmanager "build-tools;30.0.2" + before_script: - # Launch emulator before the execution - - echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a - - emulator -avd test -no-audio -no-window & - - android-wait-for-emulator - - adb shell input keyevent 82 & - - "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash" + # Launch emulator before the execution + - echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a + - emulator -avd test -no-audio -no-window & + - android-wait-for-emulator + - adb shell input keyevent 82 & + - "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash" script: - ./gradlew dependencies --stacktrace --daemon - fossa --no-ansi || true - - ./gradlew -Pcoverage testPlayDebugUnitTest --stacktrace --daemon - - ./gradlew -Pcoverage createFdroidDebugCoverageReport --stacktrace --daemon + - ./gradlew -Pcoverage testFdroidDebugUnitTest --stacktrace --daemon + - ./gradlew -Pcoverage connectedFdroidDebugAndroidTest --stacktrace --daemon - ./gradlew -Pcoverage jacocoTestReport --stacktrace --daemon - | if [ $TRAVIS_TAG ]; then gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg; + gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/agconnect-services.json.gpg; gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg; gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg; ./gradlew publishPlayRelease -PenableFirebase --stacktrace; diff --git a/README.en.md b/README.en.md index 28cce1c34..4c5e53da8 100644 --- a/README.en.md +++ b/README.en.md @@ -32,14 +32,17 @@ Unofficial android VULCAN UONET+ register client for both students and their par ## Download -You can download the current beta version from the Google Play or the F-Droid store +You can download the current beta version from the Google Play, F-Droid or Huawei AppGallery store [Get it on Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) + alt="Get it on Google Play" + height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy) [Get it on F-Droid](https://f-droid.org/packages/io.github.wulkanowy/) +[Explore it on AppGallery](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=) You can also download a [development version](https://wulkanowy.github.io/#download) that includes new features being prepared for the next release diff --git a/README.md b/README.md index 02e1900c8..9e29cdb6c 100644 --- a/README.md +++ b/README.md @@ -32,14 +32,17 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica ## Pobierz -Aktualną wersję beta możesz pobrać ze sklepu Google Play lub F-Droid +Aktualną wersję beta możesz pobrać ze sklepu Google Play, F-Droid lub Huawei AppGallery [Pobierz z Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) + alt="Pobierz z Google Play" + height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy) [Pobierz z F-Droid](https://f-droid.org/packages/io.github.wulkanowy/) +[Odkrywaj w AppGallery](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=) Możesz także pobrać [wersję rozwojową](https://wulkanowy.github.io/#download), która zawiera nowe funkcje przygotowywane do następnego wydania diff --git a/app/build.gradle b/app/build.gradle index eb9329787..541aff36c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,16 +10,16 @@ apply from: 'sonarqube.gradle' apply from: 'hooks.gradle' android { - compileSdkVersion 29 - buildToolsVersion '29.0.3' + compileSdkVersion 30 + buildToolsVersion '30.0.2' defaultConfig { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" minSdkVersion 17 - targetSdkVersion 29 - versionCode 64 - versionName "0.20.0" + targetSdkVersion 30 + versionCode 75 + versionName "0.22.2" multiDexEnabled true resValue "string", "app_name", "Wulkanowy" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -69,12 +69,26 @@ android { flavorDimensions "platform" productFlavors { + hms { + dimension "platform" + minSdkVersion 19 + manifestPlaceholders = [ + install_channel: "AppGallery" + ] + } + play { dimension "platform" + manifestPlaceholders = [ + install_channel: "Google Play" + ] } fdroid { dimension "platform" + manifestPlaceholders = [ + install_channel: "F-Droid" + ] } } @@ -112,13 +126,15 @@ play { serviceAccountCredentials = file('key.p12') defaultToAppBundles = false track = 'alpha' + updatePriority = 1 } ext { work_manager = "2.4.0" room = "2.2.5" - chucker = "3.2.0" - mockk = "1.10.0" + chucker = "3.3.0" + mockk = "1.10.2" + moshi = "1.11.0" } configurations.all { @@ -126,14 +142,14 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:sdk:0.20.1" + implementation "io.github.wulkanowy:sdk:0.22.2" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' - implementation "androidx.core:core-ktx:1.3.1" + implementation "androidx.core:core-ktx:1.3.2" implementation "androidx.activity:activity-ktx:1.1.0" implementation "androidx.appcompat:appcompat:1.2.0" implementation "androidx.appcompat:appcompat-resources:1.2.0" @@ -147,7 +163,7 @@ dependencies { implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation "androidx.constraintlayout:constraintlayout:2.0.1" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" - implementation "com.google.android.material:material:1.2.0" + implementation "com.google.android.material:material:1.2.1" implementation "com.github.wulkanowy:material-chips-input:2.1.1" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation "me.zhanghai.android.materialprogressbar:library:1.6.1" @@ -170,29 +186,35 @@ dependencies { implementation "com.ncapdevi:frag-nav:3.3.0" implementation "com.github.YarikSOffice:lingver:1.2.2" - implementation "com.google.code.gson:gson:2.8.6" + implementation "com.squareup.moshi:moshi:$moshi" + implementation "com.squareup.moshi:moshi-adapters:$moshi" + kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi" 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:$about_libraries" implementation 'com.wdullaer:materialdatetimepicker:4.2.3' - implementation "io.coil-kt:coil:1.0.0-rc1" + implementation "io.coil-kt:coil:1.0.0-rc3" implementation "io.github.wulkanowy:AppKillerManager:3.0.0" implementation 'me.xdrop:fuzzywuzzy:1.3.1' - playImplementation 'com.google.firebase:firebase-analytics:17.5.0' + playImplementation 'com.google.firebase:firebase-analytics:17.6.0' playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.1' playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.1" - playImplementation 'com.google.firebase:firebase-messaging:20.2.4' - playImplementation 'com.google.firebase:firebase-crashlytics:17.2.1' + playImplementation 'com.google.firebase:firebase-messaging:20.3.0' + playImplementation 'com.google.firebase:firebase-crashlytics:17.2.2' + playImplementation 'com.google.android.play:core-ktx:1.8.1' playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' + hmsImplementation 'com.huawei.hms:hianalytics:5.0.4.301' + hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.4.1.300' + releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker" debugImplementation "com.amitshekhar.android:debug-db:1.0.6" - testImplementation "junit:junit:4.13" + testImplementation "junit:junit:4.13.1" testImplementation "io.mockk:mockk:$mockk" testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.9' @@ -205,3 +227,4 @@ dependencies { } apply plugin: 'com.google.gms.google-services' +apply plugin: 'com.huawei.agconnect' diff --git a/app/jacoco.gradle b/app/jacoco.gradle index e9abfb613..a5cf84e63 100644 --- a/app/jacoco.gradle +++ b/app/jacoco.gradle @@ -35,13 +35,13 @@ task jacocoTestReport(type: JacocoReport) { dir: "$buildDir/intermediates/classes/debug", excludes: excludes ) + fileTree( - dir: "$buildDir/tmp/kotlin-classes/playDebug", + dir: "$buildDir/tmp/kotlin-classes/fdroidDebug", excludes: excludes )) sourceDirectories.setFrom(files([ "src/main/java", - "src/play/java" + "src/fdroid/java" ])) executionData.setFrom(fileTree( dir: project.projectDir, diff --git a/app/src/debug/agconnect-services.json b/app/src/debug/agconnect-services.json new file mode 100644 index 000000000..48192df01 --- /dev/null +++ b/app/src/debug/agconnect-services.json @@ -0,0 +1,33 @@ +{ + "agcgw":{ + "backurl":"connect-dre.dbankcloud.cn", + "url":"connect-dre.hispace.hicloud.com" + }, + "client":{ + "cp_id":"890048000024105546", + "product_id":"", + "client_id":"", + "client_secret":"", + "app_id":"101440411", + "package_name":"io.github.wulkanowy.dev", + "api_key":"" + }, + "service":{ + "analytics":{ + "collector_url":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", + "resource_id":"p1", + "channel_id":"" + }, + "search":{ + "url":"https://search-dre.cloud.huawei.com" + }, + "cloudstorage":{ + "storage_url":"https://ops-dre.agcstorage.link" + }, + "ml":{ + "mlservice_url":"ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn" + } + }, + "region":"DE", + "configuration_version":"1.0" +} diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt similarity index 86% rename from app/src/fdroid/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt rename to app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt index f23645bc3..0cd9a52e4 100644 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt @@ -6,7 +6,7 @@ import javax.inject.Singleton @Singleton @Suppress("UNUSED_PARAMETER") -class FirebaseAnalyticsHelper @Inject constructor() { +class AnalyticsHelper @Inject constructor() { fun logEvent(name: String, vararg params: Pair) { // do nothing diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/CrashLogUtils.kt similarity index 71% rename from app/src/fdroid/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt rename to app/src/fdroid/java/io/github/wulkanowy/utils/CrashLogUtils.kt index d03a319a2..5d58270d4 100644 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/CrashLogUtils.kt @@ -8,6 +8,6 @@ open class TimberTreeNoOp : Timber.Tree() { override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {} } -class CrashlyticsTree : TimberTreeNoOp() +class CrashLogTree : TimberTreeNoOp() -class CrashlyticsExceptionTree : TimberTreeNoOp() +class CrashLogExceptionTree : TimberTreeNoOp() diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/UpdateHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/UpdateHelper.kt new file mode 100644 index 000000000..3abab9629 --- /dev/null +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/UpdateHelper.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.utils + +import android.app.Activity +import android.view.View +import javax.inject.Inject + +@Suppress("UNUSED_PARAMETER") +class UpdateHelper @Inject constructor() { + + lateinit var messageContainer: View + + fun checkAndInstallUpdates(activity: Activity) {} + + fun onActivityResult(requestCode: Int, resultCode: Int) {} + + fun onResume(activity: Activity) {} +} diff --git a/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt new file mode 100644 index 000000000..b3cecf243 --- /dev/null +++ b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.utils + +import android.app.Activity +import android.content.Context +import android.os.Bundle +import com.huawei.hms.analytics.HiAnalytics +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AnalyticsHelper @Inject constructor( + @ApplicationContext private val context: Context +) { + + private val analytics by lazy { HiAnalytics.getInstance(context) } + + fun logEvent(name: String, vararg params: Pair) { + Bundle().apply { + params.forEach { + if (it.second == null) return@forEach + when (it.second) { + is String, is String? -> putString(it.first, it.second as String) + is Int, is Int? -> putInt(it.first, it.second as Int) + is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean) + } + } + analytics.onEvent(name, this) + } + } + + fun setCurrentScreen(activity: Activity, name: String?) { + analytics.onEvent("screen_view", Bundle().apply { + putString("screen_name", name) + putString("screen_class", activity::class.simpleName) + }) + } +} diff --git a/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt new file mode 100644 index 000000000..7f4bedae4 --- /dev/null +++ b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt @@ -0,0 +1,52 @@ +package io.github.wulkanowy.utils + +import android.util.Log +import com.huawei.agconnect.crash.AGConnectCrash +import fr.bipi.tressence.base.FormatterPriorityTree +import fr.bipi.tressence.common.StackTraceRecorder +import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException +import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException +import java.io.InterruptedIOException +import java.net.SocketTimeoutException +import java.net.UnknownHostException + +class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) { + + private val connectCrash by lazy { AGConnectCrash.getInstance() } + + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + if (skipLog(priority, tag, message, t)) return + + connectCrash.log(format(priority, tag, message)) + } +} + +class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR) { + + private val connectCrash by lazy { AGConnectCrash.getInstance() } + + override fun skipLog(priority: Int, tag: String?, message: String, t: Throwable?): Boolean { + return when (t) { + is FeatureDisabledException, + is FeatureNotAvailableException, + is UnknownHostException, + is SocketTimeoutException, + is InterruptedIOException -> true + else -> super.skipLog(priority, tag, message, t) + } + } + + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + if (skipLog(priority, tag, message, t)) return + + connectCrash.setCustomKey("priority", priority) + connectCrash.setCustomKey("tag", tag.orEmpty()) + connectCrash.setCustomKey("message", message) + connectCrash.log(priority, t?.stackTraceToString()) + if (t != null) { + connectCrash.log(priority, t.stackTraceToString()) + } else { + connectCrash.log(priority, StackTraceRecorder(format(priority, tag, message)).stackTraceToString()) + } + } +} diff --git a/app/src/hms/java/io/github/wulkanowy/utils/UpdateHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/UpdateHelper.kt new file mode 100644 index 000000000..3abab9629 --- /dev/null +++ b/app/src/hms/java/io/github/wulkanowy/utils/UpdateHelper.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.utils + +import android.app.Activity +import android.view.View +import javax.inject.Inject + +@Suppress("UNUSED_PARAMETER") +class UpdateHelper @Inject constructor() { + + lateinit var messageContainer: View + + fun checkAndInstallUpdates(activity: Activity) {} + + fun onActivityResult(requestCode: Int, resultCode: Int) {} + + fun onResume(activity: Activity) {} +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4ec2f7816..a8d2b49e3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -110,6 +110,11 @@ android:resource="@xml/provider_paths" /> + + + (val status: Status, val data: T?, val error: Throwable?) { +data class Resource(val status: Status, val data: T?, val error: Throwable?) { companion object { fun success(data: T?): Resource { return Resource(Status.SUCCESS, data, null) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt index b21c4834f..def0b3715 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt @@ -1,8 +1,9 @@ package io.github.wulkanowy.data.db import androidx.room.TypeConverter -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types +import io.github.wulkanowy.data.db.adapters.PairAdapterFactory import java.time.Instant import java.time.LocalDate import java.time.LocalDateTime @@ -12,6 +13,16 @@ import java.util.Date class Converters { + private val moshi by lazy { Moshi.Builder().add(PairAdapterFactory).build() } + + private val integerListAdapter by lazy { + moshi.adapter>(Types.newParameterizedType(List::class.java, Integer::class.java)) + } + + private val stringListPairAdapter by lazy { + moshi.adapter>>(Types.newParameterizedType(List::class.java, Pair::class.java, String::class.java, String::class.java)) + } + @TypeConverter fun timestampToDate(value: Long?): LocalDate? = value?.run { Date(value).toInstant().atZone(ZoneOffset.UTC).toLocalDate() @@ -39,22 +50,22 @@ class Converters { fun intToMonth(value: Int?) = value?.let { Month.of(it) } @TypeConverter - fun intListToGson(list: List): String { - return Gson().toJson(list) + fun intListToJson(list: List): String { + return integerListAdapter.toJson(list) } @TypeConverter - fun gsonToIntList(value: String): List { - return Gson().fromJson(value, object : TypeToken>() {}.type) + fun jsonToIntList(value: String): List { + return integerListAdapter.fromJson(value).orEmpty() } @TypeConverter - fun stringPairListToGson(list: List>): String { - return Gson().toJson(list) + fun stringPairListToJson(list: List>): String { + return stringListPairAdapter.toJson(list) } @TypeConverter - fun gsonToStringPairList(value: String): List> { - return Gson().fromJson(value, object : TypeToken>>() {}.type) + fun jsonToStringPairList(value: String): List> { + return stringListPairAdapter.fromJson(value).orEmpty() } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/adapters/PairAdapterFactory.kt b/app/src/main/java/io/github/wulkanowy/data/db/adapters/PairAdapterFactory.kt new file mode 100644 index 000000000..4a9b168dd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/adapters/PairAdapterFactory.kt @@ -0,0 +1,68 @@ +package io.github.wulkanowy.data.db.adapters + +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonReader +import com.squareup.moshi.JsonWriter +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type + +object PairAdapterFactory : JsonAdapter.Factory { + + override fun create(type: Type, annotations: MutableSet, moshi: Moshi): JsonAdapter<*>? { + if (type !is ParameterizedType || List::class.java != type.rawType) return null + if (type.actualTypeArguments[0] != Pair::class.java) return null + + val listType = Types.newParameterizedType(List::class.java, Map::class.java, String::class.java) + val listAdapter = moshi.adapter>>(listType) + + val mapType = Types.newParameterizedType(MutableMap::class.java, String::class.java, String::class.java) + val mapAdapter = moshi.adapter>(mapType) + + return PairAdapter(listAdapter, mapAdapter) + } + + private class PairAdapter( + private val listAdapter: JsonAdapter>>, + private val mapAdapter: JsonAdapter>, + ) : JsonAdapter>>() { + + override fun toJson(writer: JsonWriter, value: List>?) { + writer.beginArray() + value?.forEach { + writer.beginObject() + writer.name("first").value(it.first) + writer.name("second").value(it.second) + writer.endObject() + } + writer.endArray() + } + + override fun fromJson(reader: JsonReader): List>? { + return if (reader.peek() == JsonReader.Token.BEGIN_OBJECT) deserializeMoshiMap(reader) + else deserializeGsonPair(reader) + } + + // for compatibility with 0.21.0 + private fun deserializeMoshiMap(reader: JsonReader): List>? { + val map = mapAdapter.fromJson(reader) ?: return null + + return map.entries.map { + it.key to it.value + } + } + + private fun deserializeGsonPair(reader: JsonReader): List>? { + val list = listAdapter.fromJson(reader) ?: return null + + return list.map { + require(it.size == 2) { + "pair with more or less than two elements: $list" + } + + it["first"].orEmpty() to it["second"].orEmpty() + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt index 79050de0f..729ba6a68 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt @@ -12,7 +12,7 @@ interface MessagesDao : BaseDao { @Transaction @Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId") - fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow + fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder ORDER BY date DESC") fun loadAll(studentId: Int, folder: Int): Flow> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt index 914dc3816..1f10a1645 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt @@ -36,12 +36,6 @@ data class Message( var unread: Boolean, - @ColumnInfo(name = "unread_by") - val unreadBy: Int, - - @ColumnInfo(name = "read_by") - val readBy: Int, - val removed: Boolean, @ColumnInfo(name = "has_attachments") @@ -54,5 +48,11 @@ data class Message( @ColumnInfo(name = "is_notified") var isNotified: Boolean = true + @ColumnInfo(name = "unread_by") + var unreadBy: Int = 0 + + @ColumnInfo(name = "read_by") + var readBy: Int = 0 + var content: String = "" } diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt index e792bde46..d2338c281 100644 --- a/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt @@ -1,3 +1,9 @@ package io.github.wulkanowy.data.pojos -class Contributor(val displayName: String, val githubUsername: String) +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +class Contributor( + val displayName: String, + val githubUsername: String +) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt index d19565579..ff538969b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt @@ -1,7 +1,8 @@ package io.github.wulkanowy.data.repositories.appcreator import android.content.res.AssetManager -import com.google.gson.Gson +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types import io.github.wulkanowy.data.pojos.Contributor import io.github.wulkanowy.utils.DispatchersProvider import kotlinx.coroutines.withContext @@ -15,9 +16,9 @@ class AppCreatorRepository @Inject constructor( ) { suspend fun getAppCreators() = withContext(dispatchers.backgroundThread) { - Gson().fromJson( - assets.open("contributors.json").bufferedReader().use { it.readText() }, - Array::class.java - ).toList() + val moshi = Moshi.Builder().build() + val type = Types.newParameterizedType(List::class.java, Contributor::class.java) + val adapter = moshi.adapter>(type) + adapter.fromJson(assets.open("contributors.json").bufferedReader().use { it.readText() }) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt index e7f115ac6..3f4591a25 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt @@ -2,9 +2,9 @@ package io.github.wulkanowy.data.repositories.exam import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.endExamsDay import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.startExamsDay import io.github.wulkanowy.utils.uniqueSubtract import java.time.LocalDate import javax.inject.Inject @@ -18,8 +18,8 @@ class ExamRepository @Inject constructor( fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource( shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getExams(semester, start.monday, end.sunday) }, - fetch = { remote.getExams(student, semester, start.monday, end.sunday) }, + query = { local.getExams(semester, start.startExamsDay, start.endExamsDay) }, + fetch = { remote.getExams(student, semester, start.startExamsDay, start.endExamsDay) }, saveFetchResult = { old, new -> local.deleteExams(old uniqueSubtract new) local.saveExams(new uniqueSubtract old) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt index 730bd005b..f1c8eaf08 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt @@ -6,7 +6,6 @@ import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.MessageAttachment import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED import kotlinx.coroutines.flow.Flow import javax.inject.Inject import javax.inject.Singleton @@ -29,7 +28,7 @@ class MessageLocal @Inject constructor( messagesDb.deleteAll(messages) } - fun getMessageWithAttachment(student: Student, message: Message): Flow { + fun getMessageWithAttachment(student: Student, message: Message): Flow { return messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt index fdf0c6754..044a13a28 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt @@ -30,12 +30,12 @@ class MessageRemote @Inject constructor(private val sdk: Sdk) { date = it.date ?: now(), folderId = it.folderId, unread = it.unread ?: false, - unreadBy = it.unreadBy ?: 0, - readBy = it.readBy ?: 0, removed = it.removed, hasAttachments = it.hasAttachments ).apply { content = it.content.orEmpty() + unreadBy = it.unreadBy ?: 0 + readBy = it.readBy ?: 0 } } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt index f653f2683..bb9326992 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt @@ -34,12 +34,14 @@ class MessageRepository @Inject constructor( fun getMessage(student: Student, message: Message, markAsRead: Boolean = false) = networkBoundResource( shouldFetch = { + checkNotNull(it, { "This message no longer exist!" }) Timber.d("Message content in db empty: ${it.message.content.isEmpty()}") it.message.unread || it.message.content.isEmpty() }, query = { local.getMessageWithAttachment(student, message) }, - fetch = { remote.getMessagesContentDetails(student, it.message, markAsRead) }, + fetch = { remote.getMessagesContentDetails(student, it!!.message, markAsRead) }, saveFetchResult = { old, (downloadedMessage, attachments) -> + checkNotNull(old, { "Fetched message no longer exist!" }) local.updateMessages(listOf(old.message.copy(unread = !markAsRead).apply { id = old.message.id content = content.ifBlank { downloadedMessage } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt index 1d5e57f8a..da31751a1 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt @@ -5,6 +5,7 @@ import android.content.SharedPreferences import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.ui.modules.grade.GradeAverageMode +import io.github.wulkanowy.ui.modules.grade.GradeSortingMode import javax.inject.Inject import javax.inject.Singleton @@ -74,12 +75,25 @@ class PreferencesRepository @Inject constructor( val fillMessageContent: Boolean get() = getBoolean(R.string.pref_key_fill_message_content, R.bool.pref_default_fill_message_content) + val showGroupsInPlan: Boolean + get() = getBoolean(R.string.pref_key_timetable_show_groups, R.bool.pref_default_timetable_show_groups) + val showWholeClassPlan: String get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class) + val gradeSortingMode: GradeSortingMode + get() = GradeSortingMode.getByValue(getString(R.string.pref_key_grade_sorting_mode, R.string.pref_default_grade_sorting_mode)) + val showTimetableTimers: Boolean get() = getBoolean(R.string.pref_key_timetable_show_timers, R.bool.pref_default_timetable_show_timers) + var isHomeworkFullscreen: Boolean + get() = getBoolean(R.string.pref_key_homework_fullscreen, R.bool.pref_default_homework_fullscreen) + set(value) = sharedPref.edit().putBoolean("homework_fullscreen", value).apply() + + val showSubjectsWithoutGrades: Boolean + get() = getBoolean(R.string.pref_key_subjects_without_grades, R.bool.pref_default_subjects_without_grades) + 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) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt index 7a76503f2..2748f1df5 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt @@ -8,6 +8,7 @@ import io.github.wulkanowy.utils.getCurrentOrLast import io.github.wulkanowy.utils.isCurrent import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.withContext +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -41,7 +42,7 @@ class SemesterRepository @Inject constructor( private suspend fun refreshSemesters(student: Student) { val new = remote.getSemesters(student) - if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!") + if (new.isEmpty()) return Timber.i("Empty semester list!") val old = local.getSemesters(student) local.deleteSemesters(old.uniqueSubtract(new)) diff --git a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt index 8f8782a2f..2593a555b 100644 --- a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt +++ b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt @@ -26,7 +26,9 @@ import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companio import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_ID import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_NAME import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.DispatchersProvider import io.github.wulkanowy.utils.toTimestamp +import kotlinx.coroutines.withContext import timber.log.Timber import java.time.LocalDateTime import java.time.LocalDateTime.now @@ -35,7 +37,8 @@ import javax.inject.Inject class TimetableNotificationSchedulerHelper @Inject constructor( @ApplicationContext private val context: Context, private val alarmManager: AlarmManager, - private val preferencesRepository: PreferencesRepository + private val preferencesRepository: PreferencesRepository, + private val dispatchersProvider: DispatchersProvider, ) { private fun getRequestCode(time: LocalDateTime, studentId: Int) = (time.toTimestamp() * studentId).toInt() @@ -44,13 +47,15 @@ class TimetableNotificationSchedulerHelper @Inject constructor( return day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30) } - fun cancelScheduled(lessons: List, studentId: Int = 1) { - lessons.sortedBy { it.start }.forEachIndexed { index, lesson -> - val upcomingTime = getUpcomingLessonTime(index, lessons, lesson) - cancelScheduledTo(upcomingTime..lesson.start, getRequestCode(upcomingTime, studentId)) - cancelScheduledTo(lesson.start..lesson.end, getRequestCode(lesson.start, studentId)) + suspend fun cancelScheduled(lessons: List, studentId: Int = 1) { + withContext(dispatchersProvider.backgroundThread) { + lessons.sortedBy { it.start }.forEachIndexed { index, lesson -> + val upcomingTime = getUpcomingLessonTime(index, lessons, lesson) + cancelScheduledTo(upcomingTime..lesson.start, getRequestCode(upcomingTime, studentId)) + cancelScheduledTo(lesson.start..lesson.end, getRequestCode(lesson.start, studentId)) - Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId") + Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId") + } } } @@ -61,28 +66,34 @@ class TimetableNotificationSchedulerHelper @Inject constructor( fun cancelNotification() = NotificationManagerCompat.from(context).cancel(MainView.Section.TIMETABLE.id) - fun scheduleNotifications(lessons: List, student: Student) { + suspend fun scheduleNotifications(lessons: List, student: Student) { if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) return cancelScheduled(lessons, student.studentId) - lessons.groupBy { it.date } - .map { it.value.sortedBy { lesson -> lesson.start } } - .map { it.filter { lesson -> !lesson.canceled && lesson.isStudentPlan } } - .map { day -> - day.forEachIndexed { index, lesson -> - val intent = createIntent(student, lesson, day.getOrNull(index + 1)) + withContext(dispatchersProvider.backgroundThread) { + lessons.groupBy { it.date } + .map { it.value.sortedBy { lesson -> lesson.start } } + .map { it.filter { lesson -> lesson.isStudentPlan } } + .map { day -> + val canceled = day.filter { it.canceled } + val active = day.filter { !it.canceled } - if (lesson.start > now()) { - scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, day, lesson)) - } + cancelScheduled(canceled) + active.forEachIndexed { index, lesson -> + val intent = createIntent(student, lesson, active.getOrNull(index + 1)) - if (lesson.end > now()) { - scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_CURRENT, lesson.start) - if (day.lastIndex == index) { - scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, lesson.end) + if (lesson.start > now()) { + scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, active, lesson)) + } + + if (lesson.end > now()) { + scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_CURRENT, lesson.start) + if (active.lastIndex == index) { + scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, lesson.end) + } } } } - } + } } private fun createIntent(student: Student, lesson: Timetable, nextLesson: Timetable?): Intent { diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt index 1d005ae8e..47a949273 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt @@ -44,6 +44,7 @@ class SyncManager @Inject constructor( if (SDK_INT >= O) { channels.forEach { it.create() } + notificationManager.deleteNotificationChannel("lesson_channel") notificationManager.deleteNotificationChannel("new_entries_channel") } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt index 2ba0435b4..13326ca06 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt @@ -50,13 +50,16 @@ class SyncWorker @WorkerInject constructor( } catch (e: Throwable) { Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred") if (e is FeatureDisabledException || e is FeatureNotAvailableException) null - else e + else { + Timber.e(e) + e + } } } val result = when { exceptions.isNotEmpty() && inputData.getBoolean("one_time", false) -> { Result.failure(Data.Builder() - .putString("error", exceptions.toString()) + .putString("error", exceptions.map { it.stackTraceToString() }.toString()) .build() ) } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/UpcomingLessonsChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/UpcomingLessonsChannel.kt index 41fb61925..63b3a4f91 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/channels/UpcomingLessonsChannel.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/UpcomingLessonsChannel.kt @@ -17,7 +17,7 @@ class UpcomingLessonsChannel @Inject constructor( ) : Channel { companion object { - const val CHANNEL_ID = "lesson_channel" + const val CHANNEL_ID = "upcoming_lesson_channel" } override fun create() { diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt index f7d8db0ae..899d45cba 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt @@ -3,8 +3,6 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.exam.ExamRepository -import io.github.wulkanowy.utils.monday -import io.github.wulkanowy.utils.sunday import io.github.wulkanowy.utils.waitForResult import java.time.LocalDate.now import javax.inject.Inject @@ -12,6 +10,6 @@ import javax.inject.Inject class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work { override suspend fun doWork(student: Student, semester: Semester) { - examRepository.getExams(student, semester, now().monday, now().sunday, true).waitForResult() + examRepository.getExams(student, semester, now(), now(), true).waitForResult() } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt index 84b806795..b08896966 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt @@ -14,6 +14,9 @@ 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.sdk.scrapper.notes.NoteCategory +import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory.NEUTRAL +import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory.POSITIVE import io.github.wulkanowy.services.sync.channels.NewNotesChannel import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView @@ -41,8 +44,20 @@ class NoteWork @Inject constructor( private fun notify(notes: List) { 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)) + .setContentTitle( + when (NoteCategory.getByValue(notes.first().categoryType)) { + POSITIVE -> context.resources.getQuantityString(R.plurals.praise_new_items, notes.size, notes.size) + NEUTRAL -> context.resources.getQuantityString(R.plurals.neutral_note_new_items, notes.size, notes.size) + else -> context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size) + } + ) + .setContentText( + when (NoteCategory.getByValue(notes.first().categoryType)) { + POSITIVE -> context.resources.getQuantityString(R.plurals.praise_notify_new_items, notes.size, notes.size) + NEUTRAL -> context.resources.getQuantityString(R.plurals.neutral_note_notify_new_items, notes.size, notes.size) + else -> context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size) + } + ) .setSmallIcon(R.drawable.ic_stat_note) .setAutoCancel(true) .setDefaults(DEFAULT_ALL) @@ -52,7 +67,13 @@ class NoteWork @Inject constructor( PendingIntent.getActivity(context, MainView.Section.NOTE.id, MainActivity.getStartIntent(context, MainView.Section.NOTE, true), FLAG_UPDATE_CURRENT)) .setStyle(NotificationCompat.InboxStyle().run { - setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size)) + setSummaryText( + when (NoteCategory.getByValue(notes.first().categoryType)) { + POSITIVE -> context.resources.getQuantityString(R.plurals.praise_number_item, notes.size, notes.size) + NEUTRAL -> context.resources.getQuantityString(R.plurals.neutral_note_number_item, notes.size, notes.size) + else -> context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size) + } + ) notes.forEach { addLine("${it.teacher}: ${it.category}") } this }) diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt index aec524259..ba8bfd85f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import timber.log.Timber @@ -63,7 +64,7 @@ open class BasePresenter( fun Flow.launch(individualJobTag: String = "load"): Job { jobs[individualJobTag]?.cancel() - val job = launchIn(this@BasePresenter) + val job = catch { errorHandler.dispatch(it) }.launchIn(this@BasePresenter) jobs[individualJobTag] = job Timber.d("Job $individualJobTag launched in ${this@BasePresenter.javaClass.simpleName}: $job") return job diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt index 8d6739a15..341ae4590 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt @@ -22,9 +22,11 @@ import io.github.wulkanowy.utils.getString import io.github.wulkanowy.utils.openAppInMarket import io.github.wulkanowy.utils.openEmailClient import io.github.wulkanowy.utils.openInternetBrowser +import okhttp3.internal.http2.StreamResetException import java.io.InterruptedIOException import java.io.PrintWriter import java.io.StringWriter +import java.net.ConnectException import java.net.SocketTimeoutException import java.net.UnknownHostException import javax.inject.Inject @@ -85,6 +87,8 @@ class ErrorDialog : BaseDialogFragment() { errorDialogReport.isEnabled = when (error) { is UnknownHostException, is InterruptedIOException, + is ConnectException, + is StreamResetException, is SocketTimeoutException, is ServiceUnavailableException, is FeatureDisabledException, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt index 24e59d36b..cd08b67eb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt @@ -4,7 +4,7 @@ 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.AppInfo -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import timber.log.Timber import javax.inject.Inject @@ -12,7 +12,7 @@ class AboutPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val appInfo: AppInfo, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { override fun onAttachView(view: AboutView) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt index a63d5045a..8e8a6149d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt @@ -9,6 +9,7 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.repositories.attendance.SentExcuseStatus import io.github.wulkanowy.databinding.ItemAttendanceBinding +import io.github.wulkanowy.utils.description import javax.inject.Inject class AttendanceAdapter @Inject constructor() : @@ -34,7 +35,7 @@ class AttendanceAdapter @Inject constructor() : with(holder.binding) { attendanceItemNumber.text = item.number.toString() attendanceItemSubject.text = item.subject - attendanceItemDescription.text = item.name + attendanceItemDescription.setText(item.description) attendanceItemAlert.visibility = item.run { if (absence && !excused) View.VISIBLE else View.INVISIBLE } attendanceItemNumber.visibility = View.GONE attendanceItemExcuseInfo.visibility = View.GONE diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt index 97b76e812..d5e2fe122 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt @@ -7,6 +7,7 @@ import android.view.ViewGroup import androidx.fragment.app.DialogFragment import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.databinding.DialogAttendanceBinding +import io.github.wulkanowy.utils.description import io.github.wulkanowy.utils.lifecycleAwareVariable import io.github.wulkanowy.utils.toFormattedString @@ -43,7 +44,7 @@ class AttendanceDialog : DialogFragment() { with(binding) { attendanceDialogSubject.text = attendance.subject - attendanceDialogDescription.text = attendance.name + attendanceDialogDescription.setText(attendance.description) attendanceDialogDate.text = attendance.date.toFormattedString() attendanceDialogNumber.text = attendance.number.toString() attendanceDialogClose.setOnClickListener { dismiss() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt index 158f08ba3..19f8523d4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -9,7 +9,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository 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.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResourceIn @@ -34,7 +34,7 @@ class AttendancePresenter @Inject constructor( private val attendanceRepository: AttendanceRepository, private val semesterRepository: SemesterRepository, private val prefRepository: PreferencesRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var baseDate: LocalDate = now().previousOrSameSchoolDay diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt index e5dce9ac8..4ce49d960 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt @@ -8,7 +8,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.subject.SubjectRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.flow.onEach @@ -22,7 +22,7 @@ class AttendanceSummaryPresenter @Inject constructor( private val attendanceSummaryRepository: AttendanceSummaryRepository, private val subjectRepository: SubjectRepository, private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var subjects = emptyList() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt index f63316a8d..406d011c0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt @@ -7,7 +7,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository 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.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday @@ -30,7 +30,7 @@ class ExamPresenter @Inject constructor( studentRepository: StudentRepository, private val examRepository: ExamRepository, private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var baseDate: LocalDate = now().nextOrSameSchoolDay diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt index d4c9f210a..e91c94eb2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt @@ -6,7 +6,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository 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.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.getCurrentOrLast import kotlinx.coroutines.delay @@ -18,7 +18,7 @@ class GradePresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { var selectedIndex = 0 diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeSortingMode.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeSortingMode.kt new file mode 100644 index 000000000..1e6b26e8c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeSortingMode.kt @@ -0,0 +1,10 @@ +package io.github.wulkanowy.ui.modules.grade + +enum class GradeSortingMode(val value: String) { + ALPHABETIC("alphabetic"), + DATE("date"); + + companion object { + fun getByValue(value: String) = values().firstOrNull { it.value == value } ?: ALPHABETIC + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt index c129d9485..6f9321692 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt @@ -40,10 +40,6 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter -) +) { + var newGrades = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt index a36517938..a34dd7acb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.grade.details +import android.annotation.SuppressLint import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.repositories.grade.GradeRepository @@ -10,7 +11,9 @@ import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.ALPHABETIC +import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.DATE +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResourceIn @@ -26,7 +29,7 @@ class GradeDetailsPresenter @Inject constructor( private val semesterRepository: SemesterRepository, private val preferencesRepository: PreferencesRepository, private val averageProvider: GradeAverageProvider, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var newGradesAmount: Int = 0 @@ -78,7 +81,10 @@ class GradeDetailsPresenter @Inject constructor( }.onEach { when (it.status) { Status.LOADING -> Timber.i("Select mark grades as read") - Status.SUCCESS -> Timber.i("Mark as read result: Success") + Status.SUCCESS -> { + Timber.i("Mark as read result: Success") + loadData(currentSemesterId, false) + } Status.ERROR -> { Timber.i("Mark as read result: An exception occurred") errorHandler.dispatch(it.error!!) @@ -184,10 +190,20 @@ class GradeDetailsPresenter @Inject constructor( } } + @SuppressLint("DefaultLocale") private fun createGradeItems(items: List): List { return items - .filter { it.grades.isNotEmpty() } - .sortedBy { it.subject } + .let { gradesWithAverages -> + if (!preferencesRepository.showSubjectsWithoutGrades) { + gradesWithAverages.filter { it.grades.isNotEmpty() } + } else gradesWithAverages + } + .let { + when (preferencesRepository.gradeSortingMode) { + DATE -> it.sortedByDescending { gradeDetailsWithAverage -> gradeDetailsWithAverage.grades.firstOrNull()?.date } + ALPHABETIC -> it.sortedBy { gradeDetailsWithAverage -> gradeDetailsWithAverage.subject.toLowerCase() } + } + } .map { (subject, average, points, _, grades) -> val subItems = grades .sortedByDescending { it.date } @@ -197,9 +213,10 @@ class GradeDetailsPresenter @Inject constructor( subject = subject, average = average, pointsSum = points, - newGrades = grades.filter { grade -> !grade.isRead }.size, grades = subItems - ), ViewType.HEADER)) + if (preferencesRepository.isGradeExpandable) emptyList() else subItems + ).apply { + newGrades = grades.filter { grade -> !grade.isRead }.size + }, ViewType.HEADER)) + if (preferencesRepository.isGradeExpandable) emptyList() else subItems }.flatten() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt index 112f4c582..2f46d21db 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt @@ -9,7 +9,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.subject.SubjectRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.flow.onEach @@ -23,7 +23,7 @@ class GradeStatisticsPresenter @Inject constructor( private val subjectRepository: SubjectRepository, private val semesterRepository: SemesterRepository, private val preferencesRepository: PreferencesRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var subjects = emptyList() @@ -164,8 +164,8 @@ class GradeStatisticsPresenter @Inject constructor( Status.SUCCESS -> { Timber.i("Loading grade stats result: Success") view?.run { - showEmpty(it.data!!.isEmpty()) - showContent(it.data.isNotEmpty()) + showEmpty(it.data!!.isEmpty() || it.data.first().partial.isEmpty()) + showContent(it.data.isNotEmpty() && it.data.first().partial.isNotEmpty()) showErrorView(false) updateData(it.data, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList) showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt index caa3cb84d..7ce98d105 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt @@ -7,7 +7,7 @@ import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.flow.onEach @@ -18,7 +18,7 @@ class GradeSummaryPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val averageProvider: GradeAverageProvider, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private lateinit var lastError: Throwable diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt index 8af0d83f6..f264d58e0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt @@ -7,7 +7,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository 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.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday @@ -29,7 +29,7 @@ class HomeworkPresenter @Inject constructor( studentRepository: StudentRepository, private val homeworkRepository: HomeworkRepository, private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var baseDate: LocalDate = LocalDate.now().nextOrSameSchoolDay diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt index 5d6ee162a..cd9a7e851 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt @@ -29,6 +29,8 @@ class HomeworkDetailsAdapter @Inject constructor() : attachments = value?.attachments.orEmpty() } + var isHomeworkFullscreen = false + var onAttachmentClickListener: (url: String) -> Unit = {} var onFullScreenClickListener = {} @@ -67,6 +69,8 @@ class HomeworkDetailsAdapter @Inject constructor() : homeworkDialogSubject.text = homework?.subject homeworkDialogTeacher.text = homework?.teacher homeworkDialogContent.text = homework?.content + homeworkDialogFullScreen.visibility = if (isHomeworkFullscreen) GONE else VISIBLE + homeworkDialogFullScreenExit.visibility = if (isHomeworkFullscreen) VISIBLE else GONE homeworkDialogFullScreen.setOnClickListener { homeworkDialogFullScreen.visibility = GONE homeworkDialogFullScreenExit.visibility = VISIBLE diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt index 78abfffd0..aecaa394d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt @@ -62,12 +62,25 @@ class HomeworkDetailsDialog : BaseDialogFragment(), Homew homeworkDialogClose.setOnClickListener { dismiss() } } + if (presenter.isHomeworkFullscreen) { + dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT) + } else { + dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT) + } + with(binding.homeworkDialogRecycler) { layoutManager = LinearLayoutManager(context) adapter = detailsAdapter.apply { onAttachmentClickListener = { context.openInternetBrowser(it, ::showMessage) } - onFullScreenClickListener = { dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT) } - onFullScreenExitClickListener = { dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT) } + onFullScreenClickListener = { + dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT) + presenter.isHomeworkFullscreen = true + } + onFullScreenExitClickListener = { + dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT) + presenter.isHomeworkFullscreen = false + } + isHomeworkFullscreen = presenter.isHomeworkFullscreen homework = this@HomeworkDetailsDialog.homework } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt index a53d38e86..1d4dac2cb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt @@ -3,10 +3,11 @@ package io.github.wulkanowy.ui.modules.homework.details import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.repositories.homework.HomeworkRepository +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository 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.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.flowWithResource import kotlinx.coroutines.flow.onEach import timber.log.Timber @@ -16,9 +17,16 @@ class HomeworkDetailsPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val homeworkRepository: HomeworkRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper, + private val preferencesRepository: PreferencesRepository ) : BasePresenter(errorHandler, studentRepository) { + var isHomeworkFullscreen + get() = preferencesRepository.isHomeworkFullscreen + set(value) { + preferencesRepository.isHomeworkFullscreen = value + } + override fun onAttachView(view: HomeworkDetailsView) { super.onAttachView(view) view.initView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt index 749989e3a..aff1c84ca 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt @@ -14,6 +14,7 @@ import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment import io.github.wulkanowy.ui.modules.login.recover.LoginRecoverFragment import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment +import io.github.wulkanowy.utils.UpdateHelper import io.github.wulkanowy.utils.setOnSelectPageListener import javax.inject.Inject @@ -25,6 +26,9 @@ class LoginActivity : BaseActivity(), Logi private val loginAdapter = BaseFragmentPagerAdapter(supportFragmentManager) + @Inject + lateinit var updateHelper: UpdateHelper + companion object { fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java) @@ -37,8 +41,20 @@ class LoginActivity : BaseActivity(), Logi setContentView(ActivityLoginBinding.inflate(layoutInflater).apply { binding = this }.root) setSupportActionBar(binding.loginToolbar) messageContainer = binding.loginContainer + updateHelper.messageContainer = binding.loginContainer presenter.onAttachView(this) + updateHelper.checkAndInstallUpdates(this) + } + + override fun onResume() { + super.onResume() + updateHelper.onResume(this) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + updateHelper.onActivityResult(requestCode, resultCode) } override fun initView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt index dfe82ad8b..9dfc3011b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt @@ -6,7 +6,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank @@ -17,7 +17,7 @@ import javax.inject.Inject class LoginAdvancedPresenter @Inject constructor( studentRepository: StudentRepository, private val loginErrorHandler: LoginErrorHandler, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository) { override fun onAttachView(view: LoginAdvancedView) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt index b921140c7..3e77cd775 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt @@ -4,7 +4,7 @@ import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank @@ -15,7 +15,7 @@ import javax.inject.Inject class LoginFormPresenter @Inject constructor( studentRepository: StudentRepository, private val loginErrorHandler: LoginErrorHandler, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository) { private var lastError: Throwable? = null diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt index e58377058..319541b8e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt @@ -4,7 +4,7 @@ import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.recover.RecoverRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank @@ -15,7 +15,7 @@ import javax.inject.Inject class LoginRecoverPresenter @Inject constructor( studentRepository: StudentRepository, private val loginErrorHandler: RecoverErrorHandler, - private val analytics: FirebaseAnalyticsHelper, + private val analytics: AnalyticsHelper, private val recoverRepository: RecoverRepository ) : BasePresenter(loginErrorHandler, studentRepository) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt index 1384f25a0..dc8241992 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt @@ -6,7 +6,7 @@ import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank import kotlinx.coroutines.flow.onEach @@ -17,7 +17,7 @@ import javax.inject.Inject class LoginStudentSelectPresenter @Inject constructor( studentRepository: StudentRepository, private val loginErrorHandler: LoginErrorHandler, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository) { private var lastError: Throwable? = null diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt index a2cda4fc1..9f19539a0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt @@ -4,7 +4,7 @@ import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank @@ -16,7 +16,7 @@ import javax.inject.Inject class LoginSymbolPresenter @Inject constructor( studentRepository: StudentRepository, private val loginErrorHandler: LoginErrorHandler, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository) { private var lastError: Throwable? = null diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt index 90aea441e..cfd793f20 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt @@ -5,7 +5,7 @@ import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository 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.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.flow.onEach @@ -16,7 +16,7 @@ class LuckyNumberPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val luckyNumberRepository: LuckyNumberRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private lateinit var lastError: Throwable diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt index 035608893..245f3b16c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt @@ -1,14 +1,21 @@ package io.github.wulkanowy.ui.modules.main +import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.content.pm.ShortcutInfo +import android.content.pm.ShortcutManager +import android.graphics.drawable.Icon +import android.os.Build import android.os.Build.VERSION.SDK_INT import android.os.Build.VERSION_CODES.LOLLIPOP import android.os.Bundle import android.view.Menu import android.view.MenuItem +import androidx.annotation.RequiresApi +import androidx.core.content.getSystemService import androidx.core.view.ViewCompat import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment @@ -31,7 +38,9 @@ import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.more.MoreFragment import io.github.wulkanowy.ui.modules.note.NoteFragment import io.github.wulkanowy.ui.modules.timetable.TimetableFragment -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.UpdateHelper import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.safelyPopFragments @@ -46,7 +55,13 @@ class MainActivity : BaseActivity(), MainVie override lateinit var presenter: MainPresenter @Inject - lateinit var analytics: FirebaseAnalyticsHelper + lateinit var analytics: AnalyticsHelper + + @Inject + lateinit var updateHelper: UpdateHelper + + @Inject + lateinit var appInfo: AppInfo private val overlayProvider by lazy { ElevationOverlayProvider(this) } @@ -59,7 +74,7 @@ class MainActivity : BaseActivity(), MainVie return Intent(context, MainActivity::class.java) .apply { if (clear) flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK - startMenu?.let { putExtra(EXTRA_START_MENU, it) } + startMenu?.let { putExtra(EXTRA_START_MENU, it.id) } } } } @@ -83,18 +98,58 @@ class MainActivity : BaseActivity(), MainVie MainView.Section.LUCKY_NUMBER.id to LuckyNumberFragment.newInstance() ) + @SuppressLint("NewApi") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(ActivityMainBinding.inflate(layoutInflater).apply { binding = this }.root) setSupportActionBar(binding.mainToolbar) messageContainer = binding.mainFragmentContainer + updateHelper.messageContainer = binding.mainFragmentContainer - presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_START_MENU) as? MainView.Section) + presenter.onAttachView(this, MainView.Section.values().singleOrNull { it.id == intent.getIntExtra(EXTRA_START_MENU, -1) }) with(navController) { initialize(startMenuIndex, savedInstanceState) pushFragment(moreMenuFragments[startMenuMoreIndex]) } + updateHelper.checkAndInstallUpdates(this) + } + + override fun onResume() { + super.onResume() + updateHelper.onResume(this) + } + + @SuppressLint("NewApi") + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + updateHelper.onActivityResult(requestCode, resultCode) + if (appInfo.systemVersion >= Build.VERSION_CODES.N_MR1) initShortcuts() + } + + @RequiresApi(Build.VERSION_CODES.N_MR1) + fun initShortcuts() { + val shortcutsList = mutableListOf() + + listOf( + Triple(getString(R.string.grade_title), R.drawable.ic_shortcut_grade, MainView.Section.GRADE), + Triple(getString(R.string.attendance_title), R.drawable.ic_shortcut_attendance, MainView.Section.ATTENDANCE), + Triple(getString(R.string.exam_title), R.drawable.ic_shortcut_exam, MainView.Section.EXAM), + Triple(getString(R.string.timetable_title), R.drawable.ic_shortcut_timetable, MainView.Section.TIMETABLE), + Triple(getString(R.string.message_title), R.drawable.ic_shortcut_message, MainView.Section.MESSAGE) + ).forEach { (title, icon, enum) -> + shortcutsList.add(ShortcutInfo.Builder(applicationContext, title) + .setShortLabel(title) + .setLongLabel(title) + .setIcon(Icon.createWithResource(applicationContext, icon)) + .setIntents(arrayOf( + Intent(applicationContext, MainActivity::class.java).setAction(Intent.ACTION_VIEW), + Intent(applicationContext, MainActivity::class.java).putExtra(EXTRA_START_MENU, enum.id) + .setAction(Intent.ACTION_VIEW).addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK))) + .build()) + } + + getSystemService()?.dynamicShortcuts = shortcutsList } override fun onCreateOptionsMenu(menu: Menu?): Boolean { @@ -143,8 +198,8 @@ class MainActivity : BaseActivity(), MainVie analytics.setCurrentScreen(this, name) } - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - return if (item?.itemId == R.id.mainMenuAccount) presenter.onAccountManagerSelected() + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.mainMenuAccount) presenter.onAccountManagerSelected() else false } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt index 8d5c9d67f..7ea8197e7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt @@ -8,7 +8,7 @@ import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.main.MainView.Section.GRADE import io.github.wulkanowy.ui.modules.main.MainView.Section.MESSAGE import io.github.wulkanowy.ui.modules.main.MainView.Section.SCHOOL -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import timber.log.Timber import javax.inject.Inject @@ -17,7 +17,7 @@ class MainPresenter @Inject constructor( studentRepository: StudentRepository, private val prefRepository: PreferencesRepository, private val syncManager: SyncManager, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { fun onAttachView(view: MainView, initMenu: MainView.Section?) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt index 740f4927c..b35731cae 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -174,7 +174,7 @@ class MessagePreviewFragment : @RequiresApi(Build.VERSION_CODES.LOLLIPOP) override fun printDocument(html: String, jobName: String) { - val webView = WebView(activity) + val webView = WebView(requireContext()) webView.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = false diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt index d261d1ee1..039c7f140 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -11,7 +11,7 @@ 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.AppInfo -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResourceIn @@ -24,7 +24,7 @@ class MessagePreviewPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, - private val analytics: FirebaseAnalyticsHelper, + private val analytics: AnalyticsHelper, private var appInfo: AppInfo ) : BasePresenter(errorHandler, studentRepository) { @@ -64,7 +64,8 @@ class MessagePreviewPresenter @Inject constructor( when (it.status) { Status.LOADING -> Timber.i("Loading message ${message.messageId} preview started") Status.SUCCESS -> { - Timber.i("Loading message ${it.data!!.message.messageId} preview result: Success ") + Timber.i("Loading message ${message.messageId} preview result: Success ") + checkNotNull(it.data, { "Can't find message in local db! Probably no longer exist in this folder" }) this@MessagePreviewPresenter.message = it.data.message this@MessagePreviewPresenter.attachments = it.data.attachments view?.apply { @@ -194,6 +195,7 @@ class MessagePreviewPresenter @Inject constructor( view?.run { lastError = error setErrorDetails(message) + showContent(false) showErrorView(true) setErrorRetryCallback { retryCallback() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt index 2267279c2..59944d41d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.message.send +import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.graphics.Rect @@ -74,6 +75,7 @@ class SendMessageActivity : BaseActivity(errorHandler, studentRepository) { fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean?) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index 79062dcfc..2143a4882 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -8,7 +8,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository 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.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.toFormattedString @@ -32,7 +32,7 @@ class MessageTabPresenter @Inject constructor( studentRepository: StudentRepository, private val messageRepository: MessageRepository, private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { lateinit var folder: MessageFolder diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt index 6314c2c31..456609e43 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt @@ -7,7 +7,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository 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.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResourceIn @@ -21,7 +21,7 @@ class MobileDevicePresenter @Inject constructor( studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, private val mobileDeviceRepository: MobileDeviceRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private lateinit var lastError: Throwable diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt index f6d0de992..8270693d9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt @@ -6,7 +6,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository 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.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import kotlinx.coroutines.flow.onEach @@ -18,7 +18,7 @@ class MobileDeviceTokenPresenter @Inject constructor( studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, private val mobileDeviceRepository: MobileDeviceRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { override fun onAttachView(view: MobileDeviceTokenVIew) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt index db3b495a8..2e8bec5d1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt @@ -7,7 +7,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository 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.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResourceIn @@ -21,7 +21,7 @@ class NotePresenter @Inject constructor( studentRepository: StudentRepository, private val noteRepository: NoteRepository, private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private lateinit var lastError: Throwable diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt index 4cda5d33c..554d422df 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt @@ -6,7 +6,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository 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.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.flow.onEach @@ -18,7 +18,7 @@ class SchoolPresenter @Inject constructor( studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, private val schoolRepository: SchoolRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var address: String? = null diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt index ff02116b9..0c58317b1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt @@ -6,7 +6,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.teacher.TeacherRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.flow.onEach @@ -18,7 +18,7 @@ class TeacherPresenter @Inject constructor( studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, private val teacherRepository: TeacherRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private lateinit var lastError: Throwable diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt index 3d1e063e2..e640dd60d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt @@ -9,7 +9,7 @@ import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AppInfo -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.isHolidays import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.onEach @@ -22,7 +22,7 @@ class SettingsPresenter @Inject constructor( studentRepository: StudentRepository, private val preferencesRepository: PreferencesRepository, private val timetableNotificationHelper: TimetableNotificationSchedulerHelper, - private val analytics: FirebaseAnalyticsHelper, + private val analytics: AnalyticsHelper, private val syncManager: SyncManager, private val chuckerCollector: ChuckerCollector, private val appInfo: AppInfo diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt index d87f06207..f049f828e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt @@ -6,6 +6,7 @@ import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup import android.widget.TextView +import androidx.core.view.ViewCompat import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Timetable @@ -40,12 +41,14 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter() - private fun resetTimers() { - Timber.d("Timetable timers reset") + fun resetTimers() { + Timber.d("Timetable timers (${timers.size}) reset") with(timers) { forEach { (_, timer) -> timer.cancel() } clear() @@ -69,11 +72,6 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter(R.layout.fragme else false } - override fun updateData(data: List, showWholeClassPlanType: String, showTimetableTimers: Boolean) { + override fun updateData(data: List, showWholeClassPlanType: String, showGroupsInPlanType: Boolean, showTimetableTimers: Boolean) { with(timetableAdapter) { items = data.toMutableList() showTimers = showTimetableTimers showWholeClassPlan = showWholeClassPlanType + showGroupsInPlan = showGroupsInPlanType notifyDataSetChanged() } } @@ -185,6 +186,7 @@ class TimetableFragment : BaseFragment(R.layout.fragme } override fun onDestroyView() { + timetableAdapter.resetTimers() presenter.onDetachView() super.onDestroyView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt index 8581b73c6..0e913acf6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -9,7 +9,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday @@ -22,7 +22,6 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onEach import timber.log.Timber -import java.lang.NullPointerException import java.time.LocalDate import java.time.LocalDate.now import java.time.LocalDate.of @@ -35,7 +34,7 @@ class TimetablePresenter @Inject constructor( private val timetableRepository: TimetableRepository, private val semesterRepository: SemesterRepository, private val prefRepository: PreferencesRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var baseDate: LocalDate = now().nextOrSameSchoolDay @@ -143,6 +142,7 @@ class TimetablePresenter @Inject constructor( view?.apply { updateData( showWholeClassPlanType = prefRepository.showWholeClassPlan, + showGroupsInPlanType = prefRepository.showGroupsInPlan, showTimetableTimers = prefRepository.showTimetableTimers, data = it.data!! .filter { item -> if (prefRepository.showWholeClassPlan == "no") item.isStudentPlan else true } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt index fe34f1ee9..244120176 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt @@ -12,7 +12,7 @@ interface TimetableView : BaseView { fun initView() - fun updateData(data: List, showWholeClassPlanType: String, showTimetableTimers: Boolean) + fun updateData(data: List, showWholeClassPlanType: String, showGroupsInPlanType: Boolean, showTimetableTimers: Boolean) fun updateNavigationDay(date: String) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt index f1c2cedc6..e5551ed5f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt @@ -7,7 +7,7 @@ import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRe import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday @@ -30,7 +30,7 @@ class CompletedLessonsPresenter @Inject constructor( private val completedLessonsErrorHandler: CompletedLessonsErrorHandler, private val semesterRepository: SemesterRepository, private val completedLessonsRepository: CompletedLessonsRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(completedLessonsErrorHandler, studentRepository) { private var baseDate: LocalDate = now().nextOrSameSchoolDay diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt index 60ad9d5cc..18e0ea562 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt @@ -24,7 +24,7 @@ import io.github.wulkanowy.services.HiltBroadcastReceiver import io.github.wulkanowy.services.widgets.TimetableWidgetService import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.nextOrSameSchoolDay import io.github.wulkanowy.utils.nextSchoolDay import io.github.wulkanowy.utils.previousSchoolDay @@ -49,7 +49,7 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() { lateinit var sharedPref: SharedPrefProvider @Inject - lateinit var analytics: FirebaseAnalyticsHelper + lateinit var analytics: AnalyticsHelper companion object { diff --git a/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt index c2b1efaa8..f10b00a07 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt @@ -1,6 +1,9 @@ package io.github.wulkanowy.utils +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.github.wulkanowy.sdk.scrapper.attendance.AttendanceCategory /** * [UONET+ - Zasady tworzenia podsumowań liczb uczniów obecnych i nieobecnych w tabeli frekwencji] @@ -23,4 +26,15 @@ private fun calculatePercentage(presence: Double, absence: Double): Double { return if ((presence + absence) == 0.0) 0.0 else (presence / (presence + absence)) * 100 } - +inline val Attendance.description + get() = when (AttendanceCategory.getCategoryByName(name)) { + AttendanceCategory.PRESENCE -> R.string.attendance_present + AttendanceCategory.ABSENCE_UNEXCUSED -> R.string.attendance_absence_unexcused + AttendanceCategory.ABSENCE_EXCUSED -> R.string.attendance_absence_excused + AttendanceCategory.UNEXCUSED_LATENESS -> R.string.attendance_unexcused_lateness + AttendanceCategory.EXCUSED_LATENESS -> R.string.attendance_excused_lateness + AttendanceCategory.ABSENCE_FOR_SCHOOL_REASONS -> R.string.attendance_absence_school + AttendanceCategory.EXEMPTION -> R.string.attendance_exemption + AttendanceCategory.DELETED -> R.string.attendance_deleted + else -> R.string.attendance_unknown + } diff --git a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt index 314994054..724458c04 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt @@ -80,17 +80,13 @@ fun flowWithResource(block: suspend () -> T) = flow { fun flowWithResourceIn(block: suspend () -> Flow>) = flow { emit(Resource.loading()) - try { - block() - .catch { emit(Resource.error(it)) } - .collect { - if (it.status != Status.LOADING) { // LOADING is already emitted - emit(it) - } + block() + .catch { emit(Resource.error(it)) } + .collect { + if (it.status != Status.LOADING) { // LOADING is already emitted + emit(it) } - } catch (e: Throwable) { - emit(Resource.error(e)) - } + } } fun Flow>.afterLoading(callback: () -> Unit) = onEach { diff --git a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt index fc3528495..48d46892b 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt @@ -22,32 +22,32 @@ private fun Bundle?.checkSavedState() = if (this == null) "(STATE IS NULL)" else class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks { - override fun onActivityPaused(activity: Activity?) { - activity?.let { Timber.d("${it::class.java.simpleName} PAUSED") } + override fun onActivityPaused(activity: Activity) { + Timber.d("${activity::class.java.simpleName} PAUSED") } - override fun onActivityResumed(activity: Activity?) { - activity?.let { Timber.d("${it::class.java.simpleName} RESUMED") } + override fun onActivityResumed(activity: Activity) { + Timber.d("${activity::class.java.simpleName} RESUMED") } - override fun onActivityStarted(activity: Activity?) { - activity?.let { Timber.d("${it::class.java.simpleName} STARTED") } + override fun onActivityStarted(activity: Activity) { + Timber.d("${activity::class.java.simpleName} STARTED") } - override fun onActivityDestroyed(activity: Activity?) { - activity?.let { Timber.d("${it::class.java.simpleName} DESTROYED") } + override fun onActivityDestroyed(activity: Activity) { + Timber.d("${activity::class.java.simpleName} DESTROYED") } - override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) { - activity?.let { Timber.d("${it::class.java.simpleName} SAVED INSTANCE STATE") } + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + Timber.d("${activity::class.java.simpleName} SAVED INSTANCE STATE") } - override fun onActivityStopped(activity: Activity?) { - activity?.let { Timber.d("${it::class.java.simpleName} STOPPED") } + override fun onActivityStopped(activity: Activity) { + Timber.d("${activity::class.java.simpleName} STOPPED") } - override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { - activity?.let { Timber.d("${it::class.java.simpleName} CREATED ${savedInstanceState.checkSavedState()}") } + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + Timber.d("${activity::class.java.simpleName} CREATED ${savedInstanceState.checkSavedState()}") } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt index de9f656a2..da5fd3dbb 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt @@ -4,20 +4,26 @@ import android.content.res.Resources import io.github.wulkanowy.R import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException +import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException +import io.github.wulkanowy.sdk.scrapper.exception.VulcanException import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException +import okhttp3.internal.http2.StreamResetException import java.io.InterruptedIOException +import java.net.ConnectException import java.net.SocketTimeoutException import java.net.UnknownHostException fun Resources.getString(error: Throwable) = when (error) { is UnknownHostException -> getString(R.string.error_no_internet) - is SocketTimeoutException, is InterruptedIOException -> getString(R.string.error_timeout) + is SocketTimeoutException, is InterruptedIOException, is ConnectException, is StreamResetException -> getString(R.string.error_timeout) is NotLoggedInException -> getString(R.string.error_login_failed) is PasswordChangeRequiredException -> getString(R.string.error_password_change_required) is ServiceUnavailableException -> getString(R.string.error_service_unavailable) is FeatureDisabledException -> getString(R.string.error_feature_disabled) is FeatureNotAvailableException -> getString(R.string.error_feature_not_available) + is VulcanException -> getString(R.string.error_unknown_uonet) + is ScrapperException -> getString(R.string.error_unknown_app) else -> getString(R.string.error_unknown) } diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt index d1aba1605..9bd30e878 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt @@ -13,8 +13,7 @@ import java.time.Month import java.time.ZoneId import java.time.ZoneOffset import java.time.format.DateTimeFormatter.ofPattern -import java.time.format.TextStyle.FULL_STANDALONE -import java.time.format.TextStyle.* +import java.time.format.TextStyle.FULL import java.time.temporal.TemporalAdjusters.firstInMonth import java.time.temporal.TemporalAdjusters.next import java.time.temporal.TemporalAdjusters.previous @@ -78,6 +77,12 @@ inline val LocalDate.nextOrSameSchoolDay: LocalDate } } +inline val LocalDate.startExamsDay: LocalDate + get() = nextOrSameSchoolDay.monday + +inline val LocalDate.endExamsDay: LocalDate + get() = nextOrSameSchoolDay.monday.plusWeeks(4).minusDays(1) + inline val LocalDate.previousOrSameSchoolDay: LocalDate get() { return when (dayOfWeek) { diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 51c3c13ac..23fd77bc3 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,7 +1,6 @@ -Wersja 0.20.1 -- naprawiliśmy powiadomienie o szczęśliwym numerku -- naprawiliśmy przełączanie na nowy rok szkolny -- naprawiliśmy funkcję resetowania hasła -- dodaliśmy dziennik e-Skarżysko +Wersja 0.22.2 +- naprawiliśmy problem z wyświetlaniem pozycji na liście ocen +- zmieniliśmy komunikaty o błędach, które powinny być teraz czytelniejsze dla większej liczby użytkowników +- zwiększyliśmy maksymalny czas, przez który aplikacja będzie próbowała łączyć się z dziennikiem do 1 minuty Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases diff --git a/app/src/main/res/drawable-hdpi/ic_shortcut_attendance.png b/app/src/main/res/drawable-hdpi/ic_shortcut_attendance.png new file mode 100644 index 000000000..0b5feff2d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_shortcut_attendance.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_shortcut_exam.png b/app/src/main/res/drawable-hdpi/ic_shortcut_exam.png new file mode 100644 index 000000000..e5af0d086 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_shortcut_exam.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_shortcut_grade.png b/app/src/main/res/drawable-hdpi/ic_shortcut_grade.png new file mode 100644 index 000000000..095a8228c Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_shortcut_grade.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_shortcut_message.png b/app/src/main/res/drawable-hdpi/ic_shortcut_message.png new file mode 100644 index 000000000..7bcd79e01 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_shortcut_message.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_shortcut_timetable.png b/app/src/main/res/drawable-hdpi/ic_shortcut_timetable.png new file mode 100644 index 000000000..2808559a5 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_shortcut_timetable.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_shortcut_attendance.png b/app/src/main/res/drawable-mdpi/ic_shortcut_attendance.png new file mode 100644 index 000000000..e81e7ad92 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_shortcut_attendance.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_shortcut_exam.png b/app/src/main/res/drawable-mdpi/ic_shortcut_exam.png new file mode 100644 index 000000000..3bdb5297f Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_shortcut_exam.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_shortcut_grade.png b/app/src/main/res/drawable-mdpi/ic_shortcut_grade.png new file mode 100644 index 000000000..e35135071 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_shortcut_grade.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_shortcut_message.png b/app/src/main/res/drawable-mdpi/ic_shortcut_message.png new file mode 100644 index 000000000..392c45d24 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_shortcut_message.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_shortcut_timetable.png b/app/src/main/res/drawable-mdpi/ic_shortcut_timetable.png new file mode 100644 index 000000000..7d61306a4 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_shortcut_timetable.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_shortcut_attendance.png b/app/src/main/res/drawable-xhdpi/ic_shortcut_attendance.png new file mode 100644 index 000000000..302b9e0ee Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_shortcut_attendance.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_shortcut_exam.png b/app/src/main/res/drawable-xhdpi/ic_shortcut_exam.png new file mode 100644 index 000000000..9f36ca47a Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_shortcut_exam.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_shortcut_grade.png b/app/src/main/res/drawable-xhdpi/ic_shortcut_grade.png new file mode 100644 index 000000000..281bc7a31 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_shortcut_grade.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_shortcut_message.png b/app/src/main/res/drawable-xhdpi/ic_shortcut_message.png new file mode 100644 index 000000000..184929a3c Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_shortcut_message.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_shortcut_timetable.png b/app/src/main/res/drawable-xhdpi/ic_shortcut_timetable.png new file mode 100644 index 000000000..9a40fe61c Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_shortcut_timetable.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_shortcut_attendance.png b/app/src/main/res/drawable-xxhdpi/ic_shortcut_attendance.png new file mode 100644 index 000000000..9b4ef2daf Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_shortcut_attendance.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_shortcut_exam.png b/app/src/main/res/drawable-xxhdpi/ic_shortcut_exam.png new file mode 100644 index 000000000..c2677a139 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_shortcut_exam.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_shortcut_grade.png b/app/src/main/res/drawable-xxhdpi/ic_shortcut_grade.png new file mode 100644 index 000000000..8b51021cb Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_shortcut_grade.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_shortcut_message.png b/app/src/main/res/drawable-xxhdpi/ic_shortcut_message.png new file mode 100644 index 000000000..250c290aa Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_shortcut_message.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_shortcut_timetable.png b/app/src/main/res/drawable-xxhdpi/ic_shortcut_timetable.png new file mode 100644 index 000000000..c530153a1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_shortcut_timetable.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_shortcut_attendance.png b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_attendance.png new file mode 100644 index 000000000..7b9a68a70 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_attendance.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_shortcut_exam.png b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_exam.png new file mode 100644 index 000000000..519c50abc Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_exam.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_shortcut_grade.png b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_grade.png new file mode 100644 index 000000000..13c793d5e Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_grade.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_shortcut_message.png b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_message.png new file mode 100644 index 000000000..eaffa2dde Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_message.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_shortcut_timetable.png b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_timetable.png new file mode 100644 index 000000000..03522e9a5 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_shortcut_timetable.png differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 2ea0a4d39..2f88ecc82 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ - + app:contentInsetStartWithNavigation="0dp" + app:layout_constraintTop_toTopOf="parent" /> + android:layout_height="0dp" + app:layout_constraintBottom_toTopOf="@id/mainBottomNav" + app:layout_constraintTop_toBottomOf="@id/mainToolbar" /> - + app:layout_constraintBottom_toBottomOf="parent" /> + diff --git a/app/src/main/res/layout/fragment_contributor.xml b/app/src/main/res/layout/fragment_contributor.xml index 399ab5999..d913c5a4d 100644 --- a/app/src/main/res/layout/fragment_contributor.xml +++ b/app/src/main/res/layout/fragment_contributor.xml @@ -1,5 +1,6 @@ @@ -20,17 +21,18 @@ android:id="@+id/creatorRecycler" android:layout_width="match_parent" android:layout_height="0dp" - android:layout_weight="1" /> + android:layout_weight="1" + tools:listitem="@layout/item_contributor" /> diff --git a/app/src/main/res/layout/item_about.xml b/app/src/main/res/layout/item_about.xml index f988c47ba..6a95e5529 100644 --- a/app/src/main/res/layout/item_about.xml +++ b/app/src/main/res/layout/item_about.xml @@ -33,7 +33,8 @@ + android:orientation="vertical" + app:layout_constraintBottom_toBottomOf="@+id/attendanceItemDetailsContainer" + app:layout_constraintEnd_toStartOf="@+id/attendanceItemDetailsContainer" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/attendanceItemDetailsContainer"> - + android:layout_marginEnd="10dp" + app:layout_constraintEnd_toStartOf="@+id/attendanceItemAlert" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintStart_toEndOf="@+id/attendanceItemNumberContainer" + app:layout_constraintTop_toTopOf="parent"> - + + + + - + diff --git a/app/src/main/res/layout/item_attendance_summary_subject.xml b/app/src/main/res/layout/item_attendance_summary_subject.xml index 263c08a73..ff731461d 100644 --- a/app/src/main/res/layout/item_attendance_summary_subject.xml +++ b/app/src/main/res/layout/item_attendance_summary_subject.xml @@ -3,7 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/attendanceSummaryItemSubject" android:layout_width="match_parent" - android:layout_height="48dp" + android:layout_height="wrap_content" + android:minHeight="48dp" android:gravity="start" android:maxLines="1" android:paddingLeft="16dp" diff --git a/app/src/main/res/layout/item_completed_lesson.xml b/app/src/main/res/layout/item_completed_lesson.xml index b9beec804..e9944f56f 100644 --- a/app/src/main/res/layout/item_completed_lesson.xml +++ b/app/src/main/res/layout/item_completed_lesson.xml @@ -12,8 +12,11 @@ diff --git a/app/src/main/res/layout/item_contributor.xml b/app/src/main/res/layout/item_contributor.xml index 36eb64ecc..9b1263438 100644 --- a/app/src/main/res/layout/item_contributor.xml +++ b/app/src/main/res/layout/item_contributor.xml @@ -15,7 +15,8 @@ android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" - android:contentDescription="@string/contributor_avatar_description" /> + android:contentDescription="@string/contributor_avatar_description" + tools:src="@tools:sample/avatars" /> - + app:layout_constraintTop_toTopOf="parent"> - + - + + + + + diff --git a/app/src/main/res/layout/item_license.xml b/app/src/main/res/layout/item_license.xml index aaa7cd349..cb55ab87a 100644 --- a/app/src/main/res/layout/item_license.xml +++ b/app/src/main/res/layout/item_license.xml @@ -2,7 +2,8 @@ + tools:visibility="visible" /> + + + tools:visibility="visible" /> diff --git a/app/src/main/res/layout/item_timetable_small.xml b/app/src/main/res/layout/item_timetable_small.xml index 98a213ec3..037c10d5d 100644 --- a/app/src/main/res/layout/item_timetable_small.xml +++ b/app/src/main/res/layout/item_timetable_small.xml @@ -7,7 +7,8 @@ diff --git a/app/src/main/res/values-cs-rCZ/preferences_values.xml b/app/src/main/res/values-cs-rCZ/preferences_values.xml new file mode 100644 index 000000000..bcb690e54 --- /dev/null +++ b/app/src/main/res/values-cs-rCZ/preferences_values.xml @@ -0,0 +1,52 @@ + + + + Světlý + Tmavý + Černý (AMOLED) + + + Jazyk systému + Polski + English + Pусский + Українська + Deutsch + Čeština + + + 15 minut + 30 minut + 1 hodina + 2 hodiny + 6 hodin + 12 hodin + 24 hodin + + + 0,00 + 0,25 + 0,33 + 0,5 + 0,75 + + + Abecedně + Podle data + + + Dzienniczek+ + Wulkanowy + Barvy známek v deníku + + + Průměrná známka od druhého semestru + Průměr známek z obou semestrů + Průměr známek z celého roku + + + Neukaž + Ukázat vše + Ukázat menší + + diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml new file mode 100644 index 000000000..67babcd53 --- /dev/null +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -0,0 +1,464 @@ + + + + Přihlásit se + Wulkanowy + Známky + Prezence + Zkoušky + Plán lekce + Nastavení + Více + O aplikaci + Prohlížeč protokolů + Tvůrci + Licence + Zprávy + Nová zpráva + Poznámky a úspěchy + Domácí práce + Vyberte účet + + Semestr %1$d, %2$d/%3$d + + Přihlaste se pomocí studentského nebo nadřazeného účtu + Zadejte symbol + Uživatelské jméno + Email + Přihlášení, číslo PESEL nebo e-mail + Heslo + Deník UONET+ + Mobile API + Scraper + Hybridní + Token + PIN + Klíč API + Symbol + Přihlásit + Toto heslo je příliš krátké + Přihlašovací údaje jsou nesprávné. Zkontrolujte, zda je v poli níže vybrán správný deník UONET+ + Neplatný PIN + Neplatný token + Platnost tokenu vypršela + Nesprávná e-mailová adresa + Neplatné přihlášení + Neplatný symbol + Student nebyl nalezen. Zkontrolujte symbol + Toto pole je povinné + Vybraný student je již přihlášen + Symbol najdete na stránce deníku v Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne + Vyberte studenty k přihlášení do aplikace + Jiné možnosti + V tomto režimu nefungují následující: šťastné číslo, statistiky třídy, shrnutí docházky, ospravedlnění nepřítomnosti, absolvované lekce, informace o škole a prohlížení seznamu registrovaných zařízení + Tento režim zobrazuje stejná data, která se zobrazují na webových stránkách deníka + Kombinace nejlepších vlastností ostatních dvou režimů. Funguje rychleji než scraper a poskytuje funkce, které nejsou k dispozici v režimu Mobile API. Je to v experimentální fázi + Zásady ochrany osobních údajů + Problémy s přihlášením? Napište nám! + Email + Discord + Poslat e-mail + Popište podrobnosti problému: + Ujistěte se, že je vybrán správný UONET+ deník! + Zapomněl jsem své heslo + Obnovte svůj účet + Obnovit + Student je již přihlášen + + Správce účtu + Přihlásit se + Platnost relace vypršela + Vaše relace vypršela, přihlaste se prosím znovu + + Známka + Semestr %d + Změnit semestr + Žádné známky + Váha + Váha: %s + Komentář + Žádné nové známky + Počet nových známek: %1$d + Průměrný: %1$.2f + Body: %s + Žádný průměr + Předpovězeno: %1$s + Konečná: %1$s + Celkem bodů + Konečná známka + Předpokládaná známka + Vypočítaný průměr + Konečný průměr + Souhrn + Třída + Označit jako přečtené + Částečně + Semestr + Body + + %d známka + %d známky + %d známky + %d známky + + + Nová známka + Nové známky + Nové známky + Nové známky + + + Nová předpokládaná známka + Nové předpovídané známky + Nové předpovídané známky + Nové předpovídané známky + + + Nová konečná známka + Nové konečné známky + Nové konečné známky + Nové konečné známky + + + Máte %1$d novou známku + Máte %1$d nové známky + Máte %1$d nové známky + Máte %1$d nové známky + + + Máte %1$d novou konečnou známku + Máte %1$d nové konečné známky + Máte %1$d nové konečné známky + Máte %1$d nové konečné známky + + + Máte %1$d novou konečnou známku + Máte %1$d nové konečné známky + Máte %1$d nové konečné známky + Máte %1$d nové konečné známky + + + Lekce + Pokoj + Skupina + Hodiny + Změny + Dnes žádné lekce + %s min + %s sek + dosud %1$s + za %1$s + Lekce skončila + Nyní: %s + Okamžik: %s + Později: %s + + Dokončené lekce + Zobrazit dokončené lekce + Žádné informace o absolvovaných lekcích + Téma + Absence + Zdroje + + Souhrn docházky + Nepřítomen ze školních důvodů + Omluvená absence + Neomluvená absence + Osvobození + Oprávněné zpoždění + Neomluvená zpoždění + Přítomnost + Smazáno + Neznámý + Číslo lekce + Žádné položky + + %1$d absence + %1$d absence + %1$d absence + %1$d absence + + Důvod absence (volitelný) + Poslat + Absence úspěšně omluvena! + Musíte vybrat alespoň jednu nepřítomnost! + Ospravedlnit + + Účast + Celkový + + Tento týden žádné testy + Typ + Datum vstupu + + Doručená pošta + Odesláno + Koš + (žádné téma) + Žádné zprávy + Při stahování obsahu zprávy došlo k chybě + Od: + Do: + Datum: %s + Odpověď + Poslat dále + Vymazat + Přesunout do koše + Trvale smazat + Zpráva byla úspěšně smazána + Podíl + Vytisknout + Téma + Obsah + Zpráva úspěšně odeslána + Musíte vybrat alespoň 1 příjemce + Obsah zprávy musí mít alespoň 3 znaky + + %d zpráva + %d zprávy + %d zprávy + %d zprávy + + + Nová zpráva + Nové zprávy + Nové zprávy + Nové zprávy + + + Obdrželi jste %1$d zprávu + Obdrželi jste %1$d zpráv + Obdrželi jste %1$d zpráv + Obdrželi jste %1$d zpráv + + + Žádné informace o poznámkách + Body + + %d poznámka + %d poznámky + %d poznámky + %d poznámky + + + Nová poznámka + Nové poznámky + Nové poznámky + Nové poznámky + + + Máte %1$d novou poznámku + Máte %1$d nové poznámky + Máte %1$d nové poznámky + Máte %1$d nové poznámky + + + + %d chvála + %d chvály + %d chvály + %d chvály + + + Nová chvála + Nové chvály + Nové chvály + Nové chvály + + + Máte %1$d novou chválu + Máte %1$d nové chvály + Máte %1$d nové chvály + Máte %1$d nové chvály + + + + %d neutrální poznámka + %d neutrální poznámky + %d neutrální poznámky + %d neutrální poznámky + + + Nová neutrální poznámka + Nové neutrální poznámky + Nové neutrální poznámky + Nové neutrální poznámky + + + Máte %1$d novou neutrální pozornost + Máte %1$d nové neutrální komentáře + Máte %1$d nové neutrální komentáře + Máte %1$d nové neutrální komentáře + + + Žádný domácí úkol + Označit jako hotové + Neudělané + Přílohy + + Šťastné číslo + Dnešní šťastné číslo je + Žádné informace o šťastném čísle + Šťastné číslo pro dnešek + Dnes je šťastným číslem: %d + + Mobilní přístup + Žádná zařízení + Zrušit registraci + Zařízení odstraněno + QR kód + Token + Symbol + PIN + + Škola a učitelé + + Škola + Žádné informace o škole + Školní jméno + Adresa školy + Telefon + Jméno ředitele + Jméno pedagoga + Zobrazit na mapě + Volání + + Učitelé + Žádné informace o učitelích + Žádný předmět + + Přidat účet + Odhlásit se + Chcete se odhlásit z aktivního studenta? + Odhlášení studentů + Studentský účet + Rodičovský účet + Režimu Mobíle API + Hybridní režim + + Verze aplikace + Tvůrci + Seznam vývojářů Wulkanowy + Nahlásit chybu + Pošlete hlášení o chybě e-mailem + FAQ + Přečtěte si často kladené otázky + Server Discord + Připojte se ke komunitě Wulkanowy + Zásady ochrany osobních údajů + Pravidla pro shromažďování osobních údajů + Domovská stránka + Navštivte web a pomozte s vývojem aplikace + Licence + Licence knihoven použitých v aplikaci + + Licence + + Avatar + Zobrazit více na GitHub + + Sdílejte protokoly + Obnovit + + Kontrola aktualizací + Před nahlášením chyby nejprve zkontrolujte, zda je k dispozici aktualizace s opravou chyby + + Obsah + Zkuste to znovu + Popis + Bez popisu + Učitel + Datum + Datum vstupu + Barva + Detaily + Kategorie + Zavřít + Žádná data + Předmět + Předchozí + Další + Vyhledávání + Vyhledávání… + + Žádné lekce + Vyberte téma + Světlý + Tmavý + Téma systému + + Vzhled + Výchozí zobrazení + Výpočet koncoročního průměru + Vynutit průměrný výpočet podle aplikace + Zobrazit přítomnost v účasti + Téma aplikace + Rozbalit známky + Označte aktuální lekci v plánu lekce + Ukázat skupiny vedle předmětů v plánu lekce + Ukázat seznam grafů ve třídních známkách + Ukázat lekce pro celou třídu + Ukázat předměty bez známek v \"Známky\" + Známky barevné schéma + Předměty seřazené v \"Známky\" + Jazyk aplikace + Oznámení + Ukázat notifikace + Ukázat nadcházející oznámení o lekci + Opravte problémy se synchronizací a upozorněním + Ve vašem zařízení mohou nastat problémy se synchronizací dat a oznámenímii.\n\nChcete-li je opravit, přidejte Wulkanowy do funkce Autostart a vypněte optimalizaci/úsporu baterie v nastavení systému telefonu. + Jdi do nastavení + Ukázat oznámení o ladění + Synchronizace + Automatická aktualizace + Pozastaveno na dovolené + Interval aktualizací + Pouze Wi-Fi + Nyní synchronizovat + Synchronizováno! + Synchronizace se nezdařila + Probíhá synchronizace + Synchronizace + Ruční synchronizace neobnoví zobrazení aplikace. + \nChcete-li zobrazit synchronizovaná data, restartujte aplikaci po synchronizaci. + + Jiný + Hodnota plusu + Hodnota mínusu + Odpovědět s historií zpráv + + Nové položky v deník + Nové známky + Šťastné číslo + Nové zprávy + Nové poznámky + Oznámení push + Nadcházející lekce + Ladění + + Černý + Červený + Modrý + Zelený + Nachový + Žádná barva + + Zkopírováno + Vrátit + + Stahování aktualizací začalo… + Aktualizace byla stažena. + Restartovat + Aktualizace selhala! Wulkanowy nemusí fungovat správně. Zvažte aktualizaci + + Žádné internetové připojení + Vypršel časový limit připojení k denik + Přihlášení selhalo. Zkus to znovu + Je vyžadována změna hesla + Probíhá údržba UONET+ deník. Zkuste to později znovu + Neznámá chyba denika UONET+. Prosím zkuste to znovu později + Neznámá chyba aplikace + Došlo k neočekávané chybě + Funkce deaktivována vaší školou + Funkce není k dispozici. Přihlaste se v jiném režimu než Mobile API + diff --git a/app/src/main/res/values-de/preferences_values.xml b/app/src/main/res/values-de/preferences_values.xml index 11935b49b..2ee70f622 100644 --- a/app/src/main/res/values-de/preferences_values.xml +++ b/app/src/main/res/values-de/preferences_values.xml @@ -12,6 +12,7 @@ Pусский Українська Deutsch + Čeština 15 Minuten @@ -29,6 +30,10 @@ 0,5 0,75 + + Alphabetic + By date + Dzienniczek+ Wulkanowy diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 42169ca53..89af346c3 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -19,7 +19,7 @@ Hausaufgaben Wählen Sie ein Konto - Semester %d, %d/%d + Semester %1$d, %2$d/%3$d Melden Sie sich mit dem Studenten- oder Elternkonto an Geben Sie das Symbol @@ -154,6 +154,8 @@ Entschuldigte Verspätung Unentschuldigte Verspätung Anwesend + Deleted + Unknown Lektion Nummer Keine Einträgen @@ -222,6 +224,32 @@ Du hast %1$d Eintrag bekommen Du hast %1$d Eintragen bekommen + + + %d praise + %d praises + + + New praise + New praises + + + You received %1$d praise + You received %1$d praises + + + + %d neutral note + %d neutral notes + + + New neutral note + New neutral notes + + + You received %1$d neutral note + You received %1$d neutral notes + Keine Informationen über Hausaufgaben Gemacht @@ -327,9 +355,12 @@ Thema der Anwendung Noten erweitern Aktuelle Lektion im Stundenplan markieren + Show groups next to subjects in timetable Liste der Diagramme in Klassenbewertungen anzeigen Unterricht der ganzen Klasse anzeigen + Show subjects without grades in Grades Farbschema der Noten + Subjects sorting in \"Grades\" App Sprache Benachrichtigungen Benachrichtigungen anzeigen @@ -374,12 +405,19 @@ Kopiert lösen + + Download of updates has started… + An update has just been downloaded. + Restart + Update failed! Wulkanowy may not function properly. Consider updating Keine Internetverbindung Das Zeitlimit für die Verbindung zum Klassenbuch ist abgelaufen - Anmeldung fehlgeschlagen. Versuchen Sie es noch einmal oder starten Sie die Anwendung neu. + Login failed. Try again Passwortänderung erforderlich Wartung im Gange UONET + Klassenbuch. Versuchen Sie es später noch einmal + Unknown UONET + register error. Try again later + Unknown application error Ein unerwarteter Fehler ist aufgetreten Funktion, die von Ihrer Schule deaktiviert wurde Feature in diesem Modus nicht verfügbar diff --git a/app/src/main/res/values-pl/preferences_values.xml b/app/src/main/res/values-pl/preferences_values.xml index 1d81fb586..0634f6572 100644 --- a/app/src/main/res/values-pl/preferences_values.xml +++ b/app/src/main/res/values-pl/preferences_values.xml @@ -12,6 +12,7 @@ Pусский Українська Deutsch + Čeština 15 minut @@ -29,6 +30,10 @@ 0,5 0,75 + + Alfabetycznie + Według daty + Dzienniczek+ Wulkanowy diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index e4d3e9029..99fd05dbd 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -19,15 +19,15 @@ Zadania domowe Wybierz konto - Semestr %d, %d/%d + Semestr %1$d, %2$d/%3$d Zaloguj się za pomocą konta ucznia lub rodzica - Podaj symbol + Podaj symbol ze strony dziennika Nazwa użytkownika Email Login, PESEL lub e-mail Hasło - Dziennik UONET+ + Odmiana dziennika UONET+ Mobilne API Scraper Hybrydowe @@ -37,17 +37,17 @@ Symbol Zaloguj To hasło jest za krótkie - Dane logowania są niepoprawne. Upewnij się, że został wybrany odpowiedni dziennik UONET+ w polu poniżej + Dane logowania są niepoprawne. Upewnij się, że została wybrana odpowiednia odmiana dziennika UONET+ w polu poniżej Nieprawidłowy PIN Nieprawidłowy token Token stracił ważność Niepoprawny adres email - Niepoprawny login + Użyj przydzielonego loginu zamiast emaila Niepoprawny symbol - Nie znaleziono ucznia. Sprawdź symbol + Nie znaleziono ucznia. Sprawdź poprawność symbolu i wybranej odmiany dziennika UONET+ To pole jest wymagane Wybrany uczeń jest już zalogowany - Symbol znajdziesz na stronie dziennika w Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUpewnij się, że w polu Dziennik UONET+ na poprzednim ekranie został ustawiony odpowiedni dziennik + Symbol znajdziesz na stronie dziennika w Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUpewnij się, że w polu Dziennik UONET+ na poprzednim ekranie została ustawiona odpowiednia odmiana dziennika. Wulkanowy na chwilę obecną nie wykrywa uczniów przedszkolnych Wybierz uczniów do zalogowania w aplikacji Inne opcje W tym trybie nie działa szczęśliwy numerek, uczeń na tle klasy, podsumowanie frekwencji, usprawiedliwianie nieobecności, lekcje zrealizowane, informacje o szkole i podgląd listy zarejestrowanych urządzeń @@ -59,7 +59,7 @@ Discord Wyślij email Opisz problem: - Upewnij się, że został wybrany odpowiedni dziennik UONET+! + Upewnij się, że została wybrana odpowiednia odmiana dziennika UONET+! Nie pamiętam hasła Przywróć swoje konto Przywróć @@ -168,6 +168,8 @@ Spóźnienie usprawiedliwione Spóźnienie nieusprawiedliwione Obecność + Usunięty + Nieznany Numer lekcji Brak wpisów @@ -250,6 +252,44 @@ Masz %1$d nowych uwag Masz %1$d nowych uwag + + + %d pochwała + %d pochwały + %d pochwał + %d pochwał + + + Nowa pochwała + Nowe pochwały + Nowe pochwały + Nowe pochwały + + + Masz %1$d nową pochwałę + Masz %1$d nowe pochwały + Masz %1$d nowych pochwał + Masz %1$d nowych pochwał + + + + %d neutralna uwaga + %d neutralne uwagi + %d neutralnych uwag + %d neutralnych uwag + + + Nowa neutralna uwaga + Nowe neutralne uwagi + Nowe neutralne uwagi + Nowe neutralne uwagi + + + Masz %1$d nową neutralną uwagę + Masz %1$d nowe neutralne uwagi + Masz %1$d nowych neutralnych uwag + Masz %1$d nowych neutralnych uwag + Brak zadań domowych Wykonane @@ -355,9 +395,12 @@ Motyw aplikacji Rozwiń oceny Oznaczaj bieżącą lekcję na planie + Pokazuj grupę obok przedmiotu na planie Pokazuj listę wykresów w ocenach klasy Pokazuj lekcje całej klasy + Pokazuj przedmioty bez ocen w Oceny Schemat kolorów ocen + Sortowanie przedmiotów w \"Oceny\" Język aplikacji Powiadomienia Pokazuj powiadomienia @@ -402,12 +445,19 @@ Skopiowano Cofnij + + Rozpoczęto pobieranie aktualizacji… + Aktualizacja została pobrana. + Uruchom ponownie + Aktualizacja nie powiodła się! Wulkanowy może nie działać prawidłowo. Rozważ aktualizację Brak połączenia z internetem - Upłynął limit czasu na połączenie z dziennikiem - Logowanie nie powiodło się. Spróbuj ponownie lub zrestartuj aplikację - Wymagana zmiana hasła + Nie udało się połączyć z dziennikiem. Serwery mogą być przeciążone. Spróbuj ponownie później + Ładowanie danych nie powiodło się. Spróbuj ponownie później + Wymagana zmiana hasła do dziennika Trwa przerwa techniczna dziennika UONET+. Spróbuj ponownie później + Nieznany błąd dziennika UONET+. Spróbuj ponownie później + Nieznany błąd aplikacji. Spróbuj ponownie później Wystąpił nieoczekiwany błąd Funkcja wyłączona przez szkołę Funkcja niedostępna. Zaloguj się w trybie innym niż Mobilne API diff --git a/app/src/main/res/values-ru/preferences_values.xml b/app/src/main/res/values-ru/preferences_values.xml index a41abf350..304d678f7 100644 --- a/app/src/main/res/values-ru/preferences_values.xml +++ b/app/src/main/res/values-ru/preferences_values.xml @@ -12,6 +12,7 @@ Pусский Українська Deutsch + Čeština 15 минут @@ -29,6 +30,10 @@ 0,5 0,75 + + Алфавитный + По дате + Dzienniczek+ Wulkanowy @@ -36,8 +41,8 @@ Средняя оценка со 2 семестра - Average of grades from both semesters - Average of grades from the whole year + Средняя оценка с двух семестров + Средняя оценок со всего года Не показывать diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 15c583173..7a10553dc 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -19,7 +19,7 @@ Домашние задания Выберите аккаунт - %d семестр, %d/%d + %1$d семестр, %2$d/%3$d Авторизируйтесь при помощи аккаунта ученика или родителя Впишите \"symbol\" @@ -108,16 +108,16 @@ Новые оценки - New predicted grade - New predicted grades - New predicted grades - New predicted grades + Новая ожидаемая оценка + Новые ожидаемые оценки + Новые ожидаемые оценки + Новые ожидаемые оценки - New final grade - New final grades - New final grades - New final grades + Новая итоговая оценка + Новые итоговые оценки + Новые итоговые оценки + Новые итоговые оценки Вы получили %1$d оценку @@ -126,16 +126,16 @@ Вы получили %1$d оценок - You received %1$d predicted grade - You received %1$d predicted grades - You received %1$d predicted grades - You received %1$d predicted grades + Вы получили %1$d ожидаемую оценку + Вы получили %1$d ожидаемые оценки + Вы получили %1$d ожидаемых оценок + Вы получили %1$d ожидаемых оценок - You received %1$d final grade - You received %1$d final grades - You received %1$d final grades - You received %1$d final grades + Вы получили %1$d финальную оценку + Вы получили %1$d итоговых оценки + Вы получили %1$d итоговых оценок + Вы получили %1$d финальные оценки Урок @@ -168,6 +168,8 @@ Опоздание по уважительным причинам Опоздание по неуважительным причинам Присутствие + Удалено + Неизвестно Урок № Данные не найдены @@ -204,8 +206,8 @@ Перенести в корзину Удалить навсегда Сообщение успешно удалено - Share - Print + Поделиться + Печать Тема Текст Сообщение успешно отправлено @@ -250,6 +252,44 @@ Вы получили %1$d предупреждений Вы получили %1$d предупреждений + + + %d похвала + %d похвала + %d похвала + %d похвала + + + Новая похвала + Новые похвалы + Новые свершения + Новые похвалы + + + Вы получили %1$d похвалу + Вы получили %1$d похвалы + Вы получили %1$d похвалы + Вы получили %1$d похвалы + + + + %d нейтральное замечание + %d нейтральных замечания + %d нейтральных замечаний + %d нейтральных замечаний + + + Новое нейтральное замечание + Новые нейтральные замечания + Новые нейтральные замечания + Новые нейтральные замечания + + + Вы получили %1$d нейтральное замечание + Вы получили %1$d нейтральных замечания + Вы получили %1$d нейтральных замечаний + Вы получили %1$d нейтральных замечаний + Нет домашних заданий сделанный @@ -291,10 +331,10 @@ Выйти Вы точно хотите выйти из данного аккаунта? Выйти - Student account - Parent account - Mobile API mode - Hybrid mode + Профиль ученика + Профиль родителя + Режим Mobile API + Гибридный режим Версия приложения Разработчики @@ -353,11 +393,14 @@ Принудительно высчитать среднюю оценку через приложение Показывать присутствия в посещаемости Тема приложения - Больше оценок + Разворачивать оценки Отмечать текущий урок в расписании + Показать группу возле предмета в расписании Показывать диаграммы в оценках класса Показать уроки всего класса + Показывать предметы без оценок в \"Оценках\" Схема цветов оценок + Сортировка предметов в \"Оценках\" Язык приложения Уведомления Показывать уведомления @@ -402,12 +445,19 @@ Скопировано Отменить + + Download of updates has started… + An update has just been downloaded. + Restart + Update failed! Wulkanowy may not function properly. Consider updating Нет интернет-подключения Слишком долгое ожидание соединения с дневником - Авторизация не удалась. Попробуйте ещё раз или перезапустите дневник + Login failed. Try again Требуется смена пароля Технический перерыв в журнале UONET + продолжается. Попробуйте позже + Unknown UONET + register error. Try again later + Unknown application error Произошла неожиданная ошибка Функция была выключена школой Функция не доступна в этом режиме diff --git a/app/src/main/res/values-uk/preferences_values.xml b/app/src/main/res/values-uk/preferences_values.xml index 9942621a5..0ff8bfd0c 100644 --- a/app/src/main/res/values-uk/preferences_values.xml +++ b/app/src/main/res/values-uk/preferences_values.xml @@ -12,6 +12,7 @@ Pусский Українська Deutsch + Čeština 15 хвилин @@ -29,6 +30,10 @@ 0,5 0,75 + + Alphabetic + By date + Dzienniczek+ Wulkanowy diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 423c4e12f..4a15db9f5 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -19,7 +19,7 @@ Домашні завдання Оберіть аккаунт - %d семестр, %d/%d + %1$d семестр, %2$d/%3$d Авторизуйтеся за допомогою аккаунта учня або батька Впишіть \"symbol\" @@ -168,6 +168,8 @@ Спізнення з поважних причин Спізнення з не поважних причин Присутність + Deleted + Unknown Номер уроку Брак записів @@ -250,6 +252,44 @@ %1$d нових зауважень %1$d нових зауважень + + + %d praise + %d praises + %d praises + %d praises + + + New praise + New praises + New praises + New praises + + + You received %1$d praise + You received %1$d praises + You received %1$d praises + You received %1$d praises + + + + %d neutral note + %d neutral notes + %d neutral notes + %d neutral notes + + + New neutral note + New neutral notes + New neutral notes + New neutral notes + + + You received %1$d neutral note + You received %1$d neutral notes + You received %1$d neutral notes + You received %1$d neutral notes + Брак домашніх завдань Позначити як зроблене @@ -355,9 +395,12 @@ Тема додатку Більше оцінок Позначити поточний урок у розкладі + Show groups next to subjects in timetable Показувати діаграми в оцінках класу Показати уроки всього класу + Show subjects without grades in Grades Схема кольорів оцінок + Subjects sorting in \"Grades\" Мова додатку Повідомлення Показувати повідомлення @@ -402,12 +445,19 @@ Скопійовано Відмінити + + Download of updates has started… + An update has just been downloaded. + Restart + Update failed! Wulkanowy may not function properly. Consider updating Брак з\'єднання з інтернетом Занадто довге очікування з\'єднання з щоденником - Аутентифікація не вдалася. Спробуйте ще раз або запустіть додаток знову + Login failed. Try again Потрібно змінити пароль Технічна перерва в журналі UONET + продовжується. Спробуйте пізніше + Unknown UONET + register error. Try again later + Unknown application error Відбулася несподівана помилка Функція вимкнена школою Функція не доступна в цьому режимі diff --git a/app/src/main/res/values/api_hosts.xml b/app/src/main/res/values/api_hosts.xml index 64618568a..29434602e 100644 --- a/app/src/main/res/values/api_hosts.xml +++ b/app/src/main/res/values/api_hosts.xml @@ -7,6 +7,7 @@ Lubelski Portal Oświatowy EduNet Miasta Tarnowa ResMan Rzeszów + Platforma Edukacyjna Koszalina Rawa Mazowiecka - Platforma vEdukacja Zduńska Wola - e-Urząd Sieradz - Portal oświatowy @@ -27,6 +28,7 @@ https://edu.lublin.eu https://umt.tarnow.pl https://resman.pl + https://eduportal.koszalin.pl https://vulcan.net.pl/ https://vulcan.net.pl/ https://vulcan.net.pl/ @@ -47,6 +49,7 @@ lublin tarnow rzeszow + koszalin rawamazowiecka zdunskawola sieradz diff --git a/app/src/main/res/values/preferences_defaults.xml b/app/src/main/res/values/preferences_defaults.xml index a82b14eb7..fb82e0ed6 100644 --- a/app/src/main/res/values/preferences_defaults.xml +++ b/app/src/main/res/values/preferences_defaults.xml @@ -18,6 +18,10 @@ 0.33 0.33 true + false no + alphabetic false + false + false diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml index 6cb877ec2..0cfa485e3 100644 --- a/app/src/main/res/values/preferences_keys.xml +++ b/app/src/main/res/values/preferences_keys.xml @@ -20,6 +20,10 @@ grade_modifier_plus grade_modifier_minus fill_message_content + grade_sorting_mode show_whole_class_plan + show_groups_in_plan timetable_show_timers + homework_fullscreen + subjects_without_grades diff --git a/app/src/main/res/values/preferences_values.xml b/app/src/main/res/values/preferences_values.xml index 5824658c4..d994213c8 100644 --- a/app/src/main/res/values/preferences_values.xml +++ b/app/src/main/res/values/preferences_values.xml @@ -31,6 +31,7 @@ Pусский Українська Deutsch + Čeština system @@ -39,6 +40,7 @@ ru uk de + cs @@ -75,6 +77,15 @@ 0.75 + + Alphabetic + By date + + + alphabetic + date + + Dzienniczek+ Wulkanowy diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1eaebf284..8e849573d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -21,7 +21,7 @@ - Semester %d, %d/%d + Semester %1$d, %2$d/%3$d @@ -170,6 +170,8 @@ Excused lateness Unexcused lateness Present + Deleted + Unknown Number of lesson No entries @@ -247,6 +249,34 @@ You received %1$d notes + + + %d praise + %d praises + + + New praise + New praises + + + You received %1$d praise + You received %1$d praises + + + + + %d neutral note + %d neutral notes + + + New neutral note + New neutral notes + + + You received %1$d neutral note + You received %1$d neutral notes + + No info about homework @@ -381,9 +411,12 @@ Application theme Expand grades Mark current lesson in timetable + Show groups next to subjects in timetable Show chart list in class grades Show whole class lessons + Show subjects without grades in Grades Grades color scheme + Subjects sorting in "Grades" App language Notifications @@ -439,13 +472,21 @@ Copied Undo + + Download of updates has started… + An update has just been downloaded. + Restart + Update failed! Wulkanowy may not function properly. Consider updating + No internet connection Connection to the register timed out - Login failed. Try again or restart the app + Login failed. Try again Password change required Maintenance underway UONET + register. Try again later + Unknown UONET + register error. Try again later + Unknown application error An unexpected error occurred Feature disabled by your school Feature not available. Login in a mode other than Mobile API diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 8f30e3457..58ad1640e 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -23,7 +23,7 @@ @drawable/layer_splash_background -