Compare commits

...

33 Commits

Author SHA1 Message Date
ac10874bf1 [4.11.6] Update build.gradle, signing and changelog. 2022-05-27 21:09:40 +02:00
fa55b4901a [Messages/Librus] Disable text styling. (#130) 2022-05-27 18:55:24 +02:00
0f90430387 [4.11.5] Update build.gradle, signing and changelog. 2022-04-18 11:31:24 +02:00
745523c620 [UI] Fix touch events on swipe refresh pages (API 32+). (#129)
* [Hotfix] Fix touch events on swipe refresh pages (API 32)

* Add API SDK version check
2022-04-18 11:28:34 +02:00
8911ce2bc1 [4.11.4] Update build.gradle, signing and changelog. 2022-03-14 17:45:11 +01:00
2990fc5479 [Firebase] Disable notifications for past shared events. 2022-03-14 17:08:35 +01:00
48b7adb564 [UI/Grades] Group unknown subjects without specified name. 2022-03-14 17:05:16 +01:00
f8ac9e793a [4.11.3] Update build.gradle, signing and changelog. 2022-02-21 21:48:28 +01:00
148597e578 [UI/Messages] Fix sending message not showing the message fragment. 2022-02-21 21:46:16 +01:00
6c50a80b42 [UI/Timetable] Fix syncing a week not refreshing the current page. 2022-02-21 21:08:59 +01:00
1bf0679e92 [App] Disable SSLProvider in Google Play builds. 2022-02-21 20:36:17 +01:00
5b5dc5cade [Gradle] Add non-Play flavor without self-updater. 2022-02-21 20:19:29 +01:00
98a89b1ca1 [4.11.2] Update build.gradle, signing and changelog. 2022-02-05 17:47:00 +01:00
6d1e18cce2 [Strings] Update copyright dates. 2022-02-05 17:44:57 +01:00
8fe0f88be4 [Strings] Fix a typo. 2022-02-05 17:39:40 +01:00
aeed735521 [Mobidziennik] Fix syncing message lists. 2022-02-05 17:39:15 +01:00
e314fafaff [4.11.1] Update build.gradle, signing and changelog. 2022-01-14 15:23:31 +01:00
2ab0dd7546 [API/Librus] Revert getting time in teacher free days. (#124) 2021-12-16 12:23:33 +01:00
aa06868a4e [API/Librus] Fix LibrusApiTeacherFreeDays. (#123)
* [API/Librus] Remove LibrusApiTeacherFreeDayTypes

* [API/Librus] Fix LibrusApiTeacherFreeDay

* [API/Librus] LibrusApiTeacherFreeDay: Clean up values of unused variables

* [API/Librus] LibrusApiTeacherFreeDay: Remove unnecessary variables

* [API/Librus] LibrusApiTeacherFreeDay: Assign null to 'name' object
2021-12-10 21:48:11 +01:00
2b104e6463 [4.11] Update build.gradle, signing and changelog. 2021-11-01 13:05:51 +01:00
afb1863827 [Strings] Fix compilation problem. 2021-10-31 20:25:34 +01:00
d8228748e4 [4.11-rc.3] Update build.gradle, signing and changelog. 2021-10-31 20:16:12 +01:00
b0608c47c4 [Strings] Update English translation. (#117)
* Update translations

* Apply suggestions from code review

Co-authored-by: Kuba Szczodrzyński <kuba@szczodrzynski.pl>

* Update translations

* Update translations

Co-authored-by: Kuba Szczodrzyński <kuba@szczodrzynski.pl>
2021-10-31 20:15:24 +01:00
dda0d88f19 [API/Vulcan] Fix Web login with multiple student IDs. 2021-10-31 18:19:18 +01:00
a1b5560977 [API/Vulcan] Fix teachers endpoint last sync date. (#116) 2021-10-31 14:24:39 +01:00
86f5811bda [Login] Fix incorrect case mode validation. (#115) 2021-10-31 14:24:20 +01:00
3f11e75985 [Lab] Add Open Chucker button. (#114) 2021-10-31 14:23:59 +01:00
c39b5442c9 [API/Vulcan] Fix teacher list subjects retrieving. 2021-10-31 10:12:35 +01:00
3b80adf355 [4.11-rc.2] Update build.gradle, signing and changelog. 2021-10-30 22:04:13 +02:00
cae41d17b6 [Actions] Use Eclipse Temurin and gradle-build-action. 2021-10-30 22:02:59 +02:00
519d75d9d9 [API/Vulcan] Force presence of a TeamClass. 2021-10-30 21:55:47 +02:00
cb953ea8a8 [Config] Fix only notes card visible on new profiles. 2021-10-30 21:55:47 +02:00
52968cafad [Proguard] Add a rule for Note. 2021-10-30 21:55:47 +02:00
49 changed files with 532 additions and 206 deletions

View File

@ -54,9 +54,8 @@ jobs:
- name: Setup JDK 11
uses: actions/setup-java@v2
with:
distribution: 'zulu'
distribution: 'temurin'
java-version: '11'
cache: 'gradle'
- name: Setup Android SDK
uses: android-actions/setup-android@v2
- name: Clean build artifacts
@ -65,7 +64,9 @@ jobs:
rm -rf app/build/outputs/apk/*
rm -rf app/build/outputs/bundle/*
- name: Assemble official release with Gradle
run: ./gradlew assembleOfficialRelease
uses: gradle/gradle-build-action@v2
with:
arguments: assembleOfficialRelease
sign:
name: Sign APK
runs-on: self-hosted

View File

@ -46,9 +46,8 @@ jobs:
- name: Setup JDK 11
uses: actions/setup-java@v2
with:
distribution: 'zulu'
distribution: 'temurin'
java-version: '11'
cache: 'gradle'
- name: Setup Android SDK
uses: android-actions/setup-android@v2
- name: Clean build artifacts
@ -57,7 +56,9 @@ jobs:
rm -rf app/build/outputs/apk/*
rm -rf app/build/outputs/bundle/*
- name: Bundle play release with Gradle
run: ./gradlew bundlePlayRelease
uses: gradle/gradle-build-action@v2
with:
arguments: bundlePlayRelease
sign:
name: Sign App Bundle
runs-on: self-hosted

View File

@ -46,9 +46,8 @@ jobs:
- name: Setup JDK 11
uses: actions/setup-java@v2
with:
distribution: 'zulu'
distribution: 'temurin'
java-version: '11'
cache: 'gradle'
- name: Setup Android SDK
uses: android-actions/setup-android@v2
- name: Clean build artifacts
@ -57,7 +56,9 @@ jobs:
rm -rf app/build/outputs/apk/*
rm -rf app/build/outputs/bundle/*
- name: Assemble official release with Gradle
run: ./gradlew assembleOfficialRelease
uses: gradle/gradle-build-action@v2
with:
arguments: assembleOfficialRelease
sign:
name: Sign APK
runs-on: self-hosted

View File

@ -41,6 +41,7 @@ android {
buildTypes {
debug {
getIsDefault().set(true)
minifyEnabled = false
manifestPlaceholders = [
buildTimestamp: 0
@ -55,7 +56,8 @@ android {
}
flavorDimensions "platform"
productFlavors {
main {
unofficial {
getIsDefault().set(true)
versionName "${release.versionName}-${gitInfo.versionSuffix}"
}
official {}
@ -63,7 +65,18 @@ android {
}
variantFilter { variant ->
def flavors = variant.flavors*.name
setIgnore(variant.buildType.name == "debug" && !flavors.contains("main"))
setIgnore(variant.buildType.name == "debug" && !flavors.contains("unofficial") || flavors.contains("main"))
}
sourceSets {
unofficial {
java.srcDirs = ["src/main/java", "src/play-not/java"]
}
official {
java.srcDirs = ["src/main/java", "src/play-not/java"]
}
play {
java.srcDirs = ["src/main/java", "src/play/java"]
}
}
defaultConfig {
@ -170,7 +183,8 @@ dependencies {
implementation "eu.szkolny:mhttp:af4b62e6e9"
implementation "eu.szkolny:nachos:0e5dfcaceb"
implementation "eu.szkolny.selective-dao:annotation:27f8f3f194"
implementation "eu.szkolny:ssl-provider:1.0.0"
officialImplementation "eu.szkolny:ssl-provider:1.0.0"
unofficialImplementation "eu.szkolny:ssl-provider:1.0.0"
implementation "pl.szczodrzynski:navlib:0.8.0"
implementation "pl.szczodrzynski:numberslidingpicker:2921225f76"
implementation "pl.szczodrzynski:recyclertablayout:700f980584"

View File

@ -25,6 +25,7 @@
-keep class pl.szczodrzynski.edziennik.data.db.entity.Event { *; }
-keep class pl.szczodrzynski.edziennik.data.db.full.EventFull { *; }
-keep class pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage { *; }
-keep class pl.szczodrzynski.edziennik.data.db.entity.Note { *; }
-keep class pl.szczodrzynski.edziennik.ui.home.HomeCardModel { *; }
-keepclassmembers class pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig { public *; }
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider

View File

@ -1,23 +1,8 @@
<h3>Wersja 4.11-rc.1, 2021-10-29</h3>
<h3>Wersja 4.11.6, 2022-05-27</h3>
<ul>
<li>Nowości w module Wiadomości:</li>
<li><b>Formatowanie tekstu</b> przy wysyłaniu wiadomości oraz dodawaniu wydarzeń.</li>
<li>→ Zapisywanie <b>wersji roboczych</b> wiadomości, do późniejszej edycji i wysłania.</li>
<li>→ Możliwość <b>wyszukiwania zadań domowych</b>, podobnie jak wiadomości.</li>
<li>→ Dodawanie gwiazdki do wiadomości, w celu "przypięcia" na górę listy.</li>
<br>
<li>Opcja <b>dodawania notatek</b> do wydarzeń, ocen, lekcji, itp.</li>
<li>Możliwość udostępniania notatek w klasie, jako "ogłoszenia" widoczne na ekranie głównym.</li>
<br>
<li>Dodano <b>listę nauczycieli</b> w menu Więcej. @Antoni-Czaplicki</li>
<li>Logowanie: skaner kodów QR, uproszczone pole adresu w Mobidzienniku.</li>
<li>Naprawiono filtrowanie powiadomień.</li>
<li>Mobidziennik: dodano wyświetlanie lekcji z planów "pozalekcyjnych".</li>
<li>Mobidziennik: dodano pobieranie pełnej treści "informacji" w kalendarzu.</li>
<li>Mobidziennik: poprawiono wyświetlanie załączników oraz ich wielkości.</li>
<li>Dodano tajne funkcje. @Antoni-Czaplicki</li>
<li>Poprawiono błędy formatowania wiadomości. @BxOxSxS</li>
</ul>
<br>
<br>
Dzięki za korzystanie ze Szkolnego!<br>
<i>&copy; [Kuba Szczodrzyński](@kuba2k2), [Kacper Ziubryniewicz](@kapi2289) 2021</i>
<i>&copy; [Kuba Szczodrzyński](@kuba2k2), [Kacper Ziubryniewicz](@kapi2289) 2022</i>

View File

@ -9,7 +9,7 @@
/*secret password - removed for source code publication*/
static toys AES_IV[16] = {
0xf3, 0x0e, 0xf9, 0x06, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
0xeb, 0x3c, 0x93, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);

View File

@ -26,8 +26,6 @@ import com.google.firebase.messaging.FirebaseMessaging
import com.google.gson.Gson
import com.hypertrack.hyperlog.HyperLog
import com.mikepenz.iconics.Iconics
import eu.szkolny.sslprovider.SSLProvider
import eu.szkolny.sslprovider.enableSupportedTls
import im.wangchao.mhttp.MHttp
import kotlinx.coroutines.*
import me.leolin.shortcutbadger.ShortcutBadger
@ -42,6 +40,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.ext.DAY
import pl.szczodrzynski.edziennik.ext.MS
import pl.szczodrzynski.edziennik.ext.setLanguage
import pl.szczodrzynski.edziennik.network.SSLProviderInstaller
import pl.szczodrzynski.edziennik.network.cookie.DumbCookieJar
import pl.szczodrzynski.edziennik.sync.SyncWorker
import pl.szczodrzynski.edziennik.sync.UpdateWorker
@ -49,7 +48,6 @@ import pl.szczodrzynski.edziennik.ui.base.CrashActivity
import pl.szczodrzynski.edziennik.utils.*
import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.managers.*
import timber.log.Timber
import java.util.concurrent.TimeUnit
import kotlin.coroutines.CoroutineContext
@ -119,7 +117,8 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.enableSupportedTls(enableCleartext = true)
SSLProviderInstaller.enableSupportedTls(builder, enableCleartext = true)
if (devMode) {
HyperLog.initialize(this)
@ -203,18 +202,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
withContext(Dispatchers.Default) {
config.migrate(this@App)
SSLProvider.install(
applicationContext,
downloadIfNeeded = true,
supportTls13 = false,
onFinish = {
buildHttp()
},
onError = {
Timber.e("Failed to install SSLProvider: $it")
it.printStackTrace()
}
)
SSLProviderInstaller.install(applicationContext, this@App::buildHttp)
if (config.devModePassword != null)
checkDevModePassword()

View File

@ -37,8 +37,10 @@ class ProfileConfigMigration(config: ProfileConfig) {
}
if (dataVersion < 3) {
ui.homeCards = ui.homeCards.toMutableList().also {
it.add(HomeCardModel(config.profileId, HomeCard.CARD_NOTES))
if (ui.homeCards.isNotEmpty()) {
ui.homeCards = ui.homeCards.toMutableList().also {
it.add(HomeCardModel(config.profileId, HomeCard.CARD_NOTES))
}
}
dataVersion = 3

View File

@ -182,10 +182,6 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_pt_meetings)
LibrusApiPtMeetings(data, lastSync, onSuccess)
}
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES -> {
data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_day_types)
LibrusApiTeacherFreeDayTypes(data, lastSync, onSuccess)
}
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS -> {
data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_days)
LibrusApiTeacherFreeDays(data, lastSync, onSuccess)

View File

@ -1,43 +0,0 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-10-19
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.TeacherAbsenceType
import pl.szczodrzynski.edziennik.ext.*
class LibrusApiTeacherFreeDayTypes(override val data: DataLibrus,
override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object {
const val TAG = "LibrusApiTeacherFreeDayTypes"
}
init {
apiGet(TAG, "TeacherFreeDays/Types") { json ->
val teacherAbsenceTypes = json.getJsonArray("Types")?.asJsonObjectList()
teacherAbsenceTypes?.forEach { teacherAbsenceType ->
val id = teacherAbsenceType.getLong("Id") ?: return@forEach
val name = teacherAbsenceType.getString("Name") ?: return@forEach
val teacherAbsenceTypeObject = TeacherAbsenceType(
profileId,
id,
name
)
data.teacherAbsenceTypes.put(id, teacherAbsenceTypeObject)
}
data.setSyncNext(ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES, 7 * DAY)
onSuccess(ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES)
}
}
}

View File

@ -36,8 +36,6 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus,
val id = teacherAbsence.getLong("Id") ?: return@forEach
val teacherId = teacherAbsence.getJsonObject("Teacher")?.getLong("Id")
?: return@forEach
val type = teacherAbsence.getJsonObject("Type").getLong("Id") ?: return@forEach
val name = data.teacherAbsenceTypes.singleOrNull { it.id == type }?.name
val dateFrom = Date.fromY_m_d(teacherAbsence.getString("DateFrom"))
val dateTo = Date.fromY_m_d(teacherAbsence.getString("DateTo"))
val timeFrom = teacherAbsence.getString("TimeFrom")?.let { Time.fromH_m_s(it) }
@ -46,8 +44,8 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus,
val teacherAbsenceObject = TeacherAbsence(
profileId = profileId,
id = id,
type = type,
name = name,
type = -1L,
name = null,
dateFrom = dateFrom,
dateTo = dateTo,
timeFrom = timeFrom,

View File

@ -7,10 +7,12 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik
import android.util.LongSparseArray
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_MOBIDZIENNIK_WEB
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.models.Data
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.ext.currentTimeUnix
import pl.szczodrzynski.edziennik.ext.get
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@ -35,6 +37,31 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
override fun generateUserCode() = "$loginServerName:$loginUsername:$studentId"
fun parseDateTime(dateStr: String): Pair<Date, Time> {
// pt, 4 lut, 09:11
val dateParts = dateStr.split(',', ' ').filter { it.isNotEmpty() }
// [pt], [4], [lut], [09:11]
val date = Date.getToday()
date.day = dateParts[1].toIntOrNull() ?: 1
date.month = when (dateParts[2]) {
"sty" -> 1
"lut" -> 2
"mar" -> 3
"kwi" -> 4
"maj" -> 5
"cze" -> 6
"lip" -> 7
"sie" -> 8
"wrz" -> 9
"paź" -> 10
"lis" -> 11
"gru" -> 12
else -> 1
}
val time = Time.fromH_m(dateParts[3])
return date to time
}
val teachersMap = LongSparseArray<String>()
val subjectsMap = LongSparseArray<String>()

View File

@ -36,6 +36,10 @@ class MobidziennikWebMessagesInbox(override val data: DataMobidziennik,
val doc = Jsoup.parse(text)
val today = Date.getToday()
var currentYear = today.year
var currentMonth = today.month
val list = doc.getElementsByClass("spis").first()?.getElementsByClass("podswietl")
list?.forEach { item ->
val id = item.attr("rel").toLongOrNull() ?: return@forEach
@ -47,15 +51,20 @@ class MobidziennikWebMessagesInbox(override val data: DataMobidziennik,
}
val subject = subjectEl?.ownText() ?: ""
val addedDateEl = item.select("td:eq(1) small").first()
val addedDate = Date.fromIsoHm(addedDateEl?.text())
val addedDateEl = item.select("td:eq(4)").first()
val (date, time) = data.parseDateTime(addedDateEl?.text()?.trim() ?: "")
if (date.month > currentMonth) {
currentYear--
}
currentMonth = date.month
date.year = currentYear
val senderEl = item.select("td:eq(2)").first()
val senderEl = item.select("td:eq(3)").first()
val senderName = senderEl?.ownText().fixName()
val senderId = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }?.id
data.messageRecipientIgnoreList.add(MessageRecipient(profileId, -1, id))
val isRead = item.select("td:eq(3) span").first()?.hasClass("wiadomosc_przeczytana") == true
val isRead = item.select("td:eq(5) span").first()?.hasClass("wiadomosc_przeczytana") == true
val message = Message(
profileId = profileId,
@ -64,7 +73,7 @@ class MobidziennikWebMessagesInbox(override val data: DataMobidziennik,
subject = subject,
body = null,
senderId = senderId,
addedDate = addedDate
addedDate = date.combineWith(time)
)
if (hasAttachments)

View File

@ -40,23 +40,29 @@ class MobidziennikWebMessagesSent(override val data: DataMobidziennik,
val doc = Jsoup.parse(text)
val today = Date.getToday()
var currentYear = today.year
var currentMonth = today.month
val list = doc.getElementsByClass("spis").first()?.getElementsByClass("podswietl")
list?.forEach { item ->
val id = item.attr("rel").toLongOrNull() ?: return@forEach
val subjectEl = item.select("td:eq(0)").first()
var hasAttachments = false
if (subjectEl?.getElementsByTag("a")?.size ?: 0 > 0) {
hasAttachments = true
}
val subject = subjectEl?.ownText() ?: ""
val readByString = item.select("td:eq(2)").first()?.text() ?: ""
val attachmentsEl = item.select("td:eq(1)").first()
var hasAttachments = false
if (attachmentsEl?.getElementsByTag("a")?.size ?: 0 > 0) {
hasAttachments = true
}
val readByString = item.select("td:eq(4)").first()?.text() ?: ""
val (readBy, sentTo) = Regexes.MOBIDZIENNIK_MESSAGE_SENT_READ_BY.find(readByString).let {
(it?.get(1)?.toIntOrNull() ?: 0) to (it?.get(2)?.toIntOrNull() ?: 0)
}
val recipientEl = item.select("td:eq(1) a span").first()
val recipientEl = item.select("td:eq(2) a span").first()
val recipientNames = recipientEl?.ownText()?.split(", ")
val readState = when (readBy) {
0 -> 0
@ -69,8 +75,13 @@ class MobidziennikWebMessagesSent(override val data: DataMobidziennik,
data.messageRecipientIgnoreList.add(MessageRecipient(profileId, recipientId, -1, readState, id))
}
val addedDateEl = item.select("td:eq(3) small").first()
val addedDate = Date.fromIsoHm(addedDateEl?.text())
val addedDateEl = item.select("td:eq(3)").first()
val (date, time) = data.parseDateTime(addedDateEl?.text()?.trim() ?: "")
if (date.month > currentMonth) {
currentYear--
}
currentMonth = date.month
date.year = currentYear
val message = Message(
profileId = profileId,
@ -79,7 +90,7 @@ class MobidziennikWebMessagesSent(override val data: DataMobidziennik,
subject = subject,
body = null,
senderId = null,
addedDate = addedDate
addedDate = date.combineWith(time)
)
if (hasAttachments)

View File

@ -22,7 +22,6 @@ import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.LessonRange
import pl.szczodrzynski.edziennik.data.db.entity.Subject
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.entity.Team
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date
@ -91,41 +90,31 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) {
fun getTeamId(json: JsonObject?, key: String): Long? {
val team = json.getJsonObject(key)
val teamId = team.getLong("Id") ?: return null
if (data.teamList[teamId] == null) {
var name = team.getString("Shortcut")
?: team.getString("Name")
?: ""
name = "${profile?.studentClassName ?: ""} $name"
data.teamList[teamId] = Team(
data.profileId,
teamId,
name,
Team.TYPE_VIRTUAL,
"${data.schoolCode}:$name",
-1
)
}
return teamId
val teamId = team.getLong("Id")
var teamName = team.getString("Shortcut")
?: team.getString("Name")
?: ""
teamName = "${profile?.studentClassName ?: ""} $teamName"
return data.getTeam(
id = teamId,
name = teamName,
schoolCode = data.schoolCode ?: "",
isTeamClass = false,
).id
}
fun getClassId(json: JsonObject?, key: String): Long? {
val team = json.getJsonObject(key)
val teamId = team.getLong("Id") ?: return null
if (data.teamList[teamId] == null) {
val name = data.profile?.studentClassName
?: team.getString("Name")
?: ""
data.teamList[teamId] = Team(
data.profileId,
teamId,
name,
Team.TYPE_CLASS,
"${data.schoolCode}:$name",
-1
)
}
return teamId
val teamId = team.getLong("Id")
val teamName = data.profile?.studentClassName
?: team.getString("Name")
?: ""
return data.getTeam(
id = teamId,
name = teamName,
schoolCode = data.schoolCode ?: "",
isTeamClass = true,
).id
}
fun getLessonRange(json: JsonObject?, key: String): LessonRange? {

View File

@ -137,31 +137,6 @@ open class VulcanWebMain(open val data: DataVulcan, open val lastSync: Long?) {
}
}
data.webPermissions = data.webPermissions.toMutableMap().also { map ->
val permissions = Regexes.VULCAN_WEB_PERMISSIONS.find(text)?.let { it[1] }
if (permissions?.isNotBlank() == true) {
val studentId = permissions.split("|")
.getOrNull(0)
?.base64DecodeToString()
?.toJsonObject()
?.getJsonArray("AuthInfos")
?.asJsonObjectList()
?.flatMap { authInfo ->
authInfo.getJsonArray("UczenIds")
?.map { it.asInt }
?: listOf()
}
?.firstOrNull()
?.toString()
data.app.cookieJar.set(
data.webHost ?: "vulcan.net.pl",
"idBiezacyUczen",
studentId
)
}
map[symbol] = permissions
}
val schoolSymbols = mutableListOf<String>()
val clientUrl = "://uonetplus-uczen.${data.webHost}/$symbol/"
var clientIndex = text.indexOf(clientUrl)
@ -186,6 +161,42 @@ open class VulcanWebMain(open val data: DataVulcan, open val lastSync: Long?) {
return
}
data.webPermissions = data.webPermissions.toMutableMap().also { map ->
val permissions = Regexes.VULCAN_WEB_PERMISSIONS.find(text)?.let { it[1] }
if (permissions?.isNotBlank() == true) {
val json = permissions.split("|")
.getOrNull(0)
?.base64DecodeToString()
?.toJsonObject()
val unitIds = json
?.getJsonArray("Units")
?.asJsonObjectList()
?.filter { unit ->
unit.getString("Symbol") in schoolSymbols
}
?.mapNotNull { it.getInt("Id") } ?: emptyList()
val studentId = json
?.getJsonArray("AuthInfos")
?.asJsonObjectList()
?.filter { authInfo ->
authInfo.getInt("JednostkaSprawozdawczaId") in unitIds
}
?.flatMap { authInfo ->
authInfo.getJsonArray("UczenIds")
?.map { it.asInt }
?: listOf()
}
?.firstOrNull()
?.toString()
data.app.cookieJar.set(
data.webHost ?: "vulcan.net.pl",
"idBiezacyUczen",
studentId
)
}
map[symbol] = permissions
}
onSuccess(text, schoolSymbols)
}

View File

@ -151,6 +151,15 @@ class VulcanHebeMain(
studentData["senderAddressName"] = senderAddressName
studentData["senderAddressHash"] = senderAddressHash
studentData["hebeContext"] = hebeContext
// create the default TeamClass
data.getTeam(
id = null,
name = studentClassName,
schoolCode = schoolCode,
isTeamClass = true,
profileId = this.id,
)
}
dateSemester1Start?.let {
newProfile.dateSemester1Start = it

View File

@ -4,10 +4,12 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.hebe
import androidx.room.OnConflictStrategy
import pl.szczodrzynski.edziennik.data.api.VULCAN_HEBE_ENDPOINT_TEACHERS
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_HEBE_TEACHERS
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanHebe
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.ext.DAY
import pl.szczodrzynski.edziennik.ext.getString
@ -25,20 +27,32 @@ class VulcanHebeTeachers(
TAG,
VULCAN_HEBE_ENDPOINT_TEACHERS,
HebeFilterType.BY_PERIOD,
lastSync = lastSync,
lastSync = 0L,
) { list, _ ->
list.forEach { person ->
val name = person.getString("Name")
val surname = person.getString("Surname")
val displayName = person.getString("DisplayName")
val subjectName = person.getString("Description") ?: return@apiGetList
val subjectName = person.getString("Description") ?: return@forEach
if (subjectName.isBlank()) {
return@forEach
}
val teacher = data.getTeacherByFirstLast(
name?.plus(" ")?.plus(surname) ?: displayName ?: return@forEach
)
teacher.addSubject(data.getSubject(null, subjectName).id)
when (subjectName) {
"Pedagog" -> teacher.setTeacherType(Teacher.TYPE_PEDAGOGUE)
else -> {
val subjectId = data.getSubject(null, subjectName).id
if (!teacher.subjects.contains(subjectId))
teacher.addSubject(subjectId)
}
}
}
data.teacherOnConflictStrategy = OnConflictStrategy.REPLACE
data.setSyncNext(ENDPOINT_VULCAN_HEBE_TEACHERS, 2 * DAY)
onSuccess(ENDPOINT_VULCAN_HEBE_TEACHERS)
}

View File

@ -406,7 +406,13 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
return subject
}
fun getTeam(id: Long?, name: String, schoolCode: String, isTeamClass: Boolean = false): Team {
fun getTeam(
id: Long?,
name: String,
schoolCode: String,
isTeamClass: Boolean = false,
profileId: Int? = null,
): Team {
if (isTeamClass && teamClass != null)
return teamClass as Team
var team = teamList.singleOrNull { it.id == id }
@ -417,7 +423,7 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
if (team == null) {
team = Team(
profileId,
profileId ?: this.profileId,
id ?: name.crc32(),
name,
if (isTeamClass) Team.TYPE_CLASS else Team.TYPE_VIRTUAL,
@ -425,6 +431,8 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
-1
)
teamList[team.id] = team
} else if (id != null) {
team.id = id
}
return team
}

View File

@ -46,6 +46,6 @@ object Signing {
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
return "$param1.MTIzNDU2Nzg5MDTSWXKh0v===.$param2".sha256()
return "$param1.MTIzNDU2Nzg5MDocQtfDuE===.$param2".sha256()
}
}

View File

@ -164,7 +164,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
val type = if (event.isHomework) Notification.TYPE_NEW_SHARED_HOMEWORK else Notification.TYPE_NEW_SHARED_EVENT
val notificationFilter = app.config.getFor(event.profileId).sync.notificationFilter
if (!notificationFilter.contains(type) && event.sharedBy != "self") {
if (!notificationFilter.contains(type) && event.sharedBy != "self" && event.date >= Date.getToday()) {
val notification = Notification(
id = Notification.buildId(event.profileId, type, event.id),
title = app.getNotificationTitle(type),

View File

@ -9,7 +9,10 @@ import android.os.Process
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.sqlite.db.SimpleSQLiteQuery
import com.chuckerteam.chucker.api.Chucker
import com.chuckerteam.chucker.api.Chucker.SCREEN_HTTP
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -95,6 +98,13 @@ class LabPageFragment : LazyFragment(), CoroutineScope {
.show()
}
if (App.enableChucker) {
b.openChucker.isVisible = true
b.openChucker.onClick {
startActivity(Chucker.getLaunchIntent(activity, SCREEN_HTTP))
}
}
b.disableDebug.onClick {
app.config.devMode = false
App.devMode = false

View File

@ -36,6 +36,7 @@ class GradesAdapter(
private const val ITEM_TYPE_EMPTY = 2
private const val ITEM_TYPE_GRADE = 3
private const val ITEM_TYPE_STATS = 4
private const val ITEM_TYPE_UNKNOWN_SUBJECT = 5
const val STATE_CLOSED = 0
const val STATE_OPENED = 1
}
@ -58,6 +59,7 @@ class GradesAdapter(
ITEM_TYPE_EMPTY -> EmptyViewHolder(inflater, parent)
ITEM_TYPE_GRADE -> GradeViewHolder(inflater, parent)
ITEM_TYPE_STATS -> StatsViewHolder(inflater, parent)
ITEM_TYPE_UNKNOWN_SUBJECT -> UnknownSubjectViewHolder(inflater, parent)
else -> throw IllegalArgumentException("Incorrect viewType")
}
}
@ -69,6 +71,7 @@ class GradesAdapter(
is GradesEmpty -> ITEM_TYPE_EMPTY
is Grade -> ITEM_TYPE_GRADE
is GradesStats -> ITEM_TYPE_STATS
is GradesUnknownSubject -> ITEM_TYPE_UNKNOWN_SUBJECT
else -> throw IllegalArgumentException("Incorrect viewType")
}
}
@ -86,7 +89,7 @@ class GradesAdapter(
fun expandModel(model: ExpandableItemModel<*>?, view: View?, notifyAdapter: Boolean = true) {
model ?: return
val position = items.indexOf(model)
var position = items.indexOf(model)
if (position == -1)
return
@ -138,9 +141,16 @@ class GradesAdapter(
else -> model.items
}
if (model is GradesSubject && model.isUnknown) {
position++
items.add(position, GradesUnknownSubject())
if (notifyAdapter) notifyItemInserted(position)
}
position++
model.state = STATE_OPENED
items.addAll(position + 1, subItems.filterNotNull())
if (notifyAdapter) notifyItemRangeInserted(position + 1, subItems.size)
items.addAll(position, subItems.filterNotNull())
if (notifyAdapter) notifyItemRangeInserted(position, subItems.size)
if (model is GradesSubject) {
// auto expand first semester
@ -156,9 +166,10 @@ class GradesAdapter(
else -> semester.grades
}
position++
semester.state = STATE_OPENED
items.addAll(position + 2 + semesterIndex, grades)
if (notifyAdapter) notifyItemRangeInserted(position + 2 + semesterIndex, grades.size)
items.addAll(position + semesterIndex, grades)
if (notifyAdapter) notifyItemRangeInserted(position + semesterIndex, grades.size)
}
}
}
@ -198,6 +209,7 @@ class GradesAdapter(
is EmptyViewHolder -> ITEM_TYPE_EMPTY
is GradeViewHolder -> ITEM_TYPE_GRADE
is StatsViewHolder -> ITEM_TYPE_STATS
is UnknownSubjectViewHolder -> ITEM_TYPE_UNKNOWN_SUBJECT
else -> throw IllegalArgumentException("Incorrect viewType")
}
holder.itemView.setTag(R.string.tag_key_view_type, viewType)
@ -210,6 +222,7 @@ class GradesAdapter(
holder is EmptyViewHolder && item is GradesEmpty -> holder.onBind(activity, app, item, position, this)
holder is GradeViewHolder && item is GradeFull -> holder.onBind(activity, app, item, position, this)
holder is StatsViewHolder && item is GradesStats -> holder.onBind(activity, app, item, position, this)
holder is UnknownSubjectViewHolder && item is GradesUnknownSubject -> holder.onBind(activity, app, item, position, this)
}
if (holder is SemesterViewHolder && item is GradesSemester) {

View File

@ -182,6 +182,7 @@ class GradesListFragment : Fragment(), CoroutineScope {
@Suppress("SuspendFunctionOnCoroutineScope")
private fun processGrades(grades: List<GradeFull>): MutableList<Any> {
val items = mutableListOf<GradesSubject>()
var unknownSubjectItem: GradesSubject? = null
var subjectId = -1L
var semesterNumber = 0
@ -200,17 +201,31 @@ class GradesListFragment : Fragment(), CoroutineScope {
subjectId = grade.subjectId
semesterNumber = 0
subject = items.firstOrNull { it.subjectId == subjectId }
?: GradesSubject(grade.subjectId, grade.subjectLongName ?: "").also {
subject = items.firstOrNull { it.subjectId == subjectId } ?: run {
if (grade.subjectLongName != null) {
return@run GradesSubject(grade.subjectId, grade.subjectLongName!!).also {
items += it
it.semester = 2
}
}
if (unknownSubjectItem == null) {
unknownSubjectItem = GradesSubject(-1, "unknown").also {
items += it
it.semester = 2
it.isUnknown = true
}
}
return@run unknownSubjectItem!!
}
}
if (grade.semester != semesterNumber) {
semesterNumber = grade.semester
semester = subject.semesters.firstOrNull { it.number == semesterNumber }
?: GradesSemester(subject.subjectId, grade.semester).also { subject.semesters += it }
?: GradesSemester(subject.subjectId, grade.semester).also {
subject.semesters += it
it.hideEditor = subject.isUnknown
}
}
grade.showAsUnseen = !grade.seen
@ -221,6 +236,11 @@ class GradesListFragment : Fragment(), CoroutineScope {
semester.hasUnseen = true
}
if (subject.isUnknown) {
// unknown subjects may have final grades (i.e. Mobidziennik)
grade.type = Grade.TYPE_NORMAL
}
when (grade.type) {
Grade.TYPE_SEMESTER1_PROPOSED,
Grade.TYPE_SEMESTER2_PROPOSED -> semester.proposedGrade = grade
@ -255,6 +275,10 @@ class GradesListFragment : Fragment(), CoroutineScope {
val yearlyPoint = mutableListOf<Float>()
for (item in items) {
if (item.isUnknown) {
// do not count averages for "unknown" subjects
continue
}
item.semesters.forEach { sem ->
manager.calculateAverages(sem.averages)
if (sem.number == 1) {

View File

@ -14,6 +14,7 @@ data class GradesSemester(
override var level = 2
var hasUnseen = false
var hideEditor = false
val averages = GradesAverages()
var proposedGrade: GradeFull? = null

View File

@ -15,6 +15,7 @@ data class GradesSubject(
var lastAddedDate = 0L
var semester: Int = 1
var isUnknown = false
var hasUnseen: Boolean = false
get() = field || semesters.any { it.hasUnseen }

View File

@ -0,0 +1,7 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-3-14.
*/
package pl.szczodrzynski.edziennik.ui.grades.models
class GradesUnknownSubject

View File

@ -61,6 +61,8 @@ class SemesterViewHolder(
}
}
b.editButton.isVisible = !item.hideEditor
b.average.text = manager.getAverageString(app, item.averages)
b.proposedGrade.setGrade(item.proposedGrade, manager)
b.finalGrade.setGrade(item.finalGrade, manager)

View File

@ -19,9 +19,7 @@ import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.GradesItemSubjectBinding
import pl.szczodrzynski.edziennik.ext.dp
import pl.szczodrzynski.edziennik.ext.resolveAttr
import pl.szczodrzynski.edziennik.ext.setText
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.ui.grades.GradeView
import pl.szczodrzynski.edziennik.ui.grades.GradesAdapter
import pl.szczodrzynski.edziennik.ui.grades.GradesAdapter.Companion.STATE_CLOSED
@ -41,7 +39,12 @@ class SubjectViewHolder(
val manager = app.gradesManager
val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme)
b.subjectName.text = item.subjectName
if (!item.isUnknown) {
b.subjectName.text = item.subjectName
}
else {
b.subjectName.text = R.string.grades_subject_unknown.resolveString(activity).asItalicSpannable()
}
b.dropdownIcon.rotation = when (item.state) {
STATE_CLOSED -> 0f
else -> 180f

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-3-14.
*/
package pl.szczodrzynski.edziennik.ui.grades.viewholder
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.databinding.GradesItemUnknownSubjectBinding
import pl.szczodrzynski.edziennik.ui.grades.GradesAdapter
import pl.szczodrzynski.edziennik.ui.grades.models.GradesUnknownSubject
class UnknownSubjectViewHolder(
inflater: LayoutInflater,
parent: ViewGroup,
val b: GradesItemUnknownSubjectBinding = GradesItemUnknownSubjectBinding.inflate(inflater, parent, false)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<GradesUnknownSubject, GradesAdapter> {
companion object {
private const val TAG = "UnknownSubjectViewHolder"
}
override fun onBind(activity: AppCompatActivity, app: App, item: GradesUnknownSubject, position: Int, adapter: GradesAdapter) {
}
}

View File

@ -85,6 +85,9 @@ class HomeGradesCard(
grades.forEach { grade ->
val model = ItemGradesSubjectModel.searchModelBySubjectId(subjects, grade.subjectId)
?: run {
if (grade.subjectLongName == null) {
return@forEach
}
subjects.add(ItemGradesSubjectModel(
profile,
Subject(profile.id, grade.subjectId, grade.subjectLongName, grade.subjectShortName),

View File

@ -262,7 +262,7 @@ class LoginFormFragment : Fragment(), CoroutineScope {
if (credential.caseMode == FormField.CaseMode.UPPER_CASE)
text = text.uppercase()
if (credential.caseMode == FormField.CaseMode.LOWER_CASE)
text = text.uppercase()
text = text.lowercase()
credential.stripTextRegex?.let {
text = text.replace(it.toRegex(), "")

View File

@ -77,7 +77,7 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
private lateinit var stylingConfig: StylingConfig
private lateinit var uiConfig: UIConfig
private val enableTextStyling
get() = app.profile.loginStoreType != LoginStore.LOGIN_TYPE_VULCAN
get() = app.profile.loginStoreType != LoginStore.LOGIN_TYPE_VULCAN && app.profile.loginStoreType != LoginStore.LOGIN_TYPE_LIBRUS
private var changedRecipients = false
private var changedSubject = false
private var changedBody = false

View File

@ -62,6 +62,8 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
private var firstEventMinute = 24 * 60
private var paddingTop = 0
private var viewsRemoved = false
private val manager
get() = app.timetableManager
private val attendanceManager
@ -127,6 +129,7 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
inflater.inflate(R.layout.timetable_no_timetable, b.root) { view, _, _ ->
b.root.removeAllViews()
b.root.addView(view)
viewsRemoved = true
val b = TimetableNoTimetableBinding.bind(view)
val weekStart = date.weekStart.stringY_m_d
@ -151,6 +154,7 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
inflater.inflate(R.layout.timetable_no_lessons, b.root) { view, _, _ ->
b.root.removeAllViews()
b.root.addView(view)
viewsRemoved = true
}
return
}
@ -162,6 +166,13 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
return
}
// the timetable was not synced (the day layout views are removed) and is now available
if (viewsRemoved) {
viewsRemoved = false
activity.sendBroadcast(Intent(TimetableFragment.ACTION_RELOAD_PAGES))
return
}
b.scrollView.isVisible = true
b.dayFrame.removeView(dayView)
b.dayFrame.addView(dayView, 0)

View File

@ -36,6 +36,7 @@ class TimetableFragment : Fragment(), CoroutineScope {
companion object {
private const val TAG = "TimetableFragment"
const val ACTION_SCROLL_TO_DATE = "pl.szczodrzynski.edziennik.timetable.SCROLL_TO_DATE"
const val ACTION_RELOAD_PAGES = "pl.szczodrzynski.edziennik.timetable.RELOAD_PAGES"
const val DEFAULT_START_HOUR = 6
const val DEFAULT_END_HOUR = 19
var pageSelection: Date? = null
@ -66,14 +67,22 @@ class TimetableFragment : Fragment(), CoroutineScope {
override fun onReceive(context: Context, i: Intent) {
if (!isAdded)
return
val dateStr = i.extras?.getString("timetableDate", null) ?: return
val date = Date.fromY_m_d(dateStr)
b.viewPager.setCurrentItem(items.indexOf(date), true)
when (i.action) {
ACTION_SCROLL_TO_DATE -> {
val dateStr = i.extras?.getString("timetableDate", null) ?: return
val date = Date.fromY_m_d(dateStr)
b.viewPager.setCurrentItem(items.indexOf(date), true)
}
ACTION_RELOAD_PAGES -> {
b.viewPager.adapter?.notifyDataSetChanged()
}
}
}
}
override fun onResume() {
super.onResume()
activity.registerReceiver(broadcastReceiver, IntentFilter(ACTION_SCROLL_TO_DATE))
activity.registerReceiver(broadcastReceiver, IntentFilter(ACTION_RELOAD_PAGES))
}
override fun onPause() {
super.onPause()

View File

@ -47,4 +47,8 @@ class TimetablePagerAdapter(
}
return pageTitle
}
override fun getItemPosition(`object`: Any): Int {
return POSITION_NONE
}
}

View File

@ -17,6 +17,7 @@
package pl.szczodrzynski.edziennik.utils;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@ -83,7 +84,9 @@ public class SwipeRefreshLayoutNoIndicator extends SwipeRefreshLayout {
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
ev.setSource(0x10000000);
if (Build.VERSION.SDK_INT < 32) {
ev.setSource(0x10000000);
}
boolean parentConsumed = parent.onInterceptTouchEvent(ev);
boolean superConsumed = super.onInterceptTouchEvent(ev);
return parentConsumed && superConsumed;

View File

@ -72,6 +72,9 @@ class MessageManager(private val app: App) {
if (sentDate > 0L) {
it.addedDate = sentDate
}
withContext(Dispatchers.IO) {
it.recipients = app.db.messageRecipientDao().getAllByMessageId(profileId, it.id)
}
}
} else {
withContext(Dispatchers.IO) {

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2022-3-14.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.mikepenz.iconics.view.IconicsTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="@string/grades_subject_unknown_help"
android:textColor="?android:textColorSecondary" />
</LinearLayout>
</layout>

View File

@ -47,6 +47,15 @@
android:layout_height="wrap_content"
android:text="Chucker" />
<Button
android:id="@+id/openChucker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Open Chucker"
android:textAllCaps="false"
android:visibility="gone"
tools:visibility="visible" />
<Button
android:id="@+id/last10unseen"
android:layout_width="match_parent"

View File

@ -857,7 +857,7 @@
<string name="settings_about_licenses_text">Open-Source-Lizenzen</string>
<string name="settings_about_privacy_policy_text">Datenschutzrichtlinie</string>
<string name="settings_card_register_title">E-Klassenbuch</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nSeptember 2018 - Oktober 2021</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nSeptember 2018 - 2022</string>
<string name="settings_about_update_subtext">Klicken Sie hier, um nach Aktualisierungen zu suchen</string>
<string name="settings_about_update_text">Aktualisierung</string>
<string name="settings_about_version_text">Version</string>

View File

@ -348,7 +348,7 @@
<string name="event_information">information</string>
<string name="event_list_added_by_format">Added %1$s by %2$s%3$s</string>
<string name="event_list_added_by_self_format">Added %1$s by you%3$s</string>
<string name="event_list_added_by_unknown_format">Adde %1$s%3$s</string>
<string name="event_list_added_by_unknown_format">Added %1$s%3$s</string>
<string name="event_list_shared_by_format">{cmd-share-variant} %1$s by %2$s%3$s</string>
<string name="event_list_shared_by_self_format">{cmd-share-variant} %1$s by you%3$s</string>
<string name="event_manual_remove">Removing event…</string>
@ -859,7 +859,7 @@
<string name="settings_about_licenses_text">Open-source licenses</string>
<string name="settings_about_privacy_policy_text">Privacy policy</string>
<string name="settings_card_register_title">E-register</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nSeptember 2018 - October 2021</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nSeptember 2018 - 2022</string>
<string name="settings_about_update_subtext">Click to check for updates</string>
<string name="settings_about_update_text">Update</string>
<string name="settings_about_version_text">Version</string>
@ -1287,7 +1287,7 @@
<string name="attendance_details_time">Time</string>
<string name="settings_about_github_subtext">Help with app development on GitHub</string>
<string name="build_platform">Distribution</string>
<string name="build_validate_progress">Build verification in progress...</string>
<string name="build_validate_progress">Build verification in progress</string>
<string name="attendance_tab_months">By months</string>
<string name="attendance_tab_summary">Summary</string>
<string name="attendance_details_type">Type</string>
@ -1307,7 +1307,7 @@
<string name="settings_about_homepage_subtext">Get help or support authors</string>
<string name="build_invalid_title">Information about application version</string>
<string name="home_archive_close_no_target_title">No current profile</string>
<string name="login_platform_list_loading">Loading e-registers list...</string>
<string name="login_platform_list_loading">Loading e-registers list</string>
<string name="login_mode_podlasie_api">Log in using token</string>
<string name="login_mode_podlasie_api_guide">Provide mobile app token.</string>
<string name="attendance_config_title">Attendance configuration</string>
@ -1321,10 +1321,10 @@
<string name="login_mode_vulcan_api">Use token, symbol and PIN code</string>
<string name="login_mode_vulcan_api_hint">Register device on journal VULCAN® page</string>
<string name="login_mode_vulcan_web">Use e-mail/username and password</string>
<string name="edziennik_progress_login_podlasie_api">Logging in to PPE...</string>
<string name="edziennik_progress_login_podlasie_api">Logging in to PPE</string>
<string name="login_type_podlasie">Podlaska Platforma Edukacyjna</string>
<string name="login_mode_edudziennik_web">Log in using e-mail and password</string>
<string name="edziennik_progress_login_vulcan_web_main">Logging in to VULCAN® register...</string>
<string name="edziennik_progress_login_vulcan_web_main">Logging in to VULCAN® register</string>
<string name="login_mode_librus_jst">Login via VULCAN® platform</string>
<string name="login_mode_librus_email">Log in using e-mail</string>
<string name="attendance_details_id">Attendance ID</string>
@ -1377,4 +1377,78 @@
<string name="menu_teachers">Teachers</string>
<string name="edziennik_progress_endpoint_addressbook">Syncing addressbook…</string>
<string name="send_message">Send message</string>
<string name="color_black">Black</string>
<string name="color_grey">Gray</string>
<string name="card_type_notes">Notes</string>
<string name="menu_notes">Notes</string>
<string name="notes_editor_dialog_title">Edit note</string>
<string name="notes_editor_color">Color</string>
<string name="color_brown">Brown</string>
<string name="color_pink">Pink</string>
<string name="color_dark_blue">Dark blue</string>
<string name="color_purple">Purple</string>
<string name="color_blue">Blue</string>
<string name="color_teal">Teal</string>
<string name="color_green">Green</string>
<string name="color_yellow">Yellow</string>
<string name="color_orange">Orange</string>
<string name="color_red">Red</string>
<string name="notes_action_add">Add note</string>
<string name="notes_type_grade">Grade</string>
<string name="privacy_policy_dialog_html"><![CDATA[By using the application, you confirm that you have <a href="https://szkolny.eu/privacy-policy">read the Privacy Policy</a> and accept its provisions.<br /><br />The authors of the application are not responsible for the use of the Szkolny.eu application.]]></string>
<string name="login_chooser_version_format">Szkolny.eu v%s\n%s</string>
<string name="agenda_config_appearance">Appearance</string>
<string name="agenda_config_elearning">Online learning</string>
<string name="event_type_elearning">online lesson</string>
<string name="menu_messages_config">Messages settings</string>
<string name="message_delete">Delete</string>
<string name="message_reply">Reply</string>
<string name="message_forward">Forward</string>
<string name="discard">Discard</string>
<string name="messages_compose_draft_saved">Draft message saved</string>
<string name="messages_tab_draft">Drafts</string>
<string name="messages_compose_send_long">Send a message</string>
<string name="notes_button">Notes</string>
<string name="notes_list_dialog_title">Notes</string>
<string name="notes_added_by_you_format">Added on %1$s</string>
<string name="notes_type_attendance">Attendance</string>
<string name="notes_type_behavior">Behavior</string>
<string name="notes_type_day">Day</string>
<string name="notes_type_event">Event</string>
<string name="notes_type_lesson">Lesson</string>
<string name="notes_type_message">Message</string>
<string name="notes_details_id">Note ID</string>
<string name="notes_details_owner_id">Owner ID</string>
<string name="notes_type_announcement">Announcement</string>
<string name="styled_text_dialog_title">Edit text</string>
<string name="login_qr_decoding_error">The QR code doesn\'t seem correct</string>
<string name="notes_no_data_hint">You can add notes and share them with your class using the Add button.</string>
<string name="legend_notes_added">{cmd-playlist-edit} notes added</string>
<string name="legend_notes_added_replaced">{cmd-playlist-edit} notes added\n{cmd-swap-horizontal} text replaced with a note</string>
<string name="notes_no_data">No notes added yet</string>
<string name="messages_compose_discard_draft_text">Do you want to discard the saved version of the message?\n\nThis will also cancel your changes and delete your message.</string>
<string name="messages_compose_discard_draft">Discard draft</string>
<string name="messages_compose_draft_discarded">Draft message deleted</string>
<string name="messages_compose_discard_draft_title">Discard draft</string>
<string name="notes_details_dialog_title">Note</string>
<string name="notes_editor_topic">Note title (optional)</string>
<string name="notes_editor_body">Note text</string>
<string name="notes_editor_share_hint">The classmates will see your note next to this item.</string>
<string name="notes_editor_replace_hint">The content of the note will be visible instead of the original content of the item.</string>
<string name="notes_editor_body_error">Enter the note text</string>
<string name="color_none">None</string>
<string name="notification_type_new_shared_note">Note shared</string>
<string name="notes_editor_confirmation_text">Do you want to delete this note?\n\nIt will be deleted on your device and on classmates\' devices.</string>
<string name="notes_editor_progress_unsharing">Deleting shared note…</string>
<string name="notes_editor_progress_sharing">Sharing the note…</string>
<string name="notes_editor_replace_text">Replace original content</string>
<string name="card_notes_header_title">Newest notes</string>
<string name="messages_compose_save_draft_title">Save changes</string>
<string name="messages_compose_save_draft_text">Do you want to save your changes as a draft?\n\nIt will be possible to edit and send the message later.</string>
<string name="messages_compose_body_load_failed">The original message could not be loaded.</string>
<string name="message_download">Download again</string>
<string name="hint_message_star">Mark with a star</string>
<string name="dialog_lesson_attendance_details">Details</string>
<string name="menu_agenda_config">Agenda settings</string>
<string name="registration_config_note_sharing_title">Share notes</string>
</resources>

View File

@ -926,7 +926,7 @@
<string name="settings_about_licenses_text">Licencje open-source</string>
<string name="settings_about_privacy_policy_text">Polityka prywatności</string>
<string name="settings_card_register_title">E-dziennik</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nwrzesień 2018 - październik 2021</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nwrzesień 2018 - 2022</string>
<string name="settings_about_update_subtext">Kliknij, aby sprawdzić aktualizacje</string>
<string name="settings_about_update_text">Aktualizacja</string>
<string name="settings_about_version_text">Wersja</string>
@ -1547,4 +1547,8 @@
<string name="color_black">Czarny</string>
<string name="card_type_notes">Notatki</string>
<string name="card_notes_header_title">Najnowsze notatki</string>
<string name="login_summary_account_child">(uczeń)</string>
<string name="login_summary_account_parent">(rodzic)</string>
<string name="grades_subject_unknown">- nieznany przedmiot -</string>
<string name="grades_subject_unknown_help">{cmd-information-outline} Oceny, których przedmiot nie został podany w dzienniku. Może to być na przykład taki, który nie jest prowadzony w tym roku szkolnym.</string>
</resources>

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-2-21.
*/
package pl.szczodrzynski.edziennik.network
import android.content.Context
import eu.szkolny.sslprovider.SSLProvider
import eu.szkolny.sslprovider.enableSupportedTls
import okhttp3.OkHttpClient
import timber.log.Timber
object SSLProviderInstaller {
fun install(applicationContext: Context, rebuildCallback: () -> Unit) {
SSLProvider.install(
applicationContext,
downloadIfNeeded = true,
supportTls13 = false,
onFinish = {
rebuildCallback()
},
onError = {
Timber.e("Failed to install SSLProvider: $it")
it.printStackTrace()
}
)
}
fun enableSupportedTls(builder: OkHttpClient.Builder, enableCleartext: Boolean = true) {
builder.enableSupportedTls(enableCleartext)
}
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-2-21.
*/
package pl.szczodrzynski.edziennik.network
import android.content.Context
import okhttp3.OkHttpClient
object SSLProviderInstaller {
fun install(applicationContext: Context, rebuildCallback: () -> Unit) {
}
fun enableSupportedTls(builder: OkHttpClient.Builder, enableCleartext: Boolean = true) {
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-2-21.
*/
package pl.szczodrzynski.edziennik.sync
import android.app.IntentService
import android.content.Intent
import android.widget.Toast
import pl.szczodrzynski.edziennik.utils.Utils
class UpdateDownloaderService : IntentService(UpdateDownloaderService::class.java.simpleName) {
override fun onHandleIntent(intent: Intent?) {
try {
Utils.openGooglePlay(this, application.packageName)
}
catch (e: Exception) {
e.printStackTrace()
Toast.makeText(this, "Nie znaleziono Google Play. Pobierz aktualizację ręcznie.", Toast.LENGTH_SHORT).show()
}
}
}

View File

@ -5,8 +5,8 @@ buildscript {
kotlin_version = '1.5.30'
release = [
versionName: "4.11-rc.1",
versionCode: 4110010
versionName: "4.11.6",
versionCode: 4110699
]
setup = [