mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-06-24 11:05:48 +02:00
Compare commits
13 Commits
v4.0-beta.
...
v4.0-beta.
Author | SHA1 | Date | |
---|---|---|---|
afc88d316b | |||
b141279811 | |||
1997ea25d5 | |||
f4b49eecd4 | |||
a4493ec964 | |||
af8bda9e92 | |||
06d252e4ca | |||
67be456bb0 | |||
aa5e225148 | |||
367f46fac8 | |||
d2f14093ec | |||
43ed621879 | |||
15c8134d13 |
@ -1,4 +1,4 @@
|
|||||||
<h3>Wersja 4.0-beta.9, 2020-02-19</h3>
|
<h3>Wersja 4.0-beta.10, 2020-02-24</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkosć oraz poprawność pobieranych danych.</li>
|
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkosć oraz poprawność pobieranych danych.</li>
|
||||||
<li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli 👏</li>
|
<li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli 👏</li>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
/*secret password - removed for source code publication*/
|
/*secret password - removed for source code publication*/
|
||||||
static toys AES_IV[16] = {
|
static toys AES_IV[16] = {
|
||||||
0xe3, 0x65, 0x9e, 0xe5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
0xd6, 0x0d, 0xa0, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||||
|
|
||||||
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
|
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ import com.google.android.gms.security.ProviderInstaller
|
|||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonElement
|
import com.google.gson.JsonElement
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.JsonParser
|
||||||
import im.wangchao.mhttp.Response
|
import im.wangchao.mhttp.Response
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@ -94,8 +95,8 @@ fun JsonObject?.getInt(key: String): Int? = get(key)?.let { if (it.isJsonNull) n
|
|||||||
fun JsonObject?.getLong(key: String): Long? = get(key)?.let { if (it.isJsonNull) null else it.asLong }
|
fun JsonObject?.getLong(key: String): Long? = get(key)?.let { if (it.isJsonNull) null else it.asLong }
|
||||||
fun JsonObject?.getFloat(key: String): Float? = get(key)?.let { if(it.isJsonNull) null else it.asFloat }
|
fun JsonObject?.getFloat(key: String): Float? = get(key)?.let { if(it.isJsonNull) null else it.asFloat }
|
||||||
fun JsonObject?.getChar(key: String): Char? = get(key)?.let { if(it.isJsonNull) null else it.asCharacter }
|
fun JsonObject?.getChar(key: String): Char? = get(key)?.let { if(it.isJsonNull) null else it.asCharacter }
|
||||||
fun JsonObject?.getJsonObject(key: String): JsonObject? = get(key)?.let { if (it.isJsonNull) null else it.asJsonObject }
|
fun JsonObject?.getJsonObject(key: String): JsonObject? = get(key)?.let { if (it.isJsonObject) it.asJsonObject else null }
|
||||||
fun JsonObject?.getJsonArray(key: String): JsonArray? = get(key)?.let { if (it.isJsonNull) null else it.asJsonArray }
|
fun JsonObject?.getJsonArray(key: String): JsonArray? = get(key)?.let { if (it.isJsonArray) it.asJsonArray else null }
|
||||||
|
|
||||||
fun JsonObject?.getBoolean(key: String, defaultValue: Boolean): Boolean = get(key)?.let { if (it.isJsonNull) defaultValue else it.asBoolean } ?: defaultValue
|
fun JsonObject?.getBoolean(key: String, defaultValue: Boolean): Boolean = get(key)?.let { if (it.isJsonNull) defaultValue else it.asBoolean } ?: defaultValue
|
||||||
fun JsonObject?.getString(key: String, defaultValue: String): String = get(key)?.let { if (it.isJsonNull) defaultValue else it.asString } ?: defaultValue
|
fun JsonObject?.getString(key: String, defaultValue: String): String = get(key)?.let { if (it.isJsonNull) defaultValue else it.asString } ?: defaultValue
|
||||||
@ -103,8 +104,19 @@ fun JsonObject?.getInt(key: String, defaultValue: Int): Int = get(key)?.let { if
|
|||||||
fun JsonObject?.getLong(key: String, defaultValue: Long): Long = get(key)?.let { if (it.isJsonNull) defaultValue else it.asLong } ?: defaultValue
|
fun JsonObject?.getLong(key: String, defaultValue: Long): Long = get(key)?.let { if (it.isJsonNull) defaultValue else it.asLong } ?: defaultValue
|
||||||
fun JsonObject?.getFloat(key: String, defaultValue: Float): Float = get(key)?.let { if(it.isJsonNull) defaultValue else it.asFloat } ?: defaultValue
|
fun JsonObject?.getFloat(key: String, defaultValue: Float): Float = get(key)?.let { if(it.isJsonNull) defaultValue else it.asFloat } ?: defaultValue
|
||||||
fun JsonObject?.getChar(key: String, defaultValue: Char): Char = get(key)?.let { if(it.isJsonNull) defaultValue else it.asCharacter } ?: defaultValue
|
fun JsonObject?.getChar(key: String, defaultValue: Char): Char = get(key)?.let { if(it.isJsonNull) defaultValue else it.asCharacter } ?: defaultValue
|
||||||
fun JsonObject?.getJsonObject(key: String, defaultValue: JsonObject): JsonObject = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonObject } ?: defaultValue
|
fun JsonObject?.getJsonObject(key: String, defaultValue: JsonObject): JsonObject = get(key)?.let { if (it.isJsonObject) it.asJsonObject else defaultValue } ?: defaultValue
|
||||||
fun JsonObject?.getJsonArray(key: String, defaultValue: JsonArray): JsonArray = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonArray } ?: defaultValue
|
fun JsonObject?.getJsonArray(key: String, defaultValue: JsonArray): JsonArray = get(key)?.let { if (it.isJsonArray) it.asJsonArray else defaultValue } ?: defaultValue
|
||||||
|
|
||||||
|
fun JsonArray.getBoolean(key: Int): Boolean? = if (key >= size()) null else get(key)?.let { if (it.isJsonNull) null else it.asBoolean }
|
||||||
|
fun JsonArray.getString(key: Int): String? = if (key >= size()) null else get(key)?.let { if (it.isJsonNull) null else it.asString }
|
||||||
|
fun JsonArray.getInt(key: Int): Int? = if (key >= size()) null else get(key)?.let { if (it.isJsonNull) null else it.asInt }
|
||||||
|
fun JsonArray.getLong(key: Int): Long? = if (key >= size()) null else get(key)?.let { if (it.isJsonNull) null else it.asLong }
|
||||||
|
fun JsonArray.getFloat(key: Int): Float? = if (key >= size()) null else get(key)?.let { if(it.isJsonNull) null else it.asFloat }
|
||||||
|
fun JsonArray.getChar(key: Int): Char? = if (key >= size()) null else get(key)?.let { if(it.isJsonNull) null else it.asCharacter }
|
||||||
|
fun JsonArray.getJsonObject(key: Int): JsonObject? = if (key >= size()) null else get(key)?.let { if (it.isJsonObject) it.asJsonObject else null }
|
||||||
|
fun JsonArray.getJsonArray(key: Int): JsonArray? = if (key >= size()) null else get(key)?.let { if (it.isJsonArray) it.asJsonArray else null }
|
||||||
|
|
||||||
|
fun String.toJsonObject(): JsonObject? = try { JsonParser().parse(this).asJsonObject } catch (ignore: Exception) { null }
|
||||||
|
|
||||||
operator fun JsonObject.set(key: String, value: JsonElement) = this.add(key, value)
|
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: Boolean) = this.addProperty(key, value)
|
||||||
|
@ -567,7 +567,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
EdziennikTask.syncProfile(
|
EdziennikTask.syncProfile(
|
||||||
App.profileId,
|
App.profileId,
|
||||||
listOf(navTargetId to fragmentParam),
|
listOf(navTargetId to fragmentParam),
|
||||||
arguments
|
arguments = arguments
|
||||||
).enqueue(this)
|
).enqueue(this)
|
||||||
}
|
}
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
@ -29,8 +29,8 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
|
|||||||
|
|
||||||
val grades by lazy { ProfileConfigGrades(this) }
|
val grades by lazy { ProfileConfigGrades(this) }
|
||||||
val ui by lazy { ProfileConfigUI(this) }
|
val ui by lazy { ProfileConfigUI(this) }
|
||||||
|
val sync by lazy { ProfileConfigSync(this) }
|
||||||
/*
|
/*
|
||||||
val sync by lazy { ConfigSync(this) }
|
|
||||||
val timetable by lazy { ConfigTimetable(this) }
|
val timetable by lazy { ConfigTimetable(this) }
|
||||||
val grades by lazy { ConfigGrades(this) }*/
|
val grades by lazy { ConfigGrades(this) }*/
|
||||||
|
|
||||||
@ -56,4 +56,4 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
|
|||||||
db.configDao().add(ConfigEntry(profileId, key, value))
|
db.configDao().add(ConfigEntry(profileId, key, value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-2-21.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.config
|
||||||
|
|
||||||
|
import pl.szczodrzynski.edziennik.config.utils.get
|
||||||
|
import pl.szczodrzynski.edziennik.config.utils.set
|
||||||
|
|
||||||
|
class ProfileConfigSync(private val config: ProfileConfig) {
|
||||||
|
private var mNotificationFilter: List<Int>? = null
|
||||||
|
var notificationFilter: List<Int>
|
||||||
|
get() { mNotificationFilter = mNotificationFilter ?: config.values.get("notificationFilter", listOf()); return mNotificationFilter ?: listOf() }
|
||||||
|
set(value) { config.set("notificationFilter", value); mNotificationFilter = value }
|
||||||
|
}
|
@ -22,9 +22,9 @@ const val FAKE_LIBRUS_TOKEN = "https://librus.szkolny.eu/access_token.php"
|
|||||||
const val FAKE_LIBRUS_ACCOUNT = "/synergia_accounts_fresh.php?login="
|
const val FAKE_LIBRUS_ACCOUNT = "/synergia_accounts_fresh.php?login="
|
||||||
const val FAKE_LIBRUS_ACCOUNTS = "/synergia_accounts.php"
|
const val FAKE_LIBRUS_ACCOUNTS = "/synergia_accounts.php"
|
||||||
|
|
||||||
val LIBRUS_USER_AGENT = "$SYSTEM_USER_AGENT LibrusMobileApp"
|
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 SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0"
|
||||||
const val LIBRUS_CLIENT_ID = "wmSyUMo8llDAs4y9tJVYY92oyZ6h4lAt7KCuy0Gv"
|
const val LIBRUS_CLIENT_ID = "6XPsKf10LPz1nxgHQLcvZ1KM48DYzlBAhxipaXY8"
|
||||||
const val LIBRUS_REDIRECT_URL = "http://localhost/bar"
|
const val LIBRUS_REDIRECT_URL = "http://localhost/bar"
|
||||||
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_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_LOGIN_URL = "https://portal.librus.pl/rodzina/login/action"
|
||||||
|
@ -7,7 +7,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.EndpointTimer
|
|||||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_NEVER
|
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_NEVER
|
||||||
|
|
||||||
fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featureIds: List<Int>, viewId: Int?) {
|
fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?) {
|
||||||
val data = this
|
val data = this
|
||||||
|
|
||||||
val possibleLoginMethods = data.loginMethods.toMutableList()
|
val possibleLoginMethods = data.loginMethods.toMutableList()
|
||||||
@ -46,13 +46,18 @@ fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featu
|
|||||||
// add all endpoint IDs and required login methods, filtering using timers
|
// add all endpoint IDs and required login methods, filtering using timers
|
||||||
.onEach { feature ->
|
.onEach { feature ->
|
||||||
feature.endpointIds.forEach { endpoint ->
|
feature.endpointIds.forEach { endpoint ->
|
||||||
|
if (onlyEndpoints?.contains(endpoint.first) == false)
|
||||||
|
return@forEach
|
||||||
(data.endpointTimers
|
(data.endpointTimers
|
||||||
.singleOrNull { it.endpointId == endpoint.first } ?: EndpointTimer(data.profile?.id
|
.singleOrNull { it.endpointId == endpoint.first } ?: EndpointTimer(data.profile?.id
|
||||||
?: -1, endpoint.first))
|
?: -1, endpoint.first))
|
||||||
.let { timer ->
|
.let { timer ->
|
||||||
if (timer.nextSync == SYNC_ALWAYS ||
|
if (
|
||||||
(viewId != null && timer.viewId == viewId) ||
|
onlyEndpoints?.contains(endpoint.first) == true ||
|
||||||
(timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp)) {
|
timer.nextSync == SYNC_ALWAYS ||
|
||||||
|
viewId != null && timer.viewId == viewId ||
|
||||||
|
timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp
|
||||||
|
) {
|
||||||
data.targetEndpointIds[endpoint.first] = timer.lastSync
|
data.targetEndpointIds[endpoint.first] = timer.lastSync
|
||||||
requiredLoginMethods.add(endpoint.second)
|
requiredLoginMethods.add(endpoint.second)
|
||||||
}
|
}
|
||||||
|
@ -122,6 +122,7 @@ const val ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN = 180
|
|||||||
const val ERROR_LIBRUS_API_MAINTENANCE = 181
|
const val ERROR_LIBRUS_API_MAINTENANCE = 181
|
||||||
const val ERROR_LIBRUS_PORTAL_MAINTENANCE = 182
|
const val ERROR_LIBRUS_PORTAL_MAINTENANCE = 182
|
||||||
const val ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM = 183
|
const val ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM = 183
|
||||||
|
const val ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED = 184
|
||||||
|
|
||||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201
|
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201
|
||||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202
|
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202
|
||||||
|
@ -30,7 +30,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
|||||||
|
|
||||||
fun firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore))
|
fun firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore))
|
||||||
fun sync() = EdziennikTask(-1, SyncRequest())
|
fun sync() = EdziennikTask(-1, SyncRequest())
|
||||||
fun syncProfile(profileId: Int, viewIds: List<Pair<Int, Int>>? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds, arguments))
|
fun syncProfile(profileId: Int, viewIds: List<Pair<Int, Int>>? = null, onlyEndpoints: List<Int>? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds, onlyEndpoints, arguments))
|
||||||
fun syncProfileList(profileList: List<Int>) = EdziennikTask(-1, SyncProfileListRequest(profileList))
|
fun syncProfileList(profileList: List<Int>) = EdziennikTask(-1, SyncProfileListRequest(profileList))
|
||||||
fun messageGet(profileId: Int, message: MessageFull) = EdziennikTask(profileId, MessageGetRequest(message))
|
fun messageGet(profileId: Int, message: MessageFull) = EdziennikTask(profileId, MessageGetRequest(message))
|
||||||
fun messageSend(profileId: Int, recipients: List<Teacher>, subject: String, text: String) = EdziennikTask(profileId, MessageSendRequest(recipients, subject, text))
|
fun messageSend(profileId: Int, recipients: List<Teacher>, subject: String, text: String) = EdziennikTask(profileId, MessageSendRequest(recipients, subject, text))
|
||||||
@ -85,6 +85,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
|||||||
featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) }
|
featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) }
|
||||||
?: Features.getAllIds(),
|
?: Features.getAllIds(),
|
||||||
viewId = request.viewIds?.get(0)?.first,
|
viewId = request.viewIds?.get(0)?.first,
|
||||||
|
onlyEndpoints = request.onlyEndpoints,
|
||||||
arguments = request.arguments)
|
arguments = request.arguments)
|
||||||
is MessageGetRequest -> edziennikInterface?.getMessage(request.message)
|
is MessageGetRequest -> edziennikInterface?.getMessage(request.message)
|
||||||
is MessageSendRequest -> edziennikInterface?.sendMessage(request.recipients, request.subject, request.text)
|
is MessageSendRequest -> edziennikInterface?.sendMessage(request.recipients, request.subject, request.text)
|
||||||
@ -106,7 +107,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
|||||||
|
|
||||||
data class FirstLoginRequest(val loginStore: LoginStore)
|
data class FirstLoginRequest(val loginStore: LoginStore)
|
||||||
class SyncRequest
|
class SyncRequest
|
||||||
data class SyncProfileRequest(val viewIds: List<Pair<Int, Int>>? = null, val arguments: JsonObject? = null)
|
data class SyncProfileRequest(val viewIds: List<Pair<Int, Int>>? = null, val onlyEndpoints: List<Int>? = null, val arguments: JsonObject? = null)
|
||||||
data class SyncProfileListRequest(val profileList: List<Int>)
|
data class SyncProfileListRequest(val profileList: List<Int>)
|
||||||
data class MessageGetRequest(val message: MessageFull)
|
data class MessageGetRequest(val message: MessageFull)
|
||||||
data class MessageSendRequest(val recipients: List<Teacher>, val subject: String, val text: String)
|
data class MessageSendRequest(val recipients: List<Teacher>, val subject: String, val text: String)
|
||||||
|
@ -52,9 +52,9 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
|
|||||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||||
__/ |
|
__/ |
|
||||||
|__*/
|
|__*/
|
||||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
override fun sync(featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?, arguments: JsonObject?) {
|
||||||
data.arguments = arguments
|
data.arguments = arguments
|
||||||
data.prepare(edudziennikLoginMethods, EdudziennikFeatures, featureIds, viewId)
|
data.prepare(edudziennikLoginMethods, EdudziennikFeatures, featureIds, viewId, onlyEndpoints)
|
||||||
login()
|
login()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,15 +27,15 @@ class EdudziennikData(val data: DataEdudziennik, val onSuccess: () -> Unit) {
|
|||||||
onSuccess()
|
onSuccess()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
useEndpoint(data.targetEndpointIds.firstKey()) { endpointId ->
|
val id = data.targetEndpointIds.firstKey()
|
||||||
data.targetEndpointIds.remove(endpointId)
|
val lastSync = data.targetEndpointIds.remove(id)
|
||||||
|
useEndpoint(id, lastSync) { endpointId ->
|
||||||
data.progress(data.progressStep)
|
data.progress(data.progressStep)
|
||||||
nextEndpoint(onSuccess)
|
nextEndpoint(onSuccess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun useEndpoint(endpointId: Int, onSuccess: (endpointId: Int) -> Unit) {
|
private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
|
||||||
val lastSync = data.targetEndpointIds[endpointId]
|
|
||||||
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
|
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
|
||||||
when (endpointId) {
|
when (endpointId) {
|
||||||
ENDPOINT_EDUDZIENNIK_WEB_START -> {
|
ENDPOINT_EDUDZIENNIK_WEB_START -> {
|
||||||
|
@ -54,9 +54,9 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
|||||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||||
__/ |
|
__/ |
|
||||||
|__*/
|
|__*/
|
||||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
override fun sync(featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?, arguments: JsonObject?) {
|
||||||
data.arguments = arguments
|
data.arguments = arguments
|
||||||
data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId)
|
data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId, onlyEndpoints)
|
||||||
login()
|
login()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,15 +30,15 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) {
|
|||||||
onSuccess()
|
onSuccess()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
useEndpoint(data.targetEndpointIds.firstKey()) { endpointId ->
|
val id = data.targetEndpointIds.firstKey()
|
||||||
data.targetEndpointIds.remove(endpointId)
|
val lastSync = data.targetEndpointIds.remove(id)
|
||||||
|
useEndpoint(id, lastSync) { endpointId ->
|
||||||
data.progress(data.progressStep)
|
data.progress(data.progressStep)
|
||||||
nextEndpoint(onSuccess)
|
nextEndpoint(onSuccess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun useEndpoint(endpointId: Int, onSuccess: (endpointId: Int) -> Unit) {
|
private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
|
||||||
val lastSync = data.targetEndpointIds[endpointId]
|
|
||||||
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
|
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
|
||||||
when (endpointId) {
|
when (endpointId) {
|
||||||
ENDPOINT_IDZIENNIK_WEB_TIMETABLE -> {
|
ENDPOINT_IDZIENNIK_WEB_TIMETABLE -> {
|
||||||
|
@ -191,6 +191,16 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
|||||||
get() { mApiTokenExpiryTime = mApiTokenExpiryTime ?: profile?.getStudentData("accountTokenTime", 0L); return mApiTokenExpiryTime ?: 0L }
|
get() { mApiTokenExpiryTime = mApiTokenExpiryTime ?: profile?.getStudentData("accountTokenTime", 0L); return mApiTokenExpiryTime ?: 0L }
|
||||||
set(value) { mApiTokenExpiryTime = value; profile?.putStudentData("accountTokenTime", value) ?: return; }
|
set(value) { mApiTokenExpiryTime = value; profile?.putStudentData("accountTokenTime", value) ?: return; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A push device ID, generated by Librus when registering
|
||||||
|
* a FCM token. I don't really know if this has any use,
|
||||||
|
* but it may be worthy to save that ID.
|
||||||
|
*/
|
||||||
|
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; }
|
||||||
|
|
||||||
/* _____ _
|
/* _____ _
|
||||||
/ ____| (_)
|
/ ____| (_)
|
||||||
| (___ _ _ _ __ ___ _ __ __ _ _ __ _
|
| (___ _ _ _ __ ___ _ __ __ _ _ __ _
|
||||||
|
@ -56,9 +56,9 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
|||||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||||
__/ |
|
__/ |
|
||||||
|__*/
|
|__*/
|
||||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
override fun sync(featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?, arguments: JsonObject?) {
|
||||||
data.arguments = arguments
|
data.arguments = arguments
|
||||||
data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId)
|
data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId, onlyEndpoints)
|
||||||
login()
|
login()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,6 +180,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
|||||||
}
|
}
|
||||||
ERROR_LOGIN_LIBRUS_PORTAL_NO_CODE,
|
ERROR_LOGIN_LIBRUS_PORTAL_NO_CODE,
|
||||||
ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING,
|
ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING,
|
||||||
|
ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED,
|
||||||
ERROR_LOGIN_LIBRUS_PORTAL_CODE_REVOKED,
|
ERROR_LOGIN_LIBRUS_PORTAL_CODE_REVOKED,
|
||||||
ERROR_LOGIN_LIBRUS_PORTAL_CODE_EXPIRED -> {
|
ERROR_LOGIN_LIBRUS_PORTAL_CODE_EXPIRED -> {
|
||||||
login()
|
login()
|
||||||
|
@ -53,8 +53,6 @@ const val ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK = 2030
|
|||||||
const val ENDPOINT_LIBRUS_MESSAGES_RECEIVED = 3010
|
const val ENDPOINT_LIBRUS_MESSAGES_RECEIVED = 3010
|
||||||
const val ENDPOINT_LIBRUS_MESSAGES_SENT = 3020
|
const val ENDPOINT_LIBRUS_MESSAGES_SENT = 3020
|
||||||
const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 3030
|
const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 3030
|
||||||
const val ENDPOINT_LIBRUS_MESSAGES_RECEIVERS = 3040
|
|
||||||
const val ENDPOINT_LIBRUS_MESSAGES_GET = 3040
|
|
||||||
|
|
||||||
val LibrusFeatures = listOf(
|
val LibrusFeatures = listOf(
|
||||||
|
|
||||||
|
@ -31,15 +31,15 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
onSuccess()
|
onSuccess()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
useEndpoint(data.targetEndpointIds.firstKey()) { endpointId ->
|
val id = data.targetEndpointIds.firstKey()
|
||||||
data.targetEndpointIds.remove(endpointId)
|
val lastSync = data.targetEndpointIds.remove(id)
|
||||||
|
useEndpoint(id, lastSync) { endpointId ->
|
||||||
data.progress(data.progressStep)
|
data.progress(data.progressStep)
|
||||||
nextEndpoint(onSuccess)
|
nextEndpoint(onSuccess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun useEndpoint(endpointId: Int, onSuccess: (endpointId: Int) -> Unit) {
|
private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
|
||||||
val lastSync = data.targetEndpointIds[endpointId]
|
|
||||||
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
|
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
|
||||||
when (endpointId) {
|
when (endpointId) {
|
||||||
/**
|
/**
|
||||||
@ -81,7 +81,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
data.startProgress(R.string.edziennik_progress_endpoint_lessons)
|
data.startProgress(R.string.edziennik_progress_endpoint_lessons)
|
||||||
LibrusApiLessons(data, lastSync, onSuccess)
|
LibrusApiLessons(data, lastSync, onSuccess)
|
||||||
}
|
}
|
||||||
// TODO push config
|
ENDPOINT_LIBRUS_API_PUSH_CONFIG -> {
|
||||||
|
data.startProgress(R.string.edziennik_progress_endpoint_push_config)
|
||||||
|
LibrusApiPushConfig(data, lastSync, onSuccess)
|
||||||
|
}
|
||||||
ENDPOINT_LIBRUS_API_TIMETABLES -> {
|
ENDPOINT_LIBRUS_API_TIMETABLES -> {
|
||||||
data.startProgress(R.string.edziennik_progress_endpoint_timetable)
|
data.startProgress(R.string.edziennik_progress_endpoint_timetable)
|
||||||
LibrusApiTimetables(data, lastSync, onSuccess)
|
LibrusApiTimetables(data, lastSync, onSuccess)
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-2-21.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
|
||||||
|
|
||||||
|
import pl.szczodrzynski.edziennik.JsonObject
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_PUSH_CONFIG
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||||
|
import pl.szczodrzynski.edziennik.getInt
|
||||||
|
import pl.szczodrzynski.edziennik.getJsonObject
|
||||||
|
|
||||||
|
class LibrusApiPushConfig(override val data: DataLibrus,
|
||||||
|
override val lastSync: Long?,
|
||||||
|
val onSuccess: (endpointId: Int) -> Unit
|
||||||
|
) : LibrusApi(data, lastSync) {
|
||||||
|
companion object {
|
||||||
|
const val TAG = "LibrusApiPushConfig"
|
||||||
|
}
|
||||||
|
|
||||||
|
init { data.app.config.sync.tokenLibrus?.also { tokenLibrus ->
|
||||||
|
apiGet(TAG, "ChangeRegister", payload = JsonObject(
|
||||||
|
"provider" to "FCM",
|
||||||
|
"device" to tokenLibrus,
|
||||||
|
"sendPush" to "1",
|
||||||
|
"appVersion" to 4
|
||||||
|
)) { json ->
|
||||||
|
json.getJsonObject("ChangeRegister")?.getInt("Id")?.let { data.pushDeviceId = it }
|
||||||
|
|
||||||
|
// sync always: this endpoint has .shouldSync set
|
||||||
|
data.setSyncNext(ENDPOINT_LIBRUS_API_PUSH_CONFIG, SYNC_ALWAYS)
|
||||||
|
data.app.config.sync.tokenLibrusList =
|
||||||
|
data.app.config.sync.tokenLibrusList + profileId
|
||||||
|
onSuccess(ENDPOINT_LIBRUS_API_PUSH_CONFIG)
|
||||||
|
}
|
||||||
|
} ?: onSuccess(ENDPOINT_LIBRUS_API_PUSH_CONFIG) }
|
||||||
|
}
|
@ -8,8 +8,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
|||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
||||||
|
|
||||||
class LibrusApiTemplate(override val data: DataLibrus,
|
class LibrusApiTemplate(override val data: DataLibrus,
|
||||||
val onSuccess: () -> Unit
|
override val lastSync: Long?,
|
||||||
) : LibrusApi(data, null) {
|
val onSuccess: (endpointId: Int) -> Unit
|
||||||
|
) : LibrusApi(data, lastSync) {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "LibrusApi"
|
const val TAG = "LibrusApi"
|
||||||
}
|
}
|
||||||
@ -18,7 +19,7 @@ class LibrusApiTemplate(override val data: DataLibrus,
|
|||||||
/*apiGet(TAG, "") { json ->
|
/*apiGet(TAG, "") { json ->
|
||||||
|
|
||||||
data.setSyncNext(ENDPOINT_LIBRUS_API_, SYNC_ALWAYS)
|
data.setSyncNext(ENDPOINT_LIBRUS_API_, SYNC_ALWAYS)
|
||||||
onSuccess()
|
onSuccess(ENDPOINT_LIBRUS_API_)
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import pl.szczodrzynski.edziennik.data.api.*
|
|||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||||
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
|
import java.net.HttpURLConnection.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
@ -63,23 +63,31 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
.userAgent(LIBRUS_USER_AGENT)
|
.userAgent(LIBRUS_USER_AGENT)
|
||||||
.withClient(data.app.httpLazy)
|
.withClient(data.app.httpLazy)
|
||||||
.callback(object : TextCallbackHandler() {
|
.callback(object : TextCallbackHandler() {
|
||||||
override fun onSuccess(json: String, response: Response) {
|
override fun onSuccess(text: String, response: Response) {
|
||||||
val location = response.headers().get("Location")
|
val location = response.headers().get("Location")
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
val authMatcher = Pattern.compile("http://localhost/bar\\?code=([A-z0-9]+?)$", Pattern.DOTALL or Pattern.MULTILINE).matcher(location)
|
val authMatcher = Pattern.compile("http://localhost/bar\\?code=([A-z0-9]+?)$", Pattern.DOTALL or Pattern.MULTILINE).matcher(location)
|
||||||
if (authMatcher.find()) {
|
when {
|
||||||
accessToken(authMatcher.group(1), null)
|
authMatcher.find() -> {
|
||||||
} else {
|
accessToken(authMatcher.group(1), null)
|
||||||
authorize(location)
|
}
|
||||||
|
location.contains("rejected_client") -> {
|
||||||
|
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_INVALID_CLIENT_ID)
|
||||||
|
.withResponse(response)
|
||||||
|
.withApiResponse("Location: $location\n$text"))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
authorize(location)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val csrfMatcher = Pattern.compile("name=\"csrf-token\" content=\"([A-z0-9=+/\\-_]+?)\"", Pattern.DOTALL).matcher(json)
|
val csrfMatcher = Pattern.compile("name=\"csrf-token\" content=\"([A-z0-9=+/\\-_]+?)\"", Pattern.DOTALL).matcher(text)
|
||||||
if (csrfMatcher.find()) {
|
if (csrfMatcher.find()) {
|
||||||
login(csrfMatcher.group(1))
|
login(csrfMatcher.group(1))
|
||||||
} else {
|
} else {
|
||||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING)
|
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING)
|
||||||
.withResponse(response)
|
.withResponse(response)
|
||||||
.withApiResponse(json))
|
.withApiResponse(text))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,6 +120,8 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
it.addParameter("g-recaptcha-response", recaptchaCode)
|
it.addParameter("g-recaptcha-response", recaptchaCode)
|
||||||
}
|
}
|
||||||
.addHeader("X-CSRF-TOKEN", csrfToken)
|
.addHeader("X-CSRF-TOKEN", csrfToken)
|
||||||
|
.allowErrorCode(HTTP_BAD_REQUEST)
|
||||||
|
.allowErrorCode(HTTP_FORBIDDEN)
|
||||||
.contentType(MediaTypeUtils.APPLICATION_JSON)
|
.contentType(MediaTypeUtils.APPLICATION_JSON)
|
||||||
.post()
|
.post()
|
||||||
.callback(object : JsonCallbackHandler() {
|
.callback(object : JsonCallbackHandler() {
|
||||||
@ -140,22 +150,24 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
.withApiResponse(json))
|
.withApiResponse(json))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (json.get("errors") != null) {
|
val error = if (response.code() == 200) null else
|
||||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR)
|
json.getJsonArray("errors")?.getString(0)
|
||||||
.withResponse(response)
|
error?.let { code ->
|
||||||
.withApiResponse(json))
|
when {
|
||||||
return
|
code.contains("Sesja logowania wygasła") -> ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED
|
||||||
|
code.contains("Upewnij się, że nie") -> 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(json.getString("redirect", LIBRUS_AUTHORIZE_URL))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(response: Response, throwable: Throwable) {
|
override fun onFailure(response: Response, throwable: Throwable) {
|
||||||
if (response.code() == 403 || response.code() == 401) {
|
|
||||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN)
|
|
||||||
.withResponse(response)
|
|
||||||
.withThrowable(throwable))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
|
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
|
||||||
.withResponse(response)
|
.withResponse(response)
|
||||||
.withThrowable(throwable))
|
.withThrowable(throwable))
|
||||||
@ -165,7 +177,6 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
.enqueue()
|
.enqueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var refreshTokenFailed = false
|
|
||||||
private fun accessToken(code: String?, refreshToken: String?) {
|
private fun accessToken(code: String?, refreshToken: String?) {
|
||||||
d(TAG, "Request: Librus/Login/Portal - ${if (data.fakeLogin) FAKE_LIBRUS_TOKEN else LIBRUS_TOKEN_URL}")
|
d(TAG, "Request: Librus/Login/Portal - ${if (data.fakeLogin) FAKE_LIBRUS_TOKEN else LIBRUS_TOKEN_URL}")
|
||||||
|
|
||||||
@ -183,7 +194,7 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
val error = if (response?.code() == 200) null else
|
val error = if (response?.code() == 200) null else
|
||||||
json.getString("hint")
|
json.getString("hint") ?: json.getString("error")
|
||||||
error?.let { code ->
|
error?.let { code ->
|
||||||
when (code) {
|
when (code) {
|
||||||
"Authorization code has expired" -> ERROR_LOGIN_LIBRUS_PORTAL_CODE_EXPIRED
|
"Authorization code has expired" -> ERROR_LOGIN_LIBRUS_PORTAL_CODE_EXPIRED
|
||||||
@ -194,11 +205,9 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
"Check the `code` parameter" -> ERROR_LOGIN_LIBRUS_PORTAL_NO_CODE
|
"Check the `code` parameter" -> ERROR_LOGIN_LIBRUS_PORTAL_NO_CODE
|
||||||
"Check the `refresh_token` parameter" -> ERROR_LOGIN_LIBRUS_PORTAL_NO_REFRESH
|
"Check the `refresh_token` parameter" -> ERROR_LOGIN_LIBRUS_PORTAL_NO_REFRESH
|
||||||
"Check the `redirect_uri` parameter" -> ERROR_LOGIN_LIBRUS_PORTAL_NO_REDIRECT
|
"Check the `redirect_uri` parameter" -> ERROR_LOGIN_LIBRUS_PORTAL_NO_REDIRECT
|
||||||
else -> when (json.getString("error")) {
|
"unsupported_grant_type" -> ERROR_LOGIN_LIBRUS_PORTAL_UNSUPPORTED_GRANT
|
||||||
"unsupported_grant_type" -> ERROR_LOGIN_LIBRUS_PORTAL_UNSUPPORTED_GRANT
|
"invalid_client" -> ERROR_LOGIN_LIBRUS_PORTAL_INVALID_CLIENT_ID
|
||||||
"invalid_client" -> ERROR_LOGIN_LIBRUS_PORTAL_INVALID_CLIENT_ID
|
else -> ERROR_LOGIN_LIBRUS_PORTAL_OTHER
|
||||||
else -> ERROR_LOGIN_LIBRUS_PORTAL_OTHER
|
|
||||||
}
|
|
||||||
}.let { errorCode ->
|
}.let { errorCode ->
|
||||||
data.error(ApiError(TAG, errorCode)
|
data.error(ApiError(TAG, errorCode)
|
||||||
.withApiResponse(json)
|
.withApiResponse(json)
|
||||||
|
@ -56,9 +56,9 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
|
|||||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||||
__/ |
|
__/ |
|
||||||
|__*/
|
|__*/
|
||||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
override fun sync(featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?, arguments: JsonObject?) {
|
||||||
data.arguments = arguments
|
data.arguments = arguments
|
||||||
data.prepare(mobidziennikLoginMethods, MobidziennikFeatures, featureIds, viewId)
|
data.prepare(mobidziennikLoginMethods, MobidziennikFeatures, featureIds, viewId, onlyEndpoints)
|
||||||
login()
|
login()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,15 +29,15 @@ class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) {
|
|||||||
onSuccess()
|
onSuccess()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
useEndpoint(data.targetEndpointIds.firstKey()) { endpointId ->
|
val id = data.targetEndpointIds.firstKey()
|
||||||
data.targetEndpointIds.remove(endpointId)
|
val lastSync = data.targetEndpointIds.remove(id)
|
||||||
|
useEndpoint(id, lastSync) { endpointId ->
|
||||||
data.progress(data.progressStep)
|
data.progress(data.progressStep)
|
||||||
nextEndpoint(onSuccess)
|
nextEndpoint(onSuccess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun useEndpoint(endpointId: Int, onSuccess: (endpointId: Int) -> Unit) {
|
private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
|
||||||
val lastSync = data.targetEndpointIds[endpointId]
|
|
||||||
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
|
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
|
||||||
when (endpointId) {
|
when (endpointId) {
|
||||||
ENDPOINT_MOBIDZIENNIK_API_MAIN -> {
|
ENDPOINT_MOBIDZIENNIK_API_MAIN -> {
|
||||||
|
@ -51,9 +51,9 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
|||||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||||
__/ |
|
__/ |
|
||||||
|__*/
|
|__*/
|
||||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
override fun sync(featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?, arguments: JsonObject?) {
|
||||||
data.arguments = arguments
|
data.arguments = arguments
|
||||||
data.prepare(templateLoginMethods, TemplateFeatures, featureIds, viewId)
|
data.prepare(templateLoginMethods, TemplateFeatures, featureIds, viewId, onlyEndpoints)
|
||||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||||
TemplateLogin(data) {
|
TemplateLogin(data) {
|
||||||
|
@ -32,15 +32,15 @@ class TemplateData(val data: DataTemplate, val onSuccess: () -> Unit) {
|
|||||||
onSuccess()
|
onSuccess()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
useEndpoint(data.targetEndpointIds.firstKey()) { endpointId ->
|
val id = data.targetEndpointIds.firstKey()
|
||||||
data.targetEndpointIds.remove(endpointId)
|
val lastSync = data.targetEndpointIds.remove(id)
|
||||||
|
useEndpoint(id, lastSync) { endpointId ->
|
||||||
data.progress(data.progressStep)
|
data.progress(data.progressStep)
|
||||||
nextEndpoint(onSuccess)
|
nextEndpoint(onSuccess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun useEndpoint(endpointId: Int, onSuccess: (endpointId: Int) -> Unit) {
|
private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
|
||||||
val lastSync = data.targetEndpointIds[endpointId]
|
|
||||||
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
|
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
|
||||||
when (endpointId) {
|
when (endpointId) {
|
||||||
ENDPOINT_TEMPLATE_WEB_SAMPLE -> {
|
ENDPOINT_TEMPLATE_WEB_SAMPLE -> {
|
||||||
|
@ -55,9 +55,9 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
|||||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||||
__/ |
|
__/ |
|
||||||
|__*/
|
|__*/
|
||||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
override fun sync(featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?, arguments: JsonObject?) {
|
||||||
data.arguments = arguments
|
data.arguments = arguments
|
||||||
data.prepare(vulcanLoginMethods, VulcanFeatures, featureIds, viewId)
|
data.prepare(vulcanLoginMethods, VulcanFeatures, featureIds, viewId, onlyEndpoints)
|
||||||
login()
|
login()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import pl.szczodrzynski.edziennik.data.api.*
|
|||||||
import pl.szczodrzynski.edziennik.data.api.models.Feature
|
import pl.szczodrzynski.edziennik.data.api.models.Feature
|
||||||
|
|
||||||
const val ENDPOINT_VULCAN_API_UPDATE_SEMESTER = 1000
|
const val ENDPOINT_VULCAN_API_UPDATE_SEMESTER = 1000
|
||||||
|
const val ENDPOINT_VULCAN_API_PUSH_CONFIG = 1005
|
||||||
const val ENDPOINT_VULCAN_API_DICTIONARIES = 1010
|
const val ENDPOINT_VULCAN_API_DICTIONARIES = 1010
|
||||||
const val ENDPOINT_VULCAN_API_TIMETABLE = 1020
|
const val ENDPOINT_VULCAN_API_TIMETABLE = 1020
|
||||||
const val ENDPOINT_VULCAN_API_EVENTS = 1030
|
const val ENDPOINT_VULCAN_API_EVENTS = 1030
|
||||||
@ -53,6 +54,13 @@ val VulcanFeatures = listOf(
|
|||||||
ENDPOINT_VULCAN_API_MESSAGES_SENT to LOGIN_METHOD_VULCAN_API
|
ENDPOINT_VULCAN_API_MESSAGES_SENT to LOGIN_METHOD_VULCAN_API
|
||||||
), listOf(LOGIN_METHOD_VULCAN_API)),
|
), listOf(LOGIN_METHOD_VULCAN_API)),
|
||||||
|
|
||||||
|
// push config
|
||||||
|
Feature(LOGIN_TYPE_VULCAN, FEATURE_PUSH_CONFIG, listOf(
|
||||||
|
ENDPOINT_VULCAN_API_PUSH_CONFIG to LOGIN_METHOD_VULCAN_API
|
||||||
|
), listOf(LOGIN_METHOD_VULCAN_API)).withShouldSync { data ->
|
||||||
|
!data.app.config.sync.tokenVulcanList.contains(data.profileId)
|
||||||
|
},
|
||||||
|
|
||||||
Feature(LOGIN_TYPE_VULCAN, FEATURE_ALWAYS_NEEDED, listOf(
|
Feature(LOGIN_TYPE_VULCAN, FEATURE_ALWAYS_NEEDED, listOf(
|
||||||
ENDPOINT_VULCAN_API_UPDATE_SEMESTER to LOGIN_METHOD_VULCAN_API,
|
ENDPOINT_VULCAN_API_UPDATE_SEMESTER to LOGIN_METHOD_VULCAN_API,
|
||||||
ENDPOINT_VULCAN_API_DICTIONARIES to LOGIN_METHOD_VULCAN_API
|
ENDPOINT_VULCAN_API_DICTIONARIES to LOGIN_METHOD_VULCAN_API
|
||||||
|
@ -68,6 +68,7 @@ open class VulcanApi(open val data: DataVulcan, open val lastSync: Long?) {
|
|||||||
is Long -> finalPayload.addProperty(name, value)
|
is Long -> finalPayload.addProperty(name, value)
|
||||||
is Float -> finalPayload.addProperty(name, value)
|
is Float -> finalPayload.addProperty(name, value)
|
||||||
is Char -> finalPayload.addProperty(name, value)
|
is Char -> finalPayload.addProperty(name, value)
|
||||||
|
is Boolean -> finalPayload.addProperty(name, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finalPayload.addProperty("RemoteMobileTimeKey", System.currentTimeMillis() / 1000)
|
finalPayload.addProperty("RemoteMobileTimeKey", System.currentTimeMillis() / 1000)
|
||||||
|
@ -27,21 +27,25 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) {
|
|||||||
onSuccess()
|
onSuccess()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
useEndpoint(data.targetEndpointIds.firstKey()) { endpointId ->
|
val id = data.targetEndpointIds.firstKey()
|
||||||
data.targetEndpointIds.remove(endpointId)
|
val lastSync = data.targetEndpointIds.remove(id)
|
||||||
|
useEndpoint(id, lastSync) { endpointId ->
|
||||||
data.progress(data.progressStep)
|
data.progress(data.progressStep)
|
||||||
nextEndpoint(onSuccess)
|
nextEndpoint(onSuccess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun useEndpoint(endpointId: Int, onSuccess: (endpointId: Int) -> Unit) {
|
private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
|
||||||
val lastSync = data.targetEndpointIds[endpointId]
|
|
||||||
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
|
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
|
||||||
when (endpointId) {
|
when (endpointId) {
|
||||||
ENDPOINT_VULCAN_API_UPDATE_SEMESTER -> {
|
ENDPOINT_VULCAN_API_UPDATE_SEMESTER -> {
|
||||||
data.startProgress(R.string.edziennik_progress_endpoint_student_info)
|
data.startProgress(R.string.edziennik_progress_endpoint_student_info)
|
||||||
VulcanApiUpdateSemester(data, lastSync, onSuccess)
|
VulcanApiUpdateSemester(data, lastSync, onSuccess)
|
||||||
}
|
}
|
||||||
|
ENDPOINT_VULCAN_API_PUSH_CONFIG -> {
|
||||||
|
data.startProgress(R.string.edziennik_progress_endpoint_push_config)
|
||||||
|
VulcanApiPushConfig(data, lastSync, onSuccess)
|
||||||
|
}
|
||||||
ENDPOINT_VULCAN_API_DICTIONARIES -> {
|
ENDPOINT_VULCAN_API_DICTIONARIES -> {
|
||||||
data.startProgress(R.string.edziennik_progress_endpoint_dictionaries)
|
data.startProgress(R.string.edziennik_progress_endpoint_dictionaries)
|
||||||
VulcanApiDictionaries(data, lastSync, onSuccess)
|
VulcanApiDictionaries(data, lastSync, onSuccess)
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-2-20.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api
|
||||||
|
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.VULCAN_API_ENDPOINT_PUSH
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_PUSH_CONFIG
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||||
|
|
||||||
|
class VulcanApiPushConfig(override val data: DataVulcan,
|
||||||
|
override val lastSync: Long?,
|
||||||
|
val onSuccess: (endpointId: Int) -> Unit
|
||||||
|
) : VulcanApi(data, lastSync) {
|
||||||
|
companion object {
|
||||||
|
const val TAG = "VulcanApiPushConfig"
|
||||||
|
}
|
||||||
|
|
||||||
|
init { data.app.config.sync.tokenVulcan?.also { tokenVulcan ->
|
||||||
|
apiGet(TAG, VULCAN_API_ENDPOINT_PUSH, parameters = mapOf(
|
||||||
|
"Token" to tokenVulcan,
|
||||||
|
"IdUczen" to data.studentId,
|
||||||
|
"PushOcena" to true,
|
||||||
|
"PushFrekwencja" to true,
|
||||||
|
"PushUwaga" to true,
|
||||||
|
"PushWiadomosc" to true
|
||||||
|
)) { _, _ ->
|
||||||
|
// sync always: this endpoint has .shouldSync set
|
||||||
|
data.setSyncNext(ENDPOINT_VULCAN_API_PUSH_CONFIG, SYNC_ALWAYS)
|
||||||
|
data.app.config.sync.tokenVulcanList =
|
||||||
|
data.app.config.sync.tokenVulcanList + profileId
|
||||||
|
onSuccess(ENDPOINT_VULCAN_API_PUSH_CONFIG)
|
||||||
|
}
|
||||||
|
} ?: onSuccess(ENDPOINT_VULCAN_API_PUSH_CONFIG) }
|
||||||
|
}
|
@ -5,13 +5,13 @@
|
|||||||
package pl.szczodrzynski.edziennik.data.api.interfaces
|
package pl.szczodrzynski.edziennik.data.api.interfaces
|
||||||
|
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||||
|
|
||||||
interface EdziennikInterface {
|
interface EdziennikInterface {
|
||||||
fun sync(featureIds: List<Int>, viewId: Int? = null, arguments: JsonObject? = null)
|
fun sync(featureIds: List<Int>, viewId: Int? = null, onlyEndpoints: List<Int>? = null, arguments: JsonObject? = null)
|
||||||
fun getMessage(message: MessageFull)
|
fun getMessage(message: MessageFull)
|
||||||
fun sendMessage(recipients: List<Teacher>, subject: String, text: String)
|
fun sendMessage(recipients: List<Teacher>, subject: String, text: String)
|
||||||
fun markAllAnnouncementsAsRead()
|
fun markAllAnnouncementsAsRead()
|
||||||
|
@ -46,6 +46,6 @@ object Signing {
|
|||||||
|
|
||||||
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
||||||
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
||||||
return "$param1.MTIzNDU2Nzg5MDfKS5agyJ===.$param2".sha256()
|
return "$param1.MTIzNDU2Nzg5MDp+2J6OAn===.$param2".sha256()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,8 @@ abstract class IApiTask(open val profileId: Int) {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun enqueueAll(context: Context, tasks: List<IApiTask>) {
|
fun enqueueAll(context: Context, tasks: List<IApiTask>) {
|
||||||
|
if (tasks.isEmpty())
|
||||||
|
return
|
||||||
Intent(context, ApiService::class.java).let {
|
Intent(context, ApiService::class.java).let {
|
||||||
if (SDK_INT >= O)
|
if (SDK_INT >= O)
|
||||||
context.startForegroundService(it)
|
context.startForegroundService(it)
|
||||||
|
@ -44,6 +44,17 @@ class SzkolnyTask(val app: App, val syncingProfiles: List<Profile>) : IApiTask(-
|
|||||||
}
|
}
|
||||||
d(TAG, "Created ${notificationList.count()} notifications.")
|
d(TAG, "Created ${notificationList.count()} notifications.")
|
||||||
|
|
||||||
|
// filter notifications
|
||||||
|
notificationList
|
||||||
|
.mapNotNull { it.profileId }
|
||||||
|
.distinct()
|
||||||
|
.map { app.config.getFor(it).sync.notificationFilter }
|
||||||
|
.forEach { filter ->
|
||||||
|
filter.forEach { type ->
|
||||||
|
notificationList.removeAll { it.type == type }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// update the database
|
// update the database
|
||||||
app.db.metadataDao().setAllNotified(true)
|
app.db.metadataDao().setAllNotified(true)
|
||||||
if (notificationList.isNotEmpty())
|
if (notificationList.isNotEmpty())
|
||||||
|
@ -87,7 +87,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
|||||||
if (remoteMessage.getData().get("id_wiadomosci") != null) {
|
if (remoteMessage.getData().get("id_wiadomosci") != null) {
|
||||||
|
|
||||||
d(TAG, "Syncing profile " + profile.getId());
|
d(TAG, "Syncing profile " + profile.getId());
|
||||||
EdziennikTask.Companion.syncProfile(profile.getId(), null, null).enqueue(app);
|
EdziennikTask.Companion.syncProfile(profile.getId(), null, null, null).enqueue(app);
|
||||||
} else {
|
} else {
|
||||||
/*app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message"))
|
/*app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message"))
|
||||||
.withProfileData(profile.id, profile.name)
|
.withProfileData(profile.id, profile.name)
|
||||||
@ -98,7 +98,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
|||||||
app.notifier.postAll(profile);
|
app.notifier.postAll(profile);
|
||||||
app.saveConfig("notifications");*/
|
app.saveConfig("notifications");*/
|
||||||
d(TAG, "Syncing profile " + profile.getId());
|
d(TAG, "Syncing profile " + profile.getId());
|
||||||
EdziennikTask.Companion.syncProfile(profile.getId(), null, null).enqueue(app);
|
EdziennikTask.Companion.syncProfile(profile.getId(), null, null, null).enqueue(app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -145,25 +145,31 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
|||||||
event.topic
|
event.topic
|
||||||
)*/
|
)*/
|
||||||
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_SHARED_HOMEWORK else Notification.TYPE_NEW_SHARED_EVENT
|
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_SHARED_HOMEWORK else Notification.TYPE_NEW_SHARED_EVENT
|
||||||
val notification = Notification(
|
val notificationFilter = app.config.getFor(event.profileId).sync.notificationFilter
|
||||||
id = Notification.buildId(event.profileId, type, event.id),
|
|
||||||
title = app.getNotificationTitle(type),
|
if (!notificationFilter.contains(type)) {
|
||||||
text = message,
|
val notification = Notification(
|
||||||
type = type,
|
id = Notification.buildId(event.profileId, type, event.id),
|
||||||
profileId = profile?.id,
|
title = app.getNotificationTitle(type),
|
||||||
profileName = profile?.name,
|
text = message,
|
||||||
viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
|
type = type,
|
||||||
addedDate = metadata.addedDate
|
profileId = profile?.id,
|
||||||
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
|
profileName = profile?.name,
|
||||||
|
viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
|
||||||
|
addedDate = metadata.addedDate
|
||||||
|
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
|
||||||
|
notificationList += notification
|
||||||
|
}
|
||||||
|
|
||||||
events += event
|
events += event
|
||||||
metadataList += metadata
|
metadataList += metadata
|
||||||
notificationList += notification
|
|
||||||
}
|
}
|
||||||
app.db.eventDao().addAll(events)
|
app.db.eventDao().addAll(events)
|
||||||
app.db.metadataDao().addAllReplace(metadataList)
|
app.db.metadataDao().addAllReplace(metadataList)
|
||||||
app.db.notificationDao().addAll(notificationList)
|
if (notificationList.isNotEmpty()) {
|
||||||
PostNotifications(app, notificationList)
|
app.db.notificationDao().addAll(notificationList)
|
||||||
|
PostNotifications(app, notificationList)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun unsharedEvent(teamCode: String, eventId: Long, message: String) {
|
private fun unsharedEvent(teamCode: String, eventId: Long, message: String) {
|
||||||
@ -172,19 +178,26 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
|||||||
|
|
||||||
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
|
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
|
||||||
val profile = profiles.firstOrNull { it.id == team.profileId }
|
val profile = profiles.firstOrNull { it.id == team.profileId }
|
||||||
val notification = Notification(
|
val notificationFilter = app.config.getFor(team.profileId).sync.notificationFilter
|
||||||
id = Notification.buildId(profile?.id ?: 0, Notification.TYPE_REMOVED_SHARED_EVENT, eventId),
|
|
||||||
title = app.getNotificationTitle(Notification.TYPE_REMOVED_SHARED_EVENT),
|
if (!notificationFilter.contains(Notification.TYPE_REMOVED_SHARED_EVENT)) {
|
||||||
text = message,
|
val notification = Notification(
|
||||||
type = Notification.TYPE_REMOVED_SHARED_EVENT,
|
id = Notification.buildId(profile?.id
|
||||||
profileId = profile?.id,
|
?: 0, Notification.TYPE_REMOVED_SHARED_EVENT, eventId),
|
||||||
profileName = profile?.name,
|
title = app.getNotificationTitle(Notification.TYPE_REMOVED_SHARED_EVENT),
|
||||||
viewId = MainActivity.DRAWER_ITEM_AGENDA
|
text = message,
|
||||||
)
|
type = Notification.TYPE_REMOVED_SHARED_EVENT,
|
||||||
notificationList += notification
|
profileId = profile?.id,
|
||||||
|
profileName = profile?.name,
|
||||||
|
viewId = MainActivity.DRAWER_ITEM_AGENDA
|
||||||
|
)
|
||||||
|
notificationList += notification
|
||||||
|
}
|
||||||
app.db.eventDao().remove(team.profileId, eventId)
|
app.db.eventDao().remove(team.profileId, eventId)
|
||||||
}
|
}
|
||||||
app.db.notificationDao().addAll(notificationList)
|
if (notificationList.isNotEmpty()) {
|
||||||
PostNotifications(app, notificationList)
|
app.db.notificationDao().addAll(notificationList)
|
||||||
|
PostNotifications(app, notificationList)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,10 @@
|
|||||||
package pl.szczodrzynski.edziennik.data.firebase
|
package pl.szczodrzynski.edziennik.data.firebase
|
||||||
|
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
|
import pl.szczodrzynski.edziennik.MainActivity
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||||
import pl.szczodrzynski.edziennik.getString
|
import pl.szczodrzynski.edziennik.getString
|
||||||
@ -23,14 +26,44 @@ class SzkolnyLibrusFirebase(val app: App, val profiles: List<Profile>, val messa
|
|||||||
"objectType": "Calendars/TeacherFreeDays",
|
"objectType": "Calendars/TeacherFreeDays",
|
||||||
}*/
|
}*/
|
||||||
init { run {
|
init { run {
|
||||||
val apiLogin = message.data.getString("userId") ?: return@run
|
val type = message.data.getString("objectType") ?: return@run
|
||||||
|
val accountLogin = message.data.getString("userId")?.replace("u", "") ?: return@run
|
||||||
|
|
||||||
val tasks = profiles.filter {
|
/* ./src/store/modules/helpers/change-processor.js */
|
||||||
it.getStudentData("accountLogin", "") == apiLogin
|
val endpoints = when (type) {
|
||||||
}.map {
|
"Notes" -> listOf(ENDPOINT_LIBRUS_API_NOTICES)
|
||||||
EdziennikTask.syncProfile(it.id)
|
"Grades" -> listOf(ENDPOINT_LIBRUS_API_NORMAL_GRADES, ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES, ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS)
|
||||||
|
"PointGrades" -> listOf(ENDPOINT_LIBRUS_API_POINT_GRADES, ENDPOINT_LIBRUS_API_POINT_GRADE_CATEGORIES)
|
||||||
|
"DescriptiveGrades" -> listOf(ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES)
|
||||||
|
"DescriptiveGrades/Text/Categories" -> listOf(ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES)
|
||||||
|
"DescriptiveTextGrades" -> listOf(ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES)
|
||||||
|
"TextGrades" -> listOf(ENDPOINT_LIBRUS_API_TEXT_GRADES)
|
||||||
|
"BehaviourGrades/Points" -> listOf(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES, ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES, ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS)
|
||||||
|
"BehaviourGrades" -> listOf()
|
||||||
|
"Attendances" -> listOf(ENDPOINT_LIBRUS_API_ATTENDANCES)
|
||||||
|
"HomeWorks" -> listOf(ENDPOINT_LIBRUS_API_EVENTS)
|
||||||
|
"ParentTeacherConferences" -> listOf(ENDPOINT_LIBRUS_API_PT_MEETINGS)
|
||||||
|
"Calendars/ClassFreeDays" -> listOf(ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS)
|
||||||
|
"Calendars/TeacherFreeDays" -> listOf(ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS)
|
||||||
|
"Calendars/SchoolFreeDays" -> listOf(ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS)
|
||||||
|
"Calendars/Substitutions" -> listOf(ENDPOINT_LIBRUS_API_TIMETABLES)
|
||||||
|
"HomeWorkAssignments" -> listOf(ENDPOINT_LIBRUS_API_HOMEWORK, ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK)
|
||||||
|
"SchoolNotices" -> listOf(ENDPOINT_LIBRUS_API_ANNOUNCEMENTS)
|
||||||
|
"Messages" -> listOf(ENDPOINT_LIBRUS_MESSAGES_RECEIVED)
|
||||||
|
"LuckyNumbers" -> listOf(ENDPOINT_LIBRUS_API_LUCKY_NUMBER)
|
||||||
|
"Timetables" -> listOf(ENDPOINT_LIBRUS_API_TIMETABLES)
|
||||||
|
else -> return@run
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (endpoints.isEmpty())
|
||||||
|
return@run
|
||||||
|
|
||||||
|
val tasks = profiles.filter {
|
||||||
|
it.loginStoreType == LOGIN_TYPE_LIBRUS &&
|
||||||
|
it.getStudentData("accountLogin", "")?.replace("u", "") == accountLogin
|
||||||
|
}.map {
|
||||||
|
EdziennikTask.syncProfile(it.id, listOf(MainActivity.DRAWER_ITEM_HOME to 0), onlyEndpoints = endpoints)
|
||||||
|
}
|
||||||
IApiTask.enqueueAll(app, tasks)
|
IApiTask.enqueueAll(app, tasks)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ATTENDANCE
|
|||||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
|
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
|
||||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
|
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
|
||||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
|
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||||
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
|
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
|
||||||
@ -60,7 +61,8 @@ class SzkolnyMobidziennikFirebase(val app: App, val profiles: List<Profile>, val
|
|||||||
}
|
}
|
||||||
|
|
||||||
val tasks = profiles.filter {
|
val tasks = profiles.filter {
|
||||||
it.getStudentData("globalId", 0L) == globalId
|
it.loginStoreType == LOGIN_TYPE_MOBIDZIENNIK &&
|
||||||
|
it.getStudentData("globalId", 0L) == globalId
|
||||||
}.map {
|
}.map {
|
||||||
EdziennikTask.syncProfile(it.id, listOf(viewIdPair))
|
EdziennikTask.syncProfile(it.id, listOf(viewIdPair))
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,13 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.data.firebase
|
package pl.szczodrzynski.edziennik.data.firebase
|
||||||
|
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.*
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
|
||||||
|
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.entity.Profile
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class SzkolnyVulcanFirebase(val app: App, val profiles: List<Profile>, val message: FirebaseService.Message) {
|
class SzkolnyVulcanFirebase(val app: App, val profiles: List<Profile>, val message: FirebaseService.Message) {
|
||||||
/*{
|
/*{
|
||||||
@ -20,7 +25,28 @@ class SzkolnyVulcanFirebase(val app: App, val profiles: List<Profile>, val messa
|
|||||||
"title": "Frekwencja",
|
"title": "Frekwencja",
|
||||||
"message: "Uczeń Janósz otrzymał nieobecność na 7 lekcji"
|
"message: "Uczeń Janósz otrzymał nieobecność na 7 lekcji"
|
||||||
}*/
|
}*/
|
||||||
init {
|
init { run {
|
||||||
|
val data = message.data.getString("data")?.toJsonObject() ?: return@run
|
||||||
|
val type = data.getString("table") ?: return@run
|
||||||
|
val studentId = data.getInt("pupilid")
|
||||||
|
|
||||||
}
|
/* pl.vulcan.uonetmobile.auxilary.enums.CDCPushEnum */
|
||||||
|
val viewIdPair = when (type.toLowerCase(Locale.ROOT)) {
|
||||||
|
"wiadomosc" -> MainActivity.DRAWER_ITEM_MESSAGES to Message.TYPE_RECEIVED
|
||||||
|
"ocena" -> MainActivity.DRAWER_ITEM_GRADES to 0
|
||||||
|
"uwaga" -> MainActivity.DRAWER_ITEM_BEHAVIOUR to 0
|
||||||
|
"frekwencja" -> MainActivity.DRAWER_ITEM_ATTENDANCE to 0
|
||||||
|
// this type is not even implemented in Dzienniczek+
|
||||||
|
"sprawdzian" -> MainActivity.DRAWER_ITEM_AGENDA to 0
|
||||||
|
else -> return@run
|
||||||
|
}
|
||||||
|
|
||||||
|
val tasks = profiles.filter {
|
||||||
|
it.loginStoreType == LOGIN_TYPE_VULCAN &&
|
||||||
|
it.getStudentData("studentId", 0) == studentId
|
||||||
|
}.map {
|
||||||
|
EdziennikTask.syncProfile(it.id, listOf(viewIdPair))
|
||||||
|
}
|
||||||
|
IApiTask.enqueueAll(app, tasks)
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import kotlinx.android.synthetic.main.row_lesson_change_item.view.*
|
|||||||
import kotlinx.android.synthetic.main.row_teacher_absence_item.view.*
|
import kotlinx.android.synthetic.main.row_teacher_absence_item.view.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||||
import pl.szczodrzynski.edziennik.databinding.DialogDayBinding
|
import pl.szczodrzynski.edziennik.databinding.DialogDayBinding
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
|
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
|
||||||
@ -22,6 +23,7 @@ import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog
|
|||||||
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
|
||||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
@ -83,6 +85,27 @@ class DayDialog(
|
|||||||
date.formattedString
|
date.formattedString
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val lessons = withContext(Dispatchers.Default) {
|
||||||
|
app.db.timetableDao().getForDateNow(profileId, date)
|
||||||
|
}.filter { it.type != Lesson.TYPE_NO_LESSONS }
|
||||||
|
|
||||||
|
if (lessons.isNotEmpty()) { run {
|
||||||
|
val startTime = lessons.first().startTime ?: return@run
|
||||||
|
val endTime = lessons.last().endTime ?: return@run
|
||||||
|
val diff = Time.diff(startTime, endTime)
|
||||||
|
|
||||||
|
b.lessonsInfo.setText(
|
||||||
|
R.string.dialog_day_lessons_info,
|
||||||
|
startTime.stringHM,
|
||||||
|
endTime.stringHM,
|
||||||
|
lessons.size.toString(),
|
||||||
|
diff.hour.toString(),
|
||||||
|
diff.minute.toString()
|
||||||
|
)
|
||||||
|
|
||||||
|
b.lessonsInfo.visibility = View.VISIBLE
|
||||||
|
}}
|
||||||
|
|
||||||
val lessonChanges = withContext(Dispatchers.Default) {
|
val lessonChanges = withContext(Dispatchers.Default) {
|
||||||
app.db.timetableDao().getChangesForDateNow(profileId, date)
|
app.db.timetableDao().getChangesForDateNow(profileId, date)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-2-21.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.dialogs.sync
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import pl.szczodrzynski.edziennik.App
|
||||||
|
import pl.szczodrzynski.edziennik.R
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||||
|
import pl.szczodrzynski.edziennik.onClick
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
// TODO refactor dialog to allow configuring other profiles
|
||||||
|
// than the selected one in UI
|
||||||
|
class NotificationFilterDialog(
|
||||||
|
val activity: AppCompatActivity,
|
||||||
|
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||||
|
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||||
|
) : CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "NotificationFilterDialog"
|
||||||
|
private val notificationTypes = listOf(
|
||||||
|
Notification.TYPE_TIMETABLE_LESSON_CHANGE to R.string.notification_type_timetable_lesson_change,
|
||||||
|
Notification.TYPE_NEW_GRADE to R.string.notification_type_new_grade,
|
||||||
|
Notification.TYPE_NEW_EVENT to R.string.notification_type_new_event,
|
||||||
|
Notification.TYPE_NEW_HOMEWORK to R.string.notification_type_new_homework,
|
||||||
|
Notification.TYPE_NEW_MESSAGE to R.string.notification_type_new_message,
|
||||||
|
Notification.TYPE_LUCKY_NUMBER to R.string.notification_type_lucky_number,
|
||||||
|
Notification.TYPE_NEW_NOTICE to R.string.notification_type_notice,
|
||||||
|
Notification.TYPE_NEW_ATTENDANCE to R.string.notification_type_attendance,
|
||||||
|
Notification.TYPE_NEW_ANNOUNCEMENT to R.string.notification_type_new_announcement,
|
||||||
|
Notification.TYPE_NEW_SHARED_EVENT to R.string.notification_type_new_shared_event,
|
||||||
|
Notification.TYPE_NEW_SHARED_HOMEWORK to R.string.notification_type_new_shared_homework,
|
||||||
|
Notification.TYPE_REMOVED_SHARED_EVENT to R.string.notification_type_removed_shared_event
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
private lateinit var dialog: AlertDialog
|
||||||
|
|
||||||
|
private val job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
private val notificationFilter = mutableListOf<Int>()
|
||||||
|
|
||||||
|
init { run {
|
||||||
|
if (activity.isFinishing)
|
||||||
|
return@run
|
||||||
|
onShowListener?.invoke(TAG)
|
||||||
|
app = activity.applicationContext as App
|
||||||
|
|
||||||
|
notificationFilter.clear()
|
||||||
|
notificationFilter += app.config.forProfile().sync.notificationFilter
|
||||||
|
val items = notificationTypes.map { app.getString(it.second) }.toTypedArray()
|
||||||
|
val checkedItems = notificationTypes.map { !notificationFilter.contains(it.first) }.toBooleanArray()
|
||||||
|
|
||||||
|
dialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.dialog_notification_filter_title)
|
||||||
|
//.setMessage(R.string.dialog_notification_filter_text)
|
||||||
|
.setMultiChoiceItems(items, checkedItems) { _, which, isChecked ->
|
||||||
|
val type = notificationTypes[which].first
|
||||||
|
notificationFilter.remove(type)
|
||||||
|
if (!isChecked)
|
||||||
|
notificationFilter += type
|
||||||
|
}
|
||||||
|
.setPositiveButton(R.string.ok, null)
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.setOnDismissListener {
|
||||||
|
onDismissListener?.invoke(TAG)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.onClick {
|
||||||
|
if (notificationFilter.isEmpty()) {
|
||||||
|
app.config.forProfile().sync.notificationFilter = notificationFilter
|
||||||
|
dialog.dismiss()
|
||||||
|
return@onClick
|
||||||
|
}
|
||||||
|
// warn user when he tries to disable some notifications
|
||||||
|
MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.are_you_sure)
|
||||||
|
.setMessage(R.string.notification_filter_warning)
|
||||||
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
|
app.config.forProfile().sync.notificationFilter = notificationFilter
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
}
|
@ -24,7 +24,7 @@ class ErrorSnackbar(val activity: AppCompatActivity) : CoroutineScope {
|
|||||||
|
|
||||||
private var snackbar: Snackbar? = null
|
private var snackbar: Snackbar? = null
|
||||||
private lateinit var coordinator: CoordinatorLayout
|
private lateinit var coordinator: CoordinatorLayout
|
||||||
private val errors = mutableListOf<ApiError>()
|
private var errors = mutableListOf<ApiError>()
|
||||||
|
|
||||||
private val job = Job()
|
private val job = Job()
|
||||||
override val coroutineContext: CoroutineContext
|
override val coroutineContext: CoroutineContext
|
||||||
@ -35,7 +35,7 @@ class ErrorSnackbar(val activity: AppCompatActivity) : CoroutineScope {
|
|||||||
snackbar = Snackbar.make(coordinator, R.string.snackbar_error_text, Snackbar.LENGTH_INDEFINITE)
|
snackbar = Snackbar.make(coordinator, R.string.snackbar_error_text, Snackbar.LENGTH_INDEFINITE)
|
||||||
snackbar?.setAction(R.string.more) {
|
snackbar?.setAction(R.string.more) {
|
||||||
ErrorDetailsDialog(activity, errors)
|
ErrorDetailsDialog(activity, errors)
|
||||||
errors.clear()
|
errors = mutableListOf()
|
||||||
}
|
}
|
||||||
val bgColor = ColorUtils.compositeColors(
|
val bgColor = ColorUtils.compositeColors(
|
||||||
getColorFromAttr(activity, R.attr.colorOnSurface) and 0xcfffffff.toInt(),
|
getColorFromAttr(activity, R.attr.colorOnSurface) and 0xcfffffff.toInt(),
|
||||||
@ -46,7 +46,7 @@ class ErrorSnackbar(val activity: AppCompatActivity) : CoroutineScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun addError(apiError: ApiError): ErrorSnackbar {
|
fun addError(apiError: ApiError): ErrorSnackbar {
|
||||||
errors += apiError
|
errors.add(apiError)
|
||||||
snackbar?.setText(apiError.getStringReason(activity))
|
snackbar?.setText(apiError.getStringReason(activity))
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ import pl.szczodrzynski.edziennik.sync.UpdateWorker;
|
|||||||
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog;
|
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog;
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog;
|
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog;
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog;
|
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog;
|
||||||
|
import pl.szczodrzynski.edziennik.ui.dialogs.sync.NotificationFilterDialog;
|
||||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||||
@ -217,6 +218,24 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
|||||||
})
|
})
|
||||||
);*/
|
);*/
|
||||||
|
|
||||||
|
items.add(
|
||||||
|
new MaterialAboutActionItem(
|
||||||
|
getString(R.string.settings_profile_notifications_text),
|
||||||
|
getString(R.string.settings_profile_notifications_subtext),
|
||||||
|
new IconicsDrawable(activity)
|
||||||
|
.icon(CommunityMaterial.Icon.cmd_filter_outline)
|
||||||
|
.size(IconicsSize.dp(iconSizeDp))
|
||||||
|
.color(IconicsColor.colorInt(iconColor))
|
||||||
|
)
|
||||||
|
.setOnClickAction(() -> {
|
||||||
|
new NotificationFilterDialog(activity, null, null);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
items.add(getMoreItem(() -> addCardItems(CARD_PROFILE, getProfileCard(true))));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
items.add(
|
items.add(
|
||||||
new MaterialAboutSwitchItem(
|
new MaterialAboutSwitchItem(
|
||||||
getString(R.string.settings_profile_sync_text),
|
getString(R.string.settings_profile_sync_text),
|
||||||
@ -226,35 +245,14 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
|||||||
.size(IconicsSize.dp(iconSizeDp))
|
.size(IconicsSize.dp(iconSizeDp))
|
||||||
.color(IconicsColor.colorInt(iconColor))
|
.color(IconicsColor.colorInt(iconColor))
|
||||||
)
|
)
|
||||||
.setChecked(app.getProfile().getSyncEnabled())
|
.setChecked(app.getProfile().getSyncEnabled())
|
||||||
.setOnChangeAction(((isChecked, tag) -> {
|
.setOnChangeAction(((isChecked, tag) -> {
|
||||||
app.getProfile().setSyncEnabled(isChecked);
|
app.getProfile().setSyncEnabled(isChecked);
|
||||||
app.profileSave();
|
app.profileSave();
|
||||||
return true;
|
return true;
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
items.add(getMoreItem(() -> addCardItems(CARD_PROFILE, getProfileCard(true))));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
/*items.add(
|
|
||||||
new MaterialAboutSwitchItem(
|
|
||||||
getString(R.string.settings_profile_notify_text),
|
|
||||||
getString(R.string.settings_profile_notify_subtext),
|
|
||||||
new IconicsDrawable(activity)
|
|
||||||
.icon(CommunityMaterial.Icon.cmd_bell_ring)
|
|
||||||
.size(IconicsSize.dp(iconSizeDp))
|
|
||||||
.color(IconicsColor.colorInt(iconColor))
|
|
||||||
)
|
|
||||||
.setChecked(app.getProfile().getSyncNotifications())
|
|
||||||
.setOnChangeAction(((isChecked, tag) -> {
|
|
||||||
app.getProfile().setSyncNotifications(isChecked);
|
|
||||||
app.profileSave();
|
|
||||||
return true;
|
|
||||||
}))
|
|
||||||
);*/
|
|
||||||
|
|
||||||
items.add(
|
items.add(
|
||||||
new MaterialAboutActionItem(
|
new MaterialAboutActionItem(
|
||||||
getString(R.string.settings_profile_remove_text),
|
getString(R.string.settings_profile_remove_text),
|
||||||
|
@ -51,6 +51,8 @@ class NotificationChannelsManager(val c: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (key in toDelete) {
|
for (key in toDelete) {
|
||||||
|
if (key.contains("chucker"))
|
||||||
|
continue
|
||||||
manager.deleteNotificationChannel(key)
|
manager.deleteNotificationChannel(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,24 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
android:textAppearance="@style/NavView.TextView.Title"
|
android:textAppearance="@style/NavView.TextView.Title"
|
||||||
android:textIsSelectable="true"
|
android:textIsSelectable="true"
|
||||||
tools:text="wtorek, 17 grudnia" />
|
tools:text="wtorek, 17 grudnia" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/lessonsInfo"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:textAppearance="@style/NavView.TextView.Helper"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:text="8:00 - 14:20 (7 lekcji, 6 godzin, 20 minut)"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/lessonChangeLayout"
|
android:id="@+id/lessonChangeLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -959,4 +959,5 @@
|
|||||||
<string name="dialog_grades_config_color_by_value">By grade\'s value</string>
|
<string name="dialog_grades_config_color_by_value">By grade\'s value</string>
|
||||||
<string name="other">Other</string>
|
<string name="other">Other</string>
|
||||||
<string name="menu_grades_config">Grades settings</string>
|
<string name="menu_grades_config">Grades settings</string>
|
||||||
|
<string name="dialog_day_lessons_info">%s - %s (%s lessons - %s hours %s minutes)</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -94,6 +94,7 @@
|
|||||||
<string name="error_181" translatable="false">ERROR_LIBRUS_API_MAINTENANCE</string>
|
<string name="error_181" translatable="false">ERROR_LIBRUS_API_MAINTENANCE</string>
|
||||||
<string name="error_182" translatable="false">ERROR_LIBRUS_PORTAL_MAINTENANCE</string>
|
<string name="error_182" translatable="false">ERROR_LIBRUS_PORTAL_MAINTENANCE</string>
|
||||||
<string name="error_183" translatable="false">ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM</string>
|
<string name="error_183" translatable="false">ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM</string>
|
||||||
|
<string name="error_184" translatable="false">ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED</string>
|
||||||
|
|
||||||
<string name="error_201" translatable="false">ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN</string>
|
<string name="error_201" translatable="false">ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN</string>
|
||||||
<string name="error_202" translatable="false">ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD</string>
|
<string name="error_202" translatable="false">ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD</string>
|
||||||
@ -251,7 +252,7 @@
|
|||||||
<string name="error_168_reason">ERROR_LOGIN_LIBRUS_PORTAL_NO_REFRESH</string>
|
<string name="error_168_reason">ERROR_LOGIN_LIBRUS_PORTAL_NO_REFRESH</string>
|
||||||
<string name="error_169_reason">ERROR_LOGIN_LIBRUS_PORTAL_NO_REDIRECT</string>
|
<string name="error_169_reason">ERROR_LOGIN_LIBRUS_PORTAL_NO_REDIRECT</string>
|
||||||
<string name="error_170_reason">ERROR_LOGIN_LIBRUS_PORTAL_UNSUPPORTED_GRANT</string>
|
<string name="error_170_reason">ERROR_LOGIN_LIBRUS_PORTAL_UNSUPPORTED_GRANT</string>
|
||||||
<string name="error_171_reason">ERROR_LOGIN_LIBRUS_PORTAL_INVALID_CLIENT_ID</string>
|
<string name="error_171_reason">Błędne ID klienta. Zgłoś błąd programiście oraz zaktualizuj aplikację.</string>
|
||||||
<string name="error_172_reason">ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_INVALID</string>
|
<string name="error_172_reason">ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_INVALID</string>
|
||||||
<string name="error_173_reason">ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_REVOKED</string>
|
<string name="error_173_reason">ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_REVOKED</string>
|
||||||
<string name="error_174_reason">ERROR_LIBRUS_SYNERGIA_OTHER</string>
|
<string name="error_174_reason">ERROR_LIBRUS_SYNERGIA_OTHER</string>
|
||||||
@ -264,6 +265,7 @@
|
|||||||
<string name="error_181_reason">Librus API: przerwa techniczna</string>
|
<string name="error_181_reason">Librus API: przerwa techniczna</string>
|
||||||
<string name="error_182_reason">Librus Portal: przerwa techniczna</string>
|
<string name="error_182_reason">Librus Portal: przerwa techniczna</string>
|
||||||
<string name="error_183_reason">Wystąpił problem z tablicą ogłoszeń</string>
|
<string name="error_183_reason">Wystąpił problem z tablicą ogłoszeń</string>
|
||||||
|
<string name="error_184_reason">Librus: Sesja logowania wygasła. Zaloguj się ponownie.</string>
|
||||||
|
|
||||||
<string name="error_201_reason">Nieprawidłowy login lub hasło</string>
|
<string name="error_201_reason">Nieprawidłowy login lub hasło</string>
|
||||||
<string name="error_202_reason">Podano stare hasło</string>
|
<string name="error_202_reason">Podano stare hasło</string>
|
||||||
@ -335,7 +337,7 @@
|
|||||||
<string name="error_908_reason">EXCEPTION_MOBIDZIENNIK_WEB_FILE_REQUEST</string>
|
<string name="error_908_reason">EXCEPTION_MOBIDZIENNIK_WEB_FILE_REQUEST</string>
|
||||||
<string name="error_909_reason">EXCEPTION_LIBRUS_MESSAGES_FILE_REQUEST</string>
|
<string name="error_909_reason">EXCEPTION_LIBRUS_MESSAGES_FILE_REQUEST</string>
|
||||||
<string name="error_910_reason">EXCEPTION_NOTIFY</string>
|
<string name="error_910_reason">EXCEPTION_NOTIFY</string>
|
||||||
<string name="error_911_reason">EXCEPTION_LIBRUS_MESSAGES_REQUEST</string>
|
<string name="error_911_reason">Zgłoś błąd: pobieranie wiadomości Librus</string>
|
||||||
<string name="error_912_reason">EXCEPTION_IDZIENNIK_WEB_REQUEST</string>
|
<string name="error_912_reason">EXCEPTION_IDZIENNIK_WEB_REQUEST</string>
|
||||||
<string name="error_913_reason">EXCEPTION_IDZIENNIK_WEB_API_REQUEST</string>
|
<string name="error_913_reason">EXCEPTION_IDZIENNIK_WEB_API_REQUEST</string>
|
||||||
<string name="error_914_reason">EXCEPTION_IDZIENNIK_API_REQUEST</string>
|
<string name="error_914_reason">EXCEPTION_IDZIENNIK_API_REQUEST</string>
|
||||||
|
@ -567,7 +567,7 @@
|
|||||||
<string name="notification_type_new_homework">Nowe zadanie domowe</string>
|
<string name="notification_type_new_homework">Nowe zadanie domowe</string>
|
||||||
<string name="notification_type_new_message">Nowa wiadomość</string>
|
<string name="notification_type_new_message">Nowa wiadomość</string>
|
||||||
<string name="notification_type_new_shared_event">Udostępniono wydarzenie</string>
|
<string name="notification_type_new_shared_event">Udostępniono wydarzenie</string>
|
||||||
<string name="notification_type_notice">Ocena ucznia</string>
|
<string name="notification_type_notice">Wpis zachowania</string>
|
||||||
<string name="notification_type_server_message">Wiadomość z serwera</string>
|
<string name="notification_type_server_message">Wiadomość z serwera</string>
|
||||||
<string name="notification_type_timetable_change">Zmiana planu zajęć</string>
|
<string name="notification_type_timetable_change">Zmiana planu zajęć</string>
|
||||||
<string name="notification_type_timetable_lesson_change">Zmiana lekcji</string>
|
<string name="notification_type_timetable_lesson_change">Zmiana lekcji</string>
|
||||||
@ -1181,4 +1181,10 @@
|
|||||||
<string name="notification_user_action_required_title">Wymagane działanie w aplikacji</string>
|
<string name="notification_user_action_required_title">Wymagane działanie w aplikacji</string>
|
||||||
<string name="notification_user_action_required_text">Problem, który uniemożliwia synchronizację musi być rozwiązany przez użytkownika. Kliknij, aby uzyskać więcej informacji.</string>
|
<string name="notification_user_action_required_text">Problem, który uniemożliwia synchronizację musi być rozwiązany przez użytkownika. Kliknij, aby uzyskać więcej informacji.</string>
|
||||||
<string name="notification_user_action_required_captcha_librus">Librus: wymagane rozwiązanie zadania Captcha. Kliknij, aby kontynuować logowanie do dziennika.</string>
|
<string name="notification_user_action_required_captcha_librus">Librus: wymagane rozwiązanie zadania Captcha. Kliknij, aby kontynuować logowanie do dziennika.</string>
|
||||||
|
<string name="settings_profile_notifications_text">Filtruj powiadomienia</string>
|
||||||
|
<string name="settings_profile_notifications_subtext">Wyłącz określone rodzaje powiadomień</string>
|
||||||
|
<string name="dialog_notification_filter_title">Pokazuj wybrane powiadomienia</string>
|
||||||
|
<string name="dialog_notification_filter_text"><![CDATA[Zaznacz, które powiadomienia mają być pokazywane w systemie oraz w aplikacji.]]></string>
|
||||||
|
<string name="notification_filter_warning">Czy na pewno chcesz zastosować te ustawienia?\n\nNie będziesz widział informacji o niektórych danych, przez co możesz przeoczyć ważne komunikaty, wiadomości lub oceny.\n\nUstawienia zostaną zastosowane dla aktualnie otwartego profilu.</string>
|
||||||
|
<string name="dialog_day_lessons_info">%s - %s (%s lekcji - %s godzin %s minut)</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -93,7 +93,7 @@
|
|||||||
<item name="colorPrimary">#2196f3</item>
|
<item name="colorPrimary">#2196f3</item>
|
||||||
<item name="colorPrimaryDark">#1976d2</item>
|
<item name="colorPrimaryDark">#1976d2</item>
|
||||||
<item name="colorPrimaryVariant">#2196f3</item>
|
<item name="colorPrimaryVariant">#2196f3</item>
|
||||||
<item name="colorAccent">#2962ff</item>
|
<item name="colorAccent">#ff3d00</item>
|
||||||
<item name="colorSection">@color/colorSection</item>
|
<item name="colorSection">@color/colorSection</item>
|
||||||
|
|
||||||
<item name="colorFab">#4CAF50</item>
|
<item name="colorFab">#4CAF50</item>
|
||||||
@ -128,7 +128,7 @@
|
|||||||
<item name="colorPrimary">#64b5f6</item>
|
<item name="colorPrimary">#64b5f6</item>
|
||||||
<item name="colorPrimaryDark">#1976d2</item>
|
<item name="colorPrimaryDark">#1976d2</item>
|
||||||
<item name="colorPrimaryVariant">#2196f3</item>
|
<item name="colorPrimaryVariant">#2196f3</item>
|
||||||
<item name="colorAccent">#2962ff</item>
|
<item name="colorAccent">#ffb300</item>
|
||||||
<item name="colorSection">@color/colorSection</item>
|
<item name="colorSection">@color/colorSection</item>
|
||||||
|
|
||||||
<item name="colorFab">#4CAF50</item>
|
<item name="colorFab">#4CAF50</item>
|
||||||
|
20
build.gradle
20
build.gradle
@ -5,8 +5,8 @@ buildscript {
|
|||||||
kotlin_version = '1.3.61'
|
kotlin_version = '1.3.61'
|
||||||
|
|
||||||
release = [
|
release = [
|
||||||
versionName: "4.0-beta.9",
|
versionName: "4.0-beta.10",
|
||||||
versionCode: 4000009
|
versionCode: 4000010
|
||||||
]
|
]
|
||||||
|
|
||||||
setup = [
|
setup = [
|
||||||
@ -17,14 +17,14 @@ buildscript {
|
|||||||
]
|
]
|
||||||
|
|
||||||
versions = [
|
versions = [
|
||||||
kotlin : "1.3.61",
|
kotlin : ext.kotlin_version,
|
||||||
ktx : "1.1.0",
|
ktx : "1.2.0",
|
||||||
|
|
||||||
androidX : '1.0.0',
|
androidX : '1.0.0',
|
||||||
annotation : '1.1.0',
|
annotation : '1.1.0',
|
||||||
recyclerView : '1.2.0-alpha01',
|
recyclerView : '1.2.0-alpha01',
|
||||||
material : '1.2.0-alpha03',
|
material : '1.2.0-alpha05',
|
||||||
appcompat : '1.2.0-alpha01',
|
appcompat : '1.2.0-alpha02',
|
||||||
constraintLayout : '2.0.0-beta4',
|
constraintLayout : '2.0.0-beta4',
|
||||||
cardview : '1.0.0',
|
cardview : '1.0.0',
|
||||||
gridLayout : '1.0.0',
|
gridLayout : '1.0.0',
|
||||||
@ -32,9 +32,9 @@ buildscript {
|
|||||||
navigationFragment: "1.0.0",
|
navigationFragment: "1.0.0",
|
||||||
legacy : "1.0.0",
|
legacy : "1.0.0",
|
||||||
|
|
||||||
room : "2.2.3",
|
room : "2.2.4",
|
||||||
lifecycle : "2.2.0-rc03",
|
lifecycle : "2.2.0",
|
||||||
work : "2.2.0",
|
work : "2.3.2",
|
||||||
|
|
||||||
firebase : '17.2.2',
|
firebase : '17.2.2',
|
||||||
firebasemessaging: "20.1.0",
|
firebasemessaging: "20.1.0",
|
||||||
@ -61,7 +61,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
classpath 'com.android.tools.build:gradle:4.0.0-alpha09'
|
||||||
classpath 'me.tatarka:gradle-retrolambda:3.7.0'
|
classpath 'me.tatarka:gradle-retrolambda:3.7.0'
|
||||||
classpath 'com.google.gms:google-services:4.3.3'
|
classpath 'com.google.gms:google-services:4.3.3'
|
||||||
classpath 'io.fabric.tools:gradle:1.28.1'
|
classpath 'io.fabric.tools:gradle:1.28.1'
|
||||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-rc-1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip
|
||||||
|
Reference in New Issue
Block a user