forked from github/szkolny
Compare commits
58 Commits
v4.13-rc.2
...
develop
Author | SHA1 | Date | |
---|---|---|---|
cefb0deba8 | |||
90a151c129 | |||
9fd9721ae7 | |||
ceca75ef4b | |||
21c00bbe53 | |||
db00566ebf | |||
07ab1b984f | |||
8177d4aa2d | |||
beff1b6460 | |||
31b569b02e | |||
8bf77817d2 | |||
27b61adf1d | |||
a0244841ad | |||
12c0c6f2ec | |||
aaa3b8626e | |||
48c9e2dfe3 | |||
81d4801d27 | |||
5f8016061d | |||
5007587192 | |||
dfd1083e41 | |||
678baf46e5 | |||
4077fe448d | |||
f085e17ef7 | |||
7fd2cad46b | |||
93dc2ac9ab | |||
ac53e267fc | |||
86eb1a0f42 | |||
710d82da27 | |||
0123f50810 | |||
6d3eb65445 | |||
a9a0630226 | |||
ec7577f999 | |||
05c7c0012c | |||
d65c6db954 | |||
771dc437e6 | |||
3d5d3847cc | |||
18cc60a80b | |||
fedde9f739 | |||
9fde97bef0 | |||
742bd03e9e | |||
62ffc652ab | |||
bfd2e9883a | |||
00e077d01f | |||
c21d89cf60 | |||
f52cc1b197 | |||
c90ad97f55 | |||
845e09d875 | |||
158b69a8d3 | |||
9535f53563 | |||
eeb3fc4621 | |||
41693a9fc8 | |||
d3599b8c89 | |||
ffd81f8b82 | |||
2c34924052 | |||
26ad6373e6 | |||
cac8f94407 | |||
6628b97faf | |||
8424414317 |
4
.github/utils/extract_changelogs.py
vendored
4
.github/utils/extract_changelogs.py
vendored
@ -23,11 +23,11 @@ if __name__ == "__main__":
|
||||
(title, changelog) = get_changelog(project_dir, format="plain")
|
||||
|
||||
# plain text changelog - Firebase App Distribution
|
||||
with open(dir + "whatsnew-titled.txt", "w", encoding="utf-8") as f:
|
||||
with open(dir + "whatsnew_titled.txt", "w", encoding="utf-8") as f:
|
||||
f.write(title)
|
||||
f.write("\n")
|
||||
f.write(changelog)
|
||||
print("::set-output name=changelogPlainTitledFile::" + dir + "whatsnew-titled.txt")
|
||||
print("::set-output name=changelogPlainTitledFile::" + dir + "whatsnew_titled.txt")
|
||||
|
||||
print("::set-output name=changelogTitle::" + title)
|
||||
|
||||
|
3
.github/workflows/build-release-aab-play.yml
vendored
3
.github/workflows/build-release-aab-play.yml
vendored
@ -113,10 +113,11 @@ jobs:
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
|
||||
packageName: pl.szczodrzynski.edziennik
|
||||
releaseFile: ${{ needs.sign.outputs.signedReleaseFile }}
|
||||
releaseFiles: ${{ needs.sign.outputs.signedReleaseFile }}
|
||||
releaseName: ${{ steps.changelog.outputs.appVersionName }}
|
||||
track: ${{ secrets.PLAY_RELEASE_TRACK }}
|
||||
whatsNewDirectory: ${{ steps.changelog.outputs.changelogDir }}
|
||||
status: completed
|
||||
|
||||
- name: Upload workflow artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
|
@ -156,6 +156,7 @@ dependencies {
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:2.5.2"
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||
implementation "androidx.room:room-runtime:2.4.3"
|
||||
implementation "androidx.room:room-ktx:2.4.3"
|
||||
implementation "androidx.work:work-runtime-ktx:2.7.1"
|
||||
kapt "androidx.room:room-compiler:2.4.3"
|
||||
|
||||
@ -208,7 +209,7 @@ dependencies {
|
||||
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
||||
implementation "com.github.Applandeo:Material-Calendar-View:15de569cbc" // https://github.com/Applandeo/Material-Calendar-View
|
||||
implementation "com.github.CanHub:Android-Image-Cropper:2.2.2" // https://github.com/CanHub/Android-Image-Cropper
|
||||
implementation "com.github.ChuckerTeam.Chucker:library:3.0.1" // https://github.com/ChuckerTeam/chucker
|
||||
implementation "com.github.ChuckerTeam.Chucker:library:3.5.2" // https://github.com/ChuckerTeam/chucker
|
||||
implementation "com.github.antonKozyriatskyi:CircularProgressIndicator:1.2.2" // https://github.com/antonKozyriatskyi/CircularProgressIndicator
|
||||
implementation "com.github.bassaer:chatmessageview:2.0.1" // https://github.com/bassaer/ChatMessageView
|
||||
implementation "com.github.hypertrack:hyperlog-android:0.0.10" // https://github.com/hypertrack/hyperlog-android
|
||||
|
2320
app/schemas/pl.szczodrzynski.edziennik.data.db.AppDb/100.json
Normal file
2320
app/schemas/pl.szczodrzynski.edziennik.data.db.AppDb/100.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -160,7 +160,11 @@
|
||||
<activity android:name=".ui.login.oauth.OAuthLoginActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="false"
|
||||
android:theme="@style/AppTheme.Light" />
|
||||
android:theme="@style/Theme.MaterialComponents.Light.DarkActionBar" />
|
||||
<activity android:name=".ui.login.recaptcha.RecaptchaActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.MaterialComponents.Light.DarkActionBar" />
|
||||
<activity android:name=".ui.base.BuildInvalidActivity" android:exported="false" />
|
||||
<activity android:name=".ui.settings.contributors.ContributorsActivity" android:exported="false" />
|
||||
|
||||
|
@ -1,13 +1,10 @@
|
||||
<h3>Wersja 4.13-rc.2, 2022-10-22</h3>
|
||||
<h3>Wersja 4.13.6, 2023-03-24</h3>
|
||||
<ul>
|
||||
<li>Poprawione powiadomienia na Androidzie 13. @santoni0</li>
|
||||
<li>Możliwość dostosowania wyświetlania planu lekcji.</li>
|
||||
<li>Opcja kolorowania bloków w planie lekcji.</li>
|
||||
<li><b>USOS</b> - pierwsza wersja obsługi systemu. Osobne rodzaje wydarzeń (oraz wygląd niektórych części aplikacji) lepiej dostosowany do nauki na studiach.</li>
|
||||
<li>Poprawione opcje filtrowania powiadomień i wyboru przycisków menu bocznego.</li>
|
||||
<li>Ulepszony system pobierania aktualizacji aplikacji.</li>
|
||||
<li>Naprawiono pobieranie załączników na Androidzie 13 i nowszym.</li>
|
||||
<li>Dodano opcję odświeżenia planu lekcji na wybrany tydzień.</li>
|
||||
<li>Usunięto błędy logowania. @BxOxSxS</li>
|
||||
</ul>
|
||||
<br>
|
||||
<br>
|
||||
Dzięki za korzystanie ze Szkolnego!<br>
|
||||
<i>© [Kuba Szczodrzyński](@kuba2k2), [Kacper Ziubryniewicz](@kapi2289) 2022</i>
|
||||
<i>© [Kuba Szczodrzyński](@kuba2k2) 2023</i>
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
/*secret password - removed for source code publication*/
|
||||
static toys AES_IV[16] = {
|
||||
0x54, 0xe7, 0x2a, 0xdd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
0x6d, 0xa5, 0x32, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
|
||||
|
||||
|
@ -235,6 +235,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
}
|
||||
|
||||
Signing.getCert(this)
|
||||
Utils.initializeStorageDir(this)
|
||||
|
||||
launch {
|
||||
withContext(Dispatchers.Default) {
|
||||
@ -422,6 +423,12 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
try {
|
||||
App.data = AppData.get(profile.loginStoreType)
|
||||
d("App", "Loaded AppData: ${App.data}")
|
||||
// apply newly-added config overrides, if not changed by the user yet
|
||||
for ((key, value) in App.data.configOverrides) {
|
||||
val config = App.profile.config
|
||||
if (!config.has(key))
|
||||
config.set(key, value)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("App", "Cannot load AppData", e)
|
||||
Toast.makeText(this, R.string.app_cannot_load_data, Toast.LENGTH_LONG).show()
|
||||
@ -462,6 +469,8 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
}
|
||||
fun profileSave() = profileSave(profile)
|
||||
fun profileSave(profile: Profile) {
|
||||
if (profile.id == profileId)
|
||||
App.profile = profile
|
||||
launch(Dispatchers.Default) {
|
||||
App.db.profileDao().add(profile)
|
||||
}
|
||||
|
@ -322,7 +322,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
|
||||
// IT'S WINTER MY DUDES
|
||||
val today = Date.getToday()
|
||||
if ((today.month % 11 == 1) && app.config.ui.snowfall) {
|
||||
if ((today.month / 3 % 4 == 0) && app.config.ui.snowfall) {
|
||||
b.rootFrame.addView(layoutInflater.inflate(R.layout.snowfall, b.rootFrame, false))
|
||||
} else if (app.config.ui.eggfall && BigNightUtil().isDataWielkanocyNearDzisiaj()) {
|
||||
val eggfall = layoutInflater.inflate(
|
||||
|
@ -59,10 +59,11 @@ data class AppData(
|
||||
val lessonHeight: Int,
|
||||
val enableMarkAsReadAnnouncements: Boolean,
|
||||
val enableNoticePoints: Boolean,
|
||||
val eventManualShowSubjectDropdown: Boolean,
|
||||
)
|
||||
|
||||
data class EventType(
|
||||
val id: Int,
|
||||
val id: Long,
|
||||
val color: String,
|
||||
val name: String,
|
||||
)
|
||||
|
@ -14,6 +14,7 @@ import pl.szczodrzynski.edziennik.ext.takePositive
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
abstract class BaseConfig(
|
||||
@Transient
|
||||
val db: AppDb,
|
||||
val profileId: Int? = null,
|
||||
protected var entries: List<ConfigEntry>? = null,
|
||||
@ -42,4 +43,6 @@ abstract class BaseConfig(
|
||||
db.configDao().add(ConfigEntry(profileId ?: -1, key, value))
|
||||
}
|
||||
}
|
||||
|
||||
fun has(key: String) = values.containsKey(key)
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class Config(db: AppDb) : BaseConfig(db) {
|
||||
var update by config<Update?>(null)
|
||||
var updatesChannel by config<String>("release")
|
||||
|
||||
var devMode by config<Boolean?>(null)
|
||||
var devMode by config<Boolean?>("debugMode", null)
|
||||
var devModePassword by config<String?>(null)
|
||||
var enableChucker by config<Boolean?>(null)
|
||||
|
||||
@ -55,11 +55,9 @@ class Config(db: AppDb) : BaseConfig(db) {
|
||||
ConfigMigration(app, this)
|
||||
}
|
||||
|
||||
fun getFor(profileId: Int): ProfileConfig {
|
||||
operator fun get(profileId: Int): ProfileConfig {
|
||||
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, entries).also {
|
||||
profileConfigs[profileId] = it
|
||||
}
|
||||
}
|
||||
|
||||
fun forProfile() = getFor(App.profileId)
|
||||
}
|
||||
|
@ -115,9 +115,9 @@ class ConfigDelegate<T>(
|
||||
is Boolean -> value
|
||||
// enums, maps & collections
|
||||
is Enum<*> -> value.toInt()
|
||||
is Collection<*> -> JsonArray(value.map {
|
||||
is Collection<*> -> value.map {
|
||||
if (it is Number || it is Boolean) it else serialize(it, serializeObjects = false)
|
||||
})
|
||||
}.toJsonElement()
|
||||
is Map<*, *> -> gson.toJson(value.mapValues { (_, it) ->
|
||||
if (it is Number || it is Boolean) it else serialize(it, serializeObjects = false)
|
||||
})
|
||||
|
@ -15,7 +15,7 @@ class ProfileConfig(
|
||||
entries: List<ConfigEntry>?,
|
||||
) : BaseConfig(db, profileId, entries) {
|
||||
companion object {
|
||||
const val DATA_VERSION = 4
|
||||
const val DATA_VERSION = 5
|
||||
}
|
||||
|
||||
val grades by lazy { ProfileConfigGrades(this) }
|
||||
@ -29,6 +29,8 @@ class ProfileConfig(
|
||||
var dataVersion by config<Int>(DATA_VERSION)
|
||||
var hash by config<String>("")
|
||||
|
||||
var shareByDefault by config<Boolean>(false)
|
||||
|
||||
init {
|
||||
if (dataVersion < DATA_VERSION)
|
||||
ProfileConfigMigration(this)
|
||||
|
@ -15,6 +15,7 @@ class ProfileConfigUI(base: ProfileConfig) {
|
||||
var agendaGroupByType by base.config<Boolean>(false)
|
||||
var agendaLessonChanges by base.config<Boolean>(true)
|
||||
var agendaTeacherAbsence by base.config<Boolean>(true)
|
||||
var agendaSubjectImportant by base.config<Boolean>(false)
|
||||
var agendaElearningMark by base.config<Boolean>(false)
|
||||
var agendaElearningGroup by base.config<Boolean>(true)
|
||||
|
||||
|
@ -16,6 +16,8 @@ import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_AL
|
||||
class ProfileConfigMigration(config: ProfileConfig) {
|
||||
init { config.apply {
|
||||
|
||||
val profile = db.profileDao().getByIdNow(profileId ?: -1)
|
||||
|
||||
if (dataVersion < 2) {
|
||||
sync.notificationFilter = sync.notificationFilter + NotificationType.TEACHER_ABSENCE
|
||||
|
||||
@ -37,11 +39,23 @@ class ProfileConfigMigration(config: ProfileConfig) {
|
||||
// switch to new event types (USOS)
|
||||
dataVersion = 4
|
||||
|
||||
val profile = db.profileDao().getByIdNow(profileId ?: -1)
|
||||
if (profile?.loginStoreType?.schoolType == SchoolType.UNIVERSITY) {
|
||||
db.eventTypeDao().clear(profileId ?: -1)
|
||||
db.eventTypeDao().addDefaultTypes(profile)
|
||||
}
|
||||
}
|
||||
|
||||
if (dataVersion < 5) {
|
||||
// update USOS event types and the appropriate events (2022-12-25)
|
||||
dataVersion = 5
|
||||
|
||||
if (profile?.loginStoreType?.schoolType == SchoolType.UNIVERSITY) {
|
||||
db.eventTypeDao().getAllWithDefaults(profile)
|
||||
// wejściówka (4) -> kartkówka (3)
|
||||
db.eventDao().getRawNow("UPDATE events SET eventType = 3 WHERE profileId = $profileId AND eventType = 4;")
|
||||
// zadanie (6) -> zadanie domowe (-1)
|
||||
db.eventDao().getRawNow("UPDATE events SET eventType = -1 WHERE profileId = $profileId AND eventType = 6;")
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -26,9 +26,10 @@ val LIBRUS_USER_AGENT = "${SYSTEM_USER_AGENT}LibrusMobileApp"
|
||||
const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0"
|
||||
const val LIBRUS_CLIENT_ID = "VaItV6oRutdo8fnjJwysnTjVlvaswf52ZqmXsJGP"
|
||||
const val LIBRUS_REDIRECT_URL = "app://librus"
|
||||
const val LIBRUS_AUTHORIZE_URL = "https://portal.librus.pl/oauth2/authorize?client_id=$LIBRUS_CLIENT_ID&redirect_uri=$LIBRUS_REDIRECT_URL&response_type=code"
|
||||
const val LIBRUS_LOGIN_URL = "https://portal.librus.pl/rodzina/login/action"
|
||||
const val LIBRUS_AUTHORIZE_URL = "https://portal.librus.pl/konto-librus/redirect/dru"
|
||||
const val LIBRUS_LOGIN_URL = "https://portal.librus.pl/konto-librus/login/action"
|
||||
const val LIBRUS_TOKEN_URL = "https://portal.librus.pl/oauth2/access_token"
|
||||
const val LIBRUS_HEADER = "pl.librus.synergiaDru2"
|
||||
|
||||
const val LIBRUS_ACCOUNT_URL = "/v3/SynergiaAccounts/fresh/" // + login
|
||||
const val LIBRUS_ACCOUNTS_URL = "/v3/SynergiaAccounts"
|
||||
@ -59,9 +60,6 @@ const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action="
|
||||
const val LIBRUS_SYNERGIA_HOMEWORK_ATTACHMENT_URL = "https://synergia.librus.pl/homework/downloadFile"
|
||||
const val LIBRUS_SYNERGIA_MESSAGES_ATTACHMENT_URL = "https://synergia.librus.pl/wiadomosci/pobierz_zalacznik"
|
||||
|
||||
const val LIBRUS_PORTAL_RECAPTCHA_KEY = "6Lf48moUAAAAAB9ClhdvHr46gRWR"
|
||||
const val LIBRUS_PORTAL_RECAPTCHA_REFERER = "https://portal.librus.pl/rodzina/login"
|
||||
|
||||
|
||||
val MOBIDZIENNIK_USER_AGENT = SYSTEM_USER_AGENT
|
||||
|
||||
|
@ -24,6 +24,25 @@ object Regexes {
|
||||
"""^\[META:([A-z0-9-&=]+)]""".toRegex()
|
||||
}
|
||||
|
||||
val HTML_INPUT_HIDDEN by lazy {
|
||||
"""<input .*?type="hidden".+?>""".toRegex()
|
||||
}
|
||||
val HTML_INPUT_NAME by lazy {
|
||||
"""name="(.+?)"""".toRegex()
|
||||
}
|
||||
val HTML_INPUT_VALUE by lazy {
|
||||
"""value="(.+?)"""".toRegex()
|
||||
}
|
||||
val HTML_CSRF_TOKEN by lazy {
|
||||
"""name="csrf-token" content="([A-z0-9=+/\-_]+?)"""".toRegex()
|
||||
}
|
||||
val HTML_FORM_ACTION by lazy {
|
||||
"""<form .*?action="(.+?)"""".toRegex()
|
||||
}
|
||||
val HTML_RECAPTCHA_KEY by lazy {
|
||||
"""data-sitekey="(.+?)"""".toRegex()
|
||||
}
|
||||
|
||||
|
||||
|
||||
val MOBIDZIENNIK_GRADES_SUBJECT_NAME by lazy {
|
||||
|
@ -28,6 +28,8 @@ import pl.szczodrzynski.edziennik.data.db.enums.LoginType
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.ext.isBeforeYear
|
||||
import pl.szczodrzynski.edziennik.ext.shouldArchive
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import pl.szczodrzynski.edziennik.utils.managers.AvailabilityManager.Error.Type
|
||||
|
||||
|
@ -52,28 +52,29 @@ class ProfileArchiver(val app: App, val profile: Profile) {
|
||||
|
||||
when (profile.loginStoreType) {
|
||||
LoginType.LIBRUS -> {
|
||||
profile.removeStudentData("isPremium")
|
||||
profile.removeStudentData("pushDeviceId")
|
||||
profile.removeStudentData("startPointsSemester1")
|
||||
profile.removeStudentData("startPointsSemester2")
|
||||
profile.removeStudentData("enablePointGrades")
|
||||
profile.removeStudentData("enableDescriptiveGrades")
|
||||
profile.studentData.remove("isPremium")
|
||||
profile.studentData.remove("pushDeviceId")
|
||||
profile.studentData.remove("startPointsSemester1")
|
||||
profile.studentData.remove("startPointsSemester2")
|
||||
profile.studentData.remove("enablePointGrades")
|
||||
profile.studentData.remove("enableDescriptiveGrades")
|
||||
}
|
||||
LoginType.MOBIDZIENNIK -> {}
|
||||
LoginType.VULCAN -> {
|
||||
// DataVulcan.isApiLoginValid() returns false so it will update the semester
|
||||
profile.removeStudentData("currentSemesterEndDate")
|
||||
profile.removeStudentData("studentSemesterId")
|
||||
profile.removeStudentData("studentSemesterNumber")
|
||||
profile.removeStudentData("semester1Id")
|
||||
profile.removeStudentData("semester2Id")
|
||||
profile.removeStudentData("studentClassId")
|
||||
profile.studentData.remove("currentSemesterEndDate")
|
||||
profile.studentData.remove("studentSemesterId")
|
||||
profile.studentData.remove("studentSemesterNumber")
|
||||
profile.studentData.remove("semester1Id")
|
||||
profile.studentData.remove("semester2Id")
|
||||
profile.studentData.remove("studentClassId")
|
||||
}
|
||||
LoginType.IDZIENNIK -> {
|
||||
profile.removeStudentData("schoolYearId")
|
||||
profile.studentData.remove("schoolYearId")
|
||||
}
|
||||
LoginType.EDUDZIENNIK -> {}
|
||||
LoginType.PODLASIE -> {}
|
||||
LoginType.USOS -> {}
|
||||
LoginType.DEMO -> {}
|
||||
LoginType.TEMPLATE -> {}
|
||||
}
|
||||
|
@ -10,7 +10,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
|
||||
import pl.szczodrzynski.edziennik.ext.currentTimeUnix
|
||||
import pl.szczodrzynski.edziennik.ext.getStudentData
|
||||
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
|
||||
import pl.szczodrzynski.edziennik.ext.set
|
||||
|
||||
class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
|
||||
|
||||
@ -117,7 +119,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mApiLogin: String? = null
|
||||
var apiLogin: String?
|
||||
get() { mApiLogin = mApiLogin ?: profile?.getStudentData("accountLogin", null); return mApiLogin }
|
||||
set(value) { profile?.putStudentData("accountLogin", value); mApiLogin = value }
|
||||
set(value) { profile["accountLogin"] = value; mApiLogin = value }
|
||||
/**
|
||||
* A Synergia password.
|
||||
* Used: for login (API Login Method) in Synergia mode.
|
||||
@ -126,7 +128,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mApiPassword: String? = null
|
||||
var apiPassword: String?
|
||||
get() { mApiPassword = mApiPassword ?: profile?.getStudentData("accountPassword", null); return mApiPassword }
|
||||
set(value) { profile?.putStudentData("accountPassword", value); mApiPassword = value }
|
||||
set(value) { profile["accountPassword"] = value; mApiPassword = value }
|
||||
|
||||
/**
|
||||
* A JST login Code.
|
||||
@ -135,7 +137,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mApiCode: String? = null
|
||||
var apiCode: String?
|
||||
get() { mApiCode = mApiCode ?: loginStore.getLoginData("accountCode", null); return mApiCode }
|
||||
set(value) { profile?.putStudentData("accountCode", value); mApiCode = value }
|
||||
set(value) { profile["accountCode"] = value; mApiCode = value }
|
||||
/**
|
||||
* A JST login PIN.
|
||||
* Used only during first login in JST mode.
|
||||
@ -143,7 +145,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mApiPin: String? = null
|
||||
var apiPin: String?
|
||||
get() { mApiPin = mApiPin ?: loginStore.getLoginData("accountPin", null); return mApiPin }
|
||||
set(value) { profile?.putStudentData("accountPin", value); mApiPin = value }
|
||||
set(value) { profile["accountPin"] = value; mApiPin = value }
|
||||
|
||||
/**
|
||||
* A Synergia API access token.
|
||||
@ -154,7 +156,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mApiAccessToken: String? = null
|
||||
var apiAccessToken: String?
|
||||
get() { mApiAccessToken = mApiAccessToken ?: profile?.getStudentData("accountToken", null); return mApiAccessToken }
|
||||
set(value) { mApiAccessToken = value; profile?.putStudentData("accountToken", value) ?: return; }
|
||||
set(value) { mApiAccessToken = value; profile["accountToken"] = value ?: return; }
|
||||
/**
|
||||
* A Synergia API refresh token.
|
||||
* Used when refreshing the [apiAccessToken] in JST, Synergia modes.
|
||||
@ -162,7 +164,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mApiRefreshToken: String? = null
|
||||
var apiRefreshToken: String?
|
||||
get() { mApiRefreshToken = mApiRefreshToken ?: profile?.getStudentData("accountRefreshToken", null); return mApiRefreshToken }
|
||||
set(value) { mApiRefreshToken = value; profile?.putStudentData("accountRefreshToken", value) ?: return; }
|
||||
set(value) { mApiRefreshToken = value; profile["accountRefreshToken"] = value ?: return; }
|
||||
/**
|
||||
* The expiry time for [apiAccessToken], as a UNIX timestamp.
|
||||
* Used when refreshing the [apiAccessToken] in JST, Synergia modes.
|
||||
@ -171,7 +173,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mApiTokenExpiryTime: Long? = null
|
||||
var apiTokenExpiryTime: Long
|
||||
get() { mApiTokenExpiryTime = mApiTokenExpiryTime ?: profile?.getStudentData("accountTokenTime", 0L); return mApiTokenExpiryTime ?: 0L }
|
||||
set(value) { mApiTokenExpiryTime = value; profile?.putStudentData("accountTokenTime", value) ?: return; }
|
||||
set(value) { mApiTokenExpiryTime = value; profile["accountTokenTime"] = value; }
|
||||
|
||||
/**
|
||||
* A push device ID, generated by Librus when registering
|
||||
@ -181,7 +183,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mPushDeviceId: Int? = null
|
||||
var pushDeviceId: Int
|
||||
get() { mPushDeviceId = mPushDeviceId ?: profile?.getStudentData("pushDeviceId", 0); return mPushDeviceId ?: 0 }
|
||||
set(value) { mPushDeviceId = value; profile?.putStudentData("pushDeviceId", value) ?: return; }
|
||||
set(value) { mPushDeviceId = value; profile["pushDeviceId"] = value; }
|
||||
|
||||
/* _____ _
|
||||
/ ____| (_)
|
||||
@ -198,7 +200,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mSynergiaSessionId: String? = null
|
||||
var synergiaSessionId: String?
|
||||
get() { mSynergiaSessionId = mSynergiaSessionId ?: profile?.getStudentData("accountSID", null); return mSynergiaSessionId }
|
||||
set(value) { profile?.putStudentData("accountSID", value) ?: return; mSynergiaSessionId = value }
|
||||
set(value) { profile["accountSID"] = value; mSynergiaSessionId = value }
|
||||
/**
|
||||
* The expiry time for [synergiaSessionId], as a UNIX timestamp.
|
||||
* Used in endpoints with Synergia login method.
|
||||
@ -207,7 +209,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mSynergiaSessionIdExpiryTime: Long? = null
|
||||
var synergiaSessionIdExpiryTime: Long
|
||||
get() { mSynergiaSessionIdExpiryTime = mSynergiaSessionIdExpiryTime ?: profile?.getStudentData("accountSIDTime", 0L); return mSynergiaSessionIdExpiryTime ?: 0L }
|
||||
set(value) { profile?.putStudentData("accountSIDTime", value) ?: return; mSynergiaSessionIdExpiryTime = value }
|
||||
set(value) { profile["accountSIDTime"] = value; mSynergiaSessionIdExpiryTime = value }
|
||||
|
||||
|
||||
/**
|
||||
@ -217,7 +219,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mMessagesSessionId: String? = null
|
||||
var messagesSessionId: String?
|
||||
get() { mMessagesSessionId = mMessagesSessionId ?: profile?.getStudentData("messagesSID", null); return mMessagesSessionId }
|
||||
set(value) { profile?.putStudentData("messagesSID", value) ?: return; mMessagesSessionId = value }
|
||||
set(value) { profile["messagesSID"] = value; mMessagesSessionId = value }
|
||||
/**
|
||||
* The expiry time for [messagesSessionId], as a UNIX timestamp.
|
||||
* Used in endpoints with Messages login method.
|
||||
@ -226,7 +228,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mMessagesSessionIdExpiryTime: Long? = null
|
||||
var messagesSessionIdExpiryTime: Long
|
||||
get() { mMessagesSessionIdExpiryTime = mMessagesSessionIdExpiryTime ?: profile?.getStudentData("messagesSIDTime", 0L); return mMessagesSessionIdExpiryTime ?: 0L }
|
||||
set(value) { profile?.putStudentData("messagesSIDTime", value) ?: return; mMessagesSessionIdExpiryTime = value }
|
||||
set(value) { profile["messagesSIDTime"] = value; mMessagesSessionIdExpiryTime = value }
|
||||
|
||||
/* ____ _ _
|
||||
/ __ \| | | |
|
||||
@ -236,42 +238,42 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
\____/ \__|_| |_|\___|*/
|
||||
var isPremium
|
||||
get() = profile?.getStudentData("isPremium", false) ?: false
|
||||
set(value) { profile?.putStudentData("isPremium", value) }
|
||||
set(value) { profile["isPremium"] = value }
|
||||
|
||||
private var mSchoolName: String? = null
|
||||
var schoolName: String?
|
||||
get() { mSchoolName = mSchoolName ?: profile?.getStudentData("schoolName", null); return mSchoolName }
|
||||
set(value) { profile?.putStudentData("schoolName", value) ?: return; mSchoolName = value }
|
||||
set(value) { profile["schoolName"] = value; mSchoolName = value }
|
||||
|
||||
private var mUnitId: Long? = null
|
||||
var unitId: Long
|
||||
get() { mUnitId = mUnitId ?: profile?.getStudentData("unitId", 0L); return mUnitId ?: 0L }
|
||||
set(value) { profile?.putStudentData("unitId", value) ?: return; mUnitId = value }
|
||||
set(value) { profile["unitId"] = value; mUnitId = value }
|
||||
|
||||
private var mStartPointsSemester1: Int? = null
|
||||
var startPointsSemester1: Int
|
||||
get() { mStartPointsSemester1 = mStartPointsSemester1 ?: profile?.getStudentData("startPointsSemester1", 0); return mStartPointsSemester1 ?: 0 }
|
||||
set(value) { profile?.putStudentData("startPointsSemester1", value) ?: return; mStartPointsSemester1 = value }
|
||||
set(value) { profile["startPointsSemester1"] = value; mStartPointsSemester1 = value }
|
||||
|
||||
private var mStartPointsSemester2: Int? = null
|
||||
var startPointsSemester2: Int
|
||||
get() { mStartPointsSemester2 = mStartPointsSemester2 ?: profile?.getStudentData("startPointsSemester2", 0); return mStartPointsSemester2 ?: 0 }
|
||||
set(value) { profile?.putStudentData("startPointsSemester2", value) ?: return; mStartPointsSemester2 = value }
|
||||
set(value) { profile["startPointsSemester2"] = value; mStartPointsSemester2 = value }
|
||||
|
||||
private var mEnablePointGrades: Boolean? = null
|
||||
var enablePointGrades: Boolean
|
||||
get() { mEnablePointGrades = mEnablePointGrades ?: profile?.getStudentData("enablePointGrades", true); return mEnablePointGrades ?: true }
|
||||
set(value) { profile?.putStudentData("enablePointGrades", value) ?: return; mEnablePointGrades = value }
|
||||
set(value) { profile["enablePointGrades"] = value; mEnablePointGrades = value }
|
||||
|
||||
private var mEnableDescriptiveGrades: Boolean? = null
|
||||
var enableDescriptiveGrades: Boolean
|
||||
get() { mEnableDescriptiveGrades = mEnableDescriptiveGrades ?: profile?.getStudentData("enableDescriptiveGrades", true); return mEnableDescriptiveGrades ?: true }
|
||||
set(value) { profile?.putStudentData("enableDescriptiveGrades", value) ?: return; mEnableDescriptiveGrades = value }
|
||||
set(value) { profile["enableDescriptiveGrades"] = value; mEnableDescriptiveGrades = value }
|
||||
|
||||
private var mTimetableNotPublic: Boolean? = null
|
||||
var timetableNotPublic: Boolean
|
||||
get() { mTimetableNotPublic = mTimetableNotPublic ?: profile?.getStudentData("timetableNotPublic", false); return mTimetableNotPublic ?: false }
|
||||
set(value) { profile?.putStudentData("timetableNotPublic", value) ?: return; mTimetableNotPublic = value }
|
||||
set(value) { profile["timetableNotPublic"] = value; mTimetableNotPublic = value }
|
||||
|
||||
/**
|
||||
* Set to false when Recaptcha helper doesn't provide a working token.
|
||||
|
@ -36,7 +36,7 @@ class LibrusApiNotices(override val data: DataLibrus,
|
||||
val id = note.getLong("Id") ?: return@forEach
|
||||
val text = note.getString("Text") ?: ""
|
||||
val categoryId = note.getJsonObject("Category")?.getLong("Id") ?: -1
|
||||
val teacherId = note.getJsonObject("AddedBy")?.getLong("Id") ?: -1
|
||||
val teacherId = note.getJsonObject("Teacher")?.getLong("Id") ?: -1
|
||||
val addedDate = note.getString("Date")?.let { Date.fromY_m_d(it) } ?: return@forEach
|
||||
|
||||
val type = when (note.getInt("Positive")) {
|
||||
|
@ -190,6 +190,7 @@ class LibrusApiTimetables(override val data: DataLibrus,
|
||||
}
|
||||
|
||||
lessonObject.id = lessonObject.buildId()
|
||||
lessonObject.ownerId = lessonObject.buildOwnerId()
|
||||
|
||||
val seen = profile.empty || lessonDate < Date.getToday()
|
||||
|
||||
|
@ -125,7 +125,7 @@ class LibrusMessagesGetMessage(override val data: DataLibrus,
|
||||
val receiverId = teacher?.id ?: -1
|
||||
teacher?.loginId = receiverLoginId
|
||||
|
||||
val readDateText = message.select("readed").text()
|
||||
val readDateText = receiver.select("readed").text()
|
||||
val readDate = when (readDateText.isNotNullNorEmpty()) {
|
||||
true -> Date.fromIso(readDateText)
|
||||
else -> 0
|
||||
|
@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
|
||||
import pl.szczodrzynski.edziennik.ext.HOUR
|
||||
import pl.szczodrzynski.edziennik.ext.get
|
||||
import pl.szczodrzynski.edziennik.ext.getSemesterStart
|
||||
import pl.szczodrzynski.edziennik.ext.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
|
@ -24,6 +24,9 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
private const val TAG = "LoginLibrusPortal"
|
||||
}
|
||||
|
||||
// loop failsafe
|
||||
private var loginPerformed = false
|
||||
|
||||
init { run {
|
||||
if (data.loginStore.mode != LoginMode.LIBRUS_EMAIL) {
|
||||
data.error(ApiError(TAG, ERROR_INVALID_LOGIN_MODE))
|
||||
@ -33,6 +36,7 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING))
|
||||
return@run
|
||||
}
|
||||
loginPerformed = false
|
||||
|
||||
// succeed having a non-expired access token and a refresh token
|
||||
if (data.isPortalLoginValid()) {
|
||||
@ -58,18 +62,23 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
}
|
||||
}}
|
||||
|
||||
private fun authorize(url: String?) {
|
||||
private fun authorize(url: String, referer: String? = null) {
|
||||
d(TAG, "Request: Librus/Login/Portal - $url")
|
||||
|
||||
Request.builder()
|
||||
.url(url)
|
||||
.userAgent(LIBRUS_USER_AGENT)
|
||||
.also {
|
||||
if (referer != null)
|
||||
it.addHeader("Referer", referer)
|
||||
}
|
||||
.addHeader("X-Requested-With", LIBRUS_HEADER)
|
||||
.withClient(data.app.httpLazy)
|
||||
.callback(object : TextCallbackHandler() {
|
||||
override fun onSuccess(text: String, response: Response) {
|
||||
val location = response.headers().get("Location")
|
||||
if (location != null) {
|
||||
val authMatcher = Pattern.compile("$LIBRUS_REDIRECT_URL\\?code=([A-z0-9]+?)$", Pattern.DOTALL or Pattern.MULTILINE).matcher(location)
|
||||
val authMatcher = Pattern.compile("$LIBRUS_REDIRECT_URL\\?code=([^&?]+)", Pattern.DOTALL or Pattern.MULTILINE).matcher(location)
|
||||
when {
|
||||
authMatcher.find() -> {
|
||||
accessToken(authMatcher.group(1), null)
|
||||
@ -83,16 +92,31 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
authorize(location)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val csrfMatcher = Pattern.compile("name=\"csrf-token\" content=\"([A-z0-9=+/\\-_]+?)\"", Pattern.DOTALL).matcher(text)
|
||||
if (csrfMatcher.find()) {
|
||||
login(csrfMatcher.group(1) ?: "")
|
||||
} else {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING)
|
||||
.withResponse(response)
|
||||
.withApiResponse(text))
|
||||
return
|
||||
}
|
||||
|
||||
if (checkError(text, response))
|
||||
return
|
||||
|
||||
var loginUrl = if (data.fakeLogin) FAKE_LIBRUS_LOGIN else LIBRUS_LOGIN_URL
|
||||
val csrfToken = Regexes.HTML_CSRF_TOKEN.find(text)?.get(1) ?: ""
|
||||
|
||||
for (match in Regexes.HTML_FORM_ACTION.findAll(text)) {
|
||||
val form = match.value.lowercase()
|
||||
if ("login" in form && "post" in form) {
|
||||
loginUrl = match[1]
|
||||
}
|
||||
}
|
||||
|
||||
val params = mutableMapOf<String, String>()
|
||||
for (match in Regexes.HTML_INPUT_HIDDEN.findAll(text)) {
|
||||
val input = match.value
|
||||
val name = Regexes.HTML_INPUT_NAME.find(input)?.get(1) ?: continue
|
||||
val value = Regexes.HTML_INPUT_VALUE.find(input)?.get(1) ?: continue
|
||||
params[name] = value
|
||||
}
|
||||
|
||||
login(url = loginUrl, referer = url, csrfToken, params)
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response, throwable: Throwable) {
|
||||
@ -105,8 +129,54 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
.enqueue()
|
||||
}
|
||||
|
||||
private fun login(csrfToken: String) {
|
||||
d(TAG, "Request: Librus/Login/Portal - ${if (data.fakeLogin) FAKE_LIBRUS_LOGIN else LIBRUS_LOGIN_URL}")
|
||||
private fun checkError(text: String, response: Response): Boolean {
|
||||
when {
|
||||
text.contains("librus_account_settings_main") -> return false
|
||||
text.contains("Sesja logowania wygasła") -> ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED
|
||||
text.contains("Upewnij się, że nie") -> ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN
|
||||
text.contains("Podany adres e-mail jest nieprawidłowy.") -> ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN
|
||||
else -> null // no error for now
|
||||
}?.let { errorCode ->
|
||||
data.error(ApiError(TAG, errorCode)
|
||||
.withApiResponse(text)
|
||||
.withResponse(response))
|
||||
return true
|
||||
}
|
||||
|
||||
if ("robotem" in text || "g-recaptcha" in text || "captchaValidate" in text) {
|
||||
val siteKey = Regexes.HTML_RECAPTCHA_KEY.find(text)?.get(1)
|
||||
if (siteKey == null) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR)
|
||||
.withApiResponse(text)
|
||||
.withResponse(response))
|
||||
return true
|
||||
}
|
||||
data.requireUserAction(
|
||||
type = UserActionRequiredEvent.Type.RECAPTCHA,
|
||||
params = Bundle(
|
||||
"siteKey" to siteKey,
|
||||
"referer" to response.request().url().toString(),
|
||||
"userAgent" to LIBRUS_USER_AGENT,
|
||||
),
|
||||
errorText = R.string.notification_user_action_required_captcha_librus,
|
||||
)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun login(
|
||||
url: String,
|
||||
referer: String,
|
||||
csrfToken: String?,
|
||||
params: Map<String, String>,
|
||||
) {
|
||||
if (loginPerformed) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR))
|
||||
return
|
||||
}
|
||||
|
||||
d(TAG, "Request: Librus/Login/Portal - $url")
|
||||
|
||||
val recaptchaCode = data.arguments?.getString("recaptchaCode") ?: data.loginStore.getLoginData("recaptchaCode", null)
|
||||
val recaptchaTime = data.arguments?.getLong("recaptchaTime") ?: data.loginStore.getLoginData("recaptchaTime", 0L)
|
||||
@ -116,67 +186,46 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
Request.builder()
|
||||
.url(if (data.fakeLogin) FAKE_LIBRUS_LOGIN else LIBRUS_LOGIN_URL)
|
||||
.userAgent(LIBRUS_USER_AGENT)
|
||||
.addHeader("X-Requested-With", LIBRUS_HEADER)
|
||||
.addHeader("Referer", referer)
|
||||
.withClient(data.app.httpLazy)
|
||||
.addParameter("email", data.portalEmail)
|
||||
.addParameter("password", data.portalPassword)
|
||||
.also {
|
||||
if (recaptchaCode != null && System.currentTimeMillis() - recaptchaTime < 2*60*1000 /* 2 minutes */)
|
||||
it.addParameter("g-recaptcha-response", recaptchaCode)
|
||||
if (csrfToken != null)
|
||||
it.addHeader("X-CSRF-TOKEN", csrfToken)
|
||||
for ((key, value) in params) {
|
||||
it.addParameter(key, value)
|
||||
}
|
||||
}
|
||||
.addHeader("X-CSRF-TOKEN", csrfToken)
|
||||
.allowErrorCode(HTTP_BAD_REQUEST)
|
||||
.allowErrorCode(HTTP_FORBIDDEN)
|
||||
.contentType(MediaTypeUtils.APPLICATION_JSON)
|
||||
.contentType(MediaTypeUtils.APPLICATION_FORM)
|
||||
.post()
|
||||
.callback(object : JsonCallbackHandler() {
|
||||
override fun onSuccess(json: JsonObject?, response: Response) {
|
||||
.callback(object : TextCallbackHandler() {
|
||||
override fun onSuccess(text: String?, response: Response) {
|
||||
loginPerformed = true
|
||||
val location = response.headers()?.get("Location")
|
||||
if (location == "$LIBRUS_REDIRECT_URL?command=close") {
|
||||
data.error(ApiError(TAG, ERROR_LIBRUS_PORTAL_MAINTENANCE)
|
||||
.withApiResponse(json)
|
||||
.withApiResponse(text)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
if (json == null) {
|
||||
if (response.parserErrorBody?.contains("wciąż nieaktywne") == true) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
if (text == null) {
|
||||
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
val error = if (response.code() == 200) null else
|
||||
json.getJsonArray("errors")?.getString(0)
|
||||
?: json.getJsonObject("errors")?.entrySet()?.firstOrNull()?.value?.asString
|
||||
|
||||
if (error?.contains("robotem") == true || json.getBoolean("captchaRequired") == true) {
|
||||
data.requireUserAction(
|
||||
type = UserActionRequiredEvent.Type.RECAPTCHA,
|
||||
params = Bundle(
|
||||
"siteKey" to LIBRUS_PORTAL_RECAPTCHA_KEY,
|
||||
"referer" to LIBRUS_PORTAL_RECAPTCHA_REFERER,
|
||||
),
|
||||
errorText = R.string.notification_user_action_required_captcha_librus,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
error?.let { code ->
|
||||
when {
|
||||
code.contains("Sesja logowania wygasła") -> ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED
|
||||
code.contains("Upewnij się, że nie") -> ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN
|
||||
code.contains("Podany adres e-mail jest nieprawidłowy.") -> ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN
|
||||
else -> ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR
|
||||
}.let { errorCode ->
|
||||
data.error(ApiError(TAG, errorCode)
|
||||
.withApiResponse(json)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
}
|
||||
authorize(json.getString("redirect", LIBRUS_AUTHORIZE_URL))
|
||||
authorize(
|
||||
url = location
|
||||
?: if (data.fakeLogin)
|
||||
FAKE_LIBRUS_AUTHORIZE
|
||||
else
|
||||
LIBRUS_AUTHORIZE_URL,
|
||||
referer = referer,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response, throwable: Throwable) {
|
||||
|
@ -11,7 +11,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
|
||||
import pl.szczodrzynski.edziennik.ext.currentTimeUnix
|
||||
import pl.szczodrzynski.edziennik.ext.getStudentData
|
||||
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
|
||||
import pl.szczodrzynski.edziennik.ext.set
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
@ -85,7 +87,7 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
|
||||
private var mStudentId: Int? = null
|
||||
var studentId: Int
|
||||
get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", 0); return mStudentId ?: 0 }
|
||||
set(value) { profile?.putStudentData("studentId", value) ?: return; mStudentId = value }
|
||||
set(value) { profile["studentId"] = value; mStudentId = value }
|
||||
|
||||
/* __ __ _
|
||||
\ \ / / | |
|
||||
@ -125,7 +127,7 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
|
||||
*/
|
||||
var globalId: String?
|
||||
get() { mGlobalId = mGlobalId ?: profile?.getStudentData("globalId", null); return mGlobalId }
|
||||
set(value) { profile?.putStudentData("globalId", value) ?: return; mGlobalId = value }
|
||||
set(value) { profile["globalId"] = value; mGlobalId = value }
|
||||
private var mGlobalId: String? = null
|
||||
|
||||
/**
|
||||
@ -135,7 +137,7 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
|
||||
*/
|
||||
var loginEmail: String?
|
||||
get() { mLoginEmail = mLoginEmail ?: profile?.getStudentData("email", null); return mLoginEmail }
|
||||
set(value) { profile?.putStudentData("email", value); mLoginEmail = value }
|
||||
set(value) { profile["email"] = value; mLoginEmail = value }
|
||||
private var mLoginEmail: String? = null
|
||||
|
||||
/**
|
||||
@ -144,7 +146,7 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
|
||||
*/
|
||||
var loginId: String?
|
||||
get() { mLoginId = mLoginId ?: profile?.getStudentData("loginId", null); return mLoginId }
|
||||
set(value) { profile?.putStudentData("loginId", value) ?: return; mLoginId = value }
|
||||
set(value) { profile["loginId"] = value; mLoginId = value }
|
||||
private var mLoginId: String? = null
|
||||
|
||||
/**
|
||||
@ -152,7 +154,7 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
|
||||
*/
|
||||
var ciasteczkoAutoryzacji: String?
|
||||
get() { mCiasteczkoAutoryzacji = mCiasteczkoAutoryzacji ?: profile?.getStudentData("ciasteczkoAutoryzacji", null); return mCiasteczkoAutoryzacji }
|
||||
set(value) { profile?.putStudentData("ciasteczkoAutoryzacji", value) ?: return; mCiasteczkoAutoryzacji = value }
|
||||
set(value) { profile["ciasteczkoAutoryzacji"] = value; mCiasteczkoAutoryzacji = value }
|
||||
private var mCiasteczkoAutoryzacji: String? = null
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_RELEASED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
|
||||
import pl.szczodrzynski.edziennik.ext.dateToSemester
|
||||
|
||||
class MobidziennikApiAttendance(val data: DataMobidziennik, rows: List<String>) {
|
||||
init { run {
|
||||
|
@ -88,6 +88,7 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
}
|
||||
|
||||
it.id = it.buildId()
|
||||
it.ownerId = it.buildOwnerId()
|
||||
|
||||
val seen = profile.empty || date < Date.getToday()
|
||||
|
||||
|
@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBID
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
|
||||
import pl.szczodrzynski.edziennik.ext.DAY
|
||||
import pl.szczodrzynski.edziennik.ext.get
|
||||
import pl.szczodrzynski.edziennik.ext.isNotNullNorBlank
|
||||
|
||||
class MobidziennikWebAccountEmail(override val data: DataMobidziennik,
|
||||
override val lastSync: Long?,
|
||||
@ -24,7 +25,8 @@ class MobidziennikWebAccountEmail(override val data: DataMobidziennik,
|
||||
MobidziennikLuckyNumberExtractor(data, text)
|
||||
|
||||
val email = Regexes.MOBIDZIENNIK_ACCOUNT_EMAIL.find(text)?.let { it[1] }
|
||||
data.loginEmail = email
|
||||
if (email.isNotNullNorBlank())
|
||||
data.loginEmail = email
|
||||
|
||||
data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL, if (email == null) 3* DAY else 7* DAY)
|
||||
onSuccess(ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL)
|
||||
|
@ -21,6 +21,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_UNKNO
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
|
||||
import pl.szczodrzynski.edziennik.ext.dateToSemester
|
||||
import pl.szczodrzynski.edziennik.ext.fixName
|
||||
import pl.szczodrzynski.edziennik.ext.get
|
||||
import pl.szczodrzynski.edziennik.ext.singleOrNull
|
||||
|
@ -15,6 +15,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
|
||||
import pl.szczodrzynski.edziennik.ext.dateToSemester
|
||||
import pl.szczodrzynski.edziennik.ext.fixWhiteSpaces
|
||||
import pl.szczodrzynski.edziennik.ext.get
|
||||
import pl.szczodrzynski.edziennik.ext.singleOrNull
|
||||
|
@ -336,6 +336,7 @@ class MobidziennikWebTimetable(
|
||||
}
|
||||
|
||||
it.id = it.buildId()
|
||||
it.ownerId = it.buildOwnerId()
|
||||
it.isExtra = isExtra
|
||||
|
||||
val seen = profile?.empty == false || lessonDate < Date.getToday()
|
||||
|
@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.ext.JsonObject
|
||||
import pl.szczodrzynski.edziennik.ext.getJsonObject
|
||||
import pl.szczodrzynski.edziennik.ext.getString
|
||||
import pl.szczodrzynski.edziennik.ext.isNotNullNorBlank
|
||||
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
|
||||
@ -77,7 +78,9 @@ class MobidziennikLoginApi2(val data: DataMobidziennik, val onSuccess: () -> Uni
|
||||
}
|
||||
}
|
||||
|
||||
data.loginEmail = json.getString("email")
|
||||
val email = json.getString("email")
|
||||
if (email.isNotNullNorBlank())
|
||||
data.loginEmail = email
|
||||
data.globalId = json.getString("id_global")
|
||||
data.loginId = json.getString("login")
|
||||
onSuccess()
|
||||
|
@ -10,7 +10,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
|
||||
import pl.szczodrzynski.edziennik.ext.crc32
|
||||
import pl.szczodrzynski.edziennik.ext.getStudentData
|
||||
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
|
||||
import pl.szczodrzynski.edziennik.ext.set
|
||||
|
||||
class DataPodlasie(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
|
||||
|
||||
@ -40,7 +42,7 @@ class DataPodlasie(app: App, profile: Profile?, loginStore: LoginStore) : Data(a
|
||||
private var mApiUrl: String? = null
|
||||
var apiUrl: String?
|
||||
get() { mApiUrl = mApiUrl ?: profile?.getStudentData("apiUrl", null); return mApiUrl }
|
||||
set(value) { profile?.putStudentData("apiUrl", value) ?: return; mApiUrl = value }
|
||||
set(value) { profile["apiUrl"] = value; mApiUrl = value }
|
||||
|
||||
/* ____ _ _
|
||||
/ __ \| | | |
|
||||
@ -51,32 +53,32 @@ class DataPodlasie(app: App, profile: Profile?, loginStore: LoginStore) : Data(a
|
||||
private var mStudentId: String? = null
|
||||
var studentId: String?
|
||||
get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", null); return mStudentId }
|
||||
set(value) { profile?.putStudentData("studentId", value) ?: return; mStudentId = value }
|
||||
set(value) { profile["studentId"] = value; mStudentId = value }
|
||||
|
||||
private var mStudentLogin: String? = null
|
||||
var studentLogin: String?
|
||||
get() { mStudentLogin = mStudentLogin ?: profile?.getStudentData("studentLogin", null); return mStudentLogin }
|
||||
set(value) { profile?.putStudentData("studentLogin", value) ?: return; mStudentLogin = value }
|
||||
set(value) { profile["studentLogin"] = value; mStudentLogin = value }
|
||||
|
||||
private var mSchoolName: String? = null
|
||||
var schoolName: String?
|
||||
get() { mSchoolName = mSchoolName ?: profile?.getStudentData("schoolName", null); return mSchoolName }
|
||||
set(value) { profile?.putStudentData("schoolName", value) ?: return; mSchoolName = value }
|
||||
set(value) { profile["schoolName"] = value; mSchoolName = value }
|
||||
|
||||
private var mClassName: String? = null
|
||||
var className: String?
|
||||
get() { mClassName = mClassName ?: profile?.getStudentData("className", null); return mClassName }
|
||||
set(value) { profile?.putStudentData("className", value) ?: return; mClassName = value }
|
||||
set(value) { profile["className"] = value; mClassName = value }
|
||||
|
||||
private var mSchoolYear: String? = null
|
||||
var schoolYear: String?
|
||||
get() { mSchoolYear = mSchoolYear ?: profile?.getStudentData("schoolYear", null); return mSchoolYear }
|
||||
set(value) { profile?.putStudentData("schoolYear", value) ?: return; mSchoolYear = value }
|
||||
set(value) { profile["schoolYear"] = value; mSchoolYear = value }
|
||||
|
||||
private var mCurrentSemester: Int? = null
|
||||
var currentSemester: Int
|
||||
get() { mCurrentSemester = mCurrentSemester ?: profile?.getStudentData("currentSemester", 0); return mCurrentSemester ?: 0 }
|
||||
set(value) { profile?.putStudentData("currentSemester", value) ?: return; mCurrentSemester = value }
|
||||
set(value) { profile["currentSemester"] = value; mCurrentSemester = value }
|
||||
|
||||
val schoolShortName: String?
|
||||
get() = studentLogin?.split('@')?.get(1)?.replace(".podlaskie.pl", "")
|
||||
|
@ -17,6 +17,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPO
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
|
||||
import pl.szczodrzynski.edziennik.ext.getLong
|
||||
import pl.szczodrzynski.edziennik.ext.getSemesterStart
|
||||
import pl.szczodrzynski.edziennik.ext.getString
|
||||
|
||||
class PodlasieApiFinalGrades(val data: DataPodlasie, val rows: List<JsonObject>) {
|
||||
|
@ -72,6 +72,7 @@ class PodlasieApiTimetable(val data: DataPodlasie, rows: List<JsonObject>) {
|
||||
it.classroom = classroom
|
||||
|
||||
it.id = it.buildId()
|
||||
it.ownerId = it.buildOwnerId()
|
||||
data.lessonList += it
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
|
||||
import pl.szczodrzynski.edziennik.ext.currentTimeUnix
|
||||
import pl.szczodrzynski.edziennik.ext.getStudentData
|
||||
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
|
||||
import pl.szczodrzynski.edziennik.ext.set
|
||||
|
||||
/**
|
||||
* Use http://patorjk.com/software/taag/#p=display&f=Big for the ascii art
|
||||
@ -43,12 +45,12 @@ class DataTemplate(app: App, profile: Profile?, loginStore: LoginStore) : Data(a
|
||||
private var mWebCookie: String? = null
|
||||
var webCookie: String?
|
||||
get() { mWebCookie = mWebCookie ?: profile?.getStudentData("webCookie", null); return mWebCookie }
|
||||
set(value) { profile?.putStudentData("webCookie", value) ?: return; mWebCookie = value }
|
||||
set(value) { profile["webCookie"] = value; mWebCookie = value }
|
||||
|
||||
private var mWebExpiryTime: Long? = null
|
||||
var webExpiryTime: Long
|
||||
get() { mWebExpiryTime = mWebExpiryTime ?: profile?.getStudentData("webExpiryTime", 0L); return mWebExpiryTime ?: 0L }
|
||||
set(value) { profile?.putStudentData("webExpiryTime", value) ?: return; mWebExpiryTime = value }
|
||||
set(value) { profile["webExpiryTime"] = value; mWebExpiryTime = value }
|
||||
|
||||
/* _
|
||||
/\ (_)
|
||||
@ -61,10 +63,10 @@ class DataTemplate(app: App, profile: Profile?, loginStore: LoginStore) : Data(a
|
||||
private var mApiToken: String? = null
|
||||
var apiToken: String?
|
||||
get() { mApiToken = mApiToken ?: profile?.getStudentData("apiToken", null); return mApiToken }
|
||||
set(value) { profile?.putStudentData("apiToken", value) ?: return; mApiToken = value }
|
||||
set(value) { profile["apiToken"] = value; mApiToken = value }
|
||||
|
||||
private var mApiExpiryTime: Long? = null
|
||||
var apiExpiryTime: Long
|
||||
get() { mApiExpiryTime = mApiExpiryTime ?: profile?.getStudentData("apiExpiryTime", 0L); return mApiExpiryTime ?: 0L }
|
||||
set(value) { profile?.putStudentData("apiExpiryTime", value) ?: return; mApiExpiryTime = value }
|
||||
set(value) { profile["apiExpiryTime"] = value; mApiExpiryTime = value }
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ 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.data.db.enums.LoginMethod
|
||||
import pl.szczodrzynski.edziennik.ext.getStudentData
|
||||
import pl.szczodrzynski.edziennik.ext.set
|
||||
|
||||
class DataUsos(
|
||||
app: App,
|
||||
@ -69,6 +71,6 @@ class DataUsos(
|
||||
|
||||
var studentId: Int
|
||||
get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", 0); return mStudentId ?: 0 }
|
||||
set(value) { profile?.putStudentData("studentId", value) ?: return; mStudentId = value }
|
||||
set(value) { profile["studentId"] = value; mStudentId = value }
|
||||
private var mStudentId: Int? = null
|
||||
}
|
||||
|
@ -41,24 +41,18 @@ class UsosApiTerms(
|
||||
}
|
||||
|
||||
private fun processResponse(json: JsonArray): Boolean {
|
||||
val dates = mutableSetOf<Date>()
|
||||
val today = Date.getToday()
|
||||
for (term in json.asJsonObjectList()) {
|
||||
if (!term.getBoolean("is_active", false))
|
||||
continue
|
||||
val startDate = term.getString("start_date")?.let { Date.fromY_m_d(it) }
|
||||
val finishDate = term.getString("finish_date")?.let { Date.fromY_m_d(it) }
|
||||
if (startDate != null)
|
||||
dates += startDate
|
||||
if (finishDate != null)
|
||||
dates += finishDate
|
||||
val startDate = term.getString("start_date")?.let { Date.fromY_m_d(it) } ?: continue
|
||||
val finishDate = term.getString("finish_date")?.let { Date.fromY_m_d(it) } ?: continue
|
||||
if (today in startDate..finishDate) {
|
||||
profile?.studentSchoolYearStart = startDate.year
|
||||
profile?.dateSemester1Start = startDate
|
||||
profile?.dateSemester2Start = finishDate
|
||||
}
|
||||
}
|
||||
val datesSorted = dates.sorted()
|
||||
if (datesSorted.size != 3)
|
||||
return false
|
||||
profile?.studentSchoolYearStart = datesSorted[0].year
|
||||
profile?.dateSemester1Start = datesSorted[0]
|
||||
profile?.dateSemester2Start = datesSorted[1]
|
||||
profile?.dateYearEnd = datesSorted[2]
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -104,8 +104,9 @@ class UsosApiTimetable(
|
||||
it.teacherId = lecturerIds?.firstOrNull() ?: -1L
|
||||
it.teamId = unitId
|
||||
val groupName = classTypeId?.plus(groupNumber)?.let { s -> "($s)" }
|
||||
it.classroom = "$buildingId / $roomNumber ${groupName ?: ""}"
|
||||
it.classroom = "Sala $roomNumber / bud. $buildingId ${groupName ?: ""}"
|
||||
it.id = it.buildId()
|
||||
it.ownerId = it.buildOwnerId()
|
||||
|
||||
it.color = when (classTypeId) {
|
||||
"WYK" -> 0xff0d6091
|
||||
|
@ -12,7 +12,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
|
||||
import pl.szczodrzynski.edziennik.ext.crc16
|
||||
import pl.szczodrzynski.edziennik.ext.currentTimeUnix
|
||||
import pl.szczodrzynski.edziennik.ext.getStudentData
|
||||
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
|
||||
import pl.szczodrzynski.edziennik.ext.set
|
||||
import pl.szczodrzynski.fslogin.realm.RealmData
|
||||
|
||||
class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
|
||||
@ -57,7 +59,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mSymbol: String? = null
|
||||
var symbol: String?
|
||||
get() { mSymbol = mSymbol ?: profile?.getStudentData("symbol", null); return mSymbol }
|
||||
set(value) { profile?.putStudentData("symbol", value); mSymbol = value }
|
||||
set(value) { profile["symbol"] = value; mSymbol = value }
|
||||
|
||||
/**
|
||||
* Group symbol/number of the student's school.
|
||||
@ -69,7 +71,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mSchoolSymbol: String? = null
|
||||
var schoolSymbol: String?
|
||||
get() { mSchoolSymbol = mSchoolSymbol ?: profile?.getStudentData("schoolSymbol", null); return mSchoolSymbol }
|
||||
set(value) { profile?.putStudentData("schoolSymbol", value) ?: return; mSchoolSymbol = value }
|
||||
set(value) { profile["schoolSymbol"] = value; mSchoolSymbol = value }
|
||||
|
||||
/**
|
||||
* Short name of the school, used in some places.
|
||||
@ -79,7 +81,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mSchoolShort: String? = null
|
||||
var schoolShort: String?
|
||||
get() { mSchoolShort = mSchoolShort ?: profile?.getStudentData("schoolShort", null); return mSchoolShort }
|
||||
set(value) { profile?.putStudentData("schoolShort", value) ?: return; mSchoolShort = value }
|
||||
set(value) { profile["schoolShort"] = value; mSchoolShort = value }
|
||||
|
||||
/**
|
||||
* A school code consisting of the [symbol] and [schoolSymbol].
|
||||
@ -91,7 +93,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mSchoolCode: String? = null
|
||||
var schoolCode: String?
|
||||
get() { mSchoolCode = mSchoolCode ?: profile?.getStudentData("schoolName", null); return mSchoolCode }
|
||||
set(value) { profile?.putStudentData("schoolName", value) ?: return; mSchoolCode = value }
|
||||
set(value) { profile["schoolName"] = value; mSchoolCode = value }
|
||||
|
||||
/**
|
||||
* ID of the student.
|
||||
@ -101,7 +103,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mStudentId: Int? = null
|
||||
var studentId: Int
|
||||
get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", 0); return mStudentId ?: 0 }
|
||||
set(value) { profile?.putStudentData("studentId", value) ?: return; mStudentId = value }
|
||||
set(value) { profile["studentId"] = value; mStudentId = value }
|
||||
|
||||
/**
|
||||
* ID of the student's account.
|
||||
@ -111,7 +113,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mStudentLoginId: Int? = null
|
||||
var studentLoginId: Int
|
||||
get() { mStudentLoginId = mStudentLoginId ?: profile?.getStudentData("studentLoginId", 0); return mStudentLoginId ?: 0 }
|
||||
set(value) { profile?.putStudentData("studentLoginId", value) ?: return; mStudentLoginId = value }
|
||||
set(value) { profile["studentLoginId"] = value; mStudentLoginId = value }
|
||||
|
||||
/**
|
||||
* ID of the student's class.
|
||||
@ -121,7 +123,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mStudentClassId: Int? = null
|
||||
var studentClassId: Int
|
||||
get() { mStudentClassId = mStudentClassId ?: profile?.getStudentData("studentClassId", 0); return mStudentClassId ?: 0 }
|
||||
set(value) { profile?.putStudentData("studentClassId", value) ?: return; mStudentClassId = value }
|
||||
set(value) { profile["studentClassId"] = value; mStudentClassId = value }
|
||||
|
||||
/**
|
||||
* ListaUczniow/IdOkresKlasyfikacyjny, e.g. 321
|
||||
@ -129,26 +131,26 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mStudentSemesterId: Int? = null
|
||||
var studentSemesterId: Int
|
||||
get() { mStudentSemesterId = mStudentSemesterId ?: profile?.getStudentData("studentSemesterId", 0); return mStudentSemesterId ?: 0 }
|
||||
set(value) { profile?.putStudentData("studentSemesterId", value) ?: return; mStudentSemesterId = value }
|
||||
set(value) { profile["studentSemesterId"] = value; mStudentSemesterId = value }
|
||||
|
||||
private var mStudentUnitId: Int? = null
|
||||
var studentUnitId: Int
|
||||
get() { mStudentUnitId = mStudentUnitId ?: profile?.getStudentData("studentUnitId", 0); return mStudentUnitId ?: 0 }
|
||||
set(value) { profile?.putStudentData("studentUnitId", value) ?: return; mStudentUnitId = value }
|
||||
set(value) { profile["studentUnitId"] = value; mStudentUnitId = value }
|
||||
|
||||
private var mStudentConstituentId: Int? = null
|
||||
var studentConstituentId: Int
|
||||
get() { mStudentConstituentId = mStudentConstituentId ?: profile?.getStudentData("studentConstituentId", 0); return mStudentConstituentId ?: 0 }
|
||||
set(value) { profile?.putStudentData("studentConstituentId", value) ?: return; mStudentConstituentId = value }
|
||||
set(value) { profile["studentConstituentId"] = value; mStudentConstituentId = value }
|
||||
|
||||
private var mSemester1Id: Int? = null
|
||||
var semester1Id: Int
|
||||
get() { mSemester1Id = mSemester1Id ?: profile?.getStudentData("semester1Id", 0); return mSemester1Id ?: 0 }
|
||||
set(value) { profile?.putStudentData("semester1Id", value) ?: return; mSemester1Id = value }
|
||||
set(value) { profile["semester1Id"] = value; mSemester1Id = value }
|
||||
private var mSemester2Id: Int? = null
|
||||
var semester2Id: Int
|
||||
get() { mSemester2Id = mSemester2Id ?: profile?.getStudentData("semester2Id", 0); return mSemester2Id ?: 0 }
|
||||
set(value) { profile?.putStudentData("semester2Id", value) ?: return; mSemester2Id = value }
|
||||
set(value) { profile["semester2Id"] = value; mSemester2Id = value }
|
||||
|
||||
/**
|
||||
* ListaUczniow/OkresNumer, e.g. 1 or 2
|
||||
@ -156,7 +158,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mStudentSemesterNumber: Int? = null
|
||||
var studentSemesterNumber: Int
|
||||
get() { mStudentSemesterNumber = mStudentSemesterNumber ?: profile?.getStudentData("studentSemesterNumber", 0); return mStudentSemesterNumber ?: 0 }
|
||||
set(value) { profile?.putStudentData("studentSemesterNumber", value) ?: return; mStudentSemesterNumber = value }
|
||||
set(value) { profile["studentSemesterNumber"] = value; mStudentSemesterNumber = value }
|
||||
|
||||
/**
|
||||
* Date of the end of the current semester ([studentSemesterNumber]).
|
||||
@ -166,7 +168,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mCurrentSemesterEndDate: Long? = null
|
||||
var currentSemesterEndDate: Long
|
||||
get() { mCurrentSemesterEndDate = mCurrentSemesterEndDate ?: profile?.getStudentData("currentSemesterEndDate", 0L); return mCurrentSemesterEndDate ?: 0L }
|
||||
set(value) { profile?.putStudentData("currentSemesterEndDate", value) ?: return; mCurrentSemesterEndDate = value }
|
||||
set(value) { profile["currentSemesterEndDate"] = value; mCurrentSemesterEndDate = value }
|
||||
|
||||
/* _____ _____ ____
|
||||
/\ | __ \_ _| |___ \
|
||||
@ -219,17 +221,17 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
private var mHebeContext: String? = null
|
||||
var hebeContext: String?
|
||||
get() { mHebeContext = mHebeContext ?: profile?.getStudentData("hebeContext", null); return mHebeContext }
|
||||
set(value) { profile?.putStudentData("hebeContext", value) ?: return; mHebeContext = value }
|
||||
set(value) { profile["hebeContext"] = value; mHebeContext = value }
|
||||
|
||||
private var mMessageBoxKey: String? = null
|
||||
var messageBoxKey: String?
|
||||
get() { mMessageBoxKey = mMessageBoxKey ?: profile?.getStudentData("messageBoxKey", null); return mMessageBoxKey }
|
||||
set(value) { profile?.putStudentData("messageBoxKey", value) ?: return; mMessageBoxKey = value }
|
||||
set(value) { profile["messageBoxKey"] = value; mMessageBoxKey = value }
|
||||
|
||||
private var mMessageBoxName: String? = null
|
||||
var messageBoxName: String?
|
||||
get() { mMessageBoxName = mMessageBoxName ?: profile?.getStudentData("messageBoxName", null); return mMessageBoxName }
|
||||
set(value) { profile?.putStudentData("messageBoxName", value) ?: return; mMessageBoxName = value }
|
||||
set(value) { profile["messageBoxName"] = value; mMessageBoxName = value }
|
||||
|
||||
val apiUrl: String?
|
||||
get() {
|
||||
|
@ -26,6 +26,7 @@ import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.HttpURLConnection.HTTP_NOT_FOUND
|
||||
import java.net.URLEncoder
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
@ -183,6 +184,7 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) {
|
||||
payload: JsonElement? = null,
|
||||
baseUrl: Boolean = false,
|
||||
firebaseToken: String? = null,
|
||||
allow404: Boolean = false,
|
||||
crossinline onSuccess: (json: T, response: Response?) -> Unit
|
||||
) {
|
||||
val url = "${if (baseUrl) data.apiUrl else data.fullApiUrl}$endpoint"
|
||||
@ -295,6 +297,19 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) {
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response?, throwable: Throwable?) {
|
||||
if (allow404 && response?.code() == HTTP_NOT_FOUND) {
|
||||
try {
|
||||
onSuccess(null as T, response)
|
||||
} catch (e: Exception) {
|
||||
data.error(
|
||||
ApiError(tag, EXCEPTION_VULCAN_HEBE_REQUEST)
|
||||
.withResponse(response)
|
||||
.withThrowable(e)
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
data.error(
|
||||
ApiError(tag, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
@ -338,6 +353,7 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) {
|
||||
query: Map<String, String> = mapOf(),
|
||||
baseUrl: Boolean = false,
|
||||
firebaseToken: String? = null,
|
||||
allow404: Boolean = false,
|
||||
crossinline onSuccess: (json: T, response: Response?) -> Unit
|
||||
) {
|
||||
val queryPath = query.map {
|
||||
@ -348,6 +364,7 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) {
|
||||
if (query.isNotEmpty()) "$endpoint?$queryPath" else endpoint,
|
||||
baseUrl = baseUrl,
|
||||
firebaseToken = firebaseToken,
|
||||
allow404 = allow404,
|
||||
onSuccess = onSuccess
|
||||
)
|
||||
}
|
||||
@ -382,6 +399,7 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) {
|
||||
messageBox: String? = null,
|
||||
params: Map<String, String> = mapOf(),
|
||||
includeFilterType: Boolean = true,
|
||||
allow404: Boolean = false,
|
||||
onSuccess: (data: List<JsonObject>, response: Response?) -> Unit
|
||||
) {
|
||||
val url = if (includeFilterType && filterType != null)
|
||||
@ -427,8 +445,8 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) {
|
||||
)
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||||
|
||||
apiGet(tag, url, query) { json: JsonArray, response ->
|
||||
onSuccess(json.map { it.asJsonObject }, response)
|
||||
apiGet(tag, url, query, allow404 = allow404) { json: JsonArray?, response ->
|
||||
onSuccess(json?.map { it.asJsonObject } ?: listOf(), response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Teacher.Companion.TYPE_PARENTS_
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher.Companion.TYPE_STUDENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher.Companion.TYPE_TEACHER
|
||||
import pl.szczodrzynski.edziennik.ext.*
|
||||
import java.net.HttpURLConnection.HTTP_NOT_FOUND
|
||||
|
||||
class VulcanHebeAddressbook(
|
||||
override val data: DataVulcan,
|
||||
@ -41,8 +42,15 @@ class VulcanHebeAddressbook(
|
||||
VULCAN_HEBE_ENDPOINT_ADDRESSBOOK,
|
||||
HebeFilterType.BY_PERSON,
|
||||
lastSync = lastSync,
|
||||
includeFilterType = false
|
||||
) { list, _ ->
|
||||
includeFilterType = false,
|
||||
allow404 = true,
|
||||
) { list, response ->
|
||||
if (response?.code() == HTTP_NOT_FOUND) {
|
||||
data.setSyncNext(ENDPOINT_VULCAN_HEBE_ADDRESSBOOK, 2 * DAY)
|
||||
onSuccess(ENDPOINT_VULCAN_HEBE_ADDRESSBOOK)
|
||||
return@apiGetList
|
||||
}
|
||||
|
||||
list.forEach { person ->
|
||||
val id = person.getString("Id") ?: return@forEach
|
||||
|
||||
|
@ -247,7 +247,9 @@ class VulcanHebeTimetable(
|
||||
}
|
||||
|
||||
lessonObject.id = lessonObject.buildId()
|
||||
lessonObject.ownerId = lessonObject.buildOwnerId()
|
||||
lessonShift?.id = lessonShift?.buildId() ?: -1
|
||||
lessonShift?.ownerId = lessonShift?.buildOwnerId() ?: -1
|
||||
|
||||
lessonList.add(lessonObject)
|
||||
lessonShift?.let { lessonList.add(it) }
|
||||
|
@ -198,7 +198,6 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
val teams = app.db.teamDao().allNow
|
||||
|
||||
val users = profiles.mapNotNull { profile ->
|
||||
val config = app.config.getFor(profile.id)
|
||||
val user = ServerSyncRequest.User(
|
||||
profile.userCode,
|
||||
profile.studentNameLong,
|
||||
@ -207,9 +206,9 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
teams.filter { it.profileId == profile.id }.map { it.code }
|
||||
)
|
||||
val hash = user.toString().md5()
|
||||
if (hash == config.hash)
|
||||
if (hash == profile.config.hash)
|
||||
return@mapNotNull null
|
||||
return@mapNotNull user to config
|
||||
return@mapNotNull user to profile.config
|
||||
}
|
||||
|
||||
val response = api.serverSync(ServerSyncRequest(
|
||||
@ -258,12 +257,10 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
seen = profile.empty
|
||||
notified = profile.empty
|
||||
|
||||
if (profile.userCode == event.sharedBy) {
|
||||
sharedBy = "self"
|
||||
addedManually = true
|
||||
} else {
|
||||
sharedBy = eventSharedBy
|
||||
}
|
||||
sharedBy = if (profile.userCode == event.sharedBy)
|
||||
"self"
|
||||
else
|
||||
eventSharedBy
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -327,11 +324,14 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun shareNote(note: Note) {
|
||||
fun shareNote(note: Note, teamId: Long? = null) {
|
||||
val profile = app.db.profileDao().getByIdNow(note.profileId)
|
||||
?: throw NullPointerException("Profile is not found")
|
||||
val team = app.db.teamDao().getClassNow(note.profileId)
|
||||
?: throw NullPointerException("TeamClass is not found")
|
||||
val team = if (teamId == null)
|
||||
app.db.teamDao().getClassNow(note.profileId)
|
||||
else
|
||||
app.db.teamDao().getByIdNow(note.profileId, teamId)
|
||||
team ?: throw NullPointerException("TeamClass is not found")
|
||||
|
||||
val response = api.shareNote(NoteShareRequest(
|
||||
deviceId = app.deviceId,
|
||||
|
@ -29,11 +29,12 @@ class SignatureInterceptor(val app: App) : Interceptor {
|
||||
return chain.proceed(
|
||||
request.newBuilder()
|
||||
.header("X-ApiKey", app.config.apiKeyCustom?.takeValue() ?: API_KEY)
|
||||
.header("X-AppVersion", BuildConfig.VERSION_CODE.toString())
|
||||
.header("X-Timestamp", timestamp.toString())
|
||||
.header("X-Signature", sign(timestamp, body, url))
|
||||
.header("X-AppBuild", BuildConfig.BUILD_TYPE)
|
||||
.header("X-AppFlavor", BuildConfig.FLAVOR)
|
||||
.header("X-AppVersion", BuildConfig.VERSION_CODE.toString())
|
||||
.header("X-DeviceId", app.deviceId)
|
||||
.header("X-Signature", sign(timestamp, body, url))
|
||||
.header("X-Timestamp", timestamp.toString())
|
||||
.build())
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,6 @@ object Signing {
|
||||
|
||||
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
||||
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
||||
return "$param1.MTIzNDU2Nzg5MDCOnsIOWR===.$param2".sha256()
|
||||
return "$param1.MTIzNDU2Nzg5MD01uMP7oW===.$param2".sha256()
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class SzkolnyTask(val app: App, val syncingProfiles: List<Profile>) : IApiTask(-
|
||||
notificationList
|
||||
.mapNotNull { it.profileId }
|
||||
.distinct()
|
||||
.map { app.config.getFor(it).sync.notificationFilter }
|
||||
.map { app.config[it].sync.notificationFilter }
|
||||
.forEach { filter ->
|
||||
filter.forEach { type ->
|
||||
notificationList.removeAll { it.type == type }
|
||||
|
@ -44,7 +44,7 @@ import pl.szczodrzynski.edziennik.data.db.migration.*
|
||||
TimetableManual::class,
|
||||
Note::class,
|
||||
Metadata::class
|
||||
], version = 99)
|
||||
], version = 100)
|
||||
@TypeConverters(
|
||||
ConverterTime::class,
|
||||
ConverterDate::class,
|
||||
@ -188,6 +188,7 @@ abstract class AppDb : RoomDatabase() {
|
||||
Migration97(),
|
||||
Migration98(),
|
||||
Migration99(),
|
||||
Migration100(),
|
||||
).allowMainThreadQueries().build()
|
||||
}
|
||||
}
|
||||
|
@ -5,13 +5,37 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.converter
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginMode
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginType
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.NotificationType
|
||||
import pl.szczodrzynski.edziennik.ext.*
|
||||
import pl.szczodrzynski.edziennik.ui.base.enums.NavTarget
|
||||
|
||||
class ConverterEnums {
|
||||
|
||||
@TypeConverter
|
||||
fun fromEnum(value: Enum<*>?) = value?.toInt()
|
||||
fun fromFeatureType(value: FeatureType?) = value?.id
|
||||
|
||||
@TypeConverter
|
||||
fun fromLoginMethod(value: LoginMethod?) = value?.id
|
||||
|
||||
@TypeConverter
|
||||
fun fromLoginMode(value: LoginMode?) = value?.id
|
||||
|
||||
@TypeConverter
|
||||
fun fromLoginType(value: LoginType?) = value?.id
|
||||
|
||||
@TypeConverter
|
||||
fun fromMetadataType(value: MetadataType?) = value?.id
|
||||
|
||||
@TypeConverter
|
||||
fun fromNotificationType(value: NotificationType?) = value?.id
|
||||
|
||||
@TypeConverter
|
||||
fun fromNavTarget(value: NavTarget?) = value?.id
|
||||
|
||||
@TypeConverter
|
||||
fun toFeatureType(value: Int?) = value.asFeatureTypeOrNull()
|
||||
|
@ -25,6 +25,9 @@ abstract class EventTypeDao {
|
||||
@Query("DELETE FROM eventTypes WHERE profileId = :profileId")
|
||||
abstract fun clear(profileId: Int)
|
||||
|
||||
@Query("DELETE FROM eventTypes WHERE profileId = :profileId AND eventTypeSource = :source")
|
||||
abstract fun clearBySource(profileId: Int, source: Int)
|
||||
|
||||
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId AND eventType = :typeId")
|
||||
abstract fun getByIdNow(profileId: Int, typeId: Long): EventType?
|
||||
|
||||
@ -43,7 +46,7 @@ abstract class EventTypeDao {
|
||||
val typeList = data.eventTypes.map {
|
||||
EventType(
|
||||
profileId = profile.id,
|
||||
id = it.id.toLong(),
|
||||
id = it.id,
|
||||
name = it.name,
|
||||
color = Color.parseColor(it.color),
|
||||
order = order++,
|
||||
@ -53,4 +56,21 @@ abstract class EventTypeDao {
|
||||
addAll(typeList)
|
||||
return typeList
|
||||
}
|
||||
|
||||
fun getAllWithDefaults(profile: Profile): List<EventType> {
|
||||
val eventTypes = getAllNow(profile.id)
|
||||
|
||||
val defaultIdsExpected = AppData.get(profile.loginStoreType).eventTypes
|
||||
.map { it.id }
|
||||
val defaultIdsFound = eventTypes.filter { it.source == SOURCE_DEFAULT }
|
||||
.sortedBy { it.order }
|
||||
.map { it.id }
|
||||
|
||||
if (defaultIdsExpected == defaultIdsFound)
|
||||
return eventTypes
|
||||
|
||||
clearBySource(profile.id, SOURCE_DEFAULT)
|
||||
addDefaultTypes(profile)
|
||||
return eventTypes
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,9 @@ interface ProfileDao {
|
||||
@Query("SELECT * FROM profiles WHERE profileId = :profileId")
|
||||
fun getByIdNow(profileId: Int): Profile?
|
||||
|
||||
@Query("SELECT * FROM profiles WHERE profileId = :profileId")
|
||||
suspend fun getByIdSuspend(profileId: Int): Profile?
|
||||
|
||||
@get:Query("SELECT * FROM profiles WHERE profileId >= 0 ORDER BY profileId")
|
||||
val all: LiveData<List<Profile>>
|
||||
|
||||
@ -49,7 +52,7 @@ interface ProfileDao {
|
||||
@get:Query("SELECT profileId FROM profiles WHERE profileId >= 0 ORDER BY profileId")
|
||||
val idsNow: List<Int>
|
||||
|
||||
@Query("SELECT profiles.* FROM teams JOIN profiles USING(profileId) WHERE teamCode = :teamCode AND registration = " + Profile.REGISTRATION_ENABLED + " AND enableSharedEvents = 1")
|
||||
@Query("SELECT profiles.* FROM teams JOIN profiles USING(profileId) WHERE teamCode = :teamCode AND registration = " + Profile.REGISTRATION_ENABLED)
|
||||
fun getByTeamCodeNowWithRegistration(teamCode: String?): List<Profile>
|
||||
|
||||
@get:Query("SELECT profileId FROM profiles WHERE profileId > 0 ORDER BY profileId ASC LIMIT 1")
|
||||
|
@ -107,6 +107,9 @@ abstract class TimetableDao : BaseDao<Lesson, LessonFull> {
|
||||
fun getByIdNow(profileId: Int, id: Long) =
|
||||
getOneNow("$QUERY WHERE timetable.profileId = $profileId AND timetable.id = $id")
|
||||
|
||||
fun getByOwnerIdNow(profileId: Int, ownerId: Long) =
|
||||
getOneNow("$QUERY WHERE timetable.profileId = $profileId AND timetable.ownerId = $ownerId")
|
||||
|
||||
@Query("UPDATE timetable SET keep = 0 WHERE profileId = :profileId AND isExtra = :isExtra AND type != -1 AND ((type != 3 AND date >= :dateFrom) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom))")
|
||||
abstract fun dontKeepFromDate(profileId: Int, dateFrom: Date, isExtra: Boolean)
|
||||
|
||||
|
@ -73,13 +73,22 @@ open class Event(
|
||||
const val COLOR_INFORMATION = 0xff039be5.toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Added manually - added by self, shared by self, or shared by someone else.
|
||||
*/
|
||||
@ColumnInfo(name = "eventAddedManually")
|
||||
var addedManually: Boolean = false
|
||||
get() = field || sharedBy == "self"
|
||||
get() = field || isShared
|
||||
|
||||
/**
|
||||
* Shared by - user code who shared the event. Null if not shared.
|
||||
* "Self" if shared by this app user.
|
||||
*/
|
||||
@ColumnInfo(name = "eventSharedBy")
|
||||
var sharedBy: String? = null
|
||||
@ColumnInfo(name = "eventSharedByName")
|
||||
var sharedByName: String? = null
|
||||
|
||||
@ColumnInfo(name = "eventBlacklisted")
|
||||
var blacklisted: Boolean = false
|
||||
@ColumnInfo(name = "eventIsDone")
|
||||
@ -104,6 +113,27 @@ open class Event(
|
||||
var attachmentIds: MutableList<Long>? = null
|
||||
var attachmentNames: MutableList<String>? = null
|
||||
|
||||
val isHomework
|
||||
get() = type == TYPE_HOMEWORK
|
||||
|
||||
/**
|
||||
* Whether the event is shared by anyone. Note that this implies [addedManually].
|
||||
*/
|
||||
val isShared
|
||||
get() = sharedBy != null
|
||||
|
||||
/**
|
||||
* Whether the event is shared by "self" (this app user).
|
||||
*/
|
||||
val isSharedSent
|
||||
get() = sharedBy == "self"
|
||||
|
||||
/**
|
||||
* Whether the event is shared by someone else from the class group.
|
||||
*/
|
||||
val isSharedReceived
|
||||
get() = sharedBy != null && sharedBy != "self"
|
||||
|
||||
/**
|
||||
* Add an attachment
|
||||
* @param id attachment ID
|
||||
@ -134,9 +164,6 @@ open class Event(
|
||||
it.timeInMillis += 45 * MINUTE * 1000
|
||||
}
|
||||
|
||||
val isHomework
|
||||
get() = type == TYPE_HOMEWORK
|
||||
|
||||
@Ignore
|
||||
fun withMetadata(metadata: Metadata) = EventFull(this, metadata)
|
||||
}
|
||||
|
@ -5,31 +5,6 @@ package pl.szczodrzynski.edziennik.data.db.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_CLASS_EVENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_ELEARNING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_ESSAY
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_EXAM
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_EXCURSION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_INFORMATION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_PROJECT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_PT_MEETING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_READING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_SHORT_QUIZ
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_CLASS_EVENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_ELEARNING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_ESSAY
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_EXAM
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_EXCURSION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_INFORMATION
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_PROJECT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_PT_MEETING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_READING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_SHORT_QUIZ
|
||||
|
||||
@Entity(
|
||||
tableName = "eventTypes",
|
||||
@ -55,35 +30,5 @@ class EventType(
|
||||
const val SOURCE_REGISTER = 1
|
||||
const val SOURCE_CUSTOM = 2
|
||||
const val SOURCE_SHARED = 3
|
||||
|
||||
fun getTypeColorMap() = mapOf(
|
||||
TYPE_ELEARNING to COLOR_ELEARNING,
|
||||
TYPE_HOMEWORK to COLOR_HOMEWORK,
|
||||
TYPE_DEFAULT to COLOR_DEFAULT,
|
||||
TYPE_EXAM to COLOR_EXAM,
|
||||
TYPE_SHORT_QUIZ to COLOR_SHORT_QUIZ,
|
||||
TYPE_ESSAY to COLOR_ESSAY,
|
||||
TYPE_PROJECT to COLOR_PROJECT,
|
||||
TYPE_PT_MEETING to COLOR_PT_MEETING,
|
||||
TYPE_EXCURSION to COLOR_EXCURSION,
|
||||
TYPE_READING to COLOR_READING,
|
||||
TYPE_CLASS_EVENT to COLOR_CLASS_EVENT,
|
||||
TYPE_INFORMATION to COLOR_INFORMATION
|
||||
)
|
||||
|
||||
fun getTypeNameMap() = mapOf(
|
||||
TYPE_ELEARNING to R.string.event_type_elearning,
|
||||
TYPE_HOMEWORK to R.string.event_type_homework,
|
||||
TYPE_DEFAULT to R.string.event_other,
|
||||
TYPE_EXAM to R.string.event_exam,
|
||||
TYPE_SHORT_QUIZ to R.string.event_short_quiz,
|
||||
TYPE_ESSAY to R.string.event_essay,
|
||||
TYPE_PROJECT to R.string.event_project,
|
||||
TYPE_PT_MEETING to R.string.event_pt_meeting,
|
||||
TYPE_EXCURSION to R.string.event_excursion,
|
||||
TYPE_READING to R.string.event_reading,
|
||||
TYPE_CLASS_EVENT to R.string.event_class_event,
|
||||
TYPE_INFORMATION to R.string.event_information
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,13 @@ open class Lesson(
|
||||
|
||||
var isExtra: Boolean = false
|
||||
|
||||
/**
|
||||
* Stable ID denoting this lesson, used for note sharing (i.e. [profileId]-independent).
|
||||
*
|
||||
* This is simply the Unix timestamp of the lesson (in seconds).
|
||||
*/
|
||||
var ownerId: Long = id
|
||||
|
||||
val displayDate: Date?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
@ -69,7 +76,12 @@ open class Lesson(
|
||||
val isChange
|
||||
get() = type == TYPE_CHANGE || type == TYPE_SHIFTED_TARGET
|
||||
|
||||
fun buildId(): Long = (displayDate?.combineWith(displayStartTime) ?: 0L) / 6L * 10L + (hashCode() and 0xFFFF)
|
||||
fun buildId(): Long =
|
||||
(displayDate?.combineWith(displayStartTime) ?: 0L) / 6L * 10L +
|
||||
(hashCode() and 0xFFFF)
|
||||
|
||||
fun buildOwnerId(): Long =
|
||||
(displayDate?.combineWith(displayStartTime) ?: 0L)
|
||||
|
||||
@Ignore
|
||||
var showAsUnseen = false
|
||||
|
@ -9,6 +9,7 @@ interface Noteable {
|
||||
fun getNoteType(): Note.OwnerType
|
||||
fun getNoteOwnerProfileId(): Int
|
||||
fun getNoteOwnerId(): Long
|
||||
fun getNoteShareTeamId(): Long? = null
|
||||
|
||||
var notes: MutableList<Note>
|
||||
|
||||
|
@ -5,25 +5,18 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.entity
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.widget.ImageView
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import com.google.gson.JsonObject
|
||||
import pl.droidsonroids.gif.GifDrawable
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginType
|
||||
import pl.szczodrzynski.edziennik.ext.*
|
||||
import pl.szczodrzynski.edziennik.utils.ProfileImageHolder
|
||||
import pl.szczodrzynski.edziennik.ext.dateToSemester
|
||||
import pl.szczodrzynski.edziennik.ext.getDrawable
|
||||
import pl.szczodrzynski.edziennik.ext.getHolder
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.navlib.ImageHolder
|
||||
import pl.szczodrzynski.navlib.R
|
||||
import pl.szczodrzynski.navlib.drawer.IDrawerProfile
|
||||
import pl.szczodrzynski.navlib.getDrawableFromRes
|
||||
|
||||
@Entity(tableName = "profiles", primaryKeys = ["profileId"])
|
||||
open class Profile(
|
||||
@ -60,9 +53,13 @@ open class Profile(
|
||||
}
|
||||
|
||||
override var image: String? = null
|
||||
|
||||
var empty = true
|
||||
var archived = false
|
||||
var syncEnabled = true
|
||||
@ColumnInfo(name = "enableSharedEvents")
|
||||
var unused1 = true
|
||||
var registration = REGISTRATION_UNSPECIFIED
|
||||
var userCode = ""
|
||||
|
||||
/**
|
||||
* A unique ID matching [archived] profiles with current ones
|
||||
@ -70,117 +67,35 @@ open class Profile(
|
||||
*/
|
||||
var archiveId: Int? = null
|
||||
|
||||
var syncEnabled = true
|
||||
var enableSharedEvents = true
|
||||
var registration = REGISTRATION_UNSPECIFIED
|
||||
var userCode = ""
|
||||
|
||||
/**
|
||||
* The student's number in the class register.
|
||||
*/
|
||||
var studentNumber = -1
|
||||
var studentClassName: String? = null
|
||||
var studentSchoolYearStart = Date.getToday().let { if (it.month < 9) it.year - 1 else it.year }
|
||||
|
||||
var dateSemester1Start = Date(studentSchoolYearStart, 9, 1)
|
||||
var dateSemester2Start = Date(studentSchoolYearStart + 1, 2, 1)
|
||||
var dateYearEnd = Date(studentSchoolYearStart + 1, 6, 30)
|
||||
fun getSemesterStart(semester: Int) = if (semester == 1) dateSemester1Start else dateSemester2Start
|
||||
fun getSemesterEnd(semester: Int) = if (semester == 1) dateSemester2Start.clone().stepForward(0, 0, -1) else dateYearEnd
|
||||
fun dateToSemester(date: Date) = if (date >= dateSemester2Start) 2 else 1
|
||||
@delegate:Ignore
|
||||
val currentSemester by lazy { dateToSemester(Date.getToday()) }
|
||||
|
||||
fun shouldArchive(): Boolean {
|
||||
// vulcan hotfix
|
||||
if (dateYearEnd.month > 6) {
|
||||
dateYearEnd.month = 6
|
||||
dateYearEnd.day = 30
|
||||
}
|
||||
// fix for when versions <4.3 synced 2020/2021 year dates to older profiles during 2020 Jun-Aug
|
||||
if (dateSemester1Start.year > studentSchoolYearStart) {
|
||||
val diff = dateSemester1Start.year - studentSchoolYearStart
|
||||
dateSemester1Start.year -= diff
|
||||
dateSemester2Start.year -= diff
|
||||
dateYearEnd.year -= diff
|
||||
}
|
||||
return App.config.archiverEnabled
|
||||
&& Date.getToday() >= dateYearEnd
|
||||
&& Date.getToday().year > studentSchoolYearStart
|
||||
}
|
||||
fun isBeforeYear() = false && Date.getToday() < dateSemester1Start
|
||||
|
||||
var disabledNotifications: List<Long>? = null
|
||||
|
||||
var lastReceiversSync: Long = 0
|
||||
|
||||
fun hasStudentData(key: String) = studentData.has(key)
|
||||
fun getStudentData(key: String, defaultValue: Boolean) = studentData.getBoolean(key) ?: defaultValue
|
||||
fun getStudentData(key: String, defaultValue: String?) = studentData.getString(key) ?: defaultValue
|
||||
fun getStudentData(key: String, defaultValue: Int) = studentData.getInt(key) ?: defaultValue
|
||||
fun getStudentData(key: String, defaultValue: Long) = studentData.getLong(key) ?: defaultValue
|
||||
fun getStudentData(key: String, defaultValue: Float) = studentData.getFloat(key) ?: defaultValue
|
||||
fun getStudentData(key: String, defaultValue: Char) = studentData.getChar(key) ?: defaultValue
|
||||
fun putStudentData(key: String, value: Boolean) { studentData[key] = value }
|
||||
fun putStudentData(key: String, value: String?) { studentData[key] = value }
|
||||
fun putStudentData(key: String, value: Number) { studentData[key] = value }
|
||||
fun putStudentData(key: String, value: Char) { studentData[key] = value }
|
||||
fun removeStudentData(key: String) { studentData.remove(key) }
|
||||
|
||||
val currentSemester
|
||||
get() = dateToSemester(Date.getToday())
|
||||
val isParent
|
||||
get() = accountName != null
|
||||
|
||||
val accountOwnerName
|
||||
get() = accountName ?: studentNameLong
|
||||
|
||||
@Ignore
|
||||
val registerName = loginStoreType.name.lowercase()
|
||||
|
||||
val registerName
|
||||
get() = loginStoreType.name.lowercase()
|
||||
val canShare
|
||||
get() = registration == REGISTRATION_ENABLED && !archived
|
||||
|
||||
override fun getImageDrawable(context: Context): Drawable {
|
||||
if (archived) {
|
||||
return context.getDrawableFromRes(pl.szczodrzynski.edziennik.R.drawable.profile_archived).also {
|
||||
it.colorFilter = PorterDuffColorFilter(colorFromName(name), PorterDuff.Mode.DST_OVER)
|
||||
}
|
||||
}
|
||||
@delegate:Ignore
|
||||
@delegate:Transient
|
||||
val config by lazy { App.config[this.id] }
|
||||
|
||||
if (!image.isNullOrEmpty()) {
|
||||
try {
|
||||
return if (image?.endsWith(".gif", true) == true) {
|
||||
GifDrawable(image ?: "")
|
||||
} else {
|
||||
RoundedBitmapDrawableFactory.create(context.resources, image ?: "")
|
||||
//return Drawable.createFromPath(image ?: "") ?: throw Exception()
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
return context.getDrawableFromRes(R.drawable.profile).also {
|
||||
it.colorFilter = PorterDuffColorFilter(colorFromName(name), PorterDuff.Mode.DST_OVER)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getImageHolder(context: Context): ImageHolder {
|
||||
if (archived) {
|
||||
return ImageHolder(pl.szczodrzynski.edziennik.R.drawable.profile_archived, colorFromName(name))
|
||||
}
|
||||
|
||||
return if (!image.isNullOrEmpty()) {
|
||||
try {
|
||||
ProfileImageHolder(image ?: "")
|
||||
} catch (_: Exception) {
|
||||
ImageHolder(R.drawable.profile, colorFromName(name))
|
||||
}
|
||||
}
|
||||
else {
|
||||
ImageHolder(R.drawable.profile, colorFromName(name))
|
||||
}
|
||||
}
|
||||
override fun getImageDrawable(context: Context) = this.getDrawable(context)
|
||||
override fun getImageHolder(context: Context) = this.getHolder()
|
||||
override fun applyImageTo(imageView: ImageView) {
|
||||
getImageHolder(imageView.context).applyTo(imageView)
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ package pl.szczodrzynski.edziennik.data.db.enums
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.ext.getString
|
||||
import pl.szczodrzynski.edziennik.ext.isNotNullNorBlank
|
||||
|
||||
enum class LoginMethod(
|
||||
val loginType: LoginType,
|
||||
@ -26,7 +28,7 @@ enum class LoginMethod(
|
||||
MOBIDZIENNIK_API2(
|
||||
loginType = LoginType.MOBIDZIENNIK,
|
||||
id = 1300,
|
||||
isPossible = { profile, _ -> profile?.studentData?.has("email") ?: false },
|
||||
isPossible = { profile, _ -> profile?.studentData?.getString("email").isNotNullNorBlank() },
|
||||
),
|
||||
LIBRUS_PORTAL(
|
||||
loginType = LoginType.LIBRUS,
|
||||
@ -57,7 +59,7 @@ enum class LoginMethod(
|
||||
VULCAN_WEB_MAIN(
|
||||
loginType = LoginType.VULCAN,
|
||||
id = 4100,
|
||||
isPossible = { _, loginStore -> loginStore.hasLoginData("webHost") },
|
||||
isPossible = { _, loginStore -> loginStore.getLoginData("webHost", null).isNotNullNorBlank() },
|
||||
),
|
||||
VULCAN_HEBE(
|
||||
loginType = LoginType.VULCAN,
|
||||
|
@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.ext.takePositive
|
||||
import pl.szczodrzynski.edziennik.ui.search.Searchable
|
||||
import pl.szczodrzynski.edziennik.utils.html.BetterHtml
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
@ -118,4 +119,5 @@ class EventFull(
|
||||
override fun getNoteType() = Note.OwnerType.EVENT
|
||||
override fun getNoteOwnerProfileId() = profileId
|
||||
override fun getNoteOwnerId() = id
|
||||
override fun getNoteShareTeamId() = teamId.takePositive()
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Note
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Noteable
|
||||
import pl.szczodrzynski.edziennik.ext.takePositive
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class LessonFull(
|
||||
@ -137,9 +138,10 @@ class LessonFull(
|
||||
var seen: Boolean = false
|
||||
var notified: Boolean = false
|
||||
|
||||
@Relation(parentColumn = "id", entityColumn = "noteOwnerId", entity = Note::class)
|
||||
@Relation(parentColumn = "ownerId", entityColumn = "noteOwnerId", entity = Note::class)
|
||||
override lateinit var notes: MutableList<Note>
|
||||
override fun getNoteType() = Note.OwnerType.LESSON
|
||||
override fun getNoteOwnerProfileId() = profileId
|
||||
override fun getNoteOwnerId() = id
|
||||
override fun getNoteOwnerId() = ownerId
|
||||
override fun getNoteShareTeamId() = teamId.takePositive()
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2022-10-25.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.migration
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration100 : Migration(99, 100) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// add Note Owner ID to Lesson, to make it profileId-independent
|
||||
// calculate the new owner ID based on the old ID
|
||||
database.execSQL("ALTER TABLE timetable ADD COLUMN ownerId INT NOT NULL DEFAULT 0;")
|
||||
// set new ID for actual lessons
|
||||
database.execSQL("UPDATE timetable SET ownerId = ROUND((id & ~65535) / 500000.0) * 300000;")
|
||||
// copy the old ID (date value) for NO_LESSONS
|
||||
database.execSQL("UPDATE timetable SET ownerId = id WHERE type = -1;")
|
||||
// update ID for notes as well
|
||||
database.execSQL("UPDATE notes SET noteOwnerId = ROUND((noteOwnerId & ~65535) / 500000.0) * 300000 WHERE noteOwnerType = 'LESSON' AND noteOwnerId > 2000000000000;")
|
||||
// force full app sync to download notes with new IDs
|
||||
database.execSQL("DELETE FROM config WHERE `key` = 'hash';")
|
||||
database.execSQL("DELETE FROM config WHERE `key` = 'lastAppSync';")
|
||||
}
|
||||
}
|
@ -160,11 +160,11 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
||||
if (event.color == -1)
|
||||
event.color = null
|
||||
|
||||
event.addedManually = true
|
||||
event.sharedBy = json.getString("sharedBy")
|
||||
event.sharedByName = json.getString("sharedByName")
|
||||
if (profile.userCode == event.sharedBy) {
|
||||
event.sharedBy = "self"
|
||||
event.addedManually = true
|
||||
}
|
||||
|
||||
val metadata = Metadata(
|
||||
@ -176,7 +176,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
||||
)
|
||||
|
||||
val type = if (event.isHomework) NotificationType.SHARED_HOMEWORK else NotificationType.SHARED_EVENT
|
||||
val notificationFilter = app.config.getFor(event.profileId).sync.notificationFilter
|
||||
val notificationFilter = app.config[event.profileId].sync.notificationFilter
|
||||
|
||||
if (!notificationFilter.contains(type) && event.sharedBy != "self" && event.date >= Date.getToday()) {
|
||||
val notification = Notification(
|
||||
@ -211,7 +211,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@forEach
|
||||
if (!profile.canShare)
|
||||
return@forEach
|
||||
val notificationFilter = app.config.getFor(team.profileId).sync.notificationFilter
|
||||
val notificationFilter = app.config[team.profileId].sync.notificationFilter
|
||||
|
||||
if (!notificationFilter.contains(NotificationType.REMOVED_SHARED_EVENT)) {
|
||||
val notification = Notification(
|
||||
@ -265,7 +265,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
||||
return@forEach
|
||||
|
||||
val type = NotificationType.SHARED_NOTE
|
||||
val notificationFilter = app.config.getFor(note.profileId).sync.notificationFilter
|
||||
val notificationFilter = app.config[note.profileId].sync.notificationFilter
|
||||
|
||||
if (!notificationFilter.contains(type) && note.sharedBy != "self") {
|
||||
val notification = Notification(
|
||||
|
@ -6,12 +6,37 @@ package pl.szczodrzynski.edziennik.data.firebase
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_ANNOUNCEMENTS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_ATTENDANCES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_EVENTS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_LUCKY_NUMBER
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NOTICES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_POINT_GRADES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_POINT_GRADE_CATEGORIES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_PT_MEETINGS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_TEXT_GRADES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_TIMETABLES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_MESSAGES_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginType
|
||||
import pl.szczodrzynski.edziennik.ext.getString
|
||||
import pl.szczodrzynski.edziennik.ext.getStudentData
|
||||
|
||||
class SzkolnyLibrusFirebase(val app: App, val profiles: List<Profile>, val message: FirebaseService.Message) {
|
||||
/*{
|
||||
|
@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginType
|
||||
import pl.szczodrzynski.edziennik.ext.getLong
|
||||
import pl.szczodrzynski.edziennik.ext.getString
|
||||
import pl.szczodrzynski.edziennik.ext.getStudentData
|
||||
|
||||
class SzkolnyMobidziennikFirebase(val app: App, val profiles: List<Profile>, val message: FirebaseService.Message) {
|
||||
/*{
|
||||
|
@ -4,15 +4,15 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.firebase
|
||||
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.LoginType
|
||||
import pl.szczodrzynski.edziennik.ext.getInt
|
||||
import pl.szczodrzynski.edziennik.ext.getString
|
||||
import pl.szczodrzynski.edziennik.ext.getStudentData
|
||||
import pl.szczodrzynski.edziennik.ext.toJsonObject
|
||||
|
||||
class SzkolnyVulcanFirebase(val app: App, val profiles: List<Profile>, val message: FirebaseService.Message) {
|
||||
|
@ -6,14 +6,9 @@ package pl.szczodrzynski.edziennik.ext
|
||||
|
||||
import android.util.LongSparseArray
|
||||
import androidx.core.util.forEach
|
||||
import com.google.android.material.datepicker.CalendarConstraints
|
||||
import com.google.gson.JsonElement
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.config.AppData
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Team
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
|
||||
|
||||
fun List<Teacher>.byId(id: Long) = firstOrNull { it.id == id }
|
||||
fun List<Teacher>.byNameFirstLast(nameFirstLast: String) = firstOrNull { it.name + " " + it.surname == nameFirstLast }
|
||||
@ -34,22 +29,3 @@ fun LongSparseArray<Team>.getById(id: Long): Team? {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
operator fun Profile.set(key: String, value: JsonElement) = this.studentData.add(key, value)
|
||||
operator fun Profile.set(key: String, value: Boolean) = this.studentData.addProperty(key, value)
|
||||
operator fun Profile.set(key: String, value: String?) = this.studentData.addProperty(key, value)
|
||||
operator fun Profile.set(key: String, value: Number) = this.studentData.addProperty(key, value)
|
||||
operator fun Profile.set(key: String, value: Char) = this.studentData.addProperty(key, value)
|
||||
|
||||
fun Profile.getSchoolYearConstrains(): CalendarConstraints {
|
||||
return CalendarConstraints.Builder()
|
||||
.setStart(dateSemester1Start.inMillisUtc)
|
||||
.setEnd(dateYearEnd.inMillisUtc)
|
||||
.build()
|
||||
}
|
||||
|
||||
fun Profile.hasFeature(featureType: FeatureType) = featureType in this.loginStoreType.features
|
||||
fun Profile.hasUIFeature(featureType: FeatureType) = featureType.isUIAlwaysAvailable || hasFeature(featureType)
|
||||
|
||||
fun Profile.getAppData() =
|
||||
if (App.profileId == this.id) App.data else AppData.get(this.loginStoreType)
|
||||
|
@ -5,6 +5,7 @@
|
||||
package pl.szczodrzynski.edziennik.ext
|
||||
|
||||
import android.os.Bundle
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
@ -48,6 +49,11 @@ fun JsonObject.putEnum(key: String, value: Enum<*>) = addProperty(key, value.toI
|
||||
fun String.toJsonObject(): JsonObject? = try { JsonParser.parseString(this).asJsonObject } catch (ignore: Exception) { null }
|
||||
fun String.toJsonArray(): JsonArray? = try { JsonParser.parseString(this).asJsonArray } catch (ignore: Exception) { null }
|
||||
|
||||
fun Any?.toJsonElement(): JsonElement = when (this) {
|
||||
is Collection<*> -> JsonArray(this)
|
||||
else -> Gson().toJsonTree(this)
|
||||
}
|
||||
|
||||
operator fun JsonObject.set(key: String, value: JsonElement) = this.add(key, value)
|
||||
operator fun JsonObject.set(key: String, value: Boolean) = this.addProperty(key, value)
|
||||
operator fun JsonObject.set(key: String, value: String?) = this.addProperty(key, value)
|
||||
@ -67,6 +73,8 @@ fun JsonObject(vararg properties: Pair<String, Any?>): JsonObject {
|
||||
is Number -> addProperty(key, value)
|
||||
is Boolean -> addProperty(key, value)
|
||||
is Enum<*> -> addProperty(key, value.toInt())
|
||||
null -> add(key, null)
|
||||
else -> add(key, value.toJsonElement())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -98,6 +106,8 @@ fun JsonArray(properties: Collection<Any?>): JsonArray {
|
||||
is Char -> add(property as Char?)
|
||||
is Number -> add(property as Number?)
|
||||
is Boolean -> add(property as Boolean?)
|
||||
is Enum<*> -> add(property.toInt())
|
||||
else -> add(property.toJsonElement())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,9 +73,18 @@ fun pendingIntentFlag(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
fun pendingIntentMutable(): Int {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
return PendingIntent.FLAG_MUTABLE
|
||||
return 0
|
||||
}
|
||||
|
||||
fun Int?.takeValue() = if (this == -1) null else this
|
||||
fun Int?.takePositive() = if (this == -1 || this == 0) null else this
|
||||
|
||||
fun Long?.takeValue() = if (this == -1L) null else this
|
||||
fun Long?.takePositive() = if (this == -1L || this == 0L) null else this
|
||||
|
||||
fun String?.takeValue() = if (this.isNullOrBlank()) null else this
|
||||
|
||||
fun Any?.ignore() = Unit
|
||||
|
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2022-10-25.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ext
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import com.google.android.material.datepicker.CalendarConstraints
|
||||
import com.google.gson.JsonElement
|
||||
import pl.droidsonroids.gif.GifDrawable
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.config.AppData
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
|
||||
import pl.szczodrzynski.edziennik.utils.ProfileImageHolder
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.navlib.ImageHolder
|
||||
import pl.szczodrzynski.navlib.getDrawableFromRes
|
||||
|
||||
// TODO refactor Data* fields and make the receiver non-nullable
|
||||
operator fun Profile?.set(key: String, value: JsonElement) = this?.studentData?.add(key, value)
|
||||
operator fun Profile?.set(key: String, value: Boolean) = this?.studentData?.addProperty(key, value)
|
||||
operator fun Profile?.set(key: String, value: String?) = this?.studentData?.addProperty(key, value)
|
||||
operator fun Profile?.set(key: String, value: Number) = this?.studentData?.addProperty(key, value)
|
||||
operator fun Profile?.set(key: String, value: Char) = this?.studentData?.addProperty(key, value)
|
||||
|
||||
fun Profile.getStudentData(key: String, defaultValue: Boolean) =
|
||||
studentData.getBoolean(key) ?: defaultValue
|
||||
|
||||
fun Profile.getStudentData(key: String, defaultValue: String?) =
|
||||
studentData.getString(key) ?: defaultValue
|
||||
|
||||
fun Profile.getStudentData(key: String, defaultValue: Int) =
|
||||
studentData.getInt(key) ?: defaultValue
|
||||
|
||||
fun Profile.getStudentData(key: String, defaultValue: Long) =
|
||||
studentData.getLong(key) ?: defaultValue
|
||||
|
||||
fun Profile.getStudentData(key: String, defaultValue: Float) =
|
||||
studentData.getFloat(key) ?: defaultValue
|
||||
|
||||
fun Profile.getStudentData(key: String, defaultValue: Char) =
|
||||
studentData.getChar(key) ?: defaultValue
|
||||
|
||||
fun Profile.getSemesterStart(semester: Int) =
|
||||
if (semester == 1) dateSemester1Start else dateSemester2Start
|
||||
|
||||
fun Profile.getSemesterEnd(semester: Int) =
|
||||
if (semester == 1) dateSemester2Start.clone().stepForward(0, 0, -1) else dateYearEnd
|
||||
|
||||
fun Profile.dateToSemester(date: Date) = if (date >= dateSemester2Start) 2 else 1
|
||||
fun Profile.isBeforeYear() = false && Date.getToday() < dateSemester1Start
|
||||
|
||||
fun Profile.getSchoolYearConstrains(): CalendarConstraints {
|
||||
return CalendarConstraints.Builder()
|
||||
.setStart(dateSemester1Start.inMillisUtc)
|
||||
.setEnd(dateYearEnd.inMillisUtc)
|
||||
.build()
|
||||
}
|
||||
|
||||
fun Profile.hasFeature(featureType: FeatureType) = featureType in this.loginStoreType.features
|
||||
fun Profile.hasUIFeature(featureType: FeatureType) =
|
||||
featureType.isUIAlwaysAvailable || hasFeature(featureType)
|
||||
|
||||
fun Profile.getAppData() =
|
||||
if (App.profileId == this.id) App.data else AppData.get(this.loginStoreType)
|
||||
|
||||
fun Profile.shouldArchive(): Boolean {
|
||||
// vulcan hotfix
|
||||
if (dateYearEnd.month > 6) {
|
||||
dateYearEnd.month = 6
|
||||
dateYearEnd.day = 30
|
||||
}
|
||||
// fix for when versions <4.3 synced 2020/2021 year dates to older profiles during 2020 Jun-Aug
|
||||
if (dateSemester1Start.year > studentSchoolYearStart) {
|
||||
val diff = dateSemester1Start.year - studentSchoolYearStart
|
||||
dateSemester1Start.year -= diff
|
||||
dateSemester2Start.year -= diff
|
||||
dateYearEnd.year -= diff
|
||||
}
|
||||
return App.config.archiverEnabled && Date.getToday() >= dateYearEnd && Date.getToday().year > studentSchoolYearStart
|
||||
}
|
||||
|
||||
fun Profile.getDrawable(context: Context): Drawable {
|
||||
if (archived) {
|
||||
return context.getDrawableFromRes(R.drawable.profile_archived).also {
|
||||
it.colorFilter = PorterDuffColorFilter(colorFromName(name), PorterDuff.Mode.DST_OVER)
|
||||
}
|
||||
}
|
||||
|
||||
if (!image.isNullOrEmpty()) {
|
||||
try {
|
||||
return if (image?.endsWith(".gif", true) == true) {
|
||||
GifDrawable(image ?: "")
|
||||
} else {
|
||||
RoundedBitmapDrawableFactory.create(context.resources, image ?: "")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
return context.getDrawableFromRes(R.drawable.profile).also {
|
||||
it.colorFilter = PorterDuffColorFilter(colorFromName(name), PorterDuff.Mode.DST_OVER)
|
||||
}
|
||||
}
|
||||
|
||||
fun Profile.getHolder(): ImageHolder {
|
||||
if (archived) {
|
||||
return ImageHolder(R.drawable.profile_archived, colorFromName(name))
|
||||
}
|
||||
|
||||
return if (!image.isNullOrEmpty()) {
|
||||
try {
|
||||
ProfileImageHolder(image ?: "")
|
||||
} catch (_: Exception) {
|
||||
ImageHolder(R.drawable.profile, colorFromName(name))
|
||||
}
|
||||
} else {
|
||||
ImageHolder(R.drawable.profile, colorFromName(name))
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ import android.text.style.CharacterStyle
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StrikethroughSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.text.style.UnderlineSpan
|
||||
import androidx.annotation.PluralsRes
|
||||
import androidx.annotation.StringRes
|
||||
import com.mikepenz.materialdrawer.holder.StringHolder
|
||||
@ -160,6 +161,11 @@ fun CharSequence?.asBoldSpannable(): Spannable {
|
||||
spannable.setSpan(StyleSpan(Typeface.BOLD), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
fun CharSequence?.asUnderlineSpannable(): Spannable {
|
||||
val spannable = SpannableString(this)
|
||||
spannable.setSpan(UnderlineSpan(), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
fun CharSequence.asSpannable(
|
||||
vararg spans: CharacterStyle,
|
||||
substring: CharSequence? = null,
|
||||
|
@ -8,6 +8,7 @@ import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.widget.*
|
||||
import androidx.annotation.StringRes
|
||||
@ -161,3 +162,12 @@ val SwipeRefreshLayout.onScrollListener: RecyclerView.OnScrollListener
|
||||
}
|
||||
}
|
||||
|
||||
fun View.removeFromParent() {
|
||||
(parent as? ViewGroup)?.removeView(this)
|
||||
}
|
||||
|
||||
fun View.appendView(child: View) {
|
||||
val parent = parent as? ViewGroup ?: return
|
||||
val index = parent.indexOfChild(this)
|
||||
parent.addView(child, index + 1)
|
||||
}
|
||||
|
@ -5,8 +5,20 @@
|
||||
package pl.szczodrzynski.edziennik.network.cookie
|
||||
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
class DumbCookie(var cookie: Cookie) {
|
||||
companion object {
|
||||
fun deserialize(key: String, value: String): DumbCookie? {
|
||||
val (domain, _) = key.split('|', limit = 2)
|
||||
val url = HttpUrl.Builder()
|
||||
.scheme("https")
|
||||
.host(domain)
|
||||
.build()
|
||||
val cookie = Cookie.parse(url, value) ?: return null
|
||||
return DumbCookie(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
constructor(domain: String, name: String, value: String, expiresAt: Long? = null) : this(
|
||||
Cookie.Builder()
|
||||
@ -21,7 +33,10 @@ class DumbCookie(var cookie: Cookie) {
|
||||
cookie = Cookie.Builder()
|
||||
.name(cookie.name())
|
||||
.value(cookie.value())
|
||||
.expiresAt(cookie.expiresAt())
|
||||
.also {
|
||||
if (cookie.persistent())
|
||||
it.expiresAt(cookie.expiresAt())
|
||||
}
|
||||
.domain(cookie.domain())
|
||||
.build()
|
||||
}
|
||||
@ -45,4 +60,7 @@ class DumbCookie(var cookie: Cookie) {
|
||||
hash = 31 * hash + cookie.domain().hashCode()
|
||||
return hash
|
||||
}
|
||||
|
||||
fun serializeKey() = cookie.domain() + "|" + cookie.name()
|
||||
fun serialize() = serializeKey() to cookie.toString()
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package pl.szczodrzynski.edziennik.network.cookie
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.HttpUrl
|
||||
@ -26,22 +27,48 @@ class DumbCookieJar(
|
||||
) : CookieJar {
|
||||
|
||||
private val prefs = context.getSharedPreferences("cookies", Context.MODE_PRIVATE)
|
||||
val sessionCookies = mutableSetOf<DumbCookie>()
|
||||
private val savedCookies = mutableSetOf<DumbCookie>()
|
||||
private val sessionCookies = mutableSetOf<DumbCookie>()
|
||||
|
||||
init {
|
||||
val toRemove = mutableListOf<String>()
|
||||
prefs.all.forEach { (key, value) ->
|
||||
if (value !is String)
|
||||
return@forEach
|
||||
val dc = DumbCookie.deserialize(key, value) ?: return@forEach
|
||||
if (dc.cookie.expiresAt() > System.currentTimeMillis())
|
||||
sessionCookies.add(dc)
|
||||
else
|
||||
toRemove.add(key)
|
||||
}
|
||||
prefs.edit {
|
||||
for (key in toRemove) {
|
||||
remove(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun save(dc: DumbCookie) {
|
||||
sessionCookies.remove(dc)
|
||||
sessionCookies.add(dc)
|
||||
if (dc.cookie.persistent() || persistAll) {
|
||||
savedCookies.remove(dc)
|
||||
savedCookies.add(dc)
|
||||
prefs.edit {
|
||||
val (key, value) = dc.serialize()
|
||||
putString(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun delete(vararg toRemove: DumbCookie) {
|
||||
sessionCookies.removeAll(toRemove)
|
||||
savedCookies.removeAll(toRemove)
|
||||
sessionCookies.removeAll(toRemove.toSet())
|
||||
prefs.edit {
|
||||
for (dc in toRemove) {
|
||||
val key = dc.serializeKey()
|
||||
if (prefs.contains(key))
|
||||
remove(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun saveFromResponse(url: HttpUrl?, cookies: List<Cookie>) {
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: MutableList<Cookie>) {
|
||||
for (cookie in cookies) {
|
||||
val dc = DumbCookie(cookie)
|
||||
save(dc)
|
||||
@ -54,6 +81,10 @@ class DumbCookieJar(
|
||||
}.map { it.cookie }
|
||||
}
|
||||
|
||||
fun getAllDomains(): List<Cookie> {
|
||||
return sessionCookies.map { it.cookie }
|
||||
}
|
||||
|
||||
fun get(domain: String, name: String): String? {
|
||||
return sessionCookies.firstOrNull {
|
||||
it.domainMatches(domain) && it.cookie.name() == name
|
||||
@ -84,7 +115,7 @@ class DumbCookieJar(
|
||||
fun getAll(domain: String): Map<String, String> {
|
||||
return sessionCookies.filter {
|
||||
it.domainMatches(domain)
|
||||
}.map { it.cookie.name() to it.cookie.value() }.toMap()
|
||||
}.associate { it.cookie.name() to it.cookie.value() }
|
||||
}
|
||||
|
||||
fun remove(domain: String, name: String) {
|
||||
@ -100,4 +131,11 @@ class DumbCookieJar(
|
||||
}
|
||||
delete(*toRemove.toTypedArray())
|
||||
}
|
||||
|
||||
fun clearAllDomains() {
|
||||
sessionCookies.clear()
|
||||
prefs.edit {
|
||||
clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,12 @@ import com.mikepenz.iconics.typeface.library.community.material.CommunityMateria
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import eu.szkolny.font.SzkolnyFont
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
@ -53,7 +58,7 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
||||
if (getActivity() == null || context == null) return null
|
||||
activity = getActivity() as MainActivity
|
||||
context?.theme?.applyStyle(Themes.appTheme, true)
|
||||
type = app.config.forProfile().ui.agendaViewType
|
||||
type = app.profile.config.ui.agendaViewType
|
||||
b = when (type) {
|
||||
Profile.AGENDA_DEFAULT -> FragmentAgendaDefaultBinding.inflate(inflater, container, false)
|
||||
Profile.AGENDA_CALENDAR -> FragmentAgendaCalendarBinding.inflate(inflater, container, false)
|
||||
@ -92,7 +97,7 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
||||
activity.bottomSheet.close()
|
||||
type =
|
||||
if (type == Profile.AGENDA_DEFAULT) Profile.AGENDA_CALENDAR else Profile.AGENDA_DEFAULT
|
||||
app.config.forProfile().ui.agendaViewType = type
|
||||
app.profile.config.ui.agendaViewType = type
|
||||
activity.reloadTarget()
|
||||
},
|
||||
BottomSheetSeparatorItem(true),
|
||||
@ -137,13 +142,7 @@ class AgendaFragment : Fragment(), CoroutineScope {
|
||||
|
||||
private suspend fun checkEventTypes() {
|
||||
withContext(Dispatchers.Default) {
|
||||
val eventTypes = app.db.eventTypeDao().getAllNow(app.profileId).map {
|
||||
it.id
|
||||
}
|
||||
val defaultEventTypes = EventType.getTypeColorMap().keys
|
||||
if (!eventTypes.containsAll(defaultEventTypes)) {
|
||||
app.db.eventTypeDao().addDefaultTypes(app.profile)
|
||||
}
|
||||
app.db.eventTypeDao().getAllWithDefaults(app.profile)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,12 @@ import com.github.tibolte.agendacalendarview.agenda.AgendaAdapter
|
||||
import com.github.tibolte.agendacalendarview.models.BaseCalendarEvent
|
||||
import com.github.tibolte.agendacalendarview.models.CalendarEvent
|
||||
import com.github.tibolte.agendacalendarview.models.IDayItem
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
@ -49,7 +54,6 @@ class AgendaFragmentDefault(
|
||||
private val unreadDates = mutableSetOf<Int>()
|
||||
private val events = mutableListOf<CalendarEvent>()
|
||||
private var isInitialized = false
|
||||
private val profileConfig by lazy { app.config.forProfile().ui }
|
||||
|
||||
private val listView
|
||||
get() = b.agendaDefaultView.agendaView.agendaListView
|
||||
@ -107,10 +111,10 @@ class AgendaFragmentDefault(
|
||||
isInitialized = false
|
||||
|
||||
withContext(Dispatchers.Default) {
|
||||
if (profileConfig.agendaLessonChanges)
|
||||
if (app.profile.config.ui.agendaLessonChanges)
|
||||
addLessonChanges(events)
|
||||
|
||||
if (profileConfig.agendaTeacherAbsence)
|
||||
if (app.profile.config.ui.agendaTeacherAbsence)
|
||||
addTeacherAbsence(events)
|
||||
}
|
||||
|
||||
@ -127,7 +131,7 @@ class AgendaFragmentDefault(
|
||||
val dateStart = app.profile.dateSemester1Start.asCalendar
|
||||
val dateEnd = app.profile.dateYearEnd.asCalendar
|
||||
|
||||
val isCompactMode = profileConfig.agendaCompactMode
|
||||
val isCompactMode = app.profile.config.ui.agendaCompactMode
|
||||
|
||||
b.agendaDefaultView.init(
|
||||
events,
|
||||
@ -247,7 +251,7 @@ class AgendaFragmentDefault(
|
||||
) {
|
||||
events.removeAll { it is AgendaEvent || it is AgendaEventGroup }
|
||||
|
||||
if (!profileConfig.agendaGroupByType) {
|
||||
if (!app.profile.config.ui.agendaGroupByType) {
|
||||
events += eventList.map {
|
||||
if (!it.seen)
|
||||
unreadDates.add(it.date.value)
|
||||
|
@ -11,6 +11,7 @@ import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
import com.github.tibolte.agendacalendarview.render.EventRenderer
|
||||
import com.mikepenz.iconics.view.IconicsTextView
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedEventBinding
|
||||
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedEventCompactBinding
|
||||
@ -53,16 +54,24 @@ class AgendaEventRenderer(
|
||||
else
|
||||
event.time!!.stringHM
|
||||
|
||||
val agendaSubjectImportant = App.profile.config.ui.agendaSubjectImportant
|
||||
val eventSubtitle = listOfNotNull(
|
||||
timeText,
|
||||
event.subjectLongName,
|
||||
event.subjectLongName.takeIf { !agendaSubjectImportant },
|
||||
event.typeName.takeIf { agendaSubjectImportant },
|
||||
event.teacherName,
|
||||
event.teamName
|
||||
).join(", ")
|
||||
|
||||
card.foreground.setTintColor(event.eventColor)
|
||||
card.background.setTintColor(event.eventColor)
|
||||
manager.setEventTopic(title, event, doneIconColor = textColor)
|
||||
manager.setEventTopic(
|
||||
title = title,
|
||||
event = event,
|
||||
doneIconColor = textColor,
|
||||
showType = !agendaSubjectImportant,
|
||||
showSubject = agendaSubjectImportant,
|
||||
)
|
||||
title.setTextColor(textColor)
|
||||
subtitle?.text = eventSubtitle
|
||||
subtitle?.setTextColor(textColor)
|
||||
|
@ -18,7 +18,6 @@ import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
|
||||
import pl.szczodrzynski.edziennik.databinding.AttendanceFragmentBinding
|
||||
import pl.szczodrzynski.edziennik.ext.Bundle
|
||||
@ -83,7 +82,7 @@ class AttendanceFragment : Fragment(), CoroutineScope {
|
||||
activity.gainAttention()
|
||||
|
||||
if (pageSelection == 1)
|
||||
pageSelection = app.config.forProfile().attendance.attendancePageSelection
|
||||
pageSelection = app.profile.config.attendance.attendancePageSelection
|
||||
|
||||
val pagerAdapter = FragmentLazyPagerAdapter(
|
||||
parentFragmentManager,
|
||||
@ -114,7 +113,7 @@ class AttendanceFragment : Fragment(), CoroutineScope {
|
||||
currentItem = pageSelection
|
||||
addOnPageSelectedListener {
|
||||
pageSelection = it
|
||||
app.config.forProfile().attendance.attendancePageSelection = it
|
||||
app.profile.config.attendance.attendancePageSelection = it
|
||||
}
|
||||
b.tabLayout.setupWithViewPager(this)
|
||||
}
|
||||
|
@ -129,8 +129,8 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
|
||||
if (attendance.isEmpty())
|
||||
return mutableListOf()
|
||||
|
||||
val groupConsecutiveDays = app.config.forProfile().attendance.groupConsecutiveDays
|
||||
val showPresenceInMonth = app.config.forProfile().attendance.showPresenceInMonth
|
||||
val groupConsecutiveDays = app.profile.config.attendance.groupConsecutiveDays
|
||||
val showPresenceInMonth = app.profile.config.attendance.showPresenceInMonth
|
||||
|
||||
if (viewType == AttendanceFragment.VIEW_DAYS) {
|
||||
val items = attendance
|
||||
|
@ -217,6 +217,7 @@ enum class NavTarget(
|
||||
location = NavTargetLocation.BOTTOM_SHEET,
|
||||
nameRes = R.string.menu_debug,
|
||||
icon = CommunityMaterial.Icon.cmd_android_debug_bridge,
|
||||
devModeOnly = true,
|
||||
),
|
||||
GRADES_EDITOR(
|
||||
id = 501,
|
||||
|
@ -25,6 +25,7 @@ class RecaptchaDialog(
|
||||
private val autoRetry: Boolean = true,
|
||||
private val onSuccess: (recaptchaCode: String) -> Unit,
|
||||
private val onFailure: (() -> Unit)? = null,
|
||||
private val onServerError: (() -> Unit)? = null,
|
||||
onShowListener: ((tag: String) -> Unit)? = null,
|
||||
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||
) : BindingDialog<RecaptchaDialogBinding>(activity, onShowListener, onDismissListener) {
|
||||
@ -44,7 +45,11 @@ class RecaptchaDialog(
|
||||
|
||||
override suspend fun onBeforeShow(): Boolean {
|
||||
val (title, text, bitmap) = withContext(Dispatchers.Default) {
|
||||
val html = loadCaptchaHtml() ?: return@withContext null
|
||||
val html = loadCaptchaHtml()
|
||||
if (html == null) {
|
||||
onServerError?.invoke()
|
||||
return@withContext null
|
||||
}
|
||||
return@withContext loadCaptchaData(html)
|
||||
} ?: run {
|
||||
onFailure?.invoke()
|
||||
|
@ -19,6 +19,7 @@ class RecaptchaPromptDialog(
|
||||
private val referer: String,
|
||||
private val onSuccess: (recaptchaCode: String) -> Unit,
|
||||
private val onCancel: (() -> Unit)?,
|
||||
private val onServerError: (() -> Unit)? = null,
|
||||
onShowListener: ((tag: String) -> Unit)? = null,
|
||||
onDismissListener: ((tag: String) -> Unit)? = null,
|
||||
) : BindingDialog<RecaptchaViewBinding>(activity, onShowListener, onDismissListener) {
|
||||
@ -62,7 +63,8 @@ class RecaptchaPromptDialog(
|
||||
b.checkbox.background = checkboxBackground
|
||||
b.checkbox.foreground = checkboxForeground
|
||||
b.progress.visibility = View.GONE
|
||||
}
|
||||
},
|
||||
onServerError = onServerError,
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import kotlinx.coroutines.launch
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.config.Config
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.SignatureInterceptor
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.EventType.Companion.SOURCE_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.databinding.LabFragmentBinding
|
||||
import pl.szczodrzynski.edziennik.ext.*
|
||||
import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment
|
||||
@ -62,8 +63,10 @@ class LabPageFragment : LazyFragment(), CoroutineScope {
|
||||
b.last10unseen.isVisible = false
|
||||
b.fullSync.isVisible = false
|
||||
b.clearProfile.isVisible = false
|
||||
b.clearEndpointTimers.isVisible = false
|
||||
b.rodo.isVisible = false
|
||||
b.removeHomework.isVisible = false
|
||||
b.resetEventTypes.isVisible = false
|
||||
b.unarchive.isVisible = false
|
||||
b.profile.isVisible = false
|
||||
}
|
||||
@ -82,19 +85,28 @@ class LabPageFragment : LazyFragment(), CoroutineScope {
|
||||
app.db.teacherDao().query(SimpleSQLiteQuery("UPDATE teachers SET teacherSurname = \"\" WHERE profileId = ${App.profileId}"))
|
||||
}
|
||||
|
||||
b.fullSync.onClick {
|
||||
app.db.query(SimpleSQLiteQuery("UPDATE profiles SET empty = 1 WHERE profileId = ${App.profileId}"))
|
||||
app.db.query(SimpleSQLiteQuery("DELETE FROM endpointTimers WHERE profileId = ${App.profileId}"))
|
||||
b.fullSync.onClick {
|
||||
app.profile.empty = true
|
||||
app.profileSave()
|
||||
}
|
||||
|
||||
b.clearProfile.onClick {
|
||||
ProfileRemoveDialog(activity, App.profileId, "FAKE", noProfileRemoval = true).show()
|
||||
}
|
||||
|
||||
b.clearEndpointTimers.onClick {
|
||||
app.db.endpointTimerDao().clear(app.profileId)
|
||||
}
|
||||
|
||||
b.removeHomework.onClick {
|
||||
app.db.eventDao().getRawNow("UPDATE events SET homeworkBody = NULL WHERE profileId = ${App.profileId}")
|
||||
}
|
||||
|
||||
b.resetEventTypes.onClick {
|
||||
app.db.eventTypeDao().clearBySource(App.profileId, SOURCE_DEFAULT)
|
||||
app.db.eventTypeDao().getAllWithDefaults(App.profile)
|
||||
}
|
||||
|
||||
b.chucker.isChecked = App.enableChucker
|
||||
b.chucker.onChange { _, isChecked ->
|
||||
app.config.enableChucker = isChecked
|
||||
@ -167,28 +179,39 @@ class LabPageFragment : LazyFragment(), CoroutineScope {
|
||||
return@setOnChangeListener true
|
||||
}
|
||||
|
||||
b.clearCookies.onClick {
|
||||
app.cookieJar.clearAllDomains()
|
||||
}
|
||||
|
||||
val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity)
|
||||
startCoroutineTimer(500L, 300L) {
|
||||
val text = app.cookieJar.sessionCookies
|
||||
.map { it.cookie }
|
||||
.sortedBy { it.domain() }
|
||||
.groupBy { it.domain() }
|
||||
.map {
|
||||
listOf(
|
||||
it.key.asBoldSpannable(),
|
||||
":\n",
|
||||
it.value
|
||||
.sortedBy { it.name() }
|
||||
.map {
|
||||
listOf(
|
||||
" ",
|
||||
it.name(),
|
||||
"=",
|
||||
it.value().decode().take(40).asItalicSpannable().asColoredSpannable(colorSecondary)
|
||||
).concat("")
|
||||
}.concat("\n")
|
||||
).concat("")
|
||||
}.concat("\n\n")
|
||||
val text = app.cookieJar.getAllDomains()
|
||||
.sortedBy { it.domain() }
|
||||
.groupBy { it.domain() }
|
||||
.map { pair ->
|
||||
listOf(
|
||||
pair.key.asBoldSpannable(),
|
||||
":\n",
|
||||
pair.value
|
||||
.sortedBy { it.name() }
|
||||
.map { cookie ->
|
||||
listOf(
|
||||
" ",
|
||||
if (cookie.persistent())
|
||||
cookie.name()
|
||||
.asUnderlineSpannable()
|
||||
else
|
||||
cookie.name(),
|
||||
"=",
|
||||
cookie.value()
|
||||
.decode()
|
||||
.take(40)
|
||||
.asItalicSpannable()
|
||||
.asColoredSpannable(colorSecondary),
|
||||
).concat("")
|
||||
}.concat("\n")
|
||||
).concat("")
|
||||
}.concat("\n\n")
|
||||
b.cookies.text = text
|
||||
}
|
||||
|
||||
|
@ -12,11 +12,16 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.gson.*
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import com.google.gson.JsonPrimitive
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.databinding.TemplateListPageFragmentBinding
|
||||
import pl.szczodrzynski.edziennik.ext.*
|
||||
@ -66,7 +71,7 @@ class LabProfileFragment : LazyFragment(), CoroutineScope {
|
||||
"LoginStore" -> loginStore
|
||||
"LoginStore / data" -> loginStore?.data ?: JsonObject()
|
||||
"Config" -> app.config.values
|
||||
"Config (profile)" -> app.config.forProfile().values
|
||||
"Config (profile)" -> app.profile.config.values
|
||||
else -> when (obj) {
|
||||
is JsonObject -> (obj as JsonObject).get(el)
|
||||
is JsonArray -> (obj as JsonArray).get(el.toInt())
|
||||
@ -176,7 +181,7 @@ class LabProfileFragment : LazyFragment(), CoroutineScope {
|
||||
json.add("LoginStore", app.gson.toJsonTree(loginStore))
|
||||
json.add("LoginStore / data", loginStore?.data ?: JsonObject())
|
||||
json.add("Config", JsonParser.parseString(app.gson.toJson(app.config.values.toSortedMap())))
|
||||
json.add("Config (profile)", JsonParser.parseString(app.gson.toJson(app.config.forProfile().values.toSortedMap())))
|
||||
json.add("Config (profile)", JsonParser.parseString(app.gson.toJson(app.profile.config.values.toSortedMap())))
|
||||
}
|
||||
adapter.items = LabJsonAdapter.expand(json, 0)
|
||||
adapter.notifyDataSetChanged()
|
||||
|
@ -6,13 +6,11 @@ package pl.szczodrzynski.edziennik.ui.dialogs.settings
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogConfigAgendaBinding
|
||||
import pl.szczodrzynski.edziennik.ext.onChange
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog
|
||||
import java.util.*
|
||||
|
||||
class AgendaConfigDialog(
|
||||
activity: AppCompatActivity,
|
||||
@ -32,48 +30,35 @@ class AgendaConfigDialog(
|
||||
override fun inflate(layoutInflater: LayoutInflater) =
|
||||
DialogConfigAgendaBinding.inflate(layoutInflater)
|
||||
|
||||
private val profileConfig by lazy { app.config.forProfile().ui }
|
||||
|
||||
override suspend fun loadConfig() {
|
||||
b.config = profileConfig
|
||||
b.isAgendaMode = profileConfig.agendaViewType == Profile.AGENDA_DEFAULT
|
||||
b.config = app.profile.config
|
||||
b.isAgendaMode = app.profile.config.ui.agendaViewType == Profile.AGENDA_DEFAULT
|
||||
|
||||
b.eventSharingEnabled.isChecked =
|
||||
app.profile.enableSharedEvents && app.profile.canShare
|
||||
var calledFromListener = false
|
||||
b.eventSharingEnabled.isChecked = app.profile.canShare
|
||||
b.shareByDefault.isEnabled = app.profile.canShare
|
||||
b.eventSharingEnabled.onChange { _, isChecked ->
|
||||
if (isChecked && !app.profile.canShare) {
|
||||
b.eventSharingEnabled.isChecked = false
|
||||
val dialog = RegistrationConfigDialog(
|
||||
activity,
|
||||
app.profile,
|
||||
onChangeListener = { enabled ->
|
||||
b.eventSharingEnabled.isChecked = enabled
|
||||
setEventSharingEnabled(enabled)
|
||||
},
|
||||
onShowListener,
|
||||
onDismissListener,
|
||||
)
|
||||
dialog.showEnableDialog()
|
||||
if (calledFromListener) {
|
||||
calledFromListener = false
|
||||
return@onChange
|
||||
}
|
||||
setEventSharingEnabled(isChecked)
|
||||
b.eventSharingEnabled.isChecked = !isChecked
|
||||
val dialog = RegistrationConfigDialog(
|
||||
activity,
|
||||
app.profile,
|
||||
onChangeListener = { enabled ->
|
||||
calledFromListener = true
|
||||
b.eventSharingEnabled.isChecked = enabled
|
||||
b.shareByDefault.isEnabled = enabled
|
||||
},
|
||||
onShowListener,
|
||||
onDismissListener,
|
||||
)
|
||||
if (isChecked)
|
||||
dialog.showEnableDialog()
|
||||
else
|
||||
dialog.showDisableDialog()
|
||||
return@onChange
|
||||
}
|
||||
}
|
||||
|
||||
private fun setEventSharingEnabled(enabled: Boolean) {
|
||||
if (enabled == app.profile.enableSharedEvents)
|
||||
return
|
||||
app.profile.enableSharedEvents = enabled
|
||||
app.profileSave()
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.event_sharing)
|
||||
.setMessage(
|
||||
if (enabled)
|
||||
R.string.settings_register_shared_events_dialog_enabled_text
|
||||
else
|
||||
R.string.settings_register_shared_events_dialog_disabled_text
|
||||
)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
@ -29,23 +29,21 @@ class AttendanceConfigDialog(
|
||||
override fun inflate(layoutInflater: LayoutInflater) =
|
||||
AttendanceConfigDialogBinding.inflate(layoutInflater)
|
||||
|
||||
private val profileConfig by lazy { app.config.getFor(app.profileId).attendance }
|
||||
|
||||
override suspend fun loadConfig() {
|
||||
b.useSymbols.isChecked = profileConfig.useSymbols
|
||||
b.groupConsecutiveDays.isChecked = profileConfig.groupConsecutiveDays
|
||||
b.showPresenceInMonth.isChecked = profileConfig.showPresenceInMonth
|
||||
b.useSymbols.isChecked = app.profile.config.attendance.useSymbols
|
||||
b.groupConsecutiveDays.isChecked = app.profile.config.attendance.groupConsecutiveDays
|
||||
b.showPresenceInMonth.isChecked = app.profile.config.attendance.showPresenceInMonth
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
b.useSymbols.onChange { _, isChecked ->
|
||||
profileConfig.useSymbols = isChecked
|
||||
app.profile.config.attendance.useSymbols = isChecked
|
||||
}
|
||||
b.groupConsecutiveDays.onChange { _, isChecked ->
|
||||
profileConfig.groupConsecutiveDays = isChecked
|
||||
app.profile.config.attendance.groupConsecutiveDays = isChecked
|
||||
}
|
||||
b.showPresenceInMonth.onChange { _, isChecked ->
|
||||
profileConfig.showPresenceInMonth = isChecked
|
||||
app.profile.config.attendance.showPresenceInMonth = isChecked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,17 +45,15 @@ class GradesConfigDialog(
|
||||
override fun inflate(layoutInflater: LayoutInflater) =
|
||||
DialogConfigGradesBinding.inflate(layoutInflater)
|
||||
|
||||
private val profileConfig by lazy { app.config.getFor(app.profileId).grades }
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override suspend fun loadConfig() {
|
||||
b.customPlusCheckBox.isChecked = profileConfig.plusValue != null
|
||||
b.customPlusCheckBox.isChecked = app.profile.config.grades.plusValue != null
|
||||
b.customPlusValue.isVisible = b.customPlusCheckBox.isChecked
|
||||
b.customMinusCheckBox.isChecked = profileConfig.minusValue != null
|
||||
b.customMinusCheckBox.isChecked = app.profile.config.grades.minusValue != null
|
||||
b.customMinusValue.isVisible = b.customMinusCheckBox.isChecked
|
||||
|
||||
b.customPlusValue.progress = profileConfig.plusValue ?: 0.5f
|
||||
b.customMinusValue.progress = profileConfig.minusValue ?: 0.25f
|
||||
b.customPlusValue.progress = app.profile.config.grades.plusValue ?: 0.5f
|
||||
b.customMinusValue.progress = app.profile.config.grades.minusValue ?: 0.25f
|
||||
|
||||
when (config.orderBy) {
|
||||
ORDER_BY_DATE_DESC -> b.sortGradesByDateRadio
|
||||
@ -63,13 +61,13 @@ class GradesConfigDialog(
|
||||
else -> null
|
||||
}?.isChecked = true
|
||||
|
||||
when (profileConfig.colorMode) {
|
||||
when (app.profile.config.grades.colorMode) {
|
||||
COLOR_MODE_DEFAULT -> b.gradeColorFromERegister
|
||||
COLOR_MODE_WEIGHTED -> b.gradeColorByValue
|
||||
else -> null
|
||||
}?.isChecked = true
|
||||
|
||||
when (profileConfig.yearAverageMode) {
|
||||
when (app.profile.config.grades.yearAverageMode) {
|
||||
YEAR_ALL_GRADES -> b.gradeAverageMode4
|
||||
YEAR_1_AVG_2_AVG -> b.gradeAverageMode0
|
||||
YEAR_1_SEM_2_AVG -> b.gradeAverageMode1
|
||||
@ -79,21 +77,21 @@ class GradesConfigDialog(
|
||||
}?.isChecked = true
|
||||
|
||||
b.dontCountGrades.isChecked =
|
||||
profileConfig.dontCountEnabled && profileConfig.dontCountGrades.isNotEmpty()
|
||||
b.hideImproved.isChecked = profileConfig.hideImproved
|
||||
b.averageWithoutWeight.isChecked = profileConfig.averageWithoutWeight
|
||||
app.profile.config.grades.dontCountEnabled && app.profile.config.grades.dontCountGrades.isNotEmpty()
|
||||
b.hideImproved.isChecked = app.profile.config.grades.hideImproved
|
||||
b.averageWithoutWeight.isChecked = app.profile.config.grades.averageWithoutWeight
|
||||
|
||||
if (profileConfig.dontCountGrades.isEmpty()) {
|
||||
if (app.profile.config.grades.dontCountGrades.isEmpty()) {
|
||||
b.dontCountGradesText.setText("nb, 0, bz, bd")
|
||||
} else {
|
||||
b.dontCountGradesText.setText(profileConfig.dontCountGrades.join(", "))
|
||||
b.dontCountGradesText.setText(app.profile.config.grades.dontCountGrades.join(", "))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun saveConfig() {
|
||||
profileConfig.plusValue =
|
||||
app.profile.config.grades.plusValue =
|
||||
if (b.customPlusCheckBox.isChecked) b.customPlusValue.progress else null
|
||||
profileConfig.minusValue =
|
||||
app.profile.config.grades.minusValue =
|
||||
if (b.customMinusCheckBox.isChecked) b.customMinusValue.progress else null
|
||||
|
||||
b.dontCountGradesText.setText(
|
||||
@ -103,8 +101,8 @@ class GradesConfigDialog(
|
||||
?.lowercase()
|
||||
?.replace(", ", ",")
|
||||
)
|
||||
profileConfig.dontCountEnabled = b.dontCountGrades.isChecked
|
||||
profileConfig.dontCountGrades = b.dontCountGradesText.text
|
||||
app.profile.config.grades.dontCountEnabled = b.dontCountGrades.isChecked
|
||||
app.profile.config.grades.dontCountGrades = b.dontCountGradesText.text
|
||||
?.split(",")
|
||||
?.map { it.trim() }
|
||||
?: listOf()
|
||||
@ -121,39 +119,39 @@ class GradesConfigDialog(
|
||||
// who the hell named those methods
|
||||
// THIS SHIT DOES NOT EVEN WORK
|
||||
b.customPlusValue.doOnStopTrackingTouch {
|
||||
profileConfig.plusValue = it.progress
|
||||
app.profile.config.grades.plusValue = it.progress
|
||||
}
|
||||
b.customMinusValue.doOnStopTrackingTouch {
|
||||
profileConfig.minusValue = it.progress
|
||||
app.profile.config.grades.minusValue = it.progress
|
||||
}
|
||||
|
||||
b.sortGradesByDateRadio.setOnSelectedListener { config.orderBy = ORDER_BY_DATE_DESC }
|
||||
b.sortGradesBySubjectRadio.setOnSelectedListener { config.orderBy = ORDER_BY_SUBJECT_ASC }
|
||||
|
||||
b.gradeColorFromERegister.setOnSelectedListener {
|
||||
profileConfig.colorMode = COLOR_MODE_DEFAULT
|
||||
app.profile.config.grades.colorMode = COLOR_MODE_DEFAULT
|
||||
}
|
||||
b.gradeColorByValue.setOnSelectedListener { profileConfig.colorMode = COLOR_MODE_WEIGHTED }
|
||||
b.gradeColorByValue.setOnSelectedListener { app.profile.config.grades.colorMode = COLOR_MODE_WEIGHTED }
|
||||
|
||||
b.gradeAverageMode4.setOnSelectedListener {
|
||||
profileConfig.yearAverageMode = YEAR_ALL_GRADES
|
||||
app.profile.config.grades.yearAverageMode = YEAR_ALL_GRADES
|
||||
}
|
||||
b.gradeAverageMode0.setOnSelectedListener {
|
||||
profileConfig.yearAverageMode = YEAR_1_AVG_2_AVG
|
||||
app.profile.config.grades.yearAverageMode = YEAR_1_AVG_2_AVG
|
||||
}
|
||||
b.gradeAverageMode1.setOnSelectedListener {
|
||||
profileConfig.yearAverageMode = YEAR_1_SEM_2_AVG
|
||||
app.profile.config.grades.yearAverageMode = YEAR_1_SEM_2_AVG
|
||||
}
|
||||
b.gradeAverageMode2.setOnSelectedListener {
|
||||
profileConfig.yearAverageMode = YEAR_1_AVG_2_SEM
|
||||
app.profile.config.grades.yearAverageMode = YEAR_1_AVG_2_SEM
|
||||
}
|
||||
b.gradeAverageMode3.setOnSelectedListener {
|
||||
profileConfig.yearAverageMode = YEAR_1_SEM_2_SEM
|
||||
app.profile.config.grades.yearAverageMode = YEAR_1_SEM_2_SEM
|
||||
}
|
||||
|
||||
b.hideImproved.onChange { _, isChecked -> profileConfig.hideImproved = isChecked }
|
||||
b.hideImproved.onChange { _, isChecked -> app.profile.config.grades.hideImproved = isChecked }
|
||||
b.averageWithoutWeight.onChange { _, isChecked ->
|
||||
profileConfig.averageWithoutWeight = isChecked
|
||||
app.profile.config.grades.averageWithoutWeight = isChecked
|
||||
}
|
||||
|
||||
b.averageWithoutWeightHelp.onClick {
|
||||
|
@ -28,13 +28,11 @@ class MessagesConfigDialog(
|
||||
override fun inflate(layoutInflater: LayoutInflater) =
|
||||
MessagesConfigDialogBinding.inflate(layoutInflater)
|
||||
|
||||
private val profileConfig by lazy { app.config.getFor(app.profileId).ui }
|
||||
|
||||
override suspend fun loadConfig() {
|
||||
b.config = profileConfig
|
||||
b.config = app.profile.config.ui
|
||||
|
||||
b.greetingText.setText(
|
||||
profileConfig.messagesGreetingText
|
||||
app.profile.config.ui.messagesGreetingText
|
||||
?: "\n\nZ poważaniem\n${app.profile.accountOwnerName}"
|
||||
)
|
||||
}
|
||||
@ -42,8 +40,8 @@ class MessagesConfigDialog(
|
||||
override suspend fun saveConfig() {
|
||||
val greetingText = b.greetingText.text?.toString()?.trim()
|
||||
if (greetingText.isNullOrEmpty())
|
||||
profileConfig.messagesGreetingText = null
|
||||
app.profile.config.ui.messagesGreetingText = null
|
||||
else
|
||||
profileConfig.messagesGreetingText = "\n\n$greetingText"
|
||||
app.profile.config.ui.messagesGreetingText = "\n\n$greetingText"
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ class NotificationFilterDialog(
|
||||
.associateBy { it.titleRes.resolveString(activity) as CharSequence }
|
||||
|
||||
override fun getDefaultSelectedItems() = NotificationType.values()
|
||||
.filter { it.enabledByDefault != null && it !in app.config.forProfile().sync.notificationFilter }
|
||||
.filter { it.enabledByDefault != null && it !in app.profile.config.sync.notificationFilter }
|
||||
.toSet()
|
||||
|
||||
override suspend fun onShow() = Unit
|
||||
@ -47,7 +47,7 @@ class NotificationFilterDialog(
|
||||
.setTitle(R.string.are_you_sure)
|
||||
.setMessage(R.string.notification_filter_warning)
|
||||
.setPositiveButton(R.string.ok) { _, _ ->
|
||||
app.config.forProfile().sync.notificationFilter = disabledTypes
|
||||
app.profile.config.sync.notificationFilter = disabledTypes
|
||||
dismiss()
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
@ -55,7 +55,7 @@ class NotificationFilterDialog(
|
||||
return NO_DISMISS
|
||||
}
|
||||
|
||||
app.config.forProfile().sync.notificationFilter = disabledTypes
|
||||
app.profile.config.sync.notificationFilter = disabledTypes
|
||||
|
||||
return DISMISS
|
||||
}
|
||||
|
@ -7,7 +7,11 @@ package pl.szczodrzynski.edziennik.ui.dialogs.settings
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||
@ -117,7 +121,7 @@ class RegistrationConfigDialog(
|
||||
profile.registration = Profile.REGISTRATION_ENABLED
|
||||
|
||||
// force full registration of the user
|
||||
App.config.getFor(profile.id).hash = ""
|
||||
profile.config.hash = ""
|
||||
|
||||
SzkolnyApi(app).runCatching(activity) {
|
||||
AppSync(app, mutableListOf(), listOf(profile), this).run(
|
||||
|
@ -30,14 +30,12 @@ class TimetableConfigDialog(
|
||||
override fun inflate(layoutInflater: LayoutInflater) =
|
||||
TimetableConfigDialogBinding.inflate(layoutInflater)
|
||||
|
||||
private val profileConfig by lazy { app.config.getFor(app.profileId).ui }
|
||||
|
||||
override fun initView() {
|
||||
b.features = app.profile.loginStoreType.features
|
||||
}
|
||||
|
||||
override suspend fun loadConfig() {
|
||||
b.config = profileConfig
|
||||
b.config = app.profile.config.ui
|
||||
}
|
||||
|
||||
override suspend fun saveConfig() {
|
||||
|
@ -113,9 +113,20 @@ class EventDetailsDialog(
|
||||
|
||||
b.typeColor.background?.setTintColor(event.eventColor)
|
||||
|
||||
b.details = mutableListOf(
|
||||
val agendaSubjectImportant = event.subjectLongName != null
|
||||
&& App.config[event.profileId].ui.agendaSubjectImportant
|
||||
|
||||
b.name = if (agendaSubjectImportant)
|
||||
event.subjectLongName
|
||||
else
|
||||
event.typeName
|
||||
|
||||
b.details = listOfNotNull(
|
||||
if (agendaSubjectImportant)
|
||||
event.typeName
|
||||
else
|
||||
event.subjectLongName,
|
||||
event.teamName?.asColoredSpannable(colorSecondary)
|
||||
event.teamName?.asColoredSpannable(colorSecondary)
|
||||
).concat(bullet)
|
||||
|
||||
b.addedBy.setText(
|
||||
|
@ -24,6 +24,7 @@ class EventListAdapter(
|
||||
val showDate: Boolean = false,
|
||||
val showColor: Boolean = true,
|
||||
val showType: Boolean = true,
|
||||
val showTypeColor: Boolean = showType,
|
||||
val showTime: Boolean = true,
|
||||
val showSubject: Boolean = true,
|
||||
val markAsSeen: Boolean = true,
|
||||
|
@ -13,23 +13,37 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialog
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.config.AppData
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskAllFinishedEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskFinishedEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Subject
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
|
||||
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
|
||||
import pl.szczodrzynski.edziennik.ext.*
|
||||
import pl.szczodrzynski.edziennik.ext.JsonObject
|
||||
import pl.szczodrzynski.edziennik.ext.appendView
|
||||
import pl.szczodrzynski.edziennik.ext.getStudentData
|
||||
import pl.szczodrzynski.edziennik.ext.onChange
|
||||
import pl.szczodrzynski.edziennik.ext.onClick
|
||||
import pl.szczodrzynski.edziennik.ext.removeFromParent
|
||||
import pl.szczodrzynski.edziennik.ext.setText
|
||||
import pl.szczodrzynski.edziennik.ext.setTintColor
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.RegistrationConfigDialog
|
||||
import pl.szczodrzynski.edziennik.ui.views.TimeDropdown.Companion.DISPLAY_LESSONS
|
||||
@ -107,8 +121,14 @@ class EventManualDialog(
|
||||
}
|
||||
|
||||
override suspend fun onShow() {
|
||||
b.shareSwitch.isChecked = editingShared
|
||||
b.shareSwitch.isEnabled = !editingShared || (editingShared && editingOwn)
|
||||
val data = withContext(Dispatchers.IO) {
|
||||
val profile = app.db.profileDao().getByIdSuspend(profileId) ?: return@withContext null
|
||||
AppData.get(profile.loginStoreType)
|
||||
}
|
||||
if (data?.uiConfig?.eventManualShowSubjectDropdown == true) {
|
||||
b.subjectDropdownLayout.removeFromParent()
|
||||
b.timeDropdownLayout.appendView(b.subjectDropdownLayout)
|
||||
}
|
||||
|
||||
b.showMore.onClick { // TODO iconics is broken
|
||||
it.apply {
|
||||
@ -137,6 +157,11 @@ class EventManualDialog(
|
||||
}
|
||||
|
||||
loadLists()
|
||||
|
||||
val shareByDefault = app.profile.config.shareByDefault && profile.canShare
|
||||
|
||||
b.shareSwitch.isChecked = editingShared || editingEvent == null && shareByDefault
|
||||
b.shareSwitch.isEnabled = !editingShared || editingOwn
|
||||
}
|
||||
|
||||
private fun updateShareText(checked: Boolean = b.shareSwitch.isChecked) {
|
||||
|
@ -113,7 +113,7 @@ class EventViewHolder(
|
||||
b.attachmentIcon.isVisible = item.hasAttachments
|
||||
|
||||
b.typeColor.background?.setTintColor(item.eventColor)
|
||||
b.typeColor.isVisible = adapter.showType && adapter.showColor
|
||||
b.typeColor.isVisible = adapter.showTypeColor
|
||||
|
||||
b.editButton.isVisible = !adapter.simpleMode
|
||||
&& item.addedManually
|
||||
|
@ -81,7 +81,7 @@ class GradesListFragment : Fragment(), CoroutineScope {
|
||||
}
|
||||
|
||||
val items = when {
|
||||
app.config.forProfile().grades.hideSticksFromOld && App.devMode -> grades.filter { it.value != 1.0f }
|
||||
app.profile.config.grades.hideSticksFromOld && App.devMode -> grades.filter { it.value != 1.0f }
|
||||
else -> grades
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,9 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentGradesEditorBinding
|
||||
import pl.szczodrzynski.edziennik.ext.getFloat
|
||||
@ -37,7 +39,7 @@ class GradesEditorFragment : Fragment() {
|
||||
private val navController: NavController by lazy { Navigation.findNavController(b.root) }
|
||||
*/
|
||||
|
||||
private val config by lazy { app.config.getFor(App.profileId).grades }
|
||||
private val config by lazy { app.profile.config.grades }
|
||||
|
||||
private var subjectId: Long = -1
|
||||
private var semester: Int = 1
|
||||
|
@ -4,8 +4,10 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.home
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.jetradarmobile.snowfall.SnowfallView
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
@ -20,6 +22,7 @@ import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
|
||||
import pl.szczodrzynski.edziennik.ext.timeLeft
|
||||
import pl.szczodrzynski.edziennik.ext.timeTill
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.BellSyncTimeChooseDialog
|
||||
import pl.szczodrzynski.edziennik.utils.BigNightUtil
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
@ -61,6 +64,7 @@ class CounterActivity : AppCompatActivity(), CoroutineScope {
|
||||
it.type != Lesson.TYPE_SHIFTED_SOURCE
|
||||
})
|
||||
}
|
||||
lessonList.onEach { it.filterNotes() }
|
||||
}
|
||||
|
||||
b.bellSync.setImageDrawable(
|
||||
@ -81,6 +85,27 @@ class CounterActivity : AppCompatActivity(), CoroutineScope {
|
||||
counterJob = startCoroutineTimer(repeatMillis = 500) {
|
||||
update()
|
||||
}
|
||||
|
||||
// IT'S WINTER MY DUDES
|
||||
val today = Date.getToday()
|
||||
if ((today.month / 3 % 4 == 0) && app.config.ui.snowfall) {
|
||||
b.rootFrame.addView(layoutInflater.inflate(R.layout.snowfall, b.rootFrame, false))
|
||||
} else if (app.config.ui.eggfall && BigNightUtil().isDataWielkanocyNearDzisiaj()) {
|
||||
val eggfall = layoutInflater.inflate(
|
||||
R.layout.eggfall,
|
||||
b.rootFrame,
|
||||
false
|
||||
) as SnowfallView
|
||||
eggfall.setSnowflakeBitmaps(listOf(
|
||||
BitmapFactory.decodeResource(resources, R.drawable.egg1),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.egg2),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.egg3),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.egg4),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.egg5),
|
||||
BitmapFactory.decodeResource(resources, R.drawable.egg6)
|
||||
))
|
||||
b.rootFrame.addView(eggfall)
|
||||
}
|
||||
}}
|
||||
|
||||
private fun update() {
|
||||
@ -101,13 +126,15 @@ class CounterActivity : AppCompatActivity(), CoroutineScope {
|
||||
|
||||
when {
|
||||
actual != null -> {
|
||||
b.lessonName.text = actual.displaySubjectName
|
||||
b.lessonName.text = actual.getNoteSubstituteText(showNotes = true)
|
||||
?: actual.displaySubjectName
|
||||
|
||||
val left = actual.displayEndTime!! - now
|
||||
b.timeLeft.text = timeLeft(left.toInt(), "\n", countInSeconds)
|
||||
}
|
||||
next != null -> {
|
||||
b.lessonName.text = next.displaySubjectName
|
||||
b.lessonName.text = next.getNoteSubstituteText(showNotes = true)
|
||||
?: next.displaySubjectName
|
||||
|
||||
val till = next.displayStartTime!! - now
|
||||
b.timeLeft.text = timeTill(till.toInt(), "\n", countInSeconds)
|
||||
|
@ -37,18 +37,17 @@ class HomeConfigDialog(
|
||||
).mapKeys { (resId, _) -> activity.getString(resId) }
|
||||
|
||||
override fun getDefaultSelectedItems() =
|
||||
profileConfig.homeCards
|
||||
app.profile.config.ui.homeCards
|
||||
.filter { it.profileId == App.profileId }
|
||||
.map { it.cardId }
|
||||
.toSet()
|
||||
|
||||
override suspend fun onShow() = Unit
|
||||
|
||||
private val profileConfig by lazy { app.config.getFor(app.profileId).ui }
|
||||
private var configChanged = false
|
||||
|
||||
override suspend fun onPositiveClick(): Boolean {
|
||||
val homeCards = profileConfig.homeCards.toMutableList()
|
||||
val homeCards = app.profile.config.ui.homeCards.toMutableList()
|
||||
homeCards.removeAll { it.profileId == App.profileId }
|
||||
homeCards += getMultiSelection().mapNotNull {
|
||||
HomeCardModel(
|
||||
@ -56,7 +55,7 @@ class HomeConfigDialog(
|
||||
cardId = it as? Int ?: return@mapNotNull null
|
||||
)
|
||||
}
|
||||
profileConfig.homeCards = homeCards
|
||||
app.profile.config.ui.homeCards = homeCards
|
||||
return DISMISS
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user