From 003ffa22511bc30d4b09649de56789439eb926cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Thu, 19 Sep 2019 11:23:48 +0200 Subject: [PATCH 001/691] APIv2: Create DataEndpoint --- .../pl/szczodrzynski/edziennik/api/v2/models/DataEndpoint.kt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataEndpoint.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataEndpoint.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataEndpoint.kt new file mode 100644 index 00000000..0505dac3 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataEndpoint.kt @@ -0,0 +1,4 @@ +package pl.szczodrzynski.edziennik.api.v2.models + +class DataEndpoint() { +} \ No newline at end of file From 4b5c14cbd5b62e44f6cd4def18c3fc27d29339e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 20 Sep 2019 19:12:05 +0200 Subject: [PATCH 002/691] APIv2: Prepare schemes --- .idea/copyright/kubasz.xml | 6 ++++ .idea/copyright/profiles_settings.xml | 3 ++ .../edziennik/api/v2/Constants.kt | 21 +++++++++++ .../edziennik/api/v2/Endpoints.kt | 36 +++++++++++++++++++ .../edziennik/api/v2/LoginMethods.kt | 6 ++++ .../api/v2/interfaces/ILoginMethod.kt | 4 +++ .../edziennik/api/v2/librus/Librus.kt | 2 +- .../api/v2/librus/data/LibrusApiGrades.kt | 18 ++++++++++ .../v2/librus/data/LibrusSynergiaGrades.kt | 4 +++ .../edziennik/api/v2/librus/login/LoginJst.kt | 11 ++++-- .../api/v2/{ => models}/ApiLoginResult.kt | 2 +- .../edziennik/api/v2/models/DataEndpoint.kt | 4 --- .../edziennik/api/v2/models/DataStore.kt | 5 +++ .../edziennik/api/v2/models/Endpoint.kt | 26 ++++++++++++++ .../edziennik/api/v2/models/Feature.kt | 2 ++ .../api/v2/{ => models}/FirstLoginResult.kt | 2 +- .../edziennik/api/v2/models/LoginMethod.kt | 29 +++++++++++++++ 17 files changed, 172 insertions(+), 9 deletions(-) create mode 100644 .idea/copyright/kubasz.xml create mode 100644 .idea/copyright/profiles_settings.xml create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/ILoginMethod.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/{ => models}/ApiLoginResult.kt (78%) delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataEndpoint.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/{ => models}/FirstLoginResult.kt (79%) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt diff --git a/.idea/copyright/kubasz.xml b/.idea/copyright/kubasz.xml new file mode 100644 index 00000000..2a54803f --- /dev/null +++ b/.idea/copyright/kubasz.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 00000000..61f16974 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index 3aae0da1..0fa7e319 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -1,5 +1,10 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-19. + */ + package pl.szczodrzynski.edziennik.api.v2 +internal const val FEATURE_ANY = -1 const val FEATURE_ALL = 0 const val FEATURE_TIMETABLE = 1 const val FEATURE_AGENDA = 2 @@ -17,9 +22,25 @@ const val LOGIN_TYPE_IUCZNIOWIE = 3 const val LOGIN_TYPE_VULCAN = 4 const val LOGIN_TYPE_DEMO = 20 +// LOGIN MODES const val LOGIN_MODE_LIBRUS_EMAIL = 0 const val LOGIN_MODE_LIBRUS_SYNERGIA = 1 const val LOGIN_MODE_LIBRUS_JST = 2 +const val LOGIN_MODE_MOBIDZIENNIK_WEB = 0 +const val LOGIN_MODE_IDZIENNIK_WEB = 0 +const val LOGIN_MODE_VULCAN_WEB = 0 + +// LOGIN METHODS +const val LOGIN_METHOD_NOT_NEEDED = -1 +const val LOGIN_METHOD_LIBRUS_PORTAL = 0 +const val LOGIN_METHOD_LIBRUS_API = 1 +const val LOGIN_METHOD_LIBRUS_SYNERGIA = 2 +const val LOGIN_METHOD_LIBRUS_MESSAGES = 3 +const val LOGIN_METHOD_MOBIDZIENNIK_API = 0 +const val LOGIN_METHOD_IDZIENNIK_WEB = 0 +const val LOGIN_METHOD_IDZIENNIK_API = 1 +const val LOGIN_METHOD_VULCAN_WEB = 0 +const val LOGIN_METHOD_VULCAN_API = 1 const val LIBRUS_USER_AGENT = "Dalvik/2.1.0 Android LibrusMobileApp" const val LIBRUS_CLIENT_ID = "wmSyUMo8llDAs4y9tJVYY92oyZ6h4lAt7KCuy0Gv" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt new file mode 100644 index 00000000..bcbc0cc9 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-20. + */ + +package pl.szczodrzynski.edziennik.api.v2 + +import android.util.Log +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApiGrades +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergiaGrades +import pl.szczodrzynski.edziennik.api.v2.models.Endpoint + +const val ENDPOINT_LIBRUS_API_ME = 0 +const val ENDPOINT_LIBRUS_API_GRADES = 0 +const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 0 + +val librusEndpoints = listOf( + Endpoint(LOGIN_TYPE_LIBRUS, 1, listOf(), LibrusSynergiaGrades::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_SYNERGIA }, + Endpoint(LOGIN_TYPE_LIBRUS, 1, listOf(), LibrusApiGrades::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_API } +) + +/* + SYNC: + + look up all endpoints for the given API and given features + + load "next sync timers" for every endpoint + + exclude every endpoint which does not need to sync now + + check all needed login methods + create a login method list, using methods' dependencies as well + use all login methods, saving completed logins to data store + + instantiate all endpoint classes and sync them (writing to data store, returns onSuccess or error Callback) + + */ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt new file mode 100644 index 00000000..aed463b5 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt @@ -0,0 +1,6 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-20. + */ + +package pl.szczodrzynski.edziennik.api.v2 + diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/ILoginMethod.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/ILoginMethod.kt new file mode 100644 index 00000000..440a5693 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/ILoginMethod.kt @@ -0,0 +1,4 @@ +package pl.szczodrzynski.edziennik.api.v2.interfaces + +interface ILoginMethod { +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index 8ba1f79e..82544fa6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -99,7 +99,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore) : | |__| |____) | | | \____/|_____/ |*/ private fun loginJst() { - LoginJst(app, loginStore, syncCallback) { + LoginJst(app, null, loginStore, syncCallback) { if (profile == null) { firstLoginSynergia() return@LoginJst diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt new file mode 100644 index 00000000..261a4476 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt @@ -0,0 +1,18 @@ +package pl.szczodrzynski.edziennik.api.v2.librus.data + +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback +import pl.szczodrzynski.edziennik.api.v2.models.DataStore +import pl.szczodrzynski.edziennik.datamodels.LoginStore +import pl.szczodrzynski.edziennik.datamodels.Profile + +class LibrusGrades(val app: App, + val profile: Profile, + val loginStore: LoginStore, + val dataStore: DataStore, + val callback: ProgressCallback, + val onSuccess: () -> Unit) { + init { + + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt new file mode 100644 index 00000000..a1693141 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt @@ -0,0 +1,4 @@ +package pl.szczodrzynski.edziennik.api.v2.librus.data + +class LibrusSynergiaGrades { +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginJst.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginJst.kt index 78ed7672..f21290eb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginJst.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginJst.kt @@ -2,10 +2,17 @@ package pl.szczodrzynski.edziennik.api.v2.librus.login import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback -import pl.szczodrzynski.edziennik.api.v2.ApiLoginResult +import pl.szczodrzynski.edziennik.api.v2.interfaces.ILoginMethod import pl.szczodrzynski.edziennik.datamodels.LoginStore +import pl.szczodrzynski.edziennik.datamodels.Profile -class LoginJst(val app: App, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) { +class LoginJst( + app: App, + profile: Profile?, + loginStore: LoginStore, + callback: ProgressCallback, + onSuccess: () -> Unit +): ILoginMethod(app, profile, loginStore, callback, onSuccess) { companion object { private const val TAG = "librus.LoginJst" } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiLoginResult.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiLoginResult.kt similarity index 78% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiLoginResult.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiLoginResult.kt index 30b0edad..366f7a73 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiLoginResult.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiLoginResult.kt @@ -1,4 +1,4 @@ -package pl.szczodrzynski.edziennik.api.v2 +package pl.szczodrzynski.edziennik.api.v2.models import pl.szczodrzynski.edziennik.api.AppError import pl.szczodrzynski.edziennik.datamodels.LoginStore diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataEndpoint.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataEndpoint.kt deleted file mode 100644 index 0505dac3..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataEndpoint.kt +++ /dev/null @@ -1,4 +0,0 @@ -package pl.szczodrzynski.edziennik.api.v2.models - -class DataEndpoint() { -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataStore.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataStore.kt index 552340fa..23d500a8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataStore.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataStore.kt @@ -7,6 +7,9 @@ import pl.szczodrzynski.edziennik.datamodels.* import pl.szczodrzynski.edziennik.models.Date data class DataStore(private val appDb: AppDb, val profileId: Int) { + + val loginMethods = mutableListOf() + val teacherList: LongSparseArray = LongSparseArray() val subjectList: LongSparseArray = LongSparseArray() val teamList = mutableListOf() @@ -43,6 +46,8 @@ data class DataStore(private val appDb: AppDb, val profileId: Int) { } fun clear() { + loginMethods.clear() + teacherList.clear() subjectList.clear() teamList.clear() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt new file mode 100644 index 00000000..d760b3ab --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt @@ -0,0 +1,26 @@ +package pl.szczodrzynski.edziennik.api.v2.models + +import pl.szczodrzynski.edziennik.datamodels.LoginStore +import pl.szczodrzynski.edziennik.datamodels.Profile + +/** + * A Endpoint descriptor class. + * + * The API runs appropriate endpoints in order to fulfill its + * [Feature] list. + * An endpoint may have its [LoginMethod] dependencies which will be + * satisfied by the API before the [endpointClass]'s constructor is invoked. + * + * @param loginType type of the e-register this endpoint handles + * @param endpointId a unique ID of this endpoint + * @param featureIds a [List] of [Feature]s (their IDs) this endpoint can download + * @param endpointClass a [Class] which constructor will be invoked when a data download is needed + * @param requiredLoginMethod a lambda returning a required login method (which will be called before this). May differ depending on the [Profile] and/or [LoginStore]. + */ +class Endpoint( + val loginType: Int, + val endpointId: Int, + val featureIds: List, + val endpointClass: Class<*>, + val requiredLoginMethod: (profile: Profile?, loginStore: LoginStore) -> Int +) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt index b502fe85..f83c4801 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt @@ -1,5 +1,7 @@ package pl.szczodrzynski.edziennik.api.v2.models +import pl.szczodrzynski.edziennik.api.v2.endpoint + data class Feature(val featureId: Int, val loginOptions: Map>) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/FirstLoginResult.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/FirstLoginResult.kt similarity index 79% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/FirstLoginResult.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/FirstLoginResult.kt index 92d3da9e..362846fd 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/FirstLoginResult.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/FirstLoginResult.kt @@ -1,4 +1,4 @@ -package pl.szczodrzynski.edziennik.api.v2 +package pl.szczodrzynski.edziennik.api.v2.models import pl.szczodrzynski.edziennik.api.AppError import pl.szczodrzynski.edziennik.datamodels.Profile diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt new file mode 100644 index 00000000..9b00bf0b --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-20. + */ + +package pl.szczodrzynski.edziennik.api.v2.models + +import pl.szczodrzynski.edziennik.datamodels.LoginStore +import pl.szczodrzynski.edziennik.datamodels.Profile + +/** + * A Login Method descriptor class. + * + * This is used by the API to satisfy all [Endpoint]s' dependencies. + * A login method may have its own dependencies which need to be + * satisfied before the [loginMethodClass]'s constructor is invoked. + * + * @param loginType type of the e-register this login method handles + * @param loginMethodId a unique ID of this login method + * @param featureIds a [List] of [Feature]s (their IDs) this login method can provide access to + * @param loginMethodClass a [Class] which constructor will be invoked when a log in is needed + * @param requiredLoginMethod a lambda returning a required login method (which will be called before this). May differ depending on the [Profile] and/or [LoginStore]. + */ +class LoginMethod( + val loginType: Int, + val loginMethodId: Int, + val featureIds: List, + val loginMethodClass: Class<*>, + val requiredLoginMethod: (profile: Profile?, loginStore: LoginStore) -> Int +) \ No newline at end of file From 3827aeb9b4315a2074fa1de3824c55b32bbc2d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 20 Sep 2019 19:12:47 +0200 Subject: [PATCH 003/691] APIv2: Update endpoints --- .../api/v2/interfaces/ILoginMethod.kt | 18 +++++++++++++++++- .../api/v2/librus/data/LibrusApiGrades.kt | 17 +++++++++++------ .../api/v2/librus/data/LibrusSynergiaGrades.kt | 17 ++++++++++++++++- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/ILoginMethod.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/ILoginMethod.kt index 440a5693..c1834b29 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/ILoginMethod.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/ILoginMethod.kt @@ -1,4 +1,20 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-20. + */ + package pl.szczodrzynski.edziennik.api.v2.interfaces -interface ILoginMethod { +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback +import pl.szczodrzynski.edziennik.datamodels.LoginStore +import pl.szczodrzynski.edziennik.datamodels.Profile + +abstract class ILoginMethod( + val app: App, + val profile: Profile?, + val loginStore: LoginStore, + val callback: ProgressCallback, + val onSuccess: () -> Unit +) { + } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt index 261a4476..eae53a75 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt @@ -6,12 +6,17 @@ import pl.szczodrzynski.edziennik.api.v2.models.DataStore import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile -class LibrusGrades(val app: App, - val profile: Profile, - val loginStore: LoginStore, - val dataStore: DataStore, - val callback: ProgressCallback, - val onSuccess: () -> Unit) { +class LibrusApiGrades(val app: App, + val profile: Profile, + val loginStore: LoginStore, + val dataStore: DataStore, + val callback: ProgressCallback, + val onSuccess: () -> Unit) : EndpointInterface { + override fun sync() { + + } + + init { } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt index a1693141..ecf10682 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt @@ -1,4 +1,19 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data -class LibrusSynergiaGrades { +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback +import pl.szczodrzynski.edziennik.api.v2.models.DataStore +import pl.szczodrzynski.edziennik.datamodels.LoginStore +import pl.szczodrzynski.edziennik.datamodels.Profile + +class LibrusSynergiaGrades(val app: App, + val profile: Profile, + val loginStore: LoginStore, + val dataStore: DataStore, + val callback: ProgressCallback, + val onSuccess: () -> Unit) { + + init { + + } } \ No newline at end of file From 1bdee7857cf7a4b7b42424c1b15a342b9b7c7909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sat, 21 Sep 2019 23:00:15 +0200 Subject: [PATCH 004/691] Update Profile, remove older app version check --- .../java/pl/szczodrzynski/edziennik/App.java | 4 +- .../pl/szczodrzynski/edziennik/Extensions.kt | 47 ++++++++++++++----- .../edziennik/datamodels/Profile.kt | 25 +++++++++- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/App.java b/app/src/main/java/pl/szczodrzynski/edziennik/App.java index 09926597..37b11cc5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/App.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/App.java @@ -514,7 +514,7 @@ public class App extends androidx.multidex.MultiDexApplication { } } - if (appConfig.lastAppVersion > BuildConfig.VERSION_CODE) { + /*if (appConfig.lastAppVersion > BuildConfig.VERSION_CODE) { BootReceiver br = new BootReceiver(); Intent i = new Intent(); //i.putExtra("UserChecked", true); @@ -522,7 +522,7 @@ public class App extends androidx.multidex.MultiDexApplication { Toast.makeText(mContext, R.string.warning_older_version_running, Toast.LENGTH_LONG).show(); //Toast.makeText(mContext, "Zaktualizuj aplikację.", Toast.LENGTH_LONG).show(); //System.exit(0); - } + }*/ if (appConfig == null) { appConfig = new AppConfig(this); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index c6d05ce8..2dff656b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -6,21 +6,14 @@ import android.content.Context import android.content.pm.PackageManager import android.os.Build import android.os.Bundle -import android.util.Log import androidx.core.app.ActivityCompat import com.google.gson.JsonArray -import com.google.gson.JsonNull import com.google.gson.JsonObject -import im.wangchao.mhttp.Response -import im.wangchao.mhttp.callback.JsonCallbackHandler -import im.wangchao.mhttp.callback.TextCallbackHandler -import im.wangchao.mhttp.internal.exception.ResponseFailException import pl.szczodrzynski.edziennik.datamodels.Profile import pl.szczodrzynski.edziennik.datamodels.Teacher -import pl.szczodrzynski.navlib.R import pl.szczodrzynski.navlib.crc16 import pl.szczodrzynski.navlib.getColorFromRes -import kotlin.contracts.contract + fun List.byId(id: Long) = firstOrNull { it.id == id } fun List.byNameFirstLast(nameFirstLast: String) = firstOrNull { it.name + " " + it.surname == nameFirstLast } @@ -28,11 +21,19 @@ fun List.byNameLastFirst(nameLastFirst: String) = firstOrNull { it.surn fun List.byNameFDotLast(nameFDotLast: String) = firstOrNull { it.name + "." + it.surname == nameFDotLast } fun List.byNameFDotSpaceLast(nameFDotSpaceLast: String) = firstOrNull { it.name + ". " + it.surname == nameFDotSpaceLast } -fun JsonObject.getString(key: String): String? = get(key).let { if (it.isJsonNull) null else it.asString } -fun JsonObject.getInt(key: String): Int? = get(key).let { if (it.isJsonNull) null else it.asInt } -fun JsonObject.getLong(key: String): Long? = get(key).let { if (it.isJsonNull) null else it.asLong } -fun JsonObject.getJsonObject(key: String): JsonObject? = get(key).let { if (it.isJsonNull) null else it.asJsonObject } -fun JsonObject.getJsonArray(key: String): JsonArray? = get(key).let { if (it.isJsonNull) null else it.asJsonArray } +fun JsonObject.getBoolean(key: String): Boolean? = get(key)?.let { if (it.isJsonNull) null else it.asBoolean } +fun JsonObject.getString(key: String): String? = get(key)?.let { if (it.isJsonNull) null else it.asString } +fun JsonObject.getInt(key: String): Int? = get(key)?.let { if (it.isJsonNull) null else it.asInt } +fun JsonObject.getLong(key: String): Long? = get(key)?.let { if (it.isJsonNull) null else it.asLong } +fun JsonObject.getJsonObject(key: String): JsonObject? = get(key)?.let { if (it.isJsonNull) null else it.asJsonObject } +fun JsonObject.getJsonArray(key: String): JsonArray? = get(key)?.let { if (it.isJsonNull) null else it.asJsonArray } + +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.getInt(key: String, defaultValue: Int): Int = get(key)?.let { if (it.isJsonNull) defaultValue else it.asInt } ?: defaultValue +fun JsonObject.getLong(key: String, defaultValue: Long): Long = get(key)?.let { if (it.isJsonNull) defaultValue else it.asLong } ?: defaultValue +fun JsonObject.getJsonObject(key: String, defaultValue: JsonObject): JsonObject = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonObject } ?: defaultValue +fun JsonObject.getJsonArray(key: String, defaultValue: JsonArray): JsonArray = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonArray } ?: defaultValue fun CharSequence?.isNotNullNorEmpty(): Boolean { return this != null && this.isNotEmpty() @@ -53,6 +54,26 @@ fun Bundle?.getString(key: String, defaultValue: String): String { return this?.getString(key, defaultValue) ?: defaultValue } +fun changeStringCase(s: String): String { + val delimiters = " '-/" + val sb = StringBuilder() + var capNext = true + for (ch in s.toCharArray()) { + var c = ch + c = if (capNext) + Character.toUpperCase(c) + else + Character.toLowerCase(c) + sb.append(c) + capNext = delimiters.indexOf(c) >= 0 + } + return sb.toString() +} + +fun buildFullName(firstName: String?, lastName: String?): String { + return changeStringCase("$firstName $lastName").trim() +} + fun colorFromName(context: Context, name: String?): Int { var crc = crc16(name ?: "") crc = (crc and 0xff) or (crc shr 8) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/datamodels/Profile.kt b/app/src/main/java/pl/szczodrzynski/edziennik/datamodels/Profile.kt index 1404e8bf..8fa92771 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/datamodels/Profile.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/datamodels/Profile.kt @@ -47,11 +47,26 @@ open class Profile : IDrawerProfile { var empty = true var archived = false + /** + * The name of the student. + * This doesn't change, no matter if it's a parent or student account. + */ var studentNameLong: String? = null var studentNameShort: String? = null + /** + * The student's number in the class register. + */ var studentNumber = -1 var studentData: JsonObject? = null + /** + * A full name of the account owner. + * If null, then it's a student account. + * If not null, then it's a parent account with this name. + */ + @Ignore + var accountNameLong: String? = null + var registration = REGISTRATION_UNSPECIFIED var gradeColorMode = COLOR_MODE_WEIGHTED @@ -61,7 +76,7 @@ open class Profile : IDrawerProfile { var currentSemester = 1 - var attendancePercentage: Float = 0.toFloat() + var attendancePercentage: Float = 0.0f var dateSemester1Start: Date? = null var dateSemester2Start: Date? = null @@ -192,6 +207,12 @@ open class Profile : IDrawerProfile { getImageHolder(imageView.context).applyTo(imageView) } + fun hasStudentData(key: String): Boolean { + if (studentData == null) + return false + return studentData?.has(key) ?: false + } + fun getStudentData(key: String, defaultValue: String?): String? { if (studentData == null) return defaultValue @@ -229,7 +250,7 @@ open class Profile : IDrawerProfile { return element?.asBoolean ?: defaultValue } - fun putStudentData(key: String, value: String) { + fun putStudentData(key: String, value: String?) { if (studentData == null) studentData = JsonObject() studentData!!.addProperty(key, value) From 76d39ac623c9848cb6ac8078390d7d882b063d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sat, 21 Sep 2019 23:01:31 +0200 Subject: [PATCH 005/691] [APIv2/Librus] Create Login methods, update API structure --- .../szczodrzynski/edziennik/api/AppError.java | 3 +- .../szczodrzynski/edziennik/api/Librus.java | 3 - .../pl/szczodrzynski/edziennik/api/v2/Api.kt | 21 ++ .../edziennik/api/v2/Constants.kt | 43 ++- .../edziennik/api/v2/Endpoints.kt | 4 +- .../szczodrzynski/edziennik/api/v2/Errors.kt | 41 +++ .../edziennik/api/v2/LoginMethods.kt | 44 ++++ .../edziennik/api/v2/librus/Librus.kt | 170 ++---------- .../edziennik/api/v2/librus/LibrusHelpers.kt | 6 + .../edziennik/api/v2/librus/LibrusOld.kt | 172 ++++++++++++ .../edziennik/api/v2/librus/LibrusTest.kt | 63 +++++ .../api/v2/librus/data/DataLibrus.kt | 116 ++++++++- .../edziennik/api/v2/librus/data/LibrusApi.kt | 115 ++++++++ .../api/v2/librus/data/LibrusApiGrades.kt | 9 +- .../api/v2/librus/data/LibrusApiMe.kt | 35 +++ .../v2/librus/data/LibrusSynergiaGrades.kt | 12 +- .../edziennik/api/v2/librus/login/LoginJst.kt | 20 -- .../api/v2/librus/login/LoginLibrus.kt | 228 +++++----------- .../api/v2/librus/login/LoginLibrusApi.kt | 246 ++++++++++++++++++ .../v2/librus/login/LoginLibrusMessages.kt | 8 + .../api/v2/librus/login/LoginLibrusPortal.kt | 179 +++++++++++++ .../v2/librus/login/LoginLibrusSynergia.kt | 8 + .../api/v2/librus/login/LoginSynergia.kt | 12 - .../v2/librus/login/SynergiaTokenExtractor.kt | 81 +++--- .../api/v2/models/{DataStore.kt => Data.kt} | 73 +++--- .../edziennik/api/v2/models/Endpoint.kt | 3 +- .../edziennik/api/v2/models/Feature.kt | 1 - .../edziennik/api/v2/models/LoginMethod.kt | 3 +- .../edziennik/fragments/HomeFragment.java | 44 +--- 29 files changed, 1258 insertions(+), 505 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Api.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusHelpers.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusOld.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiMe.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginJst.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginSynergia.kt rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/{DataStore.kt => Data.kt} (61%) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java b/app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java index d5f6c2a7..a22c8df8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java @@ -47,11 +47,10 @@ public class AppError { public static final int CODE_LIBRUS_DISCONNECTED = 31; public static final int CODE_PROFILE_ARCHIVED = 30; + public static final int CODE_INTERNAL_MISSING_DATA = 100; // internal errors - not for user's information. // these error codes are processed in API main classes - public static final int CODE_INTERNAL_LIBRUS_ACCOUNT_410 = 120; - public static final int CODE_INTERNAL_LIBRUS_ACCOUNT_410_ = 120; public String TAG; public int line; diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java b/app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java index c3d01bb3..4a552dd8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java @@ -51,7 +51,6 @@ import pl.szczodrzynski.edziennik.api.interfaces.LoginCallback; import pl.szczodrzynski.edziennik.api.interfaces.MessageGetCallback; import pl.szczodrzynski.edziennik.api.interfaces.RecipientListGetCallback; import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback; -import pl.szczodrzynski.edziennik.api.v2.models.DataStore; import pl.szczodrzynski.edziennik.datamodels.Announcement; import pl.szczodrzynski.edziennik.datamodels.Attendance; import pl.szczodrzynski.edziennik.datamodels.Event; @@ -213,8 +212,6 @@ public class Librus implements EdziennikInterface { this.fullSync = profile == null || profile.getEmpty() || profile.shouldFullSync(activityContext); this.today = Date.getToday(); - DataStore ds = new DataStore(app.db, profileId); - this.librusEmail = loginStore.getLoginData("email", ""); this.librusPassword = loginStore.getLoginData("password", ""); if (profile == null) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Api.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Api.kt new file mode 100644 index 00000000..97060c70 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Api.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + +package pl.szczodrzynski.edziennik.api.v2 + +import com.crashlytics.android.Crashlytics +import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.v2.models.Data + +open class Api(open val data: Data) { + fun finishWithError(error: AppError) { + try { + data.saveData() + } catch (e: Exception) { + Crashlytics.logException(e) + } + + data.callback.onError(null, error) + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index 0fa7e319..047dd38f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -16,33 +16,8 @@ const val FEATURE_MESSAGES_INBOX = 7 const val FEATURE_MESSAGES_OUTBOX = 8 const val FEATURE_ANNOUNCEMENTS = 9 -const val LOGIN_TYPE_MOBIDZIENNIK = 1 -const val LOGIN_TYPE_LIBRUS = 2 -const val LOGIN_TYPE_IUCZNIOWIE = 3 -const val LOGIN_TYPE_VULCAN = 4 -const val LOGIN_TYPE_DEMO = 20 - -// LOGIN MODES -const val LOGIN_MODE_LIBRUS_EMAIL = 0 -const val LOGIN_MODE_LIBRUS_SYNERGIA = 1 -const val LOGIN_MODE_LIBRUS_JST = 2 -const val LOGIN_MODE_MOBIDZIENNIK_WEB = 0 -const val LOGIN_MODE_IDZIENNIK_WEB = 0 -const val LOGIN_MODE_VULCAN_WEB = 0 - -// LOGIN METHODS -const val LOGIN_METHOD_NOT_NEEDED = -1 -const val LOGIN_METHOD_LIBRUS_PORTAL = 0 -const val LOGIN_METHOD_LIBRUS_API = 1 -const val LOGIN_METHOD_LIBRUS_SYNERGIA = 2 -const val LOGIN_METHOD_LIBRUS_MESSAGES = 3 -const val LOGIN_METHOD_MOBIDZIENNIK_API = 0 -const val LOGIN_METHOD_IDZIENNIK_WEB = 0 -const val LOGIN_METHOD_IDZIENNIK_API = 1 -const val LOGIN_METHOD_VULCAN_WEB = 0 -const val LOGIN_METHOD_VULCAN_API = 1 - const val LIBRUS_USER_AGENT = "Dalvik/2.1.0 Android LibrusMobileApp" +const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0" const val LIBRUS_CLIENT_ID = "wmSyUMo8llDAs4y9tJVYY92oyZ6h4lAt7KCuy0Gv" 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" @@ -51,3 +26,19 @@ const val LIBRUS_TOKEN_URL = "https://portal.librus.pl/oauth2/access_token" const val LIBRUS_ACCOUNT_URL = "https://portal.librus.pl/api/v2/SynergiaAccounts/fresh/" // + login const val LIBRUS_ACCOUNTS_URL = "https://portal.librus.pl/api/v2/SynergiaAccounts" + +const val LIBRUS_API_URL = "https://api.librus.pl/2.0/" +const val LIBRUS_API_TOKEN_URL = "https://api.librus.pl/OAuth/Token" +const val LIBRUS_API_TOKEN_JST_URL = "https://api.librus.pl/OAuth/TokenJST" +const val LIBRUS_API_AUTHORIZATION = "Mjg6ODRmZGQzYTg3YjAzZDNlYTZmZmU3NzdiNThiMzMyYjE=" +const val LIBRUS_API_SECRET_JST = "18b7c1ee08216f636a1b1a2440e68398" +const val LIBRUS_API_CLIENT_ID_JST = "49" +//const val LIBRUS_API_CLIENT_ID_JST_REFRESH = "42" + +const val LIBRUS_JST_DEMO_CODE = "68656A21" +const val LIBRUS_JST_DEMO_PIN = "1290" + +const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/token/\$token/przenies/" + +const val LIBRUS_MESSAGES_URL = "https://wiadomosci.librus.pl/module/" +const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action=" \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt index bcbc0cc9..8def0345 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt @@ -6,6 +6,7 @@ package pl.szczodrzynski.edziennik.api.v2 import android.util.Log import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApiGrades +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApiMe import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergiaGrades import pl.szczodrzynski.edziennik.api.v2.models.Endpoint @@ -13,7 +14,8 @@ const val ENDPOINT_LIBRUS_API_ME = 0 const val ENDPOINT_LIBRUS_API_GRADES = 0 const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 0 -val librusEndpoints = listOf( +val endpoints = listOf( + Endpoint(LOGIN_TYPE_LIBRUS, ENDPOINT_LIBRUS_API_ME, null, LibrusApiMe::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_API}, Endpoint(LOGIN_TYPE_LIBRUS, 1, listOf(), LibrusSynergiaGrades::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_SYNERGIA }, Endpoint(LOGIN_TYPE_LIBRUS, 1, listOf(), LibrusApiGrades::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_API } ) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt new file mode 100644 index 00000000..4f944965 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + +package pl.szczodrzynski.edziennik.api.v2 + +/*const val CODE_OTHER = 0 +const val CODE_OK = 1 +const val CODE_NO_INTERNET = 10 +const val CODE_SSL_ERROR = 13 +const val CODE_ARCHIVED = 5 +const val CODE_MAINTENANCE = 6 +const val CODE_LOGIN_ERROR = 7 +const val CODE_ACCOUNT_MISMATCH = 8 +const val CODE_APP_SERVER_ERROR = 9 +const val CODE_MULTIACCOUNT_SETUP = 12 +const val CODE_TIMEOUT = 11 +const val CODE_PROFILE_NOT_FOUND = 14 +const val CODE_ATTACHMENT_NOT_AVAILABLE = 28 +const val CODE_INVALID_LOGIN = 2 +const val CODE_INVALID_SERVER_ADDRESS = 21 +const val CODE_INVALID_SCHOOL_NAME = 22 +const val CODE_INVALID_DEVICE = 23 +const val CODE_OLD_PASSWORD = 4 +const val CODE_INVALID_TOKEN = 24 +const val CODE_EXPIRED_TOKEN = 27 +const val CODE_INVALID_SYMBOL = 25 +const val CODE_INVALID_PIN = 26 +const val CODE_LIBRUS_NOT_ACTIVATED = 29 +const val CODE_SYNERGIA_NOT_ACTIVATED = 32 +const val CODE_LIBRUS_DISCONNECTED = 31 +const val CODE_PROFILE_ARCHIVED = 30*/ +const val CODE_INVALID_LOGIN_MODE = 130 +const val CODE_INTERNAL_MISSING_DATA = 100 +const val CODE_INTERNAL_LIBRUS_ACCOUNT_410 = 120 +const val CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED = 121 +const val CODE_LOGIN_METHOD_NOT_SATISFIED = 122 +const val CODE_LIBRUS_PROFILE_NULL = 123 +const val ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED = 124 + +const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901 \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt index aed463b5..26a39c7c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt @@ -4,3 +4,47 @@ package pl.szczodrzynski.edziennik.api.v2 +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusPortal +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusApi +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusMessages +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusSynergia +import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod + +const val LOGIN_TYPE_MOBIDZIENNIK = 1 +const val LOGIN_TYPE_LIBRUS = 2 +const val LOGIN_TYPE_IUCZNIOWIE = 3 +const val LOGIN_TYPE_VULCAN = 4 +const val LOGIN_TYPE_DEMO = 20 + +// LOGIN MODES +const val LOGIN_MODE_LIBRUS_EMAIL = 0 +const val LOGIN_MODE_LIBRUS_SYNERGIA = 1 +const val LOGIN_MODE_LIBRUS_JST = 2 +const val LOGIN_MODE_MOBIDZIENNIK_WEB = 0 +const val LOGIN_MODE_IDZIENNIK_WEB = 0 +const val LOGIN_MODE_VULCAN_WEB = 0 + +// LOGIN METHODS +const val LOGIN_METHOD_NOT_NEEDED = -1 +const val LOGIN_METHOD_LIBRUS_PORTAL = 100// 0 +const val LOGIN_METHOD_LIBRUS_API = 200// 1 +const val LOGIN_METHOD_LIBRUS_SYNERGIA = 300 // 2 +const val LOGIN_METHOD_LIBRUS_MESSAGES = 400 +const val LOGIN_METHOD_MOBIDZIENNIK_API = 100 +const val LOGIN_METHOD_IDZIENNIK_WEB = 100 +const val LOGIN_METHOD_IDZIENNIK_API = 200 +const val LOGIN_METHOD_VULCAN_WEB = 100 +const val LOGIN_METHOD_VULCAN_API = 200 + +val librusLoginMethods = listOf( + LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_PORTAL, null, LoginLibrusPortal::class.java) { _, _ -> LOGIN_METHOD_NOT_NEEDED }, + LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_API, null, LoginLibrusApi::class.java) { _, loginStore -> + if (loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) LOGIN_METHOD_LIBRUS_PORTAL else LOGIN_METHOD_NOT_NEEDED + }, + LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, listOf(FEATURE_GRADES, FEATURE_HOMEWORKS, FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_OUTBOX), LoginLibrusSynergia::class.java) { profile, _ -> + if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED + }, + LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, listOf(FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_OUTBOX), LoginLibrusMessages::class.java) { profile, _ -> + if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED + } +) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index 82544fa6..b00d7901 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -1,111 +1,31 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + package pl.szczodrzynski.edziennik.api.v2.librus import android.content.Context import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.AppError -import pl.szczodrzynski.edziennik.api.AppError.* -import pl.szczodrzynski.edziennik.api.interfaces.* -import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_EMAIL -import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_JST -import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_SYNERGIA -import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginLibrus -import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginSynergia -import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginJst -import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrus -import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginSynergia -import pl.szczodrzynski.edziennik.api.v2.librus.login.SynergiaTokenExtractor -import pl.szczodrzynski.edziennik.api.v2.models.DataStore +import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback +import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.models.Data import pl.szczodrzynski.edziennik.datamodels.LoginStore -import pl.szczodrzynski.edziennik.datamodels.MessageFull import pl.szczodrzynski.edziennik.datamodels.Profile import pl.szczodrzynski.edziennik.datamodels.ProfileFull -import pl.szczodrzynski.edziennik.messages.MessagesComposeInfo -import pl.szczodrzynski.edziennik.models.Endpoint -import pl.szczodrzynski.edziennik.utils.Utils.d -import java.lang.Exception -class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore) : EdziennikInterface { - private val TAG = "librus.Librus" +class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: SyncCallback) { - lateinit var syncCallback: SyncCallback - lateinit var featureList: ArrayList - lateinit var dataStore: DataStore - var onLogin: (() -> Unit)? = null - val internalErrorList = ArrayList() + val internalErrorList = mutableListOf() + lateinit var data: DataLibrus + + init { + data = DataLibrus(app, profile, loginStore).apply { + callback = wrapCallback(this@Librus.callback) + } - fun isError(error: AppError?): Boolean { - if (error == null) - return false - syncCallback.onError(null, error) - return true - } - /* _ _ _ - | | (_) | - | | _| |__ _ __ _ _ ___ - | | | | '_ \| '__| | | / __| - | |____| | |_) | | | |_| \__ \ - |______|_|_.__/|_| \__,_|__*/ - private fun loginLibrus() { - LoginLibrus(app, loginStore, syncCallback) { - if (profile == null) { - firstLoginLibrus() - return@LoginLibrus - } - synergiaTokenExtractor() - } - } - private fun firstLoginLibrus() { - FirstLoginLibrus(app, loginStore, syncCallback) { profileList -> - syncCallback.onLoginFirst(profileList, loginStore) - } - } - private fun synergiaTokenExtractor() { - if (profile == null) { - throw Exception("Profile may not be null") - } - SynergiaTokenExtractor(app, profile, loginStore, syncCallback) { - d(TAG, "Profile $profile") - d(TAG, "LoginStore $loginStore") - onLogin?.invoke() - } - } - /* _____ _ - / ____| (_) - | (___ _ _ _ __ ___ _ __ __ _ _ __ _ - \___ \| | | | '_ \ / _ \ '__/ _` | |/ _` | - ____) | |_| | | | | __/ | | (_| | | (_| | - |_____/ \__, |_| |_|\___|_| \__, |_|\__,_| - __/ | __/ | - |___/ |__*/ - private fun loginSynergia() { - LoginSynergia(app, loginStore, syncCallback) { - if (profile == null) { - firstLoginSynergia() - return@LoginSynergia - } - onLogin?.invoke() - } - } - private fun firstLoginSynergia() { - FirstLoginSynergia(app, loginStore, syncCallback) { profileList -> - syncCallback.onLoginFirst(profileList, loginStore) - } - } - /* _ _____ _______ - | |/ ____|__ __| - | | (___ | | - _ | |\___ \ | | - | |__| |____) | | | - \____/|_____/ |*/ - private fun loginJst() { - LoginJst(app, null, loginStore, syncCallback) { - if (profile == null) { - firstLoginSynergia() - return@LoginJst - } - onLogin?.invoke() - } } private fun wrapCallback(callback: SyncCallback): SyncCallback { @@ -135,67 +55,11 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore) : CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> { internalErrorList.add(error.errorCode) loginStore.removeLoginData("refreshToken") // force a clean login - loginLibrus() + //loginLibrus() } else -> callback.onError(activityContext, error) } } } } - - fun login(callback: SyncCallback) { - this.internalErrorList.clear() - this.syncCallback = wrapCallback(callback) - when (loginStore.mode) { - LOGIN_MODE_LIBRUS_EMAIL -> { - loginLibrus() - } - LOGIN_MODE_LIBRUS_SYNERGIA -> { - - } - LOGIN_MODE_LIBRUS_JST -> { - - } - } - } - - fun getData() { - - } - - override fun sync(activityContext: Context, callback: SyncCallback, profileId: Int, profile: Profile?, loginStore: LoginStore) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun syncMessages(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun syncFeature(activityContext: Context, callback: SyncCallback, profile: ProfileFull, vararg featureList: Int) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getMessage(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, message: MessageFull, messageCallback: MessageGetCallback) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getAttachment(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, message: MessageFull, attachmentId: Long, attachmentCallback: AttachmentGetCallback) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getRecipientList(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, recipientListGetCallback: RecipientListGetCallback) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getComposeInfo(profile: ProfileFull): MessagesComposeInfo { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getConfigurableEndpoints(profile: Profile?): MutableMap { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun isEndpointEnabled(profile: Profile?, defaultActive: Boolean, name: String?): Boolean { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusHelpers.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusHelpers.kt new file mode 100644 index 00000000..7096c1d0 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusHelpers.kt @@ -0,0 +1,6 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus + diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusOld.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusOld.kt new file mode 100644 index 00000000..e04437bf --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusOld.kt @@ -0,0 +1,172 @@ +package pl.szczodrzynski.edziennik.api.v2.librus + +import android.content.Context +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.interfaces.* +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginSynergia +import pl.szczodrzynski.edziennik.api.v2.librus.login.SynergiaTokenExtractor +import pl.szczodrzynski.edziennik.api.v2.models.Data +import pl.szczodrzynski.edziennik.datamodels.LoginStore +import pl.szczodrzynski.edziennik.datamodels.MessageFull +import pl.szczodrzynski.edziennik.datamodels.Profile +import pl.szczodrzynski.edziennik.datamodels.ProfileFull +import pl.szczodrzynski.edziennik.messages.MessagesComposeInfo +import pl.szczodrzynski.edziennik.models.Endpoint +import pl.szczodrzynski.edziennik.utils.Utils.d +import java.lang.Exception + +class LibrusOld(val app: App, val profile: Profile?, val loginStore: LoginStore) : EdziennikInterface { + private val TAG = "librus.Librus" + + lateinit var syncCallback: SyncCallback + lateinit var featureList: ArrayList + lateinit var data: Data + var onLogin: (() -> Unit)? = null + val internalErrorList = ArrayList() + + fun isError(error: AppError?): Boolean { + if (error == null) + return false + syncCallback.onError(null, error) + return true + } + + + /* _ _ _ + | | (_) | + | | _| |__ _ __ _ _ ___ + | | | | '_ \| '__| | | / __| + | |____| | |_) | | | |_| \__ \ + |______|_|_.__/|_| \__,_|__*/ + + private fun firstLoginLibrus() { + FirstLoginLibrus(app, loginStore, syncCallback) { profileList -> + syncCallback.onLoginFirst(profileList, loginStore) + } + } + private fun synergiaTokenExtractor() { + if (profile == null) { + throw Exception("Profile may not be null") + } + + } + /* _____ _ + / ____| (_) + | (___ _ _ _ __ ___ _ __ __ _ _ __ _ + \___ \| | | | '_ \ / _ \ '__/ _` | |/ _` | + ____) | |_| | | | | __/ | | (_| | | (_| | + |_____/ \__, |_| |_|\___|_| \__, |_|\__,_| + __/ | __/ | + |___/ |__*/ + private fun loginSynergia() { + + } + private fun firstLoginSynergia() { + FirstLoginSynergia(app, loginStore, syncCallback) { profileList -> + syncCallback.onLoginFirst(profileList, loginStore) + } + } + /* _ _____ _______ + | |/ ____|__ __| + | | (___ | | + _ | |\___ \ | | + | |__| |____) | | | + \____/|_____/ |*/ + private fun loginJst() { + + } + + private fun wrapCallback(callback: SyncCallback): SyncCallback { + return object : SyncCallback { + override fun onSuccess(activityContext: Context?, profileFull: ProfileFull?) { + callback.onSuccess(activityContext, profileFull) + } + + override fun onProgress(progressStep: Int) { + callback.onProgress(progressStep) + } + + override fun onActionStarted(stringResId: Int) { + callback.onActionStarted(stringResId) + } + + override fun onLoginFirst(profileList: MutableList?, loginStore: LoginStore?) { + callback.onLoginFirst(profileList, loginStore) + } + + override fun onError(activityContext: Context?, error: AppError) { + when (error.errorCode) { + in internalErrorList -> { + // finish immediately if the same error occurs twice during the same sync + callback.onError(activityContext, error) + } + /* CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> { + internalErrorList.add(error.errorCode) + loginStore.removeLoginData("refreshToken") // force a clean login + //loginLibrus() + }*/ + else -> callback.onError(activityContext, error) + } + } + } + } + + fun login(callback: SyncCallback) { + this.internalErrorList.clear() + this.syncCallback = wrapCallback(callback) + when (loginStore.mode) { + LOGIN_MODE_LIBRUS_EMAIL -> { + //loginLibrus() + } + LOGIN_MODE_LIBRUS_SYNERGIA -> { + + } + LOGIN_MODE_LIBRUS_JST -> { + + } + } + } + + fun getData() { + + } + + override fun sync(activityContext: Context, callback: SyncCallback, profileId: Int, profile: Profile?, loginStore: LoginStore) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun syncMessages(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun syncFeature(activityContext: Context, callback: SyncCallback, profile: ProfileFull, vararg featureList: Int) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun getMessage(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, message: MessageFull, messageCallback: MessageGetCallback) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun getAttachment(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, message: MessageFull, attachmentId: Long, attachmentCallback: AttachmentGetCallback) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun getRecipientList(activityContext: Context, errorCallback: SyncCallback, profile: ProfileFull, recipientListGetCallback: RecipientListGetCallback) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun getComposeInfo(profile: ProfileFull): MessagesComposeInfo { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun getConfigurableEndpoints(profile: Profile?): MutableMap { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun isEndpointEnabled(profile: Profile?, defaultActive: Boolean, name: String?): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt new file mode 100644 index 00000000..592423b3 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus + +import android.content.Context +import com.google.gson.JsonObject +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusPortal +import pl.szczodrzynski.edziennik.datamodels.LoginStore +import pl.szczodrzynski.edziennik.datamodels.Profile +import pl.szczodrzynski.edziennik.utils.Utils.d + +class LibrusTest(val app: App) { + companion object { + private const val TAG = "LibrusTest" + } + + val profile = Profile(1, "Profil", "xd", 1).apply { + //putStudentData("accountLogin", "1234567") + //putStudentData("accountPassword", "zaq1@WSX") + + putStudentData("accountCode", LIBRUS_JST_DEMO_CODE) + putStudentData("accountPin", LIBRUS_JST_DEMO_PIN) + } + val loginStore = LoginStore(1, LOGIN_TYPE_LIBRUS, JsonObject().apply { + addProperty("email", "test@example.com") + addProperty("password", "zaq1@WSX") + }).also { + it.mode = LOGIN_MODE_LIBRUS_JST + } + + fun go() { + val data = DataLibrus(app, profile, loginStore).apply { + callback = object : ProgressCallback { + override fun onProgress(progressStep: Int) { + + } + + override fun onActionStarted(stringResId: Int) { + d(TAG, app.getString(stringResId)) + } + + override fun onError(activityContext: Context?, error: AppError) { + error.changeIfCodeOther() + d(TAG, "Error "+error.getDetails(app)) + } + } + } + + LoginLibrus(data, LOGIN_METHOD_LIBRUS_API) { + d(TAG, "Login succeeded.") + d(TAG, "Profile data: ${data.profile?.studentData?.toString()}") + d(TAG, "LoginStore data: ${data.loginStore.data}") + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt index 66fc9c39..61ede774 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt @@ -1,10 +1,122 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + package pl.szczodrzynski.edziennik.api.v2.librus.data import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback +import pl.szczodrzynski.edziennik.api.v2.models.Data import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile -class DataLibrus(val app: App, val profile: Profile, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) { +class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) { -} \ No newline at end of file + /* _____ _ _ + | __ \ | | | | + | |__) |__ _ __| |_ __ _| | + | ___/ _ \| '__| __/ _` | | + | | | (_) | | | || (_| | | + |_| \___/|_| \__\__,_|*/ + private var mPortalEmail: String? = null + var portalEmail: String? + get() { mPortalEmail = mPortalEmail ?: loginStore.getLoginData("email", null); return mPortalEmail } + set(value) { loginStore.putLoginData("email", value); mPortalEmail = value } + private var mPortalPassword: String? = null + var portalPassword: String? + get() { mPortalPassword = mPortalPassword ?: loginStore.getLoginData("password", null); return mPortalPassword } + set(value) { loginStore.putLoginData("password", value); mPortalPassword = value } + + private var mPortalAccessToken: String? = null + var portalAccessToken: String? + get() { mPortalAccessToken = mPortalAccessToken ?: loginStore.getLoginData("accessToken", null); return mPortalAccessToken } + set(value) { loginStore.putLoginData("accessToken", value); mPortalAccessToken = value } + private var mPortalRefreshToken: String? = null + var portalRefreshToken: String? + get() { mPortalRefreshToken = mPortalRefreshToken ?: loginStore.getLoginData("refreshToken", null); return mPortalRefreshToken } + set(value) { loginStore.putLoginData("refreshToken", value); mPortalRefreshToken = value } + private var mPortalTokenExpiryTime: Long? = null + var portalTokenExpiryTime: Long + get() { mPortalTokenExpiryTime = mPortalTokenExpiryTime ?: loginStore.getLoginData("tokenExpiryTime", 0L); return mPortalTokenExpiryTime ?: 0L } + set(value) { loginStore.putLoginData("tokenExpiryTime", value); mPortalTokenExpiryTime = value } + + /* _____ _____ + /\ | __ \_ _| + / \ | |__) || | + / /\ \ | ___/ | | + / ____ \| | _| |_ + /_/ \_\_| |____*/ + /** + * A Synergia login, like 1234567u. + * Used: for login (API Login Method) in Synergia mode. + * And also in various places in [pl.szczodrzynski.edziennik.api.v2.models.Endpoint]s + */ + private var mApiLogin: String? = null + var apiLogin: String? + get() { mApiLogin = mApiLogin ?: profile?.getStudentData("accountLogin", null); return mApiLogin } + set(value) { profile?.putStudentData("accountLogin", value) ?: return; mApiLogin = value } + /** + * A Synergia password. + * Used: for login (API Login Method) in Synergia mode. + */ + private var mApiPassword: String? = null + var apiPassword: String? + get() { mApiPassword = mApiPassword ?: profile?.getStudentData("accountPassword", null); return mApiPassword } + set(value) { profile?.putStudentData("accountPassword", value) ?: return; mApiPassword = value } + + /** + * A JST login Code. + * Used only during first login in JST mode. + */ + private var mApiCode: String? = null + var apiCode: String? + get() { mApiCode = mApiCode ?: profile?.getStudentData("accountCode", null); return mApiCode } + set(value) { profile?.putStudentData("accountCode", value) ?: return; mApiCode = value } + /** + * A JST login PIN. + * Used only during first login in JST mode. + */ + private var mApiPin: String? = null + var apiPin: String? + get() { mApiPin = mApiPin ?: profile?.getStudentData("accountPin", null); return mApiPin } + set(value) { profile?.putStudentData("accountPin", value) ?: return; mApiPin = value } + + /** + * A Synergia API access token. + * Used in all Api Endpoints. + * Created in Login Method Api. + * Applicable for all login modes. + */ + private var mApiAccessToken: String? = null + var apiAccessToken: String? + get() { mApiAccessToken = mApiAccessToken ?: profile?.getStudentData("accountToken", null); return mApiAccessToken } + set(value) { profile?.putStudentData("accountToken", value) ?: return; mApiAccessToken = value } + /** + * A Synergia API refresh token. + * Used when refreshing the [apiAccessToken] in JST, Synergia modes. + */ + private var mApiRefreshToken: String? = null + var apiRefreshToken: String? + get() { mApiRefreshToken = mApiRefreshToken ?: profile?.getStudentData("accountRefreshToken", null); return mApiRefreshToken } + set(value) { profile?.putStudentData("accountRefreshToken", value) ?: return; mApiRefreshToken = value } + /** + * The expiry time for [apiAccessToken], as a UNIX timestamp. + * Used when refreshing the [apiAccessToken] in JST, Synergia modes. + * Used when refreshing the [apiAccessToken] in Portal mode ([pl.szczodrzynski.edziennik.api.v2.librus.login.SynergiaTokenExtractor]) + */ + private var mApiTokenExpiryTime: Long? = null + var apiTokenExpiryTime: Long + get() { mApiTokenExpiryTime = mApiTokenExpiryTime ?: profile?.getStudentData("accountTokenTime", 0L); return mApiTokenExpiryTime ?: 0L } + set(value) { profile?.putStudentData("accountTokenTime", value) ?: return; mApiTokenExpiryTime = value } + + /* ____ _ _ + / __ \| | | | + | | | | |_| |__ ___ _ __ + | | | | __| '_ \ / _ \ '__| + | |__| | |_| | | | __/ | + \____/ \__|_| |_|\___|*/ + var isPremium + get() = profile?.getStudentData("isPremium", false) ?: false + set(value) { profile?.putStudentData("isPremium", value) } + +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt new file mode 100644 index 00000000..3720b212 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt @@ -0,0 +1,115 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data + +import com.google.gson.JsonNull +import com.google.gson.JsonObject +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.callback.JsonCallbackHandler +import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.AppError.CODE_MAINTENANCE +import pl.szczodrzynski.edziennik.api.AppError.CODE_OTHER +import pl.szczodrzynski.edziennik.api.v2.Api +import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED +import pl.szczodrzynski.edziennik.api.v2.LIBRUS_API_URL +import pl.szczodrzynski.edziennik.api.v2.LIBRUS_USER_AGENT +import pl.szczodrzynski.edziennik.utils.Utils.d +import java.net.HttpURLConnection.* + +open class LibrusApi(override val data: DataLibrus) : Api(data) { + companion object { + const val TAG = "LibrusApi" + } + fun apiRequest(endpoint: String, callback: (json: JsonObject?) -> Unit) { + d(TAG, "Requesting $LIBRUS_API_URL$endpoint") + Request.builder() + .url(if (data.fakeLogin) "http://szkolny.eu/librus/api/$endpoint" else LIBRUS_API_URL + endpoint) + .userAgent(LIBRUS_USER_AGENT) + .addHeader("Authorization", "Bearer ${data.apiAccessToken}") + .get() + .allowErrorCode(HTTP_FORBIDDEN) + .allowErrorCode(HTTP_UNAUTHORIZED) + .allowErrorCode(HTTP_BAD_REQUEST) + .callback(object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response) { + if (json == null) { + if (response.parserErrorBody != null && response.parserErrorBody == "Nieprawidłowy węzeł.") { + callback(null) + return + } + finishWithError(AppError(TAG, 453, CODE_MAINTENANCE, response)) + return + } + if (json.get("Status") != null) { + val message = json.get("Message") + val code = json.get("Code") + d(TAG, "apiRequest Error " + json.get("Status").asString + " " + (if (message == null) "" else message.asString) + " " + (if (code == null) "" else code.asString) + "\n\n" + response.request().url().toString()) + if (message != null && message !is JsonNull && message.asString == "Student timetable is not public") { + try { + callback(null) + } catch (e: NullPointerException) { + e.printStackTrace() + d(TAG, "apiRequest exception " + e.message) + finishWithError(AppError(TAG, 503, CODE_OTHER, response, e, json)) + } + + return + } + if (code != null + && code !is JsonNull + && (code.asString == "LuckyNumberIsNotActive" + || code.asString == "NotesIsNotActive" + || code.asString == "AccessDeny")) { + try { + callback(null) + } catch (e: NullPointerException) { + e.printStackTrace() + d(TAG, "apiRequest exception " + e.message) + finishWithError(AppError(TAG, 504, CODE_OTHER, response, e, json)) + } + + return + } + val errorText = json.get("Status").asString + " " + (if (message == null) "" else message.asString) + " " + if (code == null) "" else code.asString + if (code != null && code !is JsonNull && code.asString == "TokenIsExpired") { + finishWithError(AppError(TAG, 74, CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED, errorText, response, json)) + return + } + finishWithError(AppError(TAG, 497, CODE_OTHER, errorText, response, json)) + return + } + try { + callback(json) + } catch (e: NullPointerException) { + e.printStackTrace() + d(TAG, "apiRequest exception " + e.message) + finishWithError(AppError(TAG, 505, CODE_OTHER, response, e, json)) + } + + } + + override fun onFailure(response: Response, throwable: Throwable) { + if (response.code() == 405) { + // method not allowed + finishWithError(AppError(TAG, 511, CODE_OTHER, response, throwable)) + return + } + if (response.code() == 500) { + // TODO: 2019-09-10 dirty hotfix + if ("Classrooms" == endpoint) { + callback(null) + return + } + finishWithError(AppError(TAG, 516, CODE_MAINTENANCE, response, throwable)) + return + } + finishWithError(AppError(TAG, 520, CODE_OTHER, response, throwable)) + } + }) + .build() + .enqueue() + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt index eae53a75..b60fefef 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiGrades.kt @@ -2,19 +2,16 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback -import pl.szczodrzynski.edziennik.api.v2.models.DataStore +import pl.szczodrzynski.edziennik.api.v2.models.Data import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile class LibrusApiGrades(val app: App, val profile: Profile, val loginStore: LoginStore, - val dataStore: DataStore, + val data: Data, val callback: ProgressCallback, - val onSuccess: () -> Unit) : EndpointInterface { - override fun sync() { - - } + val onSuccess: () -> Unit) { init { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiMe.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiMe.kt new file mode 100644 index 00000000..a852de2b --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiMe.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback +import pl.szczodrzynski.edziennik.api.v2.models.Data +import pl.szczodrzynski.edziennik.datamodels.LoginStore +import pl.szczodrzynski.edziennik.datamodels.Profile + +class LibrusApiMe(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + init { + apiRequest("Me") { json -> + val me = json?.getJsonObject("Me") + val account = me?.getJsonObject("Account") + val user = me?.getJsonObject("User") + + data.isPremium = account?.getBoolean("isPremium") == true || account?.getBoolean("isPremiumDemo") == true + + val isParent = account?.getInt("GroupId") == 5 + data.profile?.accountNameLong = + if (isParent) + buildFullName(account?.getString("FirstName"), account?.getString("LastName")) + else null + + data.profile?.studentNameLong = + buildFullName(user?.getString("FirstName"), user?.getString("LastName")) + + onSuccess() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt index ecf10682..693715a2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergiaGrades.kt @@ -2,16 +2,16 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback -import pl.szczodrzynski.edziennik.api.v2.models.DataStore +import pl.szczodrzynski.edziennik.api.v2.models.Data import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile class LibrusSynergiaGrades(val app: App, - val profile: Profile, - val loginStore: LoginStore, - val dataStore: DataStore, - val callback: ProgressCallback, - val onSuccess: () -> Unit) { + val profile: Profile, + val loginStore: LoginStore, + val data: Data, + val callback: ProgressCallback, + val onSuccess: () -> Unit) { init { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginJst.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginJst.kt deleted file mode 100644 index f21290eb..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginJst.kt +++ /dev/null @@ -1,20 +0,0 @@ -package pl.szczodrzynski.edziennik.api.v2.librus.login - -import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback -import pl.szczodrzynski.edziennik.api.v2.interfaces.ILoginMethod -import pl.szczodrzynski.edziennik.datamodels.LoginStore -import pl.szczodrzynski.edziennik.datamodels.Profile - -class LoginJst( - app: App, - profile: Profile?, - loginStore: LoginStore, - callback: ProgressCallback, - onSuccess: () -> Unit -): ILoginMethod(app, profile, loginStore, callback, onSuccess) { - companion object { - private const val TAG = "librus.LoginJst" - } - -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt index 959fc74c..e16ada31 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt @@ -1,183 +1,79 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-21. + */ + package pl.szczodrzynski.edziennik.api.v2.librus.login -import android.util.Pair -import com.google.gson.JsonObject -import im.wangchao.mhttp.Request -import im.wangchao.mhttp.Response -import im.wangchao.mhttp.body.MediaTypeUtils -import im.wangchao.mhttp.callback.JsonCallbackHandler -import im.wangchao.mhttp.callback.TextCallbackHandler -import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.api.AppError -import pl.szczodrzynski.edziennik.api.AppError.* -import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback import pl.szczodrzynski.edziennik.api.v2.* -import pl.szczodrzynski.edziennik.datamodels.LoginStore -import pl.szczodrzynski.edziennik.getInt -import pl.szczodrzynski.edziennik.getString -import pl.szczodrzynski.edziennik.utils.Utils.c -import java.net.HttpURLConnection.HTTP_UNAUTHORIZED -import java.util.ArrayList -import java.util.regex.Pattern +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod +import pl.szczodrzynski.edziennik.utils.Utils.d +import kotlin.math.log -class LoginLibrus(val app: App, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) { +class LoginLibrus(val data: DataLibrus, vararg loginMethodIds: Int, val onSuccess: () -> Unit) { companion object { - private const val TAG = "librus.LoginLibrus" + private const val TAG = "LoginLibrus" } + private var loginMethodList = mutableListOf() + init { - // ustawiamy tokeny, generujemy itp - // nic nie robimy z dostępem do api.librus.pl - // to będzie później - val accessToken = loginStore.getLoginData("accessToken", null) - val refreshToken = loginStore.getLoginData("refreshToken", null) - val tokenExpiryTime = loginStore.getLoginData("tokenExpiryTime", 0L) + for (loginMethodId in loginMethodIds) { + var requiredLoginMethod = loginMethodId + while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) { + librusLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod -> + loginMethodList.add(requiredLoginMethod) + requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore) + } + } + } + loginMethodList = loginMethodList.toHashSet().toMutableList() + loginMethodList.sort() - // succeed having a non-expired access token and a refresh token - if (tokenExpiryTime-30 > System.currentTimeMillis() / 1000 && refreshToken != null && accessToken != null) { + nextLoginMethod() + } + + private fun nextLoginMethod() { + if (loginMethodList.isEmpty()) { onSuccess() + return } - else if (refreshToken != null) { - app.cookieJar.clearForDomain("portal.librus.pl") - accessToken(null, refreshToken) - } - else { - app.cookieJar.clearForDomain("portal.librus.pl") - authorize(LIBRUS_AUTHORIZE_URL) + useLoginMethod(loginMethodList.removeAt(0)) { + nextLoginMethod() } } - private fun authorize(url: String?) { - callback.onActionStarted(R.string.sync_action_authorizing) - Request.builder() - .url(url) - .userAgent(LIBRUS_USER_AGENT) - .withClient(app.httpLazy) - .callback(object : TextCallbackHandler() { - override fun onSuccess(data: String, response: Response) { - //d("headers "+response.headers().toString()); - val location = response.headers().get("Location") - if (location != null) { - val authMatcher = Pattern.compile("http://localhost/bar\\?code=([A-z0-9]+?)$", Pattern.DOTALL or Pattern.MULTILINE).matcher(location) - if (authMatcher.find()) { - accessToken(authMatcher.group(1), null) - } else { - //callback.onError(activityContext, Edziennik.CODE_OTHER, "Auth code not found: "+location); - authorize(location) - } - } else { - val csrfMatcher = Pattern.compile("name=\"csrf-token\" content=\"([A-z0-9=+/\\-_]+?)\"", Pattern.DOTALL).matcher(data) - if (csrfMatcher.find()) { - login(csrfMatcher.group(1)) - } else { - callback.onError(null, AppError(TAG, 463, CODE_OTHER, "CSRF token not found.", response, data)) - } - } - } - - override fun onFailure(response: Response, throwable: Throwable) { - callback.onError(null, AppError(TAG, 207, CODE_OTHER, response, throwable)) - } - }) - .build() - .enqueue() - } - - private fun login(csrfToken: String) { - callback.onActionStarted(R.string.sync_action_logging_in) - val email = loginStore.getLoginData("email", "") - val password = loginStore.getLoginData("password", "") - Request.builder() - .url(LIBRUS_LOGIN_URL) - .userAgent(LIBRUS_USER_AGENT) - .addParameter("email", email) - .addParameter("password", password) - .addHeader("X-CSRF-TOKEN", csrfToken) - .contentType(MediaTypeUtils.APPLICATION_JSON) - .post() - .callback(object : JsonCallbackHandler() { - override fun onSuccess(data: JsonObject?, response: Response) { - if (data == null) { - if (response.parserErrorBody != null && response.parserErrorBody.contains("wciąż nieaktywne")) { - callback.onError(null, AppError(TAG, 487, CODE_LIBRUS_NOT_ACTIVATED, response)) - } - callback.onError(null, AppError(TAG, 489, CODE_MAINTENANCE, response)) - return - } - if (data.get("errors") != null) { - callback.onError(null, AppError(TAG, 490, CODE_OTHER, data.get("errors").asJsonArray.get(0).asString, response, data)) - return - } - authorize(data.getString("redirect") ?: LIBRUS_AUTHORIZE_URL) - } - - override fun onFailure(response: Response, throwable: Throwable) { - if (response.code() == 403 || response.code() == 401) { - callback.onError(null, AppError(TAG, 248, CODE_INVALID_LOGIN, response, throwable)) - return - } - callback.onError(null, AppError(TAG, 251, CODE_OTHER, response, throwable)) - } - }) - .build() - .enqueue() - } - - private var refreshTokenFailed = false - private fun accessToken(code: String?, refreshToken: String?) { - callback.onActionStarted(R.string.sync_action_getting_token) - val params = ArrayList>() - params.add(Pair("client_id", LIBRUS_CLIENT_ID)) - if (code != null) { - params.add(Pair("grant_type", "authorization_code")) - params.add(Pair("code", code)) - params.add(Pair("redirect_uri", LIBRUS_REDIRECT_URL)) - } else if (refreshToken != null) { - params.add(Pair("grant_type", "refresh_token")) - params.add(Pair("refresh_token", refreshToken)) + private fun useLoginMethod(loginMethodId: Int, onSuccess: () -> Unit) { + if (data.loginMethods.contains(loginMethodId)) { + onSuccess() + return + } + d(TAG, "Using login method $loginMethodId") + when (loginMethodId) { + LOGIN_METHOD_LIBRUS_PORTAL -> { + LoginLibrusPortal(data) { + data.loginMethods.add(loginMethodId) + onSuccess() + } + } + LOGIN_METHOD_LIBRUS_API -> { + LoginLibrusApi(data) { + data.loginMethods.add(loginMethodId) + onSuccess() + } + } + LOGIN_METHOD_LIBRUS_SYNERGIA -> { + LoginLibrusApi(data) { + data.loginMethods.add(loginMethodId) + onSuccess() + } + } + LOGIN_METHOD_LIBRUS_MESSAGES -> { + LoginLibrusApi(data) { + data.loginMethods.add(loginMethodId) + onSuccess() + } + } } - Request.builder() - .url(LIBRUS_TOKEN_URL) - .userAgent(LIBRUS_USER_AGENT) - .addParams(params) - .allowErrorCode(HTTP_UNAUTHORIZED) - .post() - .callback(object : JsonCallbackHandler() { - override fun onSuccess(data: JsonObject?, response: Response) { - if (data == null) { - callback.onError(null, AppError(TAG, 539, CODE_MAINTENANCE, response)) - return - } - if (data.get("error") != null) { - val hint = data.getString("hint") - if (!refreshTokenFailed && refreshToken != null && (hint == "Token has been revoked" || hint == "Token has expired")) { - c(TAG, "refreshing the token failed. Trying to log in again.") - refreshTokenFailed = true - authorize(LIBRUS_AUTHORIZE_URL) - return - } - val errorText = data.getString("error") + " " + (data.getString("message") ?: "") + " " + (hint ?: "") - callback.onError(null, AppError(TAG, 552, CODE_OTHER, errorText, response, data)) - return - } - try { - loginStore.putLoginData("tokenType", data.getString("token_type")) - loginStore.putLoginData("accessToken", data.getString("access_token")) - loginStore.putLoginData("refreshToken", data.getString("refresh_token")) - loginStore.putLoginData("tokenExpiryTime", System.currentTimeMillis() / 1000 + (data.getInt("expires_in") ?: 86400)) - onSuccess() - } catch (e: NullPointerException) { - callback.onError(null, AppError(TAG, 311, CODE_OTHER, response, e, data)) - } - - } - - override fun onFailure(response: Response, throwable: Throwable) { - callback.onError(null, AppError(TAG, 317, CODE_OTHER, response, throwable)) - } - }) - .build() - .enqueue() } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt new file mode 100644 index 00000000..ef6da7d5 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt @@ -0,0 +1,246 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-20. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.login + +import com.google.gson.JsonObject +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.body.MediaTypeUtils +import im.wangchao.mhttp.callback.JsonCallbackHandler +import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.AppError.* +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.currentTimeUnix +import pl.szczodrzynski.edziennik.getInt +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.isNotNullNorEmpty +import java.net.HttpURLConnection +import java.net.HttpURLConnection.* + +class LoginLibrusApi { + companion object { + private const val TAG = "LoginLibrusApi" + } + + private lateinit var data: DataLibrus + private lateinit var onSuccess: () -> Unit + + constructor(data: DataLibrus, onSuccess: () -> Unit) { + this.data = data + this.onSuccess = onSuccess + + if (data.profile == null) { + data.callback.onError(null, AppError(TAG, 19, CODE_LIBRUS_PROFILE_NULL)) + return + } + + if (data.apiTokenExpiryTime-30 > currentTimeUnix() && data.apiAccessToken.isNotNullNorEmpty()) { + onSuccess() + } + else { + when (data.loginStore.mode) { + LOGIN_MODE_LIBRUS_EMAIL -> loginWithPortal() + LOGIN_MODE_LIBRUS_SYNERGIA -> loginWithSynergia() + LOGIN_MODE_LIBRUS_JST -> loginWithJst() + else -> { + data.callback.onError(null, AppError(TAG, 25, CODE_INVALID_LOGIN_MODE)) + } + } + } + } + + private fun loginWithPortal() { + if (!data.loginMethods.contains(LOGIN_METHOD_LIBRUS_PORTAL)) { + data.callback.onError(null, AppError(TAG, 26, CODE_LOGIN_METHOD_NOT_SATISFIED)) + return + } + SynergiaTokenExtractor(data) { + onSuccess() + } + } + + private fun copyFromLoginStore() { + data.loginStore.data?.apply { + if (has("accountLogin")) { + data.apiLogin = getString("accountLogin") + remove("accountLogin") + } + if (has("accountPassword")) { + data.apiPassword = getString("accountPassword") + remove("accountPassword") + } + if (has("accountCode")) { + data.apiCode = getString("accountCode") + remove("accountCode") + } + if (has("accountPin")) { + data.apiPin = getString("accountPin") + remove("accountPin") + } + } + } + + private fun loginWithSynergia() { + copyFromLoginStore() + if (data.apiRefreshToken != null) { + // refresh a Synergia token + synergiaRefreshToken() + } + else if (data.apiLogin != null && data.apiPassword != null) { + synergiaGetToken() + } + else { + // cannot log in: token expired, no login data present + data.callback.onError(null, AppError(TAG, 91, CODE_INVALID_LOGIN)) + } + } + + private fun loginWithJst() { + copyFromLoginStore() + + if (data.apiRefreshToken != null) { + // refresh a JST token + jstRefreshToken() + } + else if (data.apiCode != null && data.apiPin != null) { + // get a JST token from Code and PIN + jstGetToken() + } + else { + // cannot log in: token expired, no login data present + data.callback.onError(null, AppError(TAG, 110, CODE_INVALID_LOGIN)) + } + } + + private val tokenCallback = object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response?) { + if (json == null) { + data.callback.onError(null, AppError(TAG, 117, CODE_MAINTENANCE, response)) + return + } + json.getString("error")?.let { error -> + when (error) { + "librus_captcha_needed" -> { + + } + "connection_problems" -> { + + } + "invalid_client" -> { + + } + "librus_reg_accept_needed" -> { + + } + "librus_change_password_error" -> { + + } + "librus_password_change_required" -> { + + } + "invalid_grant" -> { + + } + else -> { + + } + } + return + } + + try { + data.apiAccessToken = json.getString("access_token") + data.apiRefreshToken = json.getString("refresh_token") + data.apiTokenExpiryTime = currentTimeUnix() + json.getInt("expires_in", 86400) + onSuccess() + } catch (e: NullPointerException) { + data.callback.onError(null, AppError(TAG, 154, EXCEPTION_LOGIN_LIBRUS_API_TOKEN, response, e, json)) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.callback.onError(null, AppError(TAG, 159, CODE_OTHER, response, throwable)) + } + } + + private fun synergiaGetToken() { + Request.builder() + .url(LIBRUS_API_TOKEN_URL) + .userAgent(LIBRUS_USER_AGENT) + .addParameter("grant_type", "password") + .addParameter("username", data.apiLogin) + .addParameter("password", data.apiPassword) + .addParameter("librus_long_term_token", "1") + .addParameter("librus_rules_accepted", "1") + .addHeader("Authorization", "Basic $LIBRUS_API_AUTHORIZATION") + .contentType(MediaTypeUtils.APPLICATION_FORM) + .post() + .allowErrorCode(HTTP_BAD_REQUEST) + .allowErrorCode(HTTP_FORBIDDEN) + .allowErrorCode(HTTP_UNAUTHORIZED) + .callback(tokenCallback) + .build() + .enqueue() + } + private fun synergiaRefreshToken() { + Request.builder() + .url(LIBRUS_API_TOKEN_URL) + .userAgent(LIBRUS_USER_AGENT) + .addParameter("grant_type", "refresh_token") + .addParameter("refresh_token", data.apiRefreshToken) + .addParameter("librus_long_term_token", "1") + .addParameter("librus_rules_accepted", "1") + .addHeader("Authorization", "Basic $LIBRUS_API_AUTHORIZATION") + .contentType(MediaTypeUtils.APPLICATION_FORM) + .post() + .allowErrorCode(HTTP_BAD_REQUEST) + .allowErrorCode(HTTP_FORBIDDEN) + .allowErrorCode(HTTP_UNAUTHORIZED) + .callback(tokenCallback) + .build() + .enqueue() + } + private fun jstGetToken() { + Request.builder() + .url(LIBRUS_API_TOKEN_JST_URL) + .userAgent(LIBRUS_USER_AGENT) + .addParameter("grant_type", "implicit_grant") + .addParameter("client_id", LIBRUS_API_CLIENT_ID_JST) + .addParameter("secret", LIBRUS_API_SECRET_JST) + .addParameter("code", data.apiCode) + .addParameter("pin", data.apiPin) + .addParameter("librus_rules_accepted", "1") + .addParameter("librus_mobile_rules_accepted", "1") + .addParameter("librus_long_term_token", "1") + .contentType(MediaTypeUtils.APPLICATION_FORM) + .post() + .allowErrorCode(HTTP_BAD_REQUEST) + .allowErrorCode(HTTP_FORBIDDEN) + .allowErrorCode(HTTP_UNAUTHORIZED) + .callback(tokenCallback) + .build() + .enqueue() + } + private fun jstRefreshToken() { + Request.builder() + .url(LIBRUS_API_TOKEN_JST_URL) + .userAgent(LIBRUS_USER_AGENT) + .addParameter("grant_type", "refresh_token") + .addParameter("client_id", LIBRUS_API_CLIENT_ID_JST) + .addParameter("refresh_token", data.apiRefreshToken) + .addParameter("librus_long_term_token", "1") + .addParameter("mobile_app_accept_rules", "1") + .addParameter("synergy_accept_rules", "1") + .contentType(MediaTypeUtils.APPLICATION_FORM) + .post() + .allowErrorCode(HTTP_BAD_REQUEST) + .allowErrorCode(HTTP_FORBIDDEN) + .allowErrorCode(HTTP_UNAUTHORIZED) + .callback(tokenCallback) + .build() + .enqueue() + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt new file mode 100644 index 00000000..26e3db39 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt @@ -0,0 +1,8 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-20. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.login + +class LoginLibrusMessages { +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt new file mode 100644 index 00000000..e0d5c309 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt @@ -0,0 +1,179 @@ +package pl.szczodrzynski.edziennik.api.v2.librus.login + +import android.util.Pair +import com.google.gson.JsonObject +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.body.MediaTypeUtils +import im.wangchao.mhttp.callback.JsonCallbackHandler +import im.wangchao.mhttp.callback.TextCallbackHandler +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.AppError.* +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.utils.Utils.c +import java.net.HttpURLConnection.HTTP_UNAUTHORIZED +import java.util.ArrayList +import java.util.regex.Pattern + +class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "LoginLibrusPortal" + } + + init { run { + if (data.loginStore.mode != LOGIN_MODE_LIBRUS_EMAIL) { + data.callback.onError(null, AppError(TAG, 27, CODE_INVALID_LOGIN_MODE)) + return@run + } + if (data.portalEmail == null || data.portalPassword == null) { + data.callback.onError(null, AppError(TAG, 31, CODE_INVALID_LOGIN)) + return@run + } + + // succeed having a non-expired access token and a refresh token + if (data.portalTokenExpiryTime-30 > currentTimeUnix() && data.portalRefreshToken.isNotNullNorEmpty() && data.portalAccessToken.isNotNullNorEmpty()) { + onSuccess() + } + else if (data.portalRefreshToken != null) { + data.app.cookieJar.clearForDomain("portal.librus.pl") + accessToken(null, data.portalRefreshToken) + } + else { + data.app.cookieJar.clearForDomain("portal.librus.pl") + authorize(LIBRUS_AUTHORIZE_URL) + } + }} + + private fun authorize(url: String?) { + data.callback.onActionStarted(R.string.sync_action_authorizing) + Request.builder() + .url(url) + .userAgent(LIBRUS_USER_AGENT) + .withClient(data.app.httpLazy) + .callback(object : TextCallbackHandler() { + override fun onSuccess(json: String, response: Response) { + val location = response.headers().get("Location") + if (location != null) { + val authMatcher = Pattern.compile("http://localhost/bar\\?code=([A-z0-9]+?)$", Pattern.DOTALL or Pattern.MULTILINE).matcher(location) + if (authMatcher.find()) { + accessToken(authMatcher.group(1), null) + } else { + authorize(location) + } + } else { + val csrfMatcher = Pattern.compile("name=\"csrf-token\" content=\"([A-z0-9=+/\\-_]+?)\"", Pattern.DOTALL).matcher(json) + if (csrfMatcher.find()) { + login(csrfMatcher.group(1)) + } else { + data.callback.onError(null, AppError(TAG, 463, CODE_OTHER, "CSRF token not found.", response, json)) + } + } + } + + override fun onFailure(response: Response, throwable: Throwable) { + data.callback.onError(null, AppError(TAG, 207, CODE_OTHER, response, throwable)) + } + }) + .build() + .enqueue() + } + + private fun login(csrfToken: String) { + data.callback.onActionStarted(R.string.sync_action_logging_in) + Request.builder() + .url(LIBRUS_LOGIN_URL) + .userAgent(LIBRUS_USER_AGENT) + .addParameter("email", data.portalEmail) + .addParameter("password", data.portalPassword) + .addHeader("X-CSRF-TOKEN", csrfToken) + .contentType(MediaTypeUtils.APPLICATION_JSON) + .post() + .callback(object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response) { + if (json == null) { + if (response.parserErrorBody?.contains("wciąż nieaktywne") == true) { + data.callback.onError(null, AppError(TAG, 487, CODE_LIBRUS_NOT_ACTIVATED, response)) + return + } + data.callback.onError(null, AppError(TAG, 489, CODE_MAINTENANCE, response)) + return + } + if (json.get("errors") != null) { + data.callback.onError(null, AppError(TAG, 490, CODE_OTHER, json.getJsonArray("errors")?.get(0)?.asString, response, json)) + return + } + authorize(json.getString("redirect", LIBRUS_AUTHORIZE_URL)) + } + + override fun onFailure(response: Response, throwable: Throwable) { + if (response.code() == 403 || response.code() == 401) { + data.callback.onError(null, AppError(TAG, 248, CODE_INVALID_LOGIN, response, throwable)) + return + } + data.callback.onError(null, AppError(TAG, 251, CODE_OTHER, response, throwable)) + } + }) + .build() + .enqueue() + } + + private var refreshTokenFailed = false + private fun accessToken(code: String?, refreshToken: String?) { + data.callback.onActionStarted(R.string.sync_action_getting_token) + val params = ArrayList>() + params.add(Pair("client_id", LIBRUS_CLIENT_ID)) + if (code != null) { + params.add(Pair("grant_type", "authorization_code")) + params.add(Pair("code", code)) + params.add(Pair("redirect_uri", LIBRUS_REDIRECT_URL)) + } else if (refreshToken != null) { + params.add(Pair("grant_type", "refresh_token")) + params.add(Pair("refresh_token", refreshToken)) + } + Request.builder() + .url(LIBRUS_TOKEN_URL) + .userAgent(LIBRUS_USER_AGENT) + .addParams(params) + .allowErrorCode(HTTP_UNAUTHORIZED) + .post() + .callback(object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response) { + if (json == null) { + data.callback.onError(null, AppError(TAG, 539, CODE_MAINTENANCE, response)) + return + } + json.getString("error")?.let { error -> + val hint = json.getString("hint", "") + val message = json.getString("message", "") + if (!refreshTokenFailed && refreshToken != null && (hint == "Token has been revoked" || hint == "Token has expired")) { + c(TAG, "refreshing the token failed. Trying to log in again.") + refreshTokenFailed = true + authorize(LIBRUS_AUTHORIZE_URL) + return + } + val errorText = "$error $message $hint" + data.callback.onError(null, AppError(TAG, 552, CODE_OTHER, errorText, response, json)) + return + } + + try { + data.portalAccessToken = json.getString("access_token") + data.portalRefreshToken = json.getString("refresh_token") + data.portalTokenExpiryTime = currentTimeUnix() + json.getInt("expires_in", 86400) + onSuccess() + } catch (e: NullPointerException) { + data.callback.onError(null, AppError(TAG, 311, CODE_OTHER, response, e, json)) + } + + } + + override fun onFailure(response: Response, throwable: Throwable) { + data.callback.onError(null, AppError(TAG, 317, CODE_OTHER, response, throwable)) + } + }) + .build() + .enqueue() + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt new file mode 100644 index 00000000..2bad6904 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt @@ -0,0 +1,8 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-20. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.login + +class LoginLibrusSynergia { +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginSynergia.kt deleted file mode 100644 index 026f3e50..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginSynergia.kt +++ /dev/null @@ -1,12 +0,0 @@ -package pl.szczodrzynski.edziennik.api.v2.librus.login - -import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback -import pl.szczodrzynski.edziennik.datamodels.LoginStore - -class LoginSynergia(val app: App, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) { - companion object { - private const val TAG = "librus.LoginSynergia" - } - -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt index c7f3f69e..80f26158 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt @@ -8,41 +8,43 @@ import im.wangchao.mhttp.callback.JsonCallbackHandler import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.AppError import pl.szczodrzynski.edziennik.api.AppError.* -import pl.szczodrzynski.edziennik.api.v2.LIBRUS_USER_AGENT -import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback -import pl.szczodrzynski.edziennik.api.v2.LIBRUS_ACCOUNT_URL -import pl.szczodrzynski.edziennik.datamodels.LoginStore -import pl.szczodrzynski.edziennik.datamodels.Profile +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus import pl.szczodrzynski.edziennik.utils.Utils.d import java.net.HttpURLConnection.* -class SynergiaTokenExtractor(val app: App, val profile: Profile, val loginStore: LoginStore, val callback: ProgressCallback, val onSuccess: () -> Unit) { +class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { companion object { private const val TAG = "librus.SynergiaToken" } - init { - val accountToken = profile.getStudentData("accountToken", null) - val accountTokenTime = profile.getStudentData("accountTokenTime", 0L) - if (accountToken.isNotNullNorEmpty() && currentTimeUnix() - accountTokenTime < 3 * 60 * 60) { + init { run { + if (data.loginStore.mode != LOGIN_MODE_LIBRUS_EMAIL) { + data.callback.onError(null, AppError(TAG, 23, CODE_INVALID_LOGIN_MODE)) + return@run + } + if (data.profile == null) { + data.callback.onError(null, AppError(TAG, 28, CODE_LIBRUS_PROFILE_NULL)) + return@run + } + + if (data.apiTokenExpiryTime-30 > currentTimeUnix() && data.apiAccessToken.isNotNullNorEmpty()) { onSuccess() } else { - if (!synergiaAccount()) - callback.onError(null, AppError(TAG, 33, CODE_INTERNAL_MISSING_DATA)) + synergiaAccount() } - } + }} private fun synergiaAccount(): Boolean { - val accountLogin = profile.getStudentData("accountLogin", null) ?: return false - val tokenType = loginStore.getLoginData("tokenType", null) ?: return false - val accessToken = loginStore.getLoginData("accessToken", null) ?: return false - callback.onActionStarted(R.string.sync_action_getting_account) + val accountLogin = data.apiLogin ?: return false + val accessToken = data.portalAccessToken ?: return false + data.callback.onActionStarted(R.string.sync_action_getting_account) d(TAG, "Requesting " + (LIBRUS_ACCOUNT_URL + accountLogin)) Request.builder() .url(LIBRUS_ACCOUNT_URL + accountLogin) .userAgent(LIBRUS_USER_AGENT) - .addHeader("Authorization", "$tokenType $accessToken") + .addHeader("Authorization", "Bearer $accessToken") .get() .allowErrorCode(HTTP_NOT_FOUND) .allowErrorCode(HTTP_FORBIDDEN) @@ -50,57 +52,56 @@ class SynergiaTokenExtractor(val app: App, val profile: Profile, val loginStore: .allowErrorCode(HTTP_BAD_REQUEST) .allowErrorCode(HTTP_GONE) .callback(object : JsonCallbackHandler() { - override fun onSuccess(data: JsonObject?, response: Response) { - if (data == null) { - callback.onError(null, AppError(TAG, 641, CODE_MAINTENANCE, response)) + override fun onSuccess(json: JsonObject?, response: Response) { + if (json == null) { + data.callback.onError(null, AppError(TAG, 641, CODE_MAINTENANCE, response)) return } if (response.code() == 410) { - val reason = data.get("reason") + val reason = json.get("reason") if (reason != null && reason !is JsonNull && reason.asString == "requires_an_action") { - callback.onError(null, AppError(TAG, 1078, CODE_LIBRUS_DISCONNECTED, response, data)) + data.callback.onError(null, AppError(TAG, 1078, CODE_LIBRUS_DISCONNECTED, response, json)) return } - callback.onError(null, AppError(TAG, 70, CODE_INTERNAL_LIBRUS_ACCOUNT_410)) + data.callback.onError(null, AppError(TAG, 70, CODE_INTERNAL_LIBRUS_ACCOUNT_410)) return } - if (data.get("message") != null) { - val message = data.get("message").asString + if (json.get("message") != null) { + val message = json.get("message").asString if (message == "Account not found") { - callback.onError(null, AppError(TAG, 651, CODE_OTHER, app.getString(R.string.sync_error_register_student_not_associated_format, profile.studentNameLong, accountLogin), response, data)) + data.callback.onError(null, AppError(TAG, 651, CODE_OTHER, data.app.getString(R.string.sync_error_register_student_not_associated_format, data.profile?.studentNameLong ?: "", accountLogin), response, json)) return } - callback.onError(null, AppError(TAG, 654, CODE_OTHER, message + "\n\n" + accountLogin, response, data)) + data.callback.onError(null, AppError(TAG, 654, CODE_OTHER, message + "\n\n" + accountLogin, response, json)) return } if (response.code() == HTTP_OK) { try { // synergiaAccount is executed when a synergia token needs a refresh - val accountId = data.getInt("id") - val accountToken = data.getString("accessToken") + val accountId = json.getInt("id") + val accountToken = json.getString("accessToken") if (accountId == null || accountToken == null) { - callback.onError(null, AppError(TAG, 1284, CODE_OTHER, data)) + data.callback.onError(null, AppError(TAG, 1284, CODE_OTHER, json)) return } - profile.putStudentData("accountId", accountId) - profile.putStudentData("accountToken", accountToken) - profile.putStudentData("accountTokenTime", System.currentTimeMillis() / 1000) - profile.studentNameLong = data.getString("studentName") - val nameParts = data.getString("studentName")?.split(" ")?.toTypedArray() - profile.studentNameShort = nameParts?.get(0) + " " + nameParts?.get(1)?.get(0) + data.apiAccessToken = accountToken + data.apiTokenExpiryTime = currentTimeUnix() + 6*60*60 + data.profile?.studentNameLong = json.getString("studentName") + val nameParts = json.getString("studentName")?.split(" ")?.toTypedArray() + data.profile?.studentNameShort = nameParts?.get(0) + " " + nameParts?.get(1)?.get(0) onSuccess() } catch (e: NullPointerException) { e.printStackTrace() - callback.onError(null, AppError(TAG, 662, CODE_OTHER, response, e, data)) + data.callback.onError(null, AppError(TAG, 662, CODE_OTHER, response, e, json)) } } else { - callback.onError(null, AppError(TAG, 425, CODE_OTHER, response, data)) + data.callback.onError(null, AppError(TAG, 425, CODE_OTHER, response, json)) } } override fun onFailure(response: Response, throwable: Throwable) { - callback.onError(null, AppError(TAG, 432, CODE_OTHER, response, throwable)) + data.callback.onError(null, AppError(TAG, 432, CODE_OTHER, response, throwable)) } }) .build() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataStore.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt similarity index 61% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataStore.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 23d500a8..7578c64d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataStore.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -3,15 +3,21 @@ package pl.szczodrzynski.edziennik.api.v2.models import android.util.LongSparseArray import androidx.core.util.forEach import androidx.core.util.isNotEmpty +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback import pl.szczodrzynski.edziennik.datamodels.* import pl.szczodrzynski.edziennik.models.Date -data class DataStore(private val appDb: AppDb, val profileId: Int) { +open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) { + + var fakeLogin = false + + lateinit var callback: ProgressCallback val loginMethods = mutableListOf() - val teacherList: LongSparseArray = LongSparseArray() - val subjectList: LongSparseArray = LongSparseArray() + val teacherList = LongSparseArray() + val subjectList = LongSparseArray() val teamList = mutableListOf() val lessonList = mutableListOf() val lessonChangeList = mutableListOf() @@ -28,15 +34,19 @@ data class DataStore(private val appDb: AppDb, val profileId: Int) { val metadataList = mutableListOf() val messageMetadataList = mutableListOf() + private val db by lazy { app.db } + init { clear() - - appDb.teacherDao().getAllNow(profileId).forEach { teacher -> - teacherList.put(teacher.id, teacher) - } - appDb.subjectDao().getAllNow(profileId).forEach { subject -> - subjectList.put(subject.id, subject) + + if (profile != null) { + db.teacherDao().getAllNow(profile.id).forEach { teacher -> + teacherList.put(teacher.id, teacher) + } + db.subjectDao().getAllNow(profile.id).forEach { subject -> + subjectList.put(subject.id, subject) + } } /*val teacher = teachers.byNameFirstLast("Jan Kowalski") ?: Teacher(1, 1, "", "").let { @@ -67,57 +77,60 @@ data class DataStore(private val appDb: AppDb, val profileId: Int) { } fun saveData() { + if (profile == null) + return + if (teacherList.isNotEmpty()) { val tempList: ArrayList = ArrayList() teacherList.forEach { _, teacher -> tempList.add(teacher) } - appDb.teacherDao().addAll(tempList) + db.teacherDao().addAll(tempList) } if (subjectList.isNotEmpty()) { val tempList: ArrayList = ArrayList() subjectList.forEach { _, subject -> tempList.add(subject) } - appDb.subjectDao().addAll(tempList) + db.subjectDao().addAll(tempList) } if (teamList.isNotEmpty()) - appDb.teamDao().addAll(teamList) + db.teamDao().addAll(teamList) if (lessonList.isNotEmpty()) { - appDb.lessonDao().clear(profileId) - appDb.lessonDao().addAll(lessonList) + db.lessonDao().clear(profile.id) + db.lessonDao().addAll(lessonList) } if (lessonChangeList.isNotEmpty()) - appDb.lessonChangeDao().addAll(lessonChangeList) + db.lessonChangeDao().addAll(lessonChangeList) if (gradeCategoryList.isNotEmpty()) - appDb.gradeCategoryDao().addAll(gradeCategoryList) + db.gradeCategoryDao().addAll(gradeCategoryList) if (gradeList.isNotEmpty()) { - appDb.gradeDao().clear(profileId) - appDb.gradeDao().addAll(gradeList) + db.gradeDao().clear(profile.id) + db.gradeDao().addAll(gradeList) } if (eventList.isNotEmpty()) { - appDb.eventDao().removeFuture(profileId, Date.getToday()) - appDb.eventDao().addAll(eventList) + db.eventDao().removeFuture(profile.id, Date.getToday()) + db.eventDao().addAll(eventList) } if (eventTypeList.isNotEmpty()) - appDb.eventTypeDao().addAll(eventTypeList) + db.eventTypeDao().addAll(eventTypeList) if (noticeList.isNotEmpty()) { - appDb.noticeDao().clear(profileId) - appDb.noticeDao().addAll(noticeList) + db.noticeDao().clear(profile.id) + db.noticeDao().addAll(noticeList) } if (attendanceList.isNotEmpty()) - appDb.attendanceDao().addAll(attendanceList) + db.attendanceDao().addAll(attendanceList) if (announcementList.isNotEmpty()) - appDb.announcementDao().addAll(announcementList) + db.announcementDao().addAll(announcementList) if (messageList.isNotEmpty()) - appDb.messageDao().addAllIgnore(messageList) + db.messageDao().addAllIgnore(messageList) if (messageRecipientList.isNotEmpty()) - appDb.messageRecipientDao().addAll(messageRecipientList) + db.messageRecipientDao().addAll(messageRecipientList) if (messageRecipientIgnoreList.isNotEmpty()) - appDb.messageRecipientDao().addAllIgnore(messageRecipientIgnoreList) + db.messageRecipientDao().addAllIgnore(messageRecipientIgnoreList) if (metadataList.isNotEmpty()) - appDb.metadataDao().addAllIgnore(metadataList) + db.metadataDao().addAllIgnore(metadataList) if (messageMetadataList.isNotEmpty()) - appDb.metadataDao().setSeen(messageMetadataList) + db.metadataDao().setSeen(messageMetadataList) } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt index d760b3ab..e2c6281b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt @@ -14,13 +14,14 @@ import pl.szczodrzynski.edziennik.datamodels.Profile * @param loginType type of the e-register this endpoint handles * @param endpointId a unique ID of this endpoint * @param featureIds a [List] of [Feature]s (their IDs) this endpoint can download + * May be null if no strict feature set is associated with this method. * @param endpointClass a [Class] which constructor will be invoked when a data download is needed * @param requiredLoginMethod a lambda returning a required login method (which will be called before this). May differ depending on the [Profile] and/or [LoginStore]. */ class Endpoint( val loginType: Int, val endpointId: Int, - val featureIds: List, + val featureIds: List?, val endpointClass: Class<*>, val requiredLoginMethod: (profile: Profile?, loginStore: LoginStore) -> Int ) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt index f83c4801..3ffda1af 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt @@ -1,6 +1,5 @@ package pl.szczodrzynski.edziennik.api.v2.models -import pl.szczodrzynski.edziennik.api.v2.endpoint data class Feature(val featureId: Int, val loginOptions: Map>) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt index 9b00bf0b..3fda0eb1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt @@ -17,13 +17,14 @@ import pl.szczodrzynski.edziennik.datamodels.Profile * @param loginType type of the e-register this login method handles * @param loginMethodId a unique ID of this login method * @param featureIds a [List] of [Feature]s (their IDs) this login method can provide access to + * May be null if no strict feature set is associated with this method. * @param loginMethodClass a [Class] which constructor will be invoked when a log in is needed * @param requiredLoginMethod a lambda returning a required login method (which will be called before this). May differ depending on the [Profile] and/or [LoginStore]. */ class LoginMethod( val loginType: Int, val loginMethodId: Int, - val featureIds: List, + val featureIds: List?, val loginMethodClass: Class<*>, val requiredLoginMethod: (profile: Profile?, loginStore: LoginStore) -> Int ) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/fragments/HomeFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/fragments/HomeFragment.java index 47c9e837..b4689a40 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/fragments/HomeFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/fragments/HomeFragment.java @@ -44,7 +44,8 @@ import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.api.AppError; import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback; -import pl.szczodrzynski.edziennik.api.v2.librus.Librus; +import pl.szczodrzynski.edziennik.api.v2.librus.LibrusOld; +import pl.szczodrzynski.edziennik.api.v2.librus.LibrusTest; import pl.szczodrzynski.edziennik.databinding.CardLuckyNumberBinding; import pl.szczodrzynski.edziennik.databinding.CardUpdateBinding; import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding; @@ -115,45 +116,18 @@ public class HomeFragment extends Fragment { return true; });*/ - b.testButton.setOnClickListener((v -> { - LoginStore loginStore = new LoginStore(1, LOGIN_TYPE_LIBRUS, new JsonObject()); - loginStore.putLoginData("email", "example@example.com"); - loginStore.putLoginData("password", "zaq1@WSX"); - Profile profile = new Profile(1, "test", "testsubname", 1); - profile.putStudentData("accountLogin", "1234567"); - new Librus(app, profile, loginStore).login(new SyncCallback() { - @Override - public void onLoginFirst(List profileList, LoginStore loginStore) { - - } - - @Override - public void onSuccess(Context activityContext, ProfileFull profileFull) { - - } - - @Override - public void onProgress(int progressStep) { - - } - - @Override - public void onActionStarted(int stringResId) { - - } - - @Override - public void onError(Context activityContext, @NonNull AppError error) { - - } - }); - })); - b.composeButton.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); b.composeButton.setOnClickListener((v -> { startActivity(new Intent(activity, MessagesComposeActivity.class)); })); + LibrusTest test = new LibrusTest(app); + + b.testButton.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); + b.testButton.setOnClickListener((v -> { + test.go(); + })); + //((TextView)v.findViewById(R.id.nextSync)).setText(getString(R.string.next_sync_format,Time.fromMillis(app.appJobs.syncJobTime).getStringHMS())); From a785db4d475a2a17c6fae7d869f6cd03a4619f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 22 Sep 2019 22:02:36 +0200 Subject: [PATCH 006/691] [APIv2/Librus] Add Librus Synergia login method. Update structure and error handling. --- .../pl/szczodrzynski/edziennik/Extensions.kt | 27 +-- .../szczodrzynski/edziennik/api/AppError.java | 10 +- .../edziennik/api/v2/Constants.kt | 8 +- .../szczodrzynski/edziennik/api/v2/Errors.kt | 114 ++++++++---- .../edziennik/api/v2/librus/LibrusTest.kt | 10 +- .../api/v2/librus/data/DataLibrus.kt | 28 +++ .../edziennik/api/v2/librus/data/LibrusApi.kt | 4 +- .../api/v2/librus/login/LoginLibrus.kt | 2 +- .../api/v2/librus/login/LoginLibrusApi.kt | 58 ++---- .../api/v2/librus/login/LoginLibrusPortal.kt | 29 ++- .../v2/librus/login/LoginLibrusSynergia.kt | 172 +++++++++++++++++- .../v2/librus/login/SynergiaTokenExtractor.kt | 28 +-- .../edziennik/api/v2/models/Data.kt | 16 ++ 13 files changed, 378 insertions(+), 128 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index 2dff656b..a6a8ab76 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -8,6 +8,7 @@ import android.os.Build import android.os.Bundle import androidx.core.app.ActivityCompat import com.google.gson.JsonArray +import com.google.gson.JsonElement import com.google.gson.JsonObject import pl.szczodrzynski.edziennik.datamodels.Profile import pl.szczodrzynski.edziennik.datamodels.Teacher @@ -21,19 +22,21 @@ fun List.byNameLastFirst(nameLastFirst: String) = firstOrNull { it.surn fun List.byNameFDotLast(nameFDotLast: String) = firstOrNull { it.name + "." + it.surname == nameFDotLast } fun List.byNameFDotSpaceLast(nameFDotSpaceLast: String) = firstOrNull { it.name + ". " + it.surname == nameFDotSpaceLast } -fun JsonObject.getBoolean(key: String): Boolean? = get(key)?.let { if (it.isJsonNull) null else it.asBoolean } -fun JsonObject.getString(key: String): String? = get(key)?.let { if (it.isJsonNull) null else it.asString } -fun JsonObject.getInt(key: String): Int? = get(key)?.let { if (it.isJsonNull) null else it.asInt } -fun JsonObject.getLong(key: String): Long? = get(key)?.let { if (it.isJsonNull) null else it.asLong } -fun JsonObject.getJsonObject(key: String): JsonObject? = get(key)?.let { if (it.isJsonNull) null else it.asJsonObject } -fun JsonObject.getJsonArray(key: String): JsonArray? = get(key)?.let { if (it.isJsonNull) null else it.asJsonArray } +fun JsonObject?.get(key: String): JsonElement? = this?.get(key) -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.getInt(key: String, defaultValue: Int): Int = get(key)?.let { if (it.isJsonNull) defaultValue else it.asInt } ?: defaultValue -fun JsonObject.getLong(key: String, defaultValue: Long): Long = get(key)?.let { if (it.isJsonNull) defaultValue else it.asLong } ?: defaultValue -fun JsonObject.getJsonObject(key: String, defaultValue: JsonObject): JsonObject = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonObject } ?: defaultValue -fun JsonObject.getJsonArray(key: String, defaultValue: JsonArray): JsonArray = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonArray } ?: defaultValue +fun JsonObject?.getBoolean(key: String): Boolean? = get(key)?.let { if (it.isJsonNull) null else it.asBoolean } +fun JsonObject?.getString(key: String): String? = get(key)?.let { if (it.isJsonNull) null else it.asString } +fun JsonObject?.getInt(key: String): Int? = get(key)?.let { if (it.isJsonNull) null else it.asInt } +fun JsonObject?.getLong(key: String): Long? = get(key)?.let { if (it.isJsonNull) null else it.asLong } +fun JsonObject?.getJsonObject(key: String): JsonObject? = get(key)?.let { if (it.isJsonNull) null else it.asJsonObject } +fun JsonObject?.getJsonArray(key: String): JsonArray? = get(key)?.let { if (it.isJsonNull) null else it.asJsonArray } + +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?.getInt(key: String, defaultValue: Int): Int = get(key)?.let { if (it.isJsonNull) defaultValue else it.asInt } ?: defaultValue +fun JsonObject?.getLong(key: String, defaultValue: Long): Long = get(key)?.let { if (it.isJsonNull) defaultValue else it.asLong } ?: defaultValue +fun JsonObject?.getJsonObject(key: String, defaultValue: JsonObject): JsonObject = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonObject } ?: defaultValue +fun JsonObject?.getJsonArray(key: String, defaultValue: JsonArray): JsonArray = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonArray } ?: defaultValue fun CharSequence?.isNotNullNorEmpty(): Boolean { return this != null && this.isNotEmpty() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java b/app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java index a22c8df8..2234783f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/AppError.java @@ -85,10 +85,10 @@ public class AppError { this(TAG, line, errorCode, null, null, null, throwable, apiResponse); } public AppError(String TAG, int line, int errorCode, Throwable throwable, JsonObject apiResponse) { - this(TAG, line, errorCode, null, null, null, throwable, apiResponse.toString()); + this(TAG, line, errorCode, null, null, null, throwable, apiResponse == null ? null : apiResponse.toString()); } public AppError(String TAG, int line, int errorCode, String errorText, Response response, JsonObject apiResponse) { - this(TAG, line, errorCode, errorText, response, response == null ? null : response.request(), null, apiResponse.toString()); + this(TAG, line, errorCode, errorText, response, response == null ? null : response.request(), null, apiResponse == null ? null : apiResponse.toString()); } public AppError(String TAG, int line, int errorCode, String errorText, Response response, String apiResponse) { this(TAG, line, errorCode, errorText, response, response == null ? null : response.request(), null, apiResponse); @@ -97,7 +97,7 @@ public class AppError { this(TAG, line, errorCode, errorText, null, null, null, apiResponse); } public AppError(String TAG, int line, int errorCode, String errorText, JsonObject apiResponse) { - this(TAG, line, errorCode, errorText, null, null, null, apiResponse.toString()); + this(TAG, line, errorCode, errorText, null, null, null, apiResponse == null ? null : apiResponse.toString()); } public AppError(String TAG, int line, int errorCode, String errorText) { this(TAG, line, errorCode, errorText, null, null, null, null); @@ -106,7 +106,7 @@ public class AppError { this(TAG, line, errorCode, null, null, null, null, apiResponse.toString()); } public AppError(String TAG, int line, int errorCode, Response response, Throwable throwable, JsonObject apiResponse) { - this(TAG, line, errorCode, null, response, response == null ? null : response.request(), throwable, apiResponse.toString()); + this(TAG, line, errorCode, null, response, response == null ? null : response.request(), throwable, apiResponse == null ? null : apiResponse.toString()); } public AppError(String TAG, int line, int errorCode, Response response, Throwable throwable, String apiResponse) { this(TAG, line, errorCode, null, response, response == null ? null : response.request(), throwable, apiResponse); @@ -115,7 +115,7 @@ public class AppError { this(TAG, line, errorCode, null, response, response == null ? null : response.request(), null, apiResponse); } public AppError(String TAG, int line, int errorCode, Response response, JsonObject apiResponse) { - this(TAG, line, errorCode, null, response, response == null ? null : response.request(), null, apiResponse.toString()); + this(TAG, line, errorCode, null, response, response == null ? null : response.request(), null, apiResponse == null ? null : apiResponse.toString()); } public String getDetails(Context context) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index 047dd38f..b80d8494 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -27,8 +27,11 @@ const val LIBRUS_TOKEN_URL = "https://portal.librus.pl/oauth2/access_token" const val LIBRUS_ACCOUNT_URL = "https://portal.librus.pl/api/v2/SynergiaAccounts/fresh/" // + login const val LIBRUS_ACCOUNTS_URL = "https://portal.librus.pl/api/v2/SynergiaAccounts" -const val LIBRUS_API_URL = "https://api.librus.pl/2.0/" +/** https://api.librus.pl/2.0 */ +const val LIBRUS_API_URL = "https://api.librus.pl/2.0" +/** https://api.librus.pl/OAuth/Token */ const val LIBRUS_API_TOKEN_URL = "https://api.librus.pl/OAuth/Token" +/** https://api.librus.pl/OAuth/TokenJST */ const val LIBRUS_API_TOKEN_JST_URL = "https://api.librus.pl/OAuth/TokenJST" const val LIBRUS_API_AUTHORIZATION = "Mjg6ODRmZGQzYTg3YjAzZDNlYTZmZmU3NzdiNThiMzMyYjE=" const val LIBRUS_API_SECRET_JST = "18b7c1ee08216f636a1b1a2440e68398" @@ -38,7 +41,8 @@ const val LIBRUS_API_CLIENT_ID_JST = "49" const val LIBRUS_JST_DEMO_CODE = "68656A21" const val LIBRUS_JST_DEMO_PIN = "1290" -const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/token/\$token/przenies/" +/** https://synergia.librus.pl/loguj/token/TOKEN/przenies */ +const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/token/TOKEN/przenies/" const val LIBRUS_MESSAGES_URL = "https://wiadomosci.librus.pl/module/" const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action=" \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index 4f944965..4c89af8a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -4,38 +4,84 @@ package pl.szczodrzynski.edziennik.api.v2 -/*const val CODE_OTHER = 0 -const val CODE_OK = 1 -const val CODE_NO_INTERNET = 10 -const val CODE_SSL_ERROR = 13 -const val CODE_ARCHIVED = 5 -const val CODE_MAINTENANCE = 6 -const val CODE_LOGIN_ERROR = 7 -const val CODE_ACCOUNT_MISMATCH = 8 -const val CODE_APP_SERVER_ERROR = 9 -const val CODE_MULTIACCOUNT_SETUP = 12 -const val CODE_TIMEOUT = 11 -const val CODE_PROFILE_NOT_FOUND = 14 -const val CODE_ATTACHMENT_NOT_AVAILABLE = 28 -const val CODE_INVALID_LOGIN = 2 -const val CODE_INVALID_SERVER_ADDRESS = 21 -const val CODE_INVALID_SCHOOL_NAME = 22 -const val CODE_INVALID_DEVICE = 23 -const val CODE_OLD_PASSWORD = 4 -const val CODE_INVALID_TOKEN = 24 -const val CODE_EXPIRED_TOKEN = 27 -const val CODE_INVALID_SYMBOL = 25 -const val CODE_INVALID_PIN = 26 -const val CODE_LIBRUS_NOT_ACTIVATED = 29 -const val CODE_SYNERGIA_NOT_ACTIVATED = 32 -const val CODE_LIBRUS_DISCONNECTED = 31 -const val CODE_PROFILE_ARCHIVED = 30*/ -const val CODE_INVALID_LOGIN_MODE = 130 -const val CODE_INTERNAL_MISSING_DATA = 100 -const val CODE_INTERNAL_LIBRUS_ACCOUNT_410 = 120 -const val CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED = 121 -const val CODE_LOGIN_METHOD_NOT_SATISFIED = 122 -const val CODE_LIBRUS_PROFILE_NULL = 123 -const val ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED = 124 +/*const val CODE_OTHER = 0 +const val CODE_OK = 1 +const val CODE_NO_INTERNET = 10 +const val CODE_SSL_ERROR = 13 +const val CODE_ARCHIVED = 5 +const val CODE_MAINTENANCE = 6 +const val CODE_LOGIN_ERROR = 7 +const val CODE_ACCOUNT_MISMATCH = 8 +const val CODE_APP_SERVER_ERROR = 9 +const val CODE_MULTIACCOUNT_SETUP = 12 +const val CODE_TIMEOUT = 11 +const val CODE_PROFILE_NOT_FOUND = 14 +const val CODE_ATTACHMENT_NOT_AVAILABLE = 28 +const val CODE_INVALID_LOGIN = 2 +const val CODE_INVALID_SERVER_ADDRESS = 21 +const val CODE_INVALID_SCHOOL_NAME = 22 +const val CODE_INVALID_DEVICE = 23 +const val CODE_OLD_PASSWORD = 4 +const val CODE_INVALID_TOKEN = 24 +const val CODE_EXPIRED_TOKEN = 27 +const val CODE_INVALID_SYMBOL = 25 +const val CODE_INVALID_PIN = 26 +const val CODE_LIBRUS_NOT_ACTIVATED = 29 +const val CODE_SYNERGIA_NOT_ACTIVATED = 32 +const val CODE_LIBRUS_DISCONNECTED = 31 +const val CODE_PROFILE_ARCHIVED = 30*/ -const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901 \ No newline at end of file +const val ERROR_REQUEST_FAILURE = 50 +const val ERROR_REQUEST_HTTP_400 = 51 +const val ERROR_REQUEST_HTTP_401 = 52 +const val ERROR_REQUEST_HTTP_403 = 53 +const val ERROR_REQUEST_HTTP_404 = 54 +const val ERROR_REQUEST_HTTP_405 = 55 +const val ERROR_REQUEST_HTTP_410 = 56 +const val ERROR_REQUEST_HTTP_500 = 57 +const val ERROR_RESPONSE_EMPTY = 100 +const val ERROR_LOGIN_DATA_MISSING = 101 +const val ERROR_LOGIN_DATA_INVALID = 102 +const val ERROR_PROFILE_MISSING = 105 +const val ERROR_INVALID_LOGIN_MODE = 110 +const val ERROR_LOGIN_METHOD_NOT_SATISFIED = 111 + +const val CODE_INTERNAL_LIBRUS_ACCOUNT_410 = 120 +const val CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED = 121 +const val ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED = 124 +const val ERROR_LOGIN_LIBRUS_API_CONNECTION_PROBLEMS = 125 +const val ERROR_LOGIN_LIBRUS_API_INVALID_CLIENT = 126 +const val ERROR_LOGIN_LIBRUS_API_REG_ACCEPT_NEEDED = 127 +const val ERROR_LOGIN_LIBRUS_API_CHANGE_PASSWORD_ERROR = 128 +const val ERROR_LOGIN_LIBRUS_API_PASSWORD_CHANGE_REQUIRED = 129 +const val ERROR_LOGIN_LIBRUS_API_INVALID_GRANT = 130 +const val ERROR_LOGIN_LIBRUS_API_OTHER = 131 +const val ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING = 132 +const val ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED = 133 +const val ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR = 134 +const val ERROR_LOGIN_LIBRUS_PORTAL_TOKEN_ERROR = 135 +const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED = 136 +const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_410 = 137 +const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND = 138 +const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_OTHER = 139 +const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN_MISSING = 139 +const val ERROR_LIBRUS_API_TOKEN_EXPIRED = 140 +const val ERROR_LIBRUS_API_INSUFFICIENT_SCOPES = 141 +const val ERROR_LIBRUS_API_OTHER = 142 +const val ERROR_LIBRUS_API_REQUEST_DENIED = 143 +const val ERROR_LIBRUS_API_RESOURCE_NOT_FOUND = 144 +const val ERROR_LIBRUS_API_DATA_NOT_FOUND = 145 +const val ERROR_LIBRUS_API_TIMETABLE_NOT_PUBLIC = 146 +const val ERROR_LIBRUS_API_RESOURCE_ACCESS_DENIED = 147 +const val ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS = 148 +const val ERROR_LIBRUS_API_INCORRECT_ENDPOINT = 149 +const val ERROR_LIBRUS_API_LUCKY_NUMBER_NOT_ACTIVE = 150 +const val ERROR_LIBRUS_API_NOTES_NOT_ACTIVE = 151 +const val ERROR_LOGIN_LIBRUS_SYNERGIA_NO_TOKEN = 152 +const val ERROR_LOGIN_LIBRUS_SYNERGIA_TOKEN_INVALID = 153 +const val ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID = 154 + +const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901 +const val EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN = 902 +const val EXCEPTION_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN = 903 +const val EXCEPTION_LIBRUS_API_REQUEST = 904 \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt index 592423b3..bb009d86 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt @@ -23,17 +23,17 @@ class LibrusTest(val app: App) { } val profile = Profile(1, "Profil", "xd", 1).apply { - //putStudentData("accountLogin", "1234567") + putStudentData("accountLogin", "1234567") //putStudentData("accountPassword", "zaq1@WSX") - putStudentData("accountCode", LIBRUS_JST_DEMO_CODE) - putStudentData("accountPin", LIBRUS_JST_DEMO_PIN) + //putStudentData("accountCode", LIBRUS_JST_DEMO_CODE) + //putStudentData("accountPin", LIBRUS_JST_DEMO_PIN) } val loginStore = LoginStore(1, LOGIN_TYPE_LIBRUS, JsonObject().apply { addProperty("email", "test@example.com") addProperty("password", "zaq1@WSX") }).also { - it.mode = LOGIN_MODE_LIBRUS_JST + it.mode = LOGIN_MODE_LIBRUS_EMAIL } fun go() { @@ -54,7 +54,7 @@ class LibrusTest(val app: App) { } } - LoginLibrus(data, LOGIN_METHOD_LIBRUS_API) { + LoginLibrus(data, LOGIN_METHOD_LIBRUS_SYNERGIA) { d(TAG, "Login succeeded.") d(TAG, "Profile data: ${data.profile?.studentData?.toString()}") d(TAG, "LoginStore data: ${data.loginStore.data}") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt index 61ede774..d7437d77 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt @@ -49,6 +49,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app /** * A Synergia login, like 1234567u. * Used: for login (API Login Method) in Synergia mode. + * Used: for login (Synergia Login Method) in Synergia mode. * And also in various places in [pl.szczodrzynski.edziennik.api.v2.models.Endpoint]s */ private var mApiLogin: String? = null @@ -58,6 +59,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app /** * A Synergia password. * Used: for login (API Login Method) in Synergia mode. + * Used: for login (Synergia Login Method) in Synergia mode. */ private var mApiPassword: String? = null var apiPassword: String? @@ -109,6 +111,32 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app get() { mApiTokenExpiryTime = mApiTokenExpiryTime ?: profile?.getStudentData("accountTokenTime", 0L); return mApiTokenExpiryTime ?: 0L } set(value) { profile?.putStudentData("accountTokenTime", value) ?: return; mApiTokenExpiryTime = value } + /* _____ _ + / ____| (_) + | (___ _ _ _ __ ___ _ __ __ _ _ __ _ + \___ \| | | | '_ \ / _ \ '__/ _` | |/ _` | + ____) | |_| | | | | __/ | | (_| | | (_| | + |_____/ \__, |_| |_|\___|_| \__, |_|\__,_| + __/ | __/ | + |___/ |__*/ + /** + * A Synergia web Session ID (DZIENNIKSID). + * Used in endpoints with Synergia login method. + */ + private var mSynergiaSessionId: String? = null + var synergiaSessionId: String? + get() { mSynergiaSessionId = mSynergiaSessionId ?: profile?.getStudentData("accountSID", null); return mSynergiaSessionId } + set(value) { profile?.putStudentData("accountSID", value) ?: return; mSynergiaSessionId = value } + /** + * The expiry time for [synergiaSessionId], as a UNIX timestamp. + * Used in endpoints with Synergia login method. + * TODO verify how long is the session ID valid. + */ + private var mSynergiaSessionIdExpiryTime: Long? = null + var synergiaSessionIdExpiryTime: Long + get() { mSynergiaSessionIdExpiryTime = mSynergiaSessionIdExpiryTime ?: profile?.getStudentData("accountSIDTime", 0L); return mSynergiaSessionIdExpiryTime ?: 0L } + set(value) { profile?.putStudentData("accountSIDTime", value) ?: return; mSynergiaSessionIdExpiryTime = value } + /* ____ _ _ / __ \| | | | | | | | |_| |__ ___ _ __ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt index 3720b212..c95dc929 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt @@ -24,9 +24,9 @@ open class LibrusApi(override val data: DataLibrus) : Api(data) { const val TAG = "LibrusApi" } fun apiRequest(endpoint: String, callback: (json: JsonObject?) -> Unit) { - d(TAG, "Requesting $LIBRUS_API_URL$endpoint") + d(TAG, "Requesting $LIBRUS_API_URL/$endpoint") Request.builder() - .url(if (data.fakeLogin) "http://szkolny.eu/librus/api/$endpoint" else LIBRUS_API_URL + endpoint) + .url(if (data.fakeLogin) "http://szkolny.eu/librus/api/$endpoint" else "$LIBRUS_API_URL/$endpoint") .userAgent(LIBRUS_USER_AGENT) .addHeader("Authorization", "Bearer ${data.apiAccessToken}") .get() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt index e16ada31..b79c09c1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt @@ -63,7 +63,7 @@ class LoginLibrus(val data: DataLibrus, vararg loginMethodIds: Int, val onSucces } } LOGIN_METHOD_LIBRUS_SYNERGIA -> { - LoginLibrusApi(data) { + LoginLibrusSynergia(data) { data.loginMethods.add(loginMethodId) onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt index ef6da7d5..3166f211 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt @@ -17,7 +17,6 @@ import pl.szczodrzynski.edziennik.currentTimeUnix import pl.szczodrzynski.edziennik.getInt import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.isNotNullNorEmpty -import java.net.HttpURLConnection import java.net.HttpURLConnection.* class LoginLibrusApi { @@ -28,12 +27,13 @@ class LoginLibrusApi { private lateinit var data: DataLibrus private lateinit var onSuccess: () -> Unit + /* do NOT move this to primary constructor */ constructor(data: DataLibrus, onSuccess: () -> Unit) { this.data = data this.onSuccess = onSuccess if (data.profile == null) { - data.callback.onError(null, AppError(TAG, 19, CODE_LIBRUS_PROFILE_NULL)) + data.error(TAG, ERROR_PROFILE_MISSING) return } @@ -46,7 +46,7 @@ class LoginLibrusApi { LOGIN_MODE_LIBRUS_SYNERGIA -> loginWithSynergia() LOGIN_MODE_LIBRUS_JST -> loginWithJst() else -> { - data.callback.onError(null, AppError(TAG, 25, CODE_INVALID_LOGIN_MODE)) + data.error(TAG, ERROR_INVALID_LOGIN_MODE) } } } @@ -54,7 +54,7 @@ class LoginLibrusApi { private fun loginWithPortal() { if (!data.loginMethods.contains(LOGIN_METHOD_LIBRUS_PORTAL)) { - data.callback.onError(null, AppError(TAG, 26, CODE_LOGIN_METHOD_NOT_SATISFIED)) + data.error(TAG, ERROR_LOGIN_METHOD_NOT_SATISFIED) return } SynergiaTokenExtractor(data) { @@ -94,7 +94,7 @@ class LoginLibrusApi { } else { // cannot log in: token expired, no login data present - data.callback.onError(null, AppError(TAG, 91, CODE_INVALID_LOGIN)) + data.error(TAG, ERROR_LOGIN_DATA_MISSING) } } @@ -111,44 +111,30 @@ class LoginLibrusApi { } else { // cannot log in: token expired, no login data present - data.callback.onError(null, AppError(TAG, 110, CODE_INVALID_LOGIN)) + data.error(TAG, ERROR_LOGIN_DATA_MISSING) } } private val tokenCallback = object : JsonCallbackHandler() { override fun onSuccess(json: JsonObject?, response: Response?) { if (json == null) { - data.callback.onError(null, AppError(TAG, 117, CODE_MAINTENANCE, response)) + data.error(TAG, ERROR_RESPONSE_EMPTY, response) return } json.getString("error")?.let { error -> when (error) { - "librus_captcha_needed" -> { - - } - "connection_problems" -> { - - } - "invalid_client" -> { - - } - "librus_reg_accept_needed" -> { - - } - "librus_change_password_error" -> { - - } - "librus_password_change_required" -> { - - } - "invalid_grant" -> { - - } - else -> { - - } + "librus_captcha_needed" -> ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED + "connection_problems" -> ERROR_LOGIN_LIBRUS_API_CONNECTION_PROBLEMS + "invalid_client" -> ERROR_LOGIN_LIBRUS_API_INVALID_CLIENT + "librus_reg_accept_needed" -> ERROR_LOGIN_LIBRUS_API_REG_ACCEPT_NEEDED + "librus_change_password_error" -> ERROR_LOGIN_LIBRUS_API_CHANGE_PASSWORD_ERROR + "librus_password_change_required" -> ERROR_LOGIN_LIBRUS_API_PASSWORD_CHANGE_REQUIRED + "invalid_grant" -> ERROR_LOGIN_LIBRUS_API_INVALID_GRANT + else -> ERROR_LOGIN_LIBRUS_API_OTHER + }.let { errorCode -> + data.error(TAG, errorCode, apiResponse = json, response = response) + return } - return } try { @@ -157,12 +143,12 @@ class LoginLibrusApi { data.apiTokenExpiryTime = currentTimeUnix() + json.getInt("expires_in", 86400) onSuccess() } catch (e: NullPointerException) { - data.callback.onError(null, AppError(TAG, 154, EXCEPTION_LOGIN_LIBRUS_API_TOKEN, response, e, json)) + data.error(TAG, EXCEPTION_LOGIN_LIBRUS_API_TOKEN, response, e, json) } } override fun onFailure(response: Response?, throwable: Throwable?) { - data.callback.onError(null, AppError(TAG, 159, CODE_OTHER, response, throwable)) + data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) } } @@ -179,7 +165,6 @@ class LoginLibrusApi { .contentType(MediaTypeUtils.APPLICATION_FORM) .post() .allowErrorCode(HTTP_BAD_REQUEST) - .allowErrorCode(HTTP_FORBIDDEN) .allowErrorCode(HTTP_UNAUTHORIZED) .callback(tokenCallback) .build() @@ -197,7 +182,6 @@ class LoginLibrusApi { .contentType(MediaTypeUtils.APPLICATION_FORM) .post() .allowErrorCode(HTTP_BAD_REQUEST) - .allowErrorCode(HTTP_FORBIDDEN) .allowErrorCode(HTTP_UNAUTHORIZED) .callback(tokenCallback) .build() @@ -218,7 +202,6 @@ class LoginLibrusApi { .contentType(MediaTypeUtils.APPLICATION_FORM) .post() .allowErrorCode(HTTP_BAD_REQUEST) - .allowErrorCode(HTTP_FORBIDDEN) .allowErrorCode(HTTP_UNAUTHORIZED) .callback(tokenCallback) .build() @@ -237,7 +220,6 @@ class LoginLibrusApi { .contentType(MediaTypeUtils.APPLICATION_FORM) .post() .allowErrorCode(HTTP_BAD_REQUEST) - .allowErrorCode(HTTP_FORBIDDEN) .allowErrorCode(HTTP_UNAUTHORIZED) .callback(tokenCallback) .build() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt index e0d5c309..242d5596 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt @@ -24,11 +24,11 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { init { run { if (data.loginStore.mode != LOGIN_MODE_LIBRUS_EMAIL) { - data.callback.onError(null, AppError(TAG, 27, CODE_INVALID_LOGIN_MODE)) + data.error(TAG, ERROR_INVALID_LOGIN_MODE) return@run } if (data.portalEmail == null || data.portalPassword == null) { - data.callback.onError(null, AppError(TAG, 31, CODE_INVALID_LOGIN)) + data.error(TAG, ERROR_LOGIN_DATA_MISSING) return@run } @@ -67,13 +67,13 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { if (csrfMatcher.find()) { login(csrfMatcher.group(1)) } else { - data.callback.onError(null, AppError(TAG, 463, CODE_OTHER, "CSRF token not found.", response, json)) + data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING, response, json) } } } override fun onFailure(response: Response, throwable: Throwable) { - data.callback.onError(null, AppError(TAG, 207, CODE_OTHER, response, throwable)) + data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) } }) .build() @@ -94,14 +94,14 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { override fun onSuccess(json: JsonObject?, response: Response) { if (json == null) { if (response.parserErrorBody?.contains("wciąż nieaktywne") == true) { - data.callback.onError(null, AppError(TAG, 487, CODE_LIBRUS_NOT_ACTIVATED, response)) + data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED, response) return } - data.callback.onError(null, AppError(TAG, 489, CODE_MAINTENANCE, response)) + data.error(TAG, ERROR_RESPONSE_EMPTY, response) return } if (json.get("errors") != null) { - data.callback.onError(null, AppError(TAG, 490, CODE_OTHER, json.getJsonArray("errors")?.get(0)?.asString, response, json)) + data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR, response, apiResponse = json) return } authorize(json.getString("redirect", LIBRUS_AUTHORIZE_URL)) @@ -109,10 +109,10 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { override fun onFailure(response: Response, throwable: Throwable) { if (response.code() == 403 || response.code() == 401) { - data.callback.onError(null, AppError(TAG, 248, CODE_INVALID_LOGIN, response, throwable)) + data.error(TAG, ERROR_LOGIN_DATA_INVALID, response, throwable) return } - data.callback.onError(null, AppError(TAG, 251, CODE_OTHER, response, throwable)) + data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) } }) .build() @@ -141,20 +141,19 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { .callback(object : JsonCallbackHandler() { override fun onSuccess(json: JsonObject?, response: Response) { if (json == null) { - data.callback.onError(null, AppError(TAG, 539, CODE_MAINTENANCE, response)) + data.error(TAG, ERROR_RESPONSE_EMPTY, response) return } json.getString("error")?.let { error -> val hint = json.getString("hint", "") - val message = json.getString("message", "") + //val message = json.getString("message", "") if (!refreshTokenFailed && refreshToken != null && (hint == "Token has been revoked" || hint == "Token has expired")) { c(TAG, "refreshing the token failed. Trying to log in again.") refreshTokenFailed = true authorize(LIBRUS_AUTHORIZE_URL) return } - val errorText = "$error $message $hint" - data.callback.onError(null, AppError(TAG, 552, CODE_OTHER, errorText, response, json)) + data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_TOKEN_ERROR, response, apiResponse = json) return } @@ -164,13 +163,13 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { data.portalTokenExpiryTime = currentTimeUnix() + json.getInt("expires_in", 86400) onSuccess() } catch (e: NullPointerException) { - data.callback.onError(null, AppError(TAG, 311, CODE_OTHER, response, e, json)) + data.error(TAG, EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN, response, e, json) } } override fun onFailure(response: Response, throwable: Throwable) { - data.callback.onError(null, AppError(TAG, 317, CODE_OTHER, response, throwable)) + data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) } }) .build() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt index 2bad6904..0134347d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt @@ -4,5 +4,175 @@ package pl.szczodrzynski.edziennik.api.v2.librus.login -class LoginLibrusSynergia { +import com.google.gson.JsonObject +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.body.MediaTypeUtils +import im.wangchao.mhttp.callback.JsonCallbackHandler +import im.wangchao.mhttp.callback.TextCallbackHandler +import okhttp3.HttpUrl +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.currentTimeUnix +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.isNotNullNorEmpty +import java.lang.Exception +import java.net.HttpURLConnection + +class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "LoginLibrusSynergia" + } + + init { run { + if (data.profile == null) { + data.error(TAG, ERROR_PROFILE_MISSING) + return@run + } + + if (data.synergiaSessionIdExpiryTime-30 > currentTimeUnix() && data.synergiaSessionId.isNotNullNorEmpty()) { + onSuccess() + } + else { + when (data.loginStore.mode) { + LOGIN_MODE_LIBRUS_SYNERGIA -> loginWithSynergia() + else -> { + loginWithApi() + } + } + } + }} + + /** + * HTML form-based login method. Uses a Synergia login and password. + */ + private fun loginWithSynergia() { + if (data.apiLogin == null || data.apiPassword == null) { + data.error(TAG, ERROR_LOGIN_DATA_MISSING) + return + } + + } + + /** + * A login method using the Synergia API (AutoLoginToken endpoint). + */ + private fun loginWithApi() { + if (!data.loginMethods.contains(LOGIN_METHOD_LIBRUS_API)) { + data.error(TAG, ERROR_LOGIN_METHOD_NOT_SATISFIED) + return + } + + val onSuccess = { json: JsonObject -> + loginWithToken(json.getString("Token")) + } + + val callback = object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response?) { + if (json == null && response?.parserErrorBody == null) { + data.error(TAG, ERROR_RESPONSE_EMPTY, response) + return + } + val error = json.getString("Code") ?: json.getString("Message") ?: response?.parserErrorBody + error?.let { code -> + when (code) { + "TokenIsExpired" -> ERROR_LIBRUS_API_TOKEN_EXPIRED + "Insufficient scopes" -> ERROR_LIBRUS_API_INSUFFICIENT_SCOPES + "Request is denied" -> ERROR_LIBRUS_API_REQUEST_DENIED + "Resource not found" -> ERROR_LIBRUS_API_RESOURCE_NOT_FOUND + "NotFound" -> ERROR_LIBRUS_API_DATA_NOT_FOUND + "AccessDeny" -> when (json.getString("Message")) { + "Student timetable is not public" -> ERROR_LIBRUS_API_TIMETABLE_NOT_PUBLIC + else -> ERROR_LIBRUS_API_RESOURCE_ACCESS_DENIED + } + "LuckyNumberIsNotActive" -> ERROR_LIBRUS_API_LUCKY_NUMBER_NOT_ACTIVE + "NotesIsNotActive" -> ERROR_LIBRUS_API_NOTES_NOT_ACTIVE + "InvalidRequest" -> ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS + "Nieprawidłowy węzeł." -> ERROR_LIBRUS_API_INCORRECT_ENDPOINT + else -> ERROR_LIBRUS_API_OTHER + }.let { errorCode -> + data.error(TAG, errorCode, apiResponse = json, response = response) + return + } + } + + if (json == null) { + data.error(TAG, ERROR_RESPONSE_EMPTY, response) + return + } + + try { + onSuccess(json) + } catch (e: Exception) { + data.error(TAG, EXCEPTION_LIBRUS_API_REQUEST, response, e, json) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + // TODO add hotfix for Classrooms 500 + data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + } + } + + Request.builder() + .url("$LIBRUS_API_URL/AutoLoginToken") + .userAgent(LIBRUS_USER_AGENT) + .addHeader("Authorization", "Bearer ${data.apiAccessToken}") + .post() + .allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST) + .allowErrorCode(HttpURLConnection.HTTP_FORBIDDEN) + .allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED) + .callback(callback) + .build() + .enqueue() + } + + private fun loginWithToken(token: String?) { + if (token == null) { + data.error(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_NO_TOKEN) + return + } + + val callback = object : TextCallbackHandler() { + override fun onSuccess(json: String?, response: Response?) { + val location = response?.headers()?.get("Location") + if (location?.endsWith("centrum_powiadomien") == true) { + val cookieList = data.app.cookieJar.loadForRequest(HttpUrl.get("https://synergia.librus.pl")) + var sessionId: String? = null + for (cookie in cookieList) { + if (cookie.name().equals("DZIENNIKSID", ignoreCase = true)) { + sessionId = cookie.value() + } + } + if (sessionId == null) { + data.error(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID, response, json) + return + } + data.synergiaSessionId = sessionId + data.synergiaSessionIdExpiryTime = currentTimeUnix() + 3600 /* 1h */ + onSuccess() + } + else { + data.error(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_TOKEN_INVALID, response, json) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + } + } + + data.app.cookieJar.clearForDomain("synergia.librus.pl") + Request.builder() + .url(LIBRUS_SYNERGIA_TOKEN_LOGIN_URL.replace("TOKEN", token) + "/uczen/widok/centrum_powiadomien") + .userAgent(LIBRUS_USER_AGENT) + .get() + .allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST) + .allowErrorCode(HttpURLConnection.HTTP_FORBIDDEN) + .allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED) + .callback(callback) + .withClient(data.app.httpLazy) + .build() + .enqueue() + } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt index 80f26158..f771afde 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt @@ -15,16 +15,16 @@ import java.net.HttpURLConnection.* class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { companion object { - private const val TAG = "librus.SynergiaToken" + private const val TAG = "SynergiaTokenExtractor" } init { run { if (data.loginStore.mode != LOGIN_MODE_LIBRUS_EMAIL) { - data.callback.onError(null, AppError(TAG, 23, CODE_INVALID_LOGIN_MODE)) + data.error(TAG, ERROR_INVALID_LOGIN_MODE) return@run } if (data.profile == null) { - data.callback.onError(null, AppError(TAG, 28, CODE_LIBRUS_PROFILE_NULL)) + data.error(TAG, ERROR_PROFILE_MISSING) return@run } @@ -32,7 +32,9 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { onSuccess() } else { - synergiaAccount() + if (!synergiaAccount()) { + + } } }} @@ -54,25 +56,25 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { .callback(object : JsonCallbackHandler() { override fun onSuccess(json: JsonObject?, response: Response) { if (json == null) { - data.callback.onError(null, AppError(TAG, 641, CODE_MAINTENANCE, response)) + data.error(TAG, ERROR_RESPONSE_EMPTY, response) return } if (response.code() == 410) { val reason = json.get("reason") if (reason != null && reason !is JsonNull && reason.asString == "requires_an_action") { - data.callback.onError(null, AppError(TAG, 1078, CODE_LIBRUS_DISCONNECTED, response, json)) + data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED, response, apiResponse = json) return } - data.callback.onError(null, AppError(TAG, 70, CODE_INTERNAL_LIBRUS_ACCOUNT_410)) + data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_410, response, apiResponse = json) return } if (json.get("message") != null) { val message = json.get("message").asString if (message == "Account not found") { - data.callback.onError(null, AppError(TAG, 651, CODE_OTHER, data.app.getString(R.string.sync_error_register_student_not_associated_format, data.profile?.studentNameLong ?: "", accountLogin), response, json)) + data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND, response, apiResponse = json) return } - data.callback.onError(null, AppError(TAG, 654, CODE_OTHER, message + "\n\n" + accountLogin, response, json)) + data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_OTHER, response, apiResponse = json) return } if (response.code() == HTTP_OK) { @@ -81,7 +83,7 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { val accountId = json.getInt("id") val accountToken = json.getString("accessToken") if (accountId == null || accountToken == null) { - data.callback.onError(null, AppError(TAG, 1284, CODE_OTHER, json)) + data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN_MISSING, response, apiResponse = json) return } data.apiAccessToken = accountToken @@ -92,16 +94,16 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { onSuccess() } catch (e: NullPointerException) { e.printStackTrace() - data.callback.onError(null, AppError(TAG, 662, CODE_OTHER, response, e, json)) + data.error(TAG, EXCEPTION_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN, response, e, json) } } else { - data.callback.onError(null, AppError(TAG, 425, CODE_OTHER, response, json)) + data.error(TAG, ERROR_REQUEST_FAILURE, response, apiResponse = json) } } override fun onFailure(response: Response, throwable: Throwable) { - data.callback.onError(null, AppError(TAG, 432, CODE_OTHER, response, throwable)) + data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) } }) .build() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 7578c64d..edca2ce6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -3,7 +3,10 @@ package pl.szczodrzynski.edziennik.api.v2.models import android.util.LongSparseArray import androidx.core.util.forEach import androidx.core.util.isNotEmpty +import com.google.gson.JsonObject +import im.wangchao.mhttp.Response import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.AppError import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback import pl.szczodrzynski.edziennik.datamodels.* import pl.szczodrzynski.edziennik.models.Date @@ -14,6 +17,12 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) lateinit var callback: ProgressCallback + /** + * A list of [LoginMethod]s *already fulfilled* during this sync. + * + * A [LoginMethod] may add elements to this list only after a successful login + * with that method. + */ val loginMethods = mutableListOf() val teacherList = LongSparseArray() @@ -133,4 +142,11 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) if (messageMetadataList.isNotEmpty()) db.metadataDao().setSeen(messageMetadataList) } + + fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) { + callback.onError(null, AppError(tag, 999, errorCode, response, throwable, apiResponse)) + } + fun error(tag: String, errorCode: Int, response: Response? = null, apiResponse: String? = null) { + callback.onError(null, AppError(tag, 999, errorCode, response, null, apiResponse)) + } } \ No newline at end of file From e95d9ee5142644e75c77ef80994ac386ecd1372d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 23 Sep 2019 09:02:59 +0200 Subject: [PATCH 007/691] [APIv2/Librus] Update Librus Synergia login method. Add Librus Messages login method. --- .../api/v2/librus/data/DataLibrus.kt | 19 +++++++++ .../v2/librus/login/LoginLibrusMessages.kt | 40 ++++++++++++++++++- .../v2/librus/login/LoginLibrusSynergia.kt | 25 +++++------- 3 files changed, 67 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt index d7437d77..139545cc 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt @@ -137,6 +137,25 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app get() { mSynergiaSessionIdExpiryTime = mSynergiaSessionIdExpiryTime ?: profile?.getStudentData("accountSIDTime", 0L); return mSynergiaSessionIdExpiryTime ?: 0L } set(value) { profile?.putStudentData("accountSIDTime", value) ?: return; mSynergiaSessionIdExpiryTime = value } + + /** + * A Messages web Session ID (DZIENNIKSID). + * Used in endpoints with Messages login method. + */ + private var mMessagesSessionId: String? = null + var messagesSessionId: String? + get() { mMessagesSessionId = mMessagesSessionId ?: profile?.getStudentData("messagesSID", null); return mMessagesSessionId } + set(value) { profile?.putStudentData("messagesSID", value) ?: return; mMessagesSessionId = value } + /** + * The expiry time for [messagesSessionId], as a UNIX timestamp. + * Used in endpoints with Messages login method. + * TODO verify how long is the session ID valid. + */ + private var mMessagesSessionIdExpiryTime: Long? = null + var messagesSessionIdExpiryTime: Long + get() { mMessagesSessionIdExpiryTime = mMessagesSessionIdExpiryTime ?: profile?.getStudentData("messagesSIDTime", 0L); return mMessagesSessionIdExpiryTime ?: 0L } + set(value) { profile?.putStudentData("messagesSIDTime", value) ?: return; mMessagesSessionIdExpiryTime = value } + /* ____ _ _ / __ \| | | | | | | | |_| |__ ___ _ __ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt index 26e3db39..1f6667bf 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt @@ -4,5 +4,43 @@ package pl.szczodrzynski.edziennik.api.v2.librus.login -class LoginLibrusMessages { +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.currentTimeUnix +import pl.szczodrzynski.edziennik.isNotNullNorEmpty + +class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "LoginLibrusMessages" + } + + init { run { + if (data.profile == null) { + data.error(TAG, ERROR_PROFILE_MISSING) + return@run + } + + if (data.messagesSessionIdExpiryTime-30 > currentTimeUnix() && data.messagesSessionId.isNotNullNorEmpty()) { + onSuccess() + } + else { + when (data.loginStore.mode) { + LOGIN_MODE_LIBRUS_SYNERGIA -> loginWithCredentials() + else -> { + loginWithSynergia() + } + } + } + }} + + private fun loginWithCredentials() { + if (data.apiLogin == null || data.apiPassword == null) { + if (!data.loginMethods.contains(LOGIN_METHOD_LIBRUS_SYNERGIA)) { + data.error(TAG, ERROR_LOGIN_METHOD_NOT_SATISFIED) + return + } + data.error(TAG, ERROR_LOGIN_DATA_MISSING) + return + } + } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt index 0134347d..e78cd860 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt @@ -7,7 +7,6 @@ package pl.szczodrzynski.edziennik.api.v2.librus.login import com.google.gson.JsonObject import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response -import im.wangchao.mhttp.body.MediaTypeUtils import im.wangchao.mhttp.callback.JsonCallbackHandler import im.wangchao.mhttp.callback.TextCallbackHandler import okhttp3.HttpUrl @@ -34,11 +33,14 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { onSuccess() } else { - when (data.loginStore.mode) { - LOGIN_MODE_LIBRUS_SYNERGIA -> loginWithSynergia() - else -> { - loginWithApi() - } + if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_API)) { + loginWithApi() + } + else if (data.apiLogin != null && data.apiPassword != null) { + loginWithCredentials() + } + else { + data.error(TAG, ERROR_LOGIN_DATA_MISSING) } } }} @@ -46,11 +48,7 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { /** * HTML form-based login method. Uses a Synergia login and password. */ - private fun loginWithSynergia() { - if (data.apiLogin == null || data.apiPassword == null) { - data.error(TAG, ERROR_LOGIN_DATA_MISSING) - return - } + private fun loginWithCredentials() { } @@ -58,11 +56,6 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { * A login method using the Synergia API (AutoLoginToken endpoint). */ private fun loginWithApi() { - if (!data.loginMethods.contains(LOGIN_METHOD_LIBRUS_API)) { - data.error(TAG, ERROR_LOGIN_METHOD_NOT_SATISFIED) - return - } - val onSuccess = { json: JsonObject -> loginWithToken(json.getString("Token")) } From 4cbb573d1729537d999eb16519597f58d0edc442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 24 Sep 2019 20:12:25 +0200 Subject: [PATCH 008/691] [APIv2/Librus] Update Librus login methods. --- .../edziennik/api/v2/librus/LibrusHelpers.kt | 6 ---- .../api/v2/librus/data/DataLibrus.kt | 23 ++++++++++++- .../api/v2/librus/login/LoginLibrusApi.kt | 2 +- .../v2/librus/login/LoginLibrusMessages.kt | 34 +++++++++++-------- .../api/v2/librus/login/LoginLibrusPortal.kt | 2 +- .../v2/librus/login/LoginLibrusSynergia.kt | 3 +- .../edziennik/api/v2/models/Data.kt | 8 +++++ 7 files changed, 54 insertions(+), 24 deletions(-) delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusHelpers.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusHelpers.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusHelpers.kt deleted file mode 100644 index 7096c1d0..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusHelpers.kt +++ /dev/null @@ -1,6 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2019-9-21. - */ - -package pl.szczodrzynski.edziennik.api.v2.librus - diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt index 139545cc..b6aa2a4e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt @@ -5,13 +5,34 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_API +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_MESSAGES +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_PORTAL +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_SYNERGIA import pl.szczodrzynski.edziennik.api.v2.models.Data +import pl.szczodrzynski.edziennik.currentTimeUnix import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile +import pl.szczodrzynski.edziennik.isNotNullNorEmpty class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) { + fun isPortalLoginValid() = portalTokenExpiryTime-30 > currentTimeUnix() && portalRefreshToken.isNotNullNorEmpty() && portalAccessToken.isNotNullNorEmpty() + fun isApiLoginValid() = apiTokenExpiryTime-30 > currentTimeUnix() && apiAccessToken.isNotNullNorEmpty() + fun isSynergiaLoginValid() = synergiaSessionIdExpiryTime-30 > currentTimeUnix() && synergiaSessionId.isNotNullNorEmpty() + fun isMessagesLoginValid() = messagesSessionIdExpiryTime-30 > currentTimeUnix() && messagesSessionId.isNotNullNorEmpty() + + override fun satisfyLoginMethods() { + if (isPortalLoginValid()) + loginMethods += LOGIN_METHOD_LIBRUS_PORTAL + if (isApiLoginValid()) + loginMethods += LOGIN_METHOD_LIBRUS_API + if (isSynergiaLoginValid()) + loginMethods += LOGIN_METHOD_LIBRUS_SYNERGIA + if (isMessagesLoginValid()) + loginMethods += LOGIN_METHOD_LIBRUS_MESSAGES + } + /* _____ _ _ | __ \ | | | | | |__) |__ _ __| |_ __ _| | diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt index 3166f211..ca8cf85e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt @@ -37,7 +37,7 @@ class LoginLibrusApi { return } - if (data.apiTokenExpiryTime-30 > currentTimeUnix() && data.apiAccessToken.isNotNullNorEmpty()) { + if (data.isApiLoginValid()) { onSuccess() } else { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt index 1f6667bf..ec3bf565 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt @@ -20,27 +20,33 @@ class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { return@run } - if (data.messagesSessionIdExpiryTime-30 > currentTimeUnix() && data.messagesSessionId.isNotNullNorEmpty()) { + if (data.isMessagesLoginValid()) { onSuccess() } else { - when (data.loginStore.mode) { - LOGIN_MODE_LIBRUS_SYNERGIA -> loginWithCredentials() - else -> { - loginWithSynergia() - } + if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_SYNERGIA)) { + loginWithSynergia() + } + else if (data.apiLogin != null && data.apiPassword != null) { + loginWithCredentials() + } + else { + data.error(TAG, ERROR_LOGIN_DATA_MISSING) } } }} + /** + * XML (Flash messages website) login method. Uses a Synergia login and password. + */ private fun loginWithCredentials() { - if (data.apiLogin == null || data.apiPassword == null) { - if (!data.loginMethods.contains(LOGIN_METHOD_LIBRUS_SYNERGIA)) { - data.error(TAG, ERROR_LOGIN_METHOD_NOT_SATISFIED) - return - } - data.error(TAG, ERROR_LOGIN_DATA_MISSING) - return - } + + } + + /** + * A login method using the Synergia website (/wiadomosci2 Auto Login). + */ + private fun loginWithSynergia() { + } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt index 242d5596..b3fbe09b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt @@ -33,7 +33,7 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { } // succeed having a non-expired access token and a refresh token - if (data.portalTokenExpiryTime-30 > currentTimeUnix() && data.portalRefreshToken.isNotNullNorEmpty() && data.portalAccessToken.isNotNullNorEmpty()) { + if (data.isPortalLoginValid()) { onSuccess() } else if (data.portalRefreshToken != null) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt index e78cd860..ea43ff37 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt @@ -29,7 +29,7 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { return@run } - if (data.synergiaSessionIdExpiryTime-30 > currentTimeUnix() && data.synergiaSessionId.isNotNullNorEmpty()) { + if (data.isSynergiaLoginValid()) { onSuccess() } else { @@ -48,6 +48,7 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { /** * HTML form-based login method. Uses a Synergia login and password. */ + // TODO if loginWithCredentials fails and it is possible to use API, use it private fun loginWithCredentials() { } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index edca2ce6..7608c756 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -25,6 +25,14 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) */ val loginMethods = mutableListOf() + /** + * A method which may be overridden in child Data* classes. + * + * Calling it should populate [loginMethods] with all + * already available login methods (e.g. non-expired OAuth token). + */ + open fun satisfyLoginMethods() {} + val teacherList = LongSparseArray() val subjectList = LongSparseArray() val teamList = mutableListOf() From 287093148183f5b39f51ad8504bbb22b89cd172a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Wed, 25 Sep 2019 21:28:04 +0200 Subject: [PATCH 009/691] [APIv2/Librus] Update Librus Messages login method. --- .../szczodrzynski/edziennik/api/v2/Errors.kt | 3 ++ .../edziennik/api/v2/LoginMethods.kt | 6 +-- .../edziennik/api/v2/librus/LibrusTest.kt | 14 +++-- .../api/v2/librus/data/DataLibrus.kt | 22 +++++++- .../api/v2/librus/login/LoginLibrus.kt | 3 +- .../v2/librus/login/LoginLibrusMessages.kt | 53 +++++++++++++++++-- .../v2/librus/login/LoginLibrusSynergia.kt | 20 +++---- .../internal/cookie/PersistentCookieJar.java | 41 ++++++++------ 8 files changed, 124 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index 4c89af8a..e28d1afe 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -80,6 +80,9 @@ const val ERROR_LIBRUS_API_NOTES_NOT_ACTIVE = 151 const val ERROR_LOGIN_LIBRUS_SYNERGIA_NO_TOKEN = 152 const val ERROR_LOGIN_LIBRUS_SYNERGIA_TOKEN_INVALID = 153 const val ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID = 154 +const val ERROR_LIBRUS_MESSAGES_ACCESS_DENIED = 155 +const val ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED = 156 +const val ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID = 157 const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901 const val EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN = 902 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt index 26a39c7c..3e5c1fe6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt @@ -26,9 +26,9 @@ const val LOGIN_MODE_VULCAN_WEB = 0 // LOGIN METHODS const val LOGIN_METHOD_NOT_NEEDED = -1 -const val LOGIN_METHOD_LIBRUS_PORTAL = 100// 0 -const val LOGIN_METHOD_LIBRUS_API = 200// 1 -const val LOGIN_METHOD_LIBRUS_SYNERGIA = 300 // 2 +const val LOGIN_METHOD_LIBRUS_PORTAL = 100 +const val LOGIN_METHOD_LIBRUS_API = 200 +const val LOGIN_METHOD_LIBRUS_SYNERGIA = 300 const val LOGIN_METHOD_LIBRUS_MESSAGES = 400 const val LOGIN_METHOD_MOBIDZIENNIK_API = 100 const val LOGIN_METHOD_IDZIENNIK_WEB = 100 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt index bb009d86..c13369f4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt @@ -23,15 +23,23 @@ class LibrusTest(val app: App) { } val profile = Profile(1, "Profil", "xd", 1).apply { - putStudentData("accountLogin", "1234567") - //putStudentData("accountPassword", "zaq1@WSX") + //putStudentData("accountLogin", "1234567") //putStudentData("accountCode", LIBRUS_JST_DEMO_CODE) //putStudentData("accountPin", LIBRUS_JST_DEMO_PIN) + + putStudentData("accountLogin", "1234567") + + putStudentData("accountToken", "token") + putStudentData("accountTokenTime", 1569523077) } val loginStore = LoginStore(1, LOGIN_TYPE_LIBRUS, JsonObject().apply { addProperty("email", "test@example.com") addProperty("password", "zaq1@WSX") + + addProperty("accessToken", "token") + addProperty("refreshToken", "refresh") + addProperty("tokenExpiryTime", 1569523077) }).also { it.mode = LOGIN_MODE_LIBRUS_EMAIL } @@ -54,7 +62,7 @@ class LibrusTest(val app: App) { } } - LoginLibrus(data, LOGIN_METHOD_LIBRUS_SYNERGIA) { + LoginLibrus(data, LOGIN_METHOD_LIBRUS_MESSAGES) { d(TAG, "Login succeeded.") d(TAG, "Profile data: ${data.profile?.studentData?.toString()}") d(TAG, "LoginStore data: ${data.loginStore.data}") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt index b6aa2a4e..8604077f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/DataLibrus.kt @@ -4,6 +4,8 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data +import okhttp3.Cookie +import okhttp3.HttpUrl import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_API import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_MESSAGES @@ -27,10 +29,26 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app loginMethods += LOGIN_METHOD_LIBRUS_PORTAL if (isApiLoginValid()) loginMethods += LOGIN_METHOD_LIBRUS_API - if (isSynergiaLoginValid()) + if (isSynergiaLoginValid()) { loginMethods += LOGIN_METHOD_LIBRUS_SYNERGIA - if (isMessagesLoginValid()) + app.cookieJar.saveFromResponse(null, listOf( + Cookie.Builder() + .name("DZIENNIKSID") + .value(synergiaSessionId!!) + .domain("synergia.librus.pl") + .secure().httpOnly().build() + )) + } + if (isMessagesLoginValid()) { loginMethods += LOGIN_METHOD_LIBRUS_MESSAGES + app.cookieJar.saveFromResponse(null, listOf( + Cookie.Builder() + .name("DZIENNIKSID") + .value(messagesSessionId!!) + .domain("wiadomosci.librus.pl") + .secure().httpOnly().build() + )) + } } /* _____ _ _ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt index b79c09c1..b370305d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt @@ -30,6 +30,7 @@ class LoginLibrus(val data: DataLibrus, vararg loginMethodIds: Int, val onSucces loginMethodList = loginMethodList.toHashSet().toMutableList() loginMethodList.sort() + data.satisfyLoginMethods() nextLoginMethod() } @@ -69,7 +70,7 @@ class LoginLibrus(val data: DataLibrus, vararg loginMethodIds: Int, val onSucces } } LOGIN_METHOD_LIBRUS_MESSAGES -> { - LoginLibrusApi(data) { + LoginLibrusMessages(data) { data.loginMethods.add(loginMethodId) onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt index ec3bf565..3af49a01 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt @@ -4,10 +4,15 @@ package pl.szczodrzynski.edziennik.api.v2.librus.login +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.callback.TextCallbackHandler +import okhttp3.Cookie +import okhttp3.HttpUrl +import okhttp3.internal.http.HttpDate import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus import pl.szczodrzynski.edziennik.currentTimeUnix -import pl.szczodrzynski.edziennik.isNotNullNorEmpty class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { companion object { @@ -21,13 +26,21 @@ class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { } if (data.isMessagesLoginValid()) { + data.app.cookieJar.saveFromResponse(null, listOf( + Cookie.Builder() + .name("DZIENNIKSID") + .value(data.messagesSessionId!!) + .domain("wiadomosci.librus.pl") + .secure().httpOnly().build() + )) onSuccess() } else { + data.app.cookieJar.clearForDomain("wiadomosci.librus.pl") if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_SYNERGIA)) { loginWithSynergia() } - else if (data.apiLogin != null && data.apiPassword != null) { + else if (data.apiLogin != null && data.apiPassword != null && false) { loginWithCredentials() } else { @@ -46,7 +59,41 @@ class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { /** * A login method using the Synergia website (/wiadomosci2 Auto Login). */ - private fun loginWithSynergia() { + private fun loginWithSynergia(url: String = "https://synergia.librus.pl/wiadomosci2") { + val callback = object : TextCallbackHandler() { + override fun onSuccess(text: String?, response: Response?) { + val location = response?.headers()?.get("Location") + when { + location?.contains("MultiDomainLogon") == true -> loginWithSynergia(location) + location?.contains("AutoLogon") == true -> { + var sessionId = data.app.cookieJar.getCookie("wiadomosci.librus.pl", "DZIENNIKSID") + sessionId = sessionId?.replace("-MAINT", "") + if (sessionId == null) { + data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID, response, text) + return + } + data.messagesSessionId = sessionId + data.messagesSessionIdExpiryTime = currentTimeUnix() + 3600 /* 1h */ + onSuccess() + } + text?.contains("eAccessDeny") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text) + text?.contains("stop.png") == true -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + } + } + + Request.builder() + .url(url) + .userAgent(SYNERGIA_USER_AGENT) + .get() + .callback(callback) + .withClient(data.app.httpLazy) + .build() + .enqueue() } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt index ea43ff37..a78ee769 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt @@ -9,6 +9,7 @@ import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response import im.wangchao.mhttp.callback.JsonCallbackHandler import im.wangchao.mhttp.callback.TextCallbackHandler +import okhttp3.Cookie import okhttp3.HttpUrl import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus @@ -30,13 +31,21 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { } if (data.isSynergiaLoginValid()) { + data.app.cookieJar.saveFromResponse(null, listOf( + Cookie.Builder() + .name("DZIENNIKSID") + .value(data.synergiaSessionId!!) + .domain("synergia.librus.pl") + .secure().httpOnly().build() + )) onSuccess() } else { + data.app.cookieJar.clearForDomain("synergia.librus.pl") if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_API)) { loginWithApi() } - else if (data.apiLogin != null && data.apiPassword != null) { + else if (data.apiLogin != null && data.apiPassword != null && false) { loginWithCredentials() } else { @@ -48,7 +57,6 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { /** * HTML form-based login method. Uses a Synergia login and password. */ - // TODO if loginWithCredentials fails and it is possible to use API, use it private fun loginWithCredentials() { } @@ -131,13 +139,7 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { override fun onSuccess(json: String?, response: Response?) { val location = response?.headers()?.get("Location") if (location?.endsWith("centrum_powiadomien") == true) { - val cookieList = data.app.cookieJar.loadForRequest(HttpUrl.get("https://synergia.librus.pl")) - var sessionId: String? = null - for (cookie in cookieList) { - if (cookie.name().equals("DZIENNIKSID", ignoreCase = true)) { - sessionId = cookie.value() - } - } + val sessionId = data.app.cookieJar.getCookie("synergia.librus.pl", "DZIENNIKSID") if (sessionId == null) { data.error(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID, response, json) return diff --git a/mhttp/src/main/java/im/wangchao/mhttp/internal/cookie/PersistentCookieJar.java b/mhttp/src/main/java/im/wangchao/mhttp/internal/cookie/PersistentCookieJar.java index 5be7c289..7f096d54 100644 --- a/mhttp/src/main/java/im/wangchao/mhttp/internal/cookie/PersistentCookieJar.java +++ b/mhttp/src/main/java/im/wangchao/mhttp/internal/cookie/PersistentCookieJar.java @@ -18,6 +18,7 @@ package im.wangchao.mhttp.internal.cookie; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; @@ -42,21 +43,13 @@ public class PersistentCookieJar implements ClearableCookieJar { } @Override - synchronized public void saveFromResponse(HttpUrl url, List cookies) { - //Log.d("PersistentCookieJar", "FINISHING "+url.toString()); + synchronized public void saveFromResponse(@Nullable HttpUrl url, List cookies) { // cookies need to be reversed, in order to replace old cookies with these coming later // (if there are duplicate cookies in the same response) List reverseCookies = new ArrayList<>(cookies); Collections.reverse(reverseCookies); - /*for (Cookie cookie: reverseCookies) { - Log.d("PersistentCookieJar", "Saving cookie "+cookie.toString()+" from URL "+url.toString()); - }*/ cache.addAll(reverseCookies); persistor.saveAll(reverseCookies); - /*Log.d("PersistentCookieJar", "Cookies saved: "); - for (Cookie cookie : cache) { - Log.d("PersistentCookieJar", "Saving cookie " + cookie.toString() + " from URL " + url.toString()); - }*/ } @NonNull @@ -65,23 +58,15 @@ public class PersistentCookieJar implements ClearableCookieJar { List removedCookies = new ArrayList<>(); List validCookies = new ArrayList<>(); - //Log.d("PersistentCookieJar", "REQUESTING "+url.toString()); - for (Iterator it = cache.iterator(); it.hasNext(); ) { Cookie currentCookie = it.next(); - //Log.d("PersistentCookieJar", "Loading "+currentCookie.toString()+" to URL "+url.toString()); if (isCookieExpired(currentCookie)) { - //Log.d("PersistentCookieJar", "Cookie expired at "+new SimpleDateFormat("dd/MM/yyyy HH:mm:ss", Locale.getDefault()).format(new Date(currentCookie.expiresAt()))); removedCookies.add(currentCookie); it.remove(); } else if (currentCookie.matches(url)) { - //Log.d("PersistentCookieJar", "Cookie is still valid until "+new SimpleDateFormat("dd/MM/yyyy HH:mm:ss", Locale.getDefault()).format(new Date(currentCookie.expiresAt()))); validCookies.add(currentCookie); } - /*else { - Log.d("PersistentCookieJar", "URL doesn't match"); - }*/ } persistor.removeAll(removedCookies); @@ -89,6 +74,28 @@ public class PersistentCookieJar implements ClearableCookieJar { return validCookies; } + @Nullable + synchronized public String getCookie(String domain, String name) { + String cookieValue = null; + List removedCookies = new ArrayList<>(); + + for (Iterator it = cache.iterator(); it.hasNext(); ) { + Cookie currentCookie = it.next(); + if (isCookieExpired(currentCookie)) { + removedCookies.add(currentCookie); + it.remove(); + + } else if (domain.equals(currentCookie.domain()) && name.equals(currentCookie.name())) { + cookieValue = currentCookie.value(); + break; + } + } + + persistor.removeAll(removedCookies); + + return cookieValue; + } + private static boolean isCookieExpired(Cookie cookie) { return cookie.expiresAt() < System.currentTimeMillis(); } From 5edd4d592276aeafde48dfb0ccd30c4ced70eb3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 27 Sep 2019 18:41:58 +0200 Subject: [PATCH 010/691] Add Chucker --- app/build.gradle | 3 + .../java/pl/szczodrzynski/edziennik/App.java | 63 +++++++++++-------- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 3abf1031..b9d3d155 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -152,6 +152,9 @@ dependencies { implementation project(":nachos") //implementation project(":Navigation") implementation project(":szkolny-font") + + debugImplementation "com.github.ChuckerTeam.Chucker:library:3.0.1" + releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:3.0.1" } repositories { mavenCentral() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/App.java b/app/src/main/java/pl/szczodrzynski/edziennik/App.java index 37b11cc5..a1360f8d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/App.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/App.java @@ -21,6 +21,9 @@ import android.util.Log; import android.util.Pair; import android.widget.Toast; +import com.chuckerteam.chucker.api.ChuckerCollector; +import com.chuckerteam.chucker.api.ChuckerInterceptor; +import com.chuckerteam.chucker.api.RetentionManager; import com.evernote.android.job.JobManager; import com.google.android.gms.security.ProviderInstaller; import com.google.firebase.FirebaseApp; @@ -209,6 +212,33 @@ public class App extends androidx.multidex.MultiDexApplication { cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(this)); + appSharedPrefs = getSharedPreferences(getString(R.string.preference_file_global), Context.MODE_PRIVATE); + + loadConfig(); + + Themes.INSTANCE.setThemeInt(appConfig.appTheme); + + try { + PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES); + for (Signature signature: packageInfo.signatures) { + byte[] signatureBytes = signature.toByteArray(); + MessageDigest md = MessageDigest.getInstance("SHA"); + md.update(signatureBytes); + this.signature = Base64.encodeToString(md.digest(), Base64.DEFAULT); + //Log.d(TAG, "Signature is "+this.signature); + } + } + catch (Exception e) { + e.printStackTrace(); + } + + if ("f054761fbdb6a238".equals(deviceId)) { + devMode = true; + } + else if (appConfig.devModePassword != null) { + checkDevModePassword(); + } + OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder() .cache(null) .followRedirects(true) @@ -256,6 +286,12 @@ public class App extends androidx.multidex.MultiDexApplication { } } + if (App.devMode || BuildConfig.DEBUG) { + ChuckerCollector chuckerCollector = new ChuckerCollector(this, true, RetentionManager.Period.ONE_HOUR); + ChuckerInterceptor chuckerInterceptor = new ChuckerInterceptor(this, chuckerCollector); + httpBuilder.addInterceptor(chuckerInterceptor); + } + http = httpBuilder.build(); httpLazy = http.newBuilder().followRedirects(false).followSslRedirects(false).build(); @@ -264,35 +300,8 @@ public class App extends androidx.multidex.MultiDexApplication { //register = new Register(mContext); - appSharedPrefs = getSharedPreferences(getString(R.string.preference_file_global), Context.MODE_PRIVATE); - - loadConfig(); - - Themes.INSTANCE.setThemeInt(appConfig.appTheme); - //profileLoadById(appSharedPrefs.getInt("current_profile_id", 1)); - try { - PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES); - for (Signature signature: packageInfo.signatures) { - byte[] signatureBytes = signature.toByteArray(); - MessageDigest md = MessageDigest.getInstance("SHA"); - md.update(signatureBytes); - this.signature = Base64.encodeToString(md.digest(), Base64.DEFAULT); - //Log.d(TAG, "Signature is "+this.signature); - } - } - catch (Exception e) { - e.printStackTrace(); - } - - if ("f054761fbdb6a238".equals(deviceId)) { - devMode = true; - } - else if (appConfig.devModePassword != null) { - checkDevModePassword(); - } - JobManager.create(this).addJobCreator(new JobsCreator()); if (appConfig.registerSyncEnabled) { SyncJob.schedule(this); From 0bf2026a64b7ce21c568b713fd6c7e5c94f852eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 29 Sep 2019 17:19:38 +0200 Subject: [PATCH 011/691] [APIv2] Add API Service. Update other APIv2 components. Update Profile DAO --- app/src/main/AndroidManifest.xml | 2 + .../java/pl/szczodrzynski/edziennik/App.java | 8 +- .../pl/szczodrzynski/edziennik/Extensions.kt | 10 ++ .../szczodrzynski/edziennik/MainActivity.kt | 2 +- .../edziennik/WidgetTimetable.java | 2 +- .../edziennik/api/Edziennik.java | 30 ++-- .../edziennik/api/Iuczniowie.java | 4 +- .../szczodrzynski/edziennik/api/Librus.java | 6 +- .../edziennik/api/Mobidziennik.java | 4 +- .../szczodrzynski/edziennik/api/Vulcan.java | 4 +- ...erface.java => OldEdziennikInterface.java} | 2 +- .../edziennik/api/v2/ApiService.kt | 158 ++++++++++++++++++ .../edziennik/api/v2/Constants.kt | 14 +- .../edziennik/api/v2/Endpoints.kt | 130 ++++++++++++-- .../szczodrzynski/edziennik/api/v2/Errors.kt | 21 ++- .../edziennik/api/v2/Features.kt | 87 ++++++++++ .../edziennik/api/v2/LoginMethods.kt | 37 ++-- .../edziennik/api/v2/events/SyncErrorEvent.kt | 9 + .../api/v2/events/SyncFinishedEvent.kt | 7 + .../api/v2/events/SyncProfileFinishedEvent.kt | 7 + .../api/v2/events/SyncProgressEvent.kt | 7 + .../api/v2/events/SyncStartedEvent.kt | 7 + .../v2/events/requests/MessageGetRequest.kt | 13 ++ .../v2/events/requests/SyncProfileRequest.kt | 13 ++ .../api/v2/events/requests/SyncRequest.kt | 7 + .../api/v2/events/requests/SyncViewRequest.kt | 13 ++ .../api/v2/interfaces/EdziennikCallback.kt | 17 ++ .../api/v2/interfaces/EdziennikInterface.kt | 10 ++ .../api/v2/interfaces/EndpointCallback.kt | 18 ++ .../api/v2/interfaces/ILoginMethod.kt | 20 --- .../edziennik/api/v2/librus/Librus.kt | 14 +- .../edziennik/api/v2/librus/LibrusOld.kt | 4 +- .../edziennik/api/v2/librus/LibrusTest.kt | 14 +- .../api/v2/librus/login/LoginLibrus.kt | 5 +- .../api/v2/librus/login/LoginLibrusApi.kt | 11 +- .../v2/librus/login/LoginLibrusMessages.kt | 3 +- .../api/v2/librus/login/LoginLibrusPortal.kt | 85 ++++++---- .../v2/librus/login/LoginLibrusSynergia.kt | 8 +- .../v2/librus/login/SynergiaTokenExtractor.kt | 124 +++++++------- .../edziennik/api/v2/models/ApiError.kt | 38 +++++ .../edziennik/api/v2/models/ApiLoginResult.kt | 6 - .../edziennik/api/v2/models/ApiTask.kt | 9 + .../edziennik/api/v2/models/Data.kt | 31 +++- .../edziennik/api/v2/models/Endpoint.kt | 17 +- .../edziennik/api/v2/models/Feature.kt | 11 -- .../edziennik/api/v2/models/Features.kt | 13 -- .../api/v2/models/FirstLoginResult.kt | 6 - .../edziennik/api/v2/models/LoginMethod.kt | 28 +++- .../edziennik/datamodels/ProfileDao.java | 8 +- .../edziennik/dialogs/EventManualDialog.java | 2 +- .../luckynumber/WidgetLuckyNumber.java | 2 +- 51 files changed, 843 insertions(+), 265 deletions(-) rename app/src/main/java/pl/szczodrzynski/edziennik/api/interfaces/{EdziennikInterface.java => OldEdziennikInterface.java} (99%) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Features.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncErrorEvent.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncFinishedEvent.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProfileFinishedEvent.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProgressEvent.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncStartedEvent.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/MessageGetRequest.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileRequest.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncRequest.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncViewRequest.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikCallback.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EndpointCallback.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/ILoginMethod.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiLoginResult.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiTask.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Features.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/FirstLoginResult.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1fbc04ec..f5f02d8b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -218,6 +218,8 @@ android:name=".sync.SyncService" android:icon="@mipmap/ic_launcher" android:label="@string/sync_service" /> + + diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/App.java b/app/src/main/java/pl/szczodrzynski/edziennik/App.java index a1360f8d..2938bec2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/App.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/App.java @@ -19,7 +19,6 @@ import android.provider.Settings; import android.util.Base64; import android.util.Log; import android.util.Pair; -import android.widget.Toast; import com.chuckerteam.chucker.api.ChuckerCollector; import com.chuckerteam.chucker.api.ChuckerInterceptor; @@ -81,7 +80,6 @@ import pl.szczodrzynski.edziennik.datamodels.ProfileFull; import pl.szczodrzynski.edziennik.models.AppConfig; import pl.szczodrzynski.edziennik.network.NetworkUtils; import pl.szczodrzynski.edziennik.network.TLSSocketFactory; -import pl.szczodrzynski.edziennik.receivers.BootReceiver; import pl.szczodrzynski.edziennik.receivers.JobsCreator; import pl.szczodrzynski.edziennik.sync.SyncJob; import pl.szczodrzynski.edziennik.utils.PermissionChecker; @@ -617,7 +615,7 @@ public class App extends androidx.multidex.MultiDexApplication { } public ProfileFull profileGetOrNull(int id) { - return db.profileDao().getByIdNow(id); + return db.profileDao().getFullByIdNow(id); } public void profileLoadById(int id) { @@ -632,7 +630,7 @@ public class App extends androidx.multidex.MultiDexApplication { return; }*/ if (profile == null || profile.getId() != id) { - profile = db.profileDao().getByIdNow(id); + profile = db.profileDao().getFullByIdNow(id); /*if (profile == null) { profileLoadById(id); return; @@ -659,7 +657,7 @@ public class App extends androidx.multidex.MultiDexApplication { /*public void profileRemove(int id) { - Profile profile = db.profileDao().getByIdNow(id); + Profile profile = db.profileDao().getFullByIdNow(id); if (profile.id == profile.loginStoreId) { // this profile is the owner of the login store diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index a6a8ab76..ac4d5366 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -10,10 +10,13 @@ import androidx.core.app.ActivityCompat import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject +import im.wangchao.mhttp.Response import pl.szczodrzynski.edziennik.datamodels.Profile import pl.szczodrzynski.edziennik.datamodels.Teacher import pl.szczodrzynski.navlib.crc16 import pl.szczodrzynski.navlib.getColorFromRes +import java.text.SimpleDateFormat +import java.util.* fun List.byId(id: Long) = firstOrNull { it.id == id } @@ -117,4 +120,11 @@ fun Activity.isStoragePermissionGranted(): Boolean { } else { true } +} + +fun Response?.getUnixDate(): Long { + val rfcDate = this?.headers()?.get("date") ?: return currentTimeUnix() + val pattern = "EEE, dd MMM yyyy HH:mm:ss Z" + val format = SimpleDateFormat(pattern, Locale.ENGLISH) + return format.parse(rfcDate).time / 1000 } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt index 1316a91a..414c41f7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt @@ -41,7 +41,7 @@ import com.mikepenz.materialdrawer.model.interfaces.IProfile import pl.droidsonroids.gif.GifDrawable import pl.szczodrzynski.edziennik.App.APP_URL import pl.szczodrzynski.edziennik.api.AppError -import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.* +import pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.* import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding import pl.szczodrzynski.edziennik.datamodels.LoginStore diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.java b/app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.java index 8a2fe720..4f7fb527 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.java @@ -244,7 +244,7 @@ public class WidgetTimetable extends AppWidgetProvider { filterOutArchived(profileList); } else { - Profile profile = app.db.profileDao().getByIdNow(widgetConfig.profileId); + Profile profile = app.db.profileDao().getFullByIdNow(widgetConfig.profileId); if (profile != null) { profileList.add(profile); } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/Edziennik.java b/app/src/main/java/pl/szczodrzynski/edziennik/api/Edziennik.java index e3091ac9..f5125615 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/Edziennik.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/Edziennik.java @@ -43,7 +43,7 @@ import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.WidgetTimetable; -import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface; +import pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface; import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback; import pl.szczodrzynski.edziennik.datamodels.AnnouncementFull; import pl.szczodrzynski.edziennik.datamodels.Attendance; @@ -81,16 +81,16 @@ import static pl.szczodrzynski.edziennik.api.AppError.CODE_PROFILE_ARCHIVED; import static pl.szczodrzynski.edziennik.api.AppError.CODE_PROFILE_NOT_FOUND; import static pl.szczodrzynski.edziennik.api.AppError.stringErrorCode; import static pl.szczodrzynski.edziennik.api.AppError.stringErrorType; -import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_AGENDA; -import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_ALL; -import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_ANNOUNCEMENTS; -import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_ATTENDANCES; -import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_GRADES; -import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_HOMEWORKS; -import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_MESSAGES_INBOX; -import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_MESSAGES_OUTBOX; -import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_NOTICES; -import static pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface.FEATURE_TIMETABLE; +import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_AGENDA; +import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_ALL; +import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_ANNOUNCEMENTS; +import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_ATTENDANCES; +import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_GRADES; +import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_HOMEWORKS; +import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_MESSAGES_INBOX; +import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_MESSAGES_OUTBOX; +import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_NOTICES; +import static pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface.FEATURE_TIMETABLE; import static pl.szczodrzynski.edziennik.datamodels.Event.TYPE_HOMEWORK; import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER1_FINAL; import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER1_PROPOSED; @@ -115,7 +115,7 @@ public class Edziennik { private static boolean registerEmpty; public static int oldLuckyNumber; - public static EdziennikInterface getApi(App app, int loginType) { + public static OldEdziennikInterface getApi(App app, int loginType) { switch (loginType) { default: case LOGIN_TYPE_MOBIDZIENNIK: @@ -625,7 +625,7 @@ public class Edziennik { * Used in services, login form and {@code guiSync} *

* May be ran on worker thread. - * {@link EdziennikInterface}.sync is ran always on worker thread. + * {@link OldEdziennikInterface}.sync is ran always on worker thread. * Every callback is ran on the UI thread. * * @param app @@ -676,7 +676,7 @@ public class Edziennik { } }; AsyncTask.execute(() -> { - ProfileFull profile = app.db.profileDao().getByIdNow(profileId); + ProfileFull profile = app.db.profileDao().getFullByIdNow(profileId); if (profile != null) { if (profile.getArchived()) { @@ -1130,7 +1130,7 @@ public class Edziennik { .show(); } public void removeProfile(int profileId) { - Profile profileObject = app.db.profileDao().getByIdNow(profileId); + Profile profileObject = app.db.profileDao().getFullByIdNow(profileId); if (profileObject == null) return; app.db.announcementDao().clear(profileId); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/Iuczniowie.java b/app/src/main/java/pl/szczodrzynski/edziennik/api/Iuczniowie.java index ca74dee7..17b6cc9c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/Iuczniowie.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/Iuczniowie.java @@ -35,7 +35,7 @@ import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.api.interfaces.AttachmentGetCallback; -import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface; +import pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface; import pl.szczodrzynski.edziennik.api.interfaces.LoginCallback; import pl.szczodrzynski.edziennik.api.interfaces.MessageGetCallback; import pl.szczodrzynski.edziennik.api.interfaces.RecipientListGetCallback; @@ -91,7 +91,7 @@ import static pl.szczodrzynski.edziennik.utils.Utils.crc32; import static pl.szczodrzynski.edziennik.utils.Utils.d; import static pl.szczodrzynski.edziennik.utils.Utils.getWordGradeValue; -public class Iuczniowie implements EdziennikInterface { +public class Iuczniowie implements OldEdziennikInterface { public Iuczniowie(App app) { this.app = app; } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java b/app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java index 4a552dd8..0199f8f0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/Librus.java @@ -46,7 +46,7 @@ import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.api.interfaces.AttachmentGetCallback; -import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface; +import pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface; import pl.szczodrzynski.edziennik.api.interfaces.LoginCallback; import pl.szczodrzynski.edziennik.api.interfaces.MessageGetCallback; import pl.szczodrzynski.edziennik.api.interfaces.RecipientListGetCallback; @@ -131,7 +131,7 @@ import static pl.szczodrzynski.edziennik.utils.Utils.d; import static pl.szczodrzynski.edziennik.utils.Utils.getGradeValue; import static pl.szczodrzynski.edziennik.utils.Utils.strToInt; -public class Librus implements EdziennikInterface { +public class Librus implements OldEdziennikInterface { public Librus(App app) { this.app = app; } @@ -2537,7 +2537,7 @@ public class Librus implements EdziennikInterface { && (el = obj.get("Id")) != null) { type = el.getAsInt(); } - /*EventType typeObject = app.db.eventTypeDao().getByIdNow(profileId, type); + /*EventType typeObject = app.db.eventTypeDao().getFullByIdNow(profileId, type); if (typeObject == null) { getCustomTypes = true; }*/ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/Mobidziennik.java b/app/src/main/java/pl/szczodrzynski/edziennik/api/Mobidziennik.java index 71c200b8..b931ea22 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/Mobidziennik.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/Mobidziennik.java @@ -41,7 +41,7 @@ import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.api.interfaces.AttachmentGetCallback; -import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface; +import pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface; import pl.szczodrzynski.edziennik.api.interfaces.LoginCallback; import pl.szczodrzynski.edziennik.api.interfaces.MessageGetCallback; import pl.szczodrzynski.edziennik.api.interfaces.RecipientListGetCallback; @@ -99,7 +99,7 @@ import static pl.szczodrzynski.edziennik.utils.Utils.d; import static pl.szczodrzynski.edziennik.utils.Utils.monthFromName; import static pl.szczodrzynski.edziennik.utils.Utils.strToInt; -public class Mobidziennik implements EdziennikInterface { +public class Mobidziennik implements OldEdziennikInterface { public Mobidziennik(App app) { this.app = app; } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/Vulcan.java b/app/src/main/java/pl/szczodrzynski/edziennik/api/Vulcan.java index 32705060..a4838253 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/Vulcan.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/Vulcan.java @@ -36,7 +36,7 @@ import okio.Buffer; import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.api.interfaces.AttachmentGetCallback; -import pl.szczodrzynski.edziennik.api.interfaces.EdziennikInterface; +import pl.szczodrzynski.edziennik.api.interfaces.OldEdziennikInterface; import pl.szczodrzynski.edziennik.api.interfaces.LoginCallback; import pl.szczodrzynski.edziennik.api.interfaces.MessageGetCallback; import pl.szczodrzynski.edziennik.api.interfaces.RecipientListGetCallback; @@ -93,7 +93,7 @@ import static pl.szczodrzynski.edziennik.utils.Utils.getGradeValue; import static pl.szczodrzynski.edziennik.utils.Utils.getVulcanGradeColor; import static pl.szczodrzynski.edziennik.utils.Utils.intToStr; -public class Vulcan implements EdziennikInterface { +public class Vulcan implements OldEdziennikInterface { public Vulcan(App app) { this.app = app; } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/interfaces/EdziennikInterface.java b/app/src/main/java/pl/szczodrzynski/edziennik/api/interfaces/OldEdziennikInterface.java similarity index 99% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/interfaces/EdziennikInterface.java rename to app/src/main/java/pl/szczodrzynski/edziennik/api/interfaces/OldEdziennikInterface.java index d7bcde5f..47014ad1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/interfaces/EdziennikInterface.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/interfaces/OldEdziennikInterface.java @@ -14,7 +14,7 @@ import pl.szczodrzynski.edziennik.datamodels.ProfileFull; import pl.szczodrzynski.edziennik.messages.MessagesComposeInfo; import pl.szczodrzynski.edziennik.models.Endpoint; -public interface EdziennikInterface { +public interface OldEdziennikInterface { /** * Sync all Edziennik data. diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt new file mode 100644 index 00000000..97732a36 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt @@ -0,0 +1,158 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-28. + */ + +package pl.szczodrzynski.edziennik.api.v2 + +import android.app.Service +import android.content.Intent +import android.os.IBinder +import android.util.Log +import androidx.core.app.NotificationCompat +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.events.requests.MessageGetRequest +import pl.szczodrzynski.edziennik.api.v2.events.SyncProgressEvent +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncViewRequest +import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback +import pl.szczodrzynski.edziennik.api.v2.librus.Librus +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.api.v2.models.ApiTask +import pl.szczodrzynski.edziennik.datamodels.LoginStore +import pl.szczodrzynski.edziennik.datamodels.Profile +import kotlin.math.min + +class ApiService : Service() { + companion object { + const val TAG = "ApiService" + const val NOTIFICATION_API_CHANNEL_ID = "pl.szczodrzynski.edziennik.GET_DATA" + } + + private val app by lazy { applicationContext as App } + + private val taskQueue = mutableListOf() + private val errorList = mutableListOf() + + private var taskRunning = false + private var taskRunningId = -1 + private var taskMaximumId = 0 + + private var taskProfileId = -1 + private var taskProfileName: String? = null + private var taskProgress = 0 + private var taskProgressRes: Int? = null + + private val taskCallback = object : EdziennikCallback { + override fun onCompleted() { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun onError(apiError: ApiError) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun onProgress(step: Int) { + taskProgress += step + taskProgress = min(100, taskProgress) + EventBus.getDefault().post(SyncProgressEvent(taskProfileId, taskProfileName, taskProgress, taskProgressRes)) + } + + override fun onStartProgress(stringRes: Int) { + + } + } + + private fun sync() { + if (taskRunning) + return + if (taskQueue.size <= 0) + return // TODO stopSelf() or sth + + val task = taskQueue.removeAt(0) + taskRunning = true + taskRunningId = task.taskId + + // get the requested profile and login store + val profile: Profile? = app.db.profileDao().getByIdNow(task.profileId) + if (profile == null || !profile.syncEnabled) { + return + } + val loginStore: LoginStore? = app.db.loginStoreDao().getByIdNow(profile.loginStoreId) + if (loginStore == null) { + return + } + // save the profile ID and name as the current task's + taskProfileId = profile.id + taskProfileName = profile.name + taskProgress = 0 + taskProgressRes = null + + + val edziennikInterface = when (loginStore.type) { + LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback) + else -> null + } + if (edziennikInterface == null) { + return + } + + when (task) { + is SyncProfileRequest -> edziennikInterface.sync(task.featureIds ?: Features.getAllIds()) + is SyncViewRequest -> edziennikInterface.sync(Features.getIdsByView(task.targetId)) + is MessageGetRequest -> edziennikInterface.getMessage(task.messageId) + } + } + + + @Subscribe(threadMode = ThreadMode.ASYNC) + fun onSyncRequest(syncRequest: SyncRequest) { + app.db.profileDao().idsForSyncNow.forEach { id -> + taskQueue += SyncProfileRequest(id, null) + } + sync() + } + + @Subscribe(threadMode = ThreadMode.ASYNC) + fun onSyncProfileRequest(syncProfileRequest: SyncProfileRequest) { + Log.d(TAG, syncProfileRequest.toString()) + taskQueue += syncProfileRequest + sync() + } + + @Subscribe(threadMode = ThreadMode.ASYNC) + fun onMessageGetRequest(messageGetRequest: MessageGetRequest) { + Log.d(TAG, messageGetRequest.toString()) + taskQueue += messageGetRequest + sync() + } + + private val notification by lazy { + NotificationCompat.Builder(this, NOTIFICATION_API_CHANNEL_ID) + .setContentTitle("API") + .setContentText("API is running") + .setSmallIcon(R.drawable.ic_notification) + .build() + } + + override fun onCreate() { + EventBus.getDefault().register(this) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + startForeground(1, notification) + return START_NOT_STICKY + } + + override fun onDestroy() { + EventBus.getDefault().unregister(this) + } + + override fun onBind(intent: Intent?): IBinder? { + return null + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index b80d8494..4992b519 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -4,18 +4,6 @@ package pl.szczodrzynski.edziennik.api.v2 -internal const val FEATURE_ANY = -1 -const val FEATURE_ALL = 0 -const val FEATURE_TIMETABLE = 1 -const val FEATURE_AGENDA = 2 -const val FEATURE_GRADES = 3 -const val FEATURE_HOMEWORKS = 4 -const val FEATURE_NOTICES = 5 -const val FEATURE_ATTENDANCES = 6 -const val FEATURE_MESSAGES_INBOX = 7 -const val FEATURE_MESSAGES_OUTBOX = 8 -const val FEATURE_ANNOUNCEMENTS = 9 - const val LIBRUS_USER_AGENT = "Dalvik/2.1.0 Android LibrusMobileApp" const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0" const val LIBRUS_CLIENT_ID = "wmSyUMo8llDAs4y9tJVYY92oyZ6h4lAt7KCuy0Gv" @@ -42,7 +30,7 @@ const val LIBRUS_JST_DEMO_CODE = "68656A21" const val LIBRUS_JST_DEMO_PIN = "1290" /** https://synergia.librus.pl/loguj/token/TOKEN/przenies */ -const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/token/TOKEN/przenies/" +const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/token/TOKEN/przenies" const val LIBRUS_MESSAGES_URL = "https://wiadomosci.librus.pl/module/" const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action=" \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt index 8def0345..940fbf57 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt @@ -4,20 +4,130 @@ package pl.szczodrzynski.edziennik.api.v2 -import android.util.Log -import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApiGrades -import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApiMe -import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergiaGrades import pl.szczodrzynski.edziennik.api.v2.models.Endpoint -const val ENDPOINT_LIBRUS_API_ME = 0 -const val ENDPOINT_LIBRUS_API_GRADES = 0 -const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 0 +const val ENDPOINT_LIBRUS_API_ME = 101 +const val ENDPOINT_LIBRUS_API_SCHOOLS = 102 +const val ENDPOINT_LIBRUS_API_CLASSES = 103 +const val ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES = 104 +const val ENDPOINT_LIBRUS_API_UNITS = 105 +const val ENDPOINT_LIBRUS_API_USERS = 106 +const val ENDPOINT_LIBRUS_API_SUBJECTS = 107 +const val ENDPOINT_LIBRUS_API_CLASSROOMS = 108 +const val ENDPOINT_LIBRUS_API_TIMETABLES = 109 +const val ENDPOINT_LIBRUS_API_SUBSTITUTIONS = 110 +const val ENDPOINT_LIBRUS_API_NORMAL_GC = 111 +const val ENDPOINT_LIBRUS_API_POINT_GC = 112 +const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC = 113 +const val ENDPOINT_LIBRUS_API_TEXT_GC = 114 +const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC = 115 +const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GC = 116 +const val ENDPOINT_LIBRUS_API_NORMAL_GRADES = 117 +const val ENDPOINT_LIBRUS_API_POINT_GRADES = 118 +const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES = 119 +const val ENDPOINT_LIBRUS_API_TEXT_GRADES = 120 +const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES = 121 +const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES = 122 +const val ENDPOINT_LIBRUS_API_EVENTS = 123 +const val ENDPOINT_LIBRUS_API_EVENT_TYPES = 124 +const val ENDPOINT_LIBRUS_API_HOMEWORK = 125 +const val ENDPOINT_LIBRUS_API_LUCKY_NUMBER = 126 +const val ENDPOINT_LIBRUS_API_NOTICES = 127 +const val ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES = 128 +const val ENDPOINT_LIBRUS_API_ATTENDANCE = 129 +const val ENDPOINT_LIBRUS_API_ANNOUNCEMENTS = 130 +const val ENDPOINT_LIBRUS_API_PT_MEETINGS = 131 +const val ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS = 132 +const val ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS = 133 +const val ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS = 134 +const val ENDPOINT_LIBRUS_SYNERGIA_INFO = 201 +const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 202 +const val ENDPOINT_LIBRUS_MESSAGES_RECEIVED = 301 +const val ENDPOINT_LIBRUS_MESSAGES_SENT = 302 +const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 303 +const val ENDPOINT_LIBRUS_MESSAGES_RECEIVERS = 304 +const val ENDPOINT_LIBRUS_MESSAGES_GET = 304 val endpoints = listOf( - Endpoint(LOGIN_TYPE_LIBRUS, ENDPOINT_LIBRUS_API_ME, null, LibrusApiMe::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_API}, - Endpoint(LOGIN_TYPE_LIBRUS, 1, listOf(), LibrusSynergiaGrades::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_SYNERGIA }, - Endpoint(LOGIN_TYPE_LIBRUS, 1, listOf(), LibrusApiGrades::class.java) { _, _ -> LOGIN_METHOD_LIBRUS_API } + + // LIBRUS: API + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TIMETABLE, listOf( + ENDPOINT_LIBRUS_API_TIMETABLES, + ENDPOINT_LIBRUS_API_SUBSTITUTIONS + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_AGENDA, listOf( + ENDPOINT_LIBRUS_API_EVENTS, + ENDPOINT_LIBRUS_API_EVENT_TYPES, + ENDPOINT_LIBRUS_API_PT_MEETINGS, + ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS, + ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS, + ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( + ENDPOINT_LIBRUS_API_NORMAL_GC, + ENDPOINT_LIBRUS_API_POINT_GC, + ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC, + ENDPOINT_LIBRUS_API_TEXT_GC, + ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC, + ENDPOINT_LIBRUS_API_BEHAVIOUR_GC, + ENDPOINT_LIBRUS_API_NORMAL_GRADES, + ENDPOINT_LIBRUS_API_POINT_GRADES, + ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES, + ENDPOINT_LIBRUS_API_TEXT_GRADES, + ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES, + ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf( + ENDPOINT_LIBRUS_API_HOMEWORK + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_NOTICES, listOf( + ENDPOINT_LIBRUS_API_NOTICES + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_ATTENDANCES, listOf( + ENDPOINT_LIBRUS_API_ATTENDANCE, + ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_ANNOUNCEMENTS, listOf( + ENDPOINT_LIBRUS_API_ANNOUNCEMENTS + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf( + ENDPOINT_LIBRUS_API_ME + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SCHOOL_INFO, listOf( + ENDPOINT_LIBRUS_API_SCHOOLS, + ENDPOINT_LIBRUS_API_UNITS + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_CLASS_INFO, listOf( + ENDPOINT_LIBRUS_API_CLASSES + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TEAM_INFO, listOf( + ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_LUCKY_NUMBER, listOf( + ENDPOINT_LIBRUS_API_LUCKY_NUMBER + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TEACHERS, listOf( + ENDPOINT_LIBRUS_API_USERS + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SUBJECTS, listOf( + ENDPOINT_LIBRUS_API_SUBJECTS + ), LOGIN_METHOD_LIBRUS_API), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_CLASSROOMS, listOf( + ENDPOINT_LIBRUS_API_CLASSROOMS + ), LOGIN_METHOD_LIBRUS_API), + + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf( + ENDPOINT_LIBRUS_SYNERGIA_INFO + ), LOGIN_METHOD_LIBRUS_SYNERGIA), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_NUMBER, listOf( + ENDPOINT_LIBRUS_SYNERGIA_INFO + ), LOGIN_METHOD_LIBRUS_SYNERGIA), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( + ENDPOINT_LIBRUS_SYNERGIA_GRADES + ), LOGIN_METHOD_LIBRUS_SYNERGIA), + + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_INBOX, listOf(ENDPOINT_LIBRUS_MESSAGES_RECEIVED), LOGIN_METHOD_LIBRUS_MESSAGES), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_OUTBOX, listOf(ENDPOINT_LIBRUS_MESSAGES_SENT), LOGIN_METHOD_LIBRUS_MESSAGES) ) /* diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index e28d1afe..f352e2a9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -59,11 +59,6 @@ const val ERROR_LOGIN_LIBRUS_API_OTHER = 131 const val ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING = 132 const val ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED = 133 const val ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR = 134 -const val ERROR_LOGIN_LIBRUS_PORTAL_TOKEN_ERROR = 135 -const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED = 136 -const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_410 = 137 -const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND = 138 -const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_OTHER = 139 const val ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN_MISSING = 139 const val ERROR_LIBRUS_API_TOKEN_EXPIRED = 140 const val ERROR_LIBRUS_API_INSUFFICIENT_SCOPES = 141 @@ -83,8 +78,22 @@ const val ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID = 154 const val ERROR_LIBRUS_MESSAGES_ACCESS_DENIED = 155 const val ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED = 156 const val ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID = 157 +const val ERROR_LIBRUS_PORTAL_TOKEN_EXPIRED = 158 +const val ERROR_LIBRUS_PORTAL_API_DISABLED = 159 +const val ERROR_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED = 160 +const val ERROR_LIBRUS_PORTAL_OTHER = 161 +const val ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND = 162 +const val ERROR_LOGIN_LIBRUS_PORTAL_OTHER = 163 +const val ERROR_LOGIN_LIBRUS_PORTAL_CODE_EXPIRED = 164 +const val ERROR_LOGIN_LIBRUS_PORTAL_CODE_REVOKED = 165 +const val ERROR_LOGIN_LIBRUS_PORTAL_NO_CLIENT_ID = 166 +const val ERROR_LOGIN_LIBRUS_PORTAL_NO_CODE = 167 +const val ERROR_LOGIN_LIBRUS_PORTAL_NO_REFRESH = 168 +const val ERROR_LOGIN_LIBRUS_PORTAL_NO_REDIRECT = 169 +const val ERROR_LOGIN_LIBRUS_PORTAL_UNSUPPORTED_GRANT = 170 +const val ERROR_LOGIN_LIBRUS_PORTAL_INVALID_CLIENT_ID = 171 const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901 const val EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN = 902 -const val EXCEPTION_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN = 903 +const val EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN = 903 const val EXCEPTION_LIBRUS_API_REQUEST = 904 \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Features.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Features.kt new file mode 100644 index 00000000..5f4bab44 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Features.kt @@ -0,0 +1,87 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-29. + */ + +package pl.szczodrzynski.edziennik.api.v2 + +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ATTENDANCES +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORKS +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_NOTICES +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE +import pl.szczodrzynski.edziennik.datamodels.Message.TYPE_RECEIVED +import pl.szczodrzynski.edziennik.datamodels.Message.TYPE_SENT +import pl.szczodrzynski.edziennik.messages.MessagesFragment +import pl.szczodrzynski.edziennik.messages.MessagesListFragment + +const val FEATURE_ALL = 0 +const val FEATURE_TIMETABLE = 1 +const val FEATURE_AGENDA = 2 +const val FEATURE_GRADES = 3 +const val FEATURE_HOMEWORK = 4 +const val FEATURE_NOTICES = 5 +const val FEATURE_ATTENDANCES = 6 +const val FEATURE_MESSAGES_INBOX = 7 +const val FEATURE_MESSAGES_OUTBOX = 8 +const val FEATURE_ANNOUNCEMENTS = 9 + +const val FEATURE_STUDENT_INFO = 101 +const val FEATURE_STUDENT_NUMBER = 109 +const val FEATURE_SCHOOL_INFO = 102 +const val FEATURE_CLASS_INFO = 103 +const val FEATURE_TEAM_INFO = 104 +const val FEATURE_LUCKY_NUMBER = 105 +const val FEATURE_TEACHERS = 106 +const val FEATURE_SUBJECTS = 107 +const val FEATURE_CLASSROOMS = 108 + +const val FEATURE_MESSAGE_GET = 201 + +object Features { + private fun getAllNecessary(): List = listOf( + FEATURE_STUDENT_INFO, + FEATURE_STUDENT_NUMBER, + FEATURE_SCHOOL_INFO, + FEATURE_CLASS_INFO, + FEATURE_TEAM_INFO, + FEATURE_LUCKY_NUMBER, + FEATURE_TEACHERS, + FEATURE_SUBJECTS, + FEATURE_CLASSROOMS) + + private fun getAllFeatures(): List = listOf( + FEATURE_TIMETABLE, + FEATURE_AGENDA, + FEATURE_GRADES, + FEATURE_HOMEWORK, + FEATURE_NOTICES, + FEATURE_ATTENDANCES, + FEATURE_MESSAGES_INBOX, + FEATURE_MESSAGES_OUTBOX, + FEATURE_ANNOUNCEMENTS) + + fun getAllIds(): List = getAllFeatures() + getAllNecessary() + + fun getIdsByView(targetId: Int): List { + return when (targetId) { + DRAWER_ITEM_HOME -> getAllFeatures() + DRAWER_ITEM_TIMETABLE -> listOf(FEATURE_TIMETABLE) + DRAWER_ITEM_AGENDA -> listOf(FEATURE_AGENDA) + DRAWER_ITEM_GRADES -> listOf(FEATURE_GRADES) + DRAWER_ITEM_MESSAGES -> when (MessagesFragment.pageSelection) { + TYPE_RECEIVED -> listOf(FEATURE_MESSAGES_INBOX) + TYPE_SENT -> listOf(FEATURE_MESSAGES_OUTBOX) + else -> listOf(FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_OUTBOX) + } + DRAWER_ITEM_HOMEWORKS -> listOf(FEATURE_HOMEWORK) + DRAWER_ITEM_NOTICES -> listOf(FEATURE_NOTICES) + DRAWER_ITEM_ATTENDANCES -> listOf(FEATURE_ATTENDANCES) + DRAWER_ITEM_ANNOUNCEMENTS -> listOf(FEATURE_ANNOUNCEMENTS) + else -> getAllFeatures() + } + getAllNecessary() + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt index 3e5c1fe6..1efc3bd6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt @@ -10,6 +10,8 @@ import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusMessages import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusSynergia import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod +val SYNERGIA_API_ENABLED = "true".toBoolean() + const val LOGIN_TYPE_MOBIDZIENNIK = 1 const val LOGIN_TYPE_LIBRUS = 2 const val LOGIN_TYPE_IUCZNIOWIE = 3 @@ -37,14 +39,29 @@ const val LOGIN_METHOD_VULCAN_WEB = 100 const val LOGIN_METHOD_VULCAN_API = 200 val librusLoginMethods = listOf( - LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_PORTAL, null, LoginLibrusPortal::class.java) { _, _ -> LOGIN_METHOD_NOT_NEEDED }, - LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_API, null, LoginLibrusApi::class.java) { _, loginStore -> - if (loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) LOGIN_METHOD_LIBRUS_PORTAL else LOGIN_METHOD_NOT_NEEDED - }, - LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, listOf(FEATURE_GRADES, FEATURE_HOMEWORKS, FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_OUTBOX), LoginLibrusSynergia::class.java) { profile, _ -> - if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED - }, - LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, listOf(FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_OUTBOX), LoginLibrusMessages::class.java) { profile, _ -> - if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED - } + LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_PORTAL, LoginLibrusPortal::class.java) + .withIsPossible { _, loginStore -> + loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL + } + .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }, + + LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_API, LoginLibrusApi::class.java) + .withIsPossible { _, loginStore -> + loginStore.mode != LOGIN_MODE_LIBRUS_SYNERGIA || SYNERGIA_API_ENABLED + } + .withRequiredLoginMethod { _, loginStore -> + if (loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) LOGIN_METHOD_LIBRUS_PORTAL else LOGIN_METHOD_NOT_NEEDED + }, + + LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, LoginLibrusSynergia::class.java) + .withIsPossible { _, _ -> true } + .withRequiredLoginMethod { profile, _ -> + if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED + }, + + LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, LoginLibrusMessages::class.java) + .withIsPossible { _, _ -> true } + .withRequiredLoginMethod { profile, _ -> + if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED + } ) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncErrorEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncErrorEvent.kt new file mode 100644 index 00000000..27dcff2c --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncErrorEvent.kt @@ -0,0 +1,9 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.events + +import pl.szczodrzynski.edziennik.api.v2.models.ApiError + +class SyncErrorEvent(val error: ApiError) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncFinishedEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncFinishedEvent.kt new file mode 100644 index 00000000..14b6f682 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncFinishedEvent.kt @@ -0,0 +1,7 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.events + +class SyncFinishedEvent \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProfileFinishedEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProfileFinishedEvent.kt new file mode 100644 index 00000000..087b97f6 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProfileFinishedEvent.kt @@ -0,0 +1,7 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.events + +class SyncProfileFinishedEvent(val profileId: Int) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProgressEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProgressEvent.kt new file mode 100644 index 00000000..83cf57d1 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProgressEvent.kt @@ -0,0 +1,7 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.events + +class SyncProgressEvent(val profileId: Int, val profileName: String?, val progress: Int, val progressRes: Int?) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncStartedEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncStartedEvent.kt new file mode 100644 index 00000000..86e5bb9d --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncStartedEvent.kt @@ -0,0 +1,7 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.events + +class SyncStartedEvent(val profileId: Int) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/MessageGetRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/MessageGetRequest.kt new file mode 100644 index 00000000..50527290 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/MessageGetRequest.kt @@ -0,0 +1,13 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.events.requests + +import pl.szczodrzynski.edziennik.api.v2.models.ApiTask + +data class MessageGetRequest(override val profileId: Int, val messageId: Int) : ApiTask(profileId) { + override fun toString(): String { + return "MessageGetRequest(profileId=$profileId, messageId=$messageId)" + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileRequest.kt new file mode 100644 index 00000000..75e21406 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileRequest.kt @@ -0,0 +1,13 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.events.requests + +import pl.szczodrzynski.edziennik.api.v2.models.ApiTask + +data class SyncProfileRequest(override val profileId: Int, val featureIds: List?) : ApiTask(profileId) { + override fun toString(): String { + return "SyncProfileRequest(profileId=$profileId, featureIds=$featureIds)" + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncRequest.kt new file mode 100644 index 00000000..4570d3a4 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncRequest.kt @@ -0,0 +1,7 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.events.requests + +class SyncRequest() \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncViewRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncViewRequest.kt new file mode 100644 index 00000000..316850fa --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncViewRequest.kt @@ -0,0 +1,13 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-29. + */ + +package pl.szczodrzynski.edziennik.api.v2.events.requests + +import pl.szczodrzynski.edziennik.api.v2.models.ApiTask + +class SyncViewRequest(override val profileId: Int, val targetId: Int) : ApiTask(profileId) { + override fun toString(): String { + return "SyncViewRequest(profileId=$profileId, targetId=$targetId)" + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikCallback.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikCallback.kt new file mode 100644 index 00000000..4e172a9b --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikCallback.kt @@ -0,0 +1,17 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-29. + */ + +package pl.szczodrzynski.edziennik.api.v2.interfaces + +import pl.szczodrzynski.edziennik.api.v2.models.Endpoint +import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod + +/** + * A callback passed only to an e-register class. + * All [Endpoint]s and [LoginMethod]s receive this callback, + * but may only use [EndpointCallback]'s methods. + */ +interface EdziennikCallback : EndpointCallback { + fun onCompleted() +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt new file mode 100644 index 00000000..8265de7e --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt @@ -0,0 +1,10 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-29. + */ + +package pl.szczodrzynski.edziennik.api.v2.interfaces + +interface EdziennikInterface { + fun sync(featureIds: List) + fun getMessage(messageId: Int) +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EndpointCallback.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EndpointCallback.kt new file mode 100644 index 00000000..5c2717d2 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EndpointCallback.kt @@ -0,0 +1,18 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-29. + */ + +package pl.szczodrzynski.edziennik.api.v2.interfaces + +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.api.v2.models.Endpoint +import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod + +/** + * A callback passed to all [Endpoint]s and [LoginMethod]s + */ +interface EndpointCallback { + fun onError(apiError: ApiError) + fun onProgress(step: Int) + fun onStartProgress(stringRes: Int) +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/ILoginMethod.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/ILoginMethod.kt deleted file mode 100644 index c1834b29..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/ILoginMethod.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2019-9-20. - */ - -package pl.szczodrzynski.edziennik.api.v2.interfaces - -import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback -import pl.szczodrzynski.edziennik.datamodels.LoginStore -import pl.szczodrzynski.edziennik.datamodels.Profile - -abstract class ILoginMethod( - val app: App, - val profile: Profile?, - val loginStore: LoginStore, - val callback: ProgressCallback, - val onSuccess: () -> Unit -) { - -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index b00d7901..6289f6b2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -9,22 +9,30 @@ import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.AppError import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 +import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback +import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus -import pl.szczodrzynski.edziennik.api.v2.models.Data import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile import pl.szczodrzynski.edziennik.datamodels.ProfileFull -class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: SyncCallback) { +class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { val internalErrorList = mutableListOf() - lateinit var data: DataLibrus + val data: DataLibrus init { data = DataLibrus(app, profile, loginStore).apply { callback = wrapCallback(this@Librus.callback) } + data.satisfyLoginMethods() + } + override fun sync(featureIds: List) { + + } + + override fun getMessage(messageId: Int) { } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusOld.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusOld.kt index e04437bf..f801e97b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusOld.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusOld.kt @@ -7,7 +7,6 @@ import pl.szczodrzynski.edziennik.api.interfaces.* import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginLibrus import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.FirstLoginSynergia -import pl.szczodrzynski.edziennik.api.v2.librus.login.SynergiaTokenExtractor import pl.szczodrzynski.edziennik.api.v2.models.Data import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.MessageFull @@ -15,10 +14,9 @@ import pl.szczodrzynski.edziennik.datamodels.Profile import pl.szczodrzynski.edziennik.datamodels.ProfileFull import pl.szczodrzynski.edziennik.messages.MessagesComposeInfo import pl.szczodrzynski.edziennik.models.Endpoint -import pl.szczodrzynski.edziennik.utils.Utils.d import java.lang.Exception -class LibrusOld(val app: App, val profile: Profile?, val loginStore: LoginStore) : EdziennikInterface { +class LibrusOld(val app: App, val profile: Profile?, val loginStore: LoginStore) : OldEdziennikInterface { private val TAG = "librus.Librus" lateinit var syncCallback: SyncCallback diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt index c13369f4..a01bc4df 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt @@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus import android.content.Context +import android.content.Intent import com.google.gson.JsonObject import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.AppError @@ -30,21 +31,24 @@ class LibrusTest(val app: App) { putStudentData("accountLogin", "1234567") - putStudentData("accountToken", "token") - putStudentData("accountTokenTime", 1569523077) + //putStudentData("accountToken", "token") + //putStudentData("accountTokenTime", 1569458277) } val loginStore = LoginStore(1, LOGIN_TYPE_LIBRUS, JsonObject().apply { addProperty("email", "test@example.com") addProperty("password", "zaq1@WSX") - addProperty("accessToken", "token") - addProperty("refreshToken", "refresh") - addProperty("tokenExpiryTime", 1569523077) + //addProperty("accessToken", "token") + //addProperty("refreshToken", "refresh") + //addProperty("tokenExpiryTime", 1569523077) }).also { it.mode = LOGIN_MODE_LIBRUS_EMAIL } fun go() { + + app.startService(Intent(app, ApiService::class.java)) + val data = DataLibrus(app, profile, loginStore).apply { callback = object : ProgressCallback { override fun onProgress(progressStep: Int) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt index b370305d..e12d6713 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt @@ -19,10 +19,11 @@ class LoginLibrus(val data: DataLibrus, vararg loginMethodIds: Int, val onSucces init { for (loginMethodId in loginMethodIds) { - var requiredLoginMethod = loginMethodId + var requiredLoginMethod: Int? = loginMethodId while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) { librusLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod -> - loginMethodList.add(requiredLoginMethod) + if (requiredLoginMethod != null) + loginMethodList.add(requiredLoginMethod!!) requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt index ca8cf85e..d687fbdb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt @@ -9,14 +9,9 @@ import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response import im.wangchao.mhttp.body.MediaTypeUtils import im.wangchao.mhttp.callback.JsonCallbackHandler -import pl.szczodrzynski.edziennik.api.AppError -import pl.szczodrzynski.edziennik.api.AppError.* +import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus -import pl.szczodrzynski.edziennik.currentTimeUnix -import pl.szczodrzynski.edziennik.getInt -import pl.szczodrzynski.edziennik.getString -import pl.szczodrzynski.edziennik.isNotNullNorEmpty import java.net.HttpURLConnection.* class LoginLibrusApi { @@ -121,7 +116,7 @@ class LoginLibrusApi { data.error(TAG, ERROR_RESPONSE_EMPTY, response) return } - json.getString("error")?.let { error -> + if (response?.code() != 200) json.getString("error")?.let { error -> when (error) { "librus_captcha_needed" -> ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED "connection_problems" -> ERROR_LOGIN_LIBRUS_API_CONNECTION_PROBLEMS @@ -140,7 +135,7 @@ class LoginLibrusApi { try { data.apiAccessToken = json.getString("access_token") data.apiRefreshToken = json.getString("refresh_token") - data.apiTokenExpiryTime = currentTimeUnix() + json.getInt("expires_in", 86400) + data.apiTokenExpiryTime = response.getUnixDate() + json.getInt("expires_in", 86400) onSuccess() } catch (e: NullPointerException) { data.error(TAG, EXCEPTION_LOGIN_LIBRUS_API_TOKEN, response, e, json) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt index 3af49a01..17581b03 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt @@ -13,6 +13,7 @@ import okhttp3.internal.http.HttpDate import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus import pl.szczodrzynski.edziennik.currentTimeUnix +import pl.szczodrzynski.edziennik.getUnixDate class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { companion object { @@ -73,7 +74,7 @@ class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { return } data.messagesSessionId = sessionId - data.messagesSessionIdExpiryTime = currentTimeUnix() + 3600 /* 1h */ + data.messagesSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45min */ onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt index b3fbe09b..71bec763 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt @@ -121,7 +121,52 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { private var refreshTokenFailed = false private fun accessToken(code: String?, refreshToken: String?) { - data.callback.onActionStarted(R.string.sync_action_getting_token) + val onSuccess = { json: JsonObject, response: Response? -> + data.portalAccessToken = json.getString("access_token") + data.portalRefreshToken = json.getString("refresh_token") + data.portalTokenExpiryTime = response.getUnixDate() + json.getInt("expires_in", 86400) + onSuccess() + } + + val callback = object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response?) { + if (json == null) { + data.error(TAG, ERROR_RESPONSE_EMPTY, response) + return + } + val error = if (response?.code() == 200) null else + json.getString("hint") + error?.let { code -> + when (code) { + "Authorization code has expired" -> ERROR_LOGIN_LIBRUS_PORTAL_CODE_EXPIRED + "Authorization code has been revoked" -> ERROR_LOGIN_LIBRUS_PORTAL_CODE_REVOKED + "Check the `client_id` parameter" -> ERROR_LOGIN_LIBRUS_PORTAL_NO_CLIENT_ID + "Check the `code` parameter" -> ERROR_LOGIN_LIBRUS_PORTAL_NO_CODE + "Check the `refresh_token` parameter" -> ERROR_LOGIN_LIBRUS_PORTAL_NO_REFRESH + "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 + "invalid_client" -> ERROR_LOGIN_LIBRUS_PORTAL_INVALID_CLIENT_ID + else -> ERROR_LOGIN_LIBRUS_PORTAL_OTHER + } + }.let { errorCode -> + data.error(TAG, errorCode, apiResponse = json, response = response) + return + } + } + + try { + onSuccess(json, response) + } catch (e: NullPointerException) { + data.error(TAG, EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN, response, e, json) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + } + } + val params = ArrayList>() params.add(Pair("client_id", LIBRUS_CLIENT_ID)) if (code != null) { @@ -132,46 +177,14 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { params.add(Pair("grant_type", "refresh_token")) params.add(Pair("refresh_token", refreshToken)) } + Request.builder() .url(LIBRUS_TOKEN_URL) .userAgent(LIBRUS_USER_AGENT) .addParams(params) - .allowErrorCode(HTTP_UNAUTHORIZED) .post() - .callback(object : JsonCallbackHandler() { - override fun onSuccess(json: JsonObject?, response: Response) { - if (json == null) { - data.error(TAG, ERROR_RESPONSE_EMPTY, response) - return - } - json.getString("error")?.let { error -> - val hint = json.getString("hint", "") - //val message = json.getString("message", "") - if (!refreshTokenFailed && refreshToken != null && (hint == "Token has been revoked" || hint == "Token has expired")) { - c(TAG, "refreshing the token failed. Trying to log in again.") - refreshTokenFailed = true - authorize(LIBRUS_AUTHORIZE_URL) - return - } - data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_TOKEN_ERROR, response, apiResponse = json) - return - } - - try { - data.portalAccessToken = json.getString("access_token") - data.portalRefreshToken = json.getString("refresh_token") - data.portalTokenExpiryTime = currentTimeUnix() + json.getInt("expires_in", 86400) - onSuccess() - } catch (e: NullPointerException) { - data.error(TAG, EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN, response, e, json) - } - - } - - override fun onFailure(response: Response, throwable: Throwable) { - data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) - } - }) + .allowErrorCode(HTTP_UNAUTHORIZED) + .callback(callback) .build() .enqueue() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt index a78ee769..7b405304 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt @@ -15,6 +15,7 @@ import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus import pl.szczodrzynski.edziennik.currentTimeUnix import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.getUnixDate import pl.szczodrzynski.edziennik.isNotNullNorEmpty import java.lang.Exception import java.net.HttpURLConnection @@ -75,7 +76,10 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { data.error(TAG, ERROR_RESPONSE_EMPTY, response) return } - val error = json.getString("Code") ?: json.getString("Message") ?: response?.parserErrorBody + val error = if (response?.code() == 200) null else + json.getString("Code") ?: + json.getString("Message") ?: + response?.parserErrorBody error?.let { code -> when (code) { "TokenIsExpired" -> ERROR_LIBRUS_API_TOKEN_EXPIRED @@ -145,7 +149,7 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { return } data.synergiaSessionId = sessionId - data.synergiaSessionIdExpiryTime = currentTimeUnix() + 3600 /* 1h */ + data.synergiaSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45min */ onSuccess() } else { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt index f771afde..67a49ffb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt @@ -33,16 +33,80 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { } else { if (!synergiaAccount()) { - + data.error(TAG, ERROR_LOGIN_DATA_MISSING) } } }} + /** + * Get an Api token from the Portal account, using Portal API. + * If necessary, refreshes the token. + */ private fun synergiaAccount(): Boolean { val accountLogin = data.apiLogin ?: return false val accessToken = data.portalAccessToken ?: return false - data.callback.onActionStarted(R.string.sync_action_getting_account) - d(TAG, "Requesting " + (LIBRUS_ACCOUNT_URL + accountLogin)) + + val onSuccess = { json: JsonObject, response: Response? -> + // synergiaAccount is executed when a synergia token needs a refresh + val accountId = json.getInt("id") + val accountToken = json.getString("accessToken") + if (accountId == null || accountToken == null) { + data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN_MISSING, response, apiResponse = json) + } + else { + data.apiAccessToken = accountToken + data.apiTokenExpiryTime = response.getUnixDate() + 6 * 60 * 60 + + // TODO remove this + data.profile?.studentNameLong = json.getString("studentName") + val nameParts = json.getString("studentName")?.split(" ")?.toTypedArray() + data.profile?.studentNameShort = nameParts?.get(0) + " " + nameParts?.get(1)?.get(0) + + onSuccess() + } + } + + val callback = object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response?) { + if (json == null) { + data.error(TAG, ERROR_RESPONSE_EMPTY, response) + return + } + val error = if (response?.code() == 200) null else + json.getString("reason") ?: + json.getString("message") ?: + json.getString("hint") ?: + json.getString("Code") + error?.let { code -> + when (code) { + "requires_an_action" -> ERROR_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED + "Access token is invalid" -> ERROR_LIBRUS_PORTAL_TOKEN_EXPIRED + "ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED + "Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND + else -> ERROR_LIBRUS_PORTAL_OTHER + }.let { errorCode -> + data.error(TAG, errorCode, apiResponse = json, response = response) + return + } + } + if (response?.code() == HTTP_OK) { + try { + onSuccess(json, response) + } catch (e: NullPointerException) { + e.printStackTrace() + data.error(TAG, EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN, response, e, json) + } + + } else { + data.error(TAG, ERROR_REQUEST_FAILURE, response, apiResponse = json) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + } + } + Request.builder() .url(LIBRUS_ACCOUNT_URL + accountLogin) .userAgent(LIBRUS_USER_AGENT) @@ -53,59 +117,7 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { .allowErrorCode(HTTP_UNAUTHORIZED) .allowErrorCode(HTTP_BAD_REQUEST) .allowErrorCode(HTTP_GONE) - .callback(object : JsonCallbackHandler() { - override fun onSuccess(json: JsonObject?, response: Response) { - if (json == null) { - data.error(TAG, ERROR_RESPONSE_EMPTY, response) - return - } - if (response.code() == 410) { - val reason = json.get("reason") - if (reason != null && reason !is JsonNull && reason.asString == "requires_an_action") { - data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED, response, apiResponse = json) - return - } - data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_410, response, apiResponse = json) - return - } - if (json.get("message") != null) { - val message = json.get("message").asString - if (message == "Account not found") { - data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND, response, apiResponse = json) - return - } - data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_OTHER, response, apiResponse = json) - return - } - if (response.code() == HTTP_OK) { - try { - // synergiaAccount is executed when a synergia token needs a refresh - val accountId = json.getInt("id") - val accountToken = json.getString("accessToken") - if (accountId == null || accountToken == null) { - data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN_MISSING, response, apiResponse = json) - return - } - data.apiAccessToken = accountToken - data.apiTokenExpiryTime = currentTimeUnix() + 6*60*60 - data.profile?.studentNameLong = json.getString("studentName") - val nameParts = json.getString("studentName")?.split(" ")?.toTypedArray() - data.profile?.studentNameShort = nameParts?.get(0) + " " + nameParts?.get(1)?.get(0) - onSuccess() - } catch (e: NullPointerException) { - e.printStackTrace() - data.error(TAG, EXCEPTION_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN, response, e, json) - } - - } else { - data.error(TAG, ERROR_REQUEST_FAILURE, response, apiResponse = json) - } - } - - override fun onFailure(response: Response, throwable: Throwable) { - data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) - } - }) + .callback(callback) .build() .enqueue() return true diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt new file mode 100644 index 00000000..b83cf514 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.models + +import com.google.gson.JsonObject +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response + +class ApiError(val profileId: Int, val tag: String, val errorCode: Int) { + private var throwable: Throwable? = null + private var apiResponse: String? = null + private var request: Request? = null + private var response: Response? = null + + fun withThrowable(throwable: Throwable?): ApiError { + this.throwable = throwable + return this + } + fun withApiResponse(apiResponse: String?): ApiError { + this.apiResponse = apiResponse + return this + } + fun withApiResponse(apiResponse: JsonObject?): ApiError { + this.apiResponse = apiResponse?.toString() + return this + } + fun withRequest(request: Request?): ApiError { + this.request = request + return this + } + fun withResponse(response: Response?): ApiError { + this.response = response + this.request = response?.request() + return this + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiLoginResult.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiLoginResult.kt deleted file mode 100644 index 366f7a73..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiLoginResult.kt +++ /dev/null @@ -1,6 +0,0 @@ -package pl.szczodrzynski.edziennik.api.v2.models - -import pl.szczodrzynski.edziennik.api.AppError -import pl.szczodrzynski.edziennik.datamodels.LoginStore - -data class ApiLoginResult(val loginStore: LoginStore, val error: AppError?) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiTask.kt new file mode 100644 index 00000000..df289ea6 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiTask.kt @@ -0,0 +1,9 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.models + +open class ApiTask(open val profileId: Int) { + var taskId: Int = 0 +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 7608c756..024bad40 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -7,15 +7,24 @@ import com.google.gson.JsonObject import im.wangchao.mhttp.Response import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.AppError.* import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback +import pl.szczodrzynski.edziennik.api.v2.interfaces.EndpointCallback import pl.szczodrzynski.edziennik.datamodels.* import pl.szczodrzynski.edziennik.models.Date +import java.io.InterruptedIOException +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import javax.net.ssl.SSLException open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) { var fakeLogin = false - lateinit var callback: ProgressCallback + /** + * A callback passed to all [Endpoint]s and [LoginMethod]s + */ + lateinit var callback: EndpointCallback /** * A list of [LoginMethod]s *already fulfilled* during this sync. @@ -152,9 +161,25 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) } fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) { - callback.onError(null, AppError(tag, 999, errorCode, response, throwable, apiResponse)) + var code = when (throwable) { + is UnknownHostException, is SSLException, is InterruptedIOException -> CODE_NO_INTERNET + is SocketTimeoutException -> CODE_TIMEOUT + else -> when (response?.code()) { + 400, 401, 424, 500, 503, 404 -> CODE_MAINTENANCE + else -> errorCode + } + } + callback.onError(ApiError(profile?.id ?: -1, tag, code).withResponse(response).withThrowable(throwable).withApiResponse(apiResponse)) } fun error(tag: String, errorCode: Int, response: Response? = null, apiResponse: String? = null) { - callback.onError(null, AppError(tag, 999, errorCode, response, null, apiResponse)) + var code = when (null) { + is UnknownHostException, is SSLException, is InterruptedIOException -> CODE_NO_INTERNET + is SocketTimeoutException -> CODE_TIMEOUT + else -> when (response?.code()) { + 400, 401, 424, 500, 503, 404 -> CODE_MAINTENANCE + else -> errorCode + } + } + callback.onError(ApiError(profile?.id ?: -1, tag, code).withResponse(response).withApiResponse(apiResponse)) } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt index e2c6281b..55167232 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt @@ -7,21 +7,18 @@ import pl.szczodrzynski.edziennik.datamodels.Profile * A Endpoint descriptor class. * * The API runs appropriate endpoints in order to fulfill its - * [Feature] list. + * feature list. * An endpoint may have its [LoginMethod] dependencies which will be * satisfied by the API before the [endpointClass]'s constructor is invoked. * * @param loginType type of the e-register this endpoint handles - * @param endpointId a unique ID of this endpoint - * @param featureIds a [List] of [Feature]s (their IDs) this endpoint can download - * May be null if no strict feature set is associated with this method. - * @param endpointClass a [Class] which constructor will be invoked when a data download is needed - * @param requiredLoginMethod a lambda returning a required login method (which will be called before this). May differ depending on the [Profile] and/or [LoginStore]. + * @param featureId a feature ID + * @param endpointIds a [List] of [Endpoint]s that satisfy this feature ID + * @param requiredLoginMethod a required login method, which will have to be executed before this endpoint. */ class Endpoint( val loginType: Int, - val endpointId: Int, - val featureIds: List?, - val endpointClass: Class<*>, - val requiredLoginMethod: (profile: Profile?, loginStore: LoginStore) -> Int + val featureId: Int, + val endpointIds: List, + val requiredLoginMethod: Int ) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt deleted file mode 100644 index 3ffda1af..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt +++ /dev/null @@ -1,11 +0,0 @@ -package pl.szczodrzynski.edziennik.api.v2.models - - - -data class Feature(val featureId: Int, val loginOptions: Map>) { - - init { - - } - -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Features.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Features.kt deleted file mode 100644 index bf40bd80..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Features.kt +++ /dev/null @@ -1,13 +0,0 @@ -package pl.szczodrzynski.edziennik.api.v2.models - -import pl.szczodrzynski.edziennik.api.v2.* - -val Features = listOf( - Feature(FEATURE_TIMETABLE, mapOf( - LOGIN_TYPE_LIBRUS to listOf( - LOGIN_MODE_LIBRUS_EMAIL, - LOGIN_MODE_LIBRUS_SYNERGIA, - LOGIN_MODE_LIBRUS_JST - ) - )) -) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/FirstLoginResult.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/FirstLoginResult.kt deleted file mode 100644 index 362846fd..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/FirstLoginResult.kt +++ /dev/null @@ -1,6 +0,0 @@ -package pl.szczodrzynski.edziennik.api.v2.models - -import pl.szczodrzynski.edziennik.api.AppError -import pl.szczodrzynski.edziennik.datamodels.Profile - -data class FirstLoginResult(val profileList: ArrayList, val error: AppError?) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt index 3fda0eb1..af14a6ee 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/LoginMethod.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.models +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_NOT_NEEDED import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile @@ -16,15 +17,30 @@ import pl.szczodrzynski.edziennik.datamodels.Profile * * @param loginType type of the e-register this login method handles * @param loginMethodId a unique ID of this login method - * @param featureIds a [List] of [Feature]s (their IDs) this login method can provide access to - * May be null if no strict feature set is associated with this method. * @param loginMethodClass a [Class] which constructor will be invoked when a log in is needed - * @param requiredLoginMethod a lambda returning a required login method (which will be called before this). May differ depending on the [Profile] and/or [LoginStore]. + * @param requiredLoginMethod a required login method (which will be called before this). May differ depending on the [Profile] and/or [LoginStore]. */ class LoginMethod( val loginType: Int, val loginMethodId: Int, - val featureIds: List?, val loginMethodClass: Class<*>, - val requiredLoginMethod: (profile: Profile?, loginStore: LoginStore) -> Int -) \ No newline at end of file + private var mIsPossible: ((profile: Profile?, loginStore: LoginStore) -> Boolean)? = null, + private var mRequiredLoginMethod: ((profile: Profile?, loginStore: LoginStore) -> Int)? = null +) { + + fun withIsPossible(isPossible: (profile: Profile?, loginStore: LoginStore) -> Boolean): LoginMethod { + this.mIsPossible = isPossible + return this + } + fun withRequiredLoginMethod(requiredLoginMethod: (profile: Profile?, loginStore: LoginStore) -> Int): LoginMethod { + this.mRequiredLoginMethod = requiredLoginMethod + return this + } + + fun isPossible(profile: Profile?, loginStore: LoginStore): Boolean { + return mIsPossible?.invoke(profile, loginStore) ?: false + } + fun requiredLoginMethod(profile: Profile?, loginStore: LoginStore): Int { + return mRequiredLoginMethod?.invoke(profile, loginStore) ?: LOGIN_METHOD_NOT_NEEDED + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/datamodels/ProfileDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/datamodels/ProfileDao.java index a22aab66..4257a4db 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/datamodels/ProfileDao.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/datamodels/ProfileDao.java @@ -25,7 +25,10 @@ public interface ProfileDao { LiveData getById(int profileId); @Query("SELECT profiles.*, loginStores.loginStoreType, loginStores.loginStoreData FROM profiles LEFT JOIN loginStores ON profiles.loginStoreId = loginStores.loginStoreId WHERE profileId = :profileId") - ProfileFull getByIdNow(int profileId); + ProfileFull getFullByIdNow(int profileId); + + @Query("SELECT* FROM profiles WHERE profileId = :profileId") + Profile getByIdNow(int profileId); @Query("SELECT * FROM profiles WHERE profileId >= 0 ORDER BY profileId") LiveData> getAll(); @@ -45,6 +48,9 @@ public interface ProfileDao { @Query("SELECT * FROM profiles WHERE syncEnabled = 1 AND archived = 0 AND profileId >= 0 ORDER BY profileId") List getProfilesForSyncNow(); + @Query("SELECT profileId FROM profiles WHERE syncEnabled = 1 AND archived = 0 AND profileId >= 0 ORDER BY profileId") + List getIdsForSyncNow(); + @Query("SELECT profileId FROM profiles WHERE profileId >= 0 ORDER BY profileId") List getIdsNow(); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/dialogs/EventManualDialog.java b/app/src/main/java/pl/szczodrzynski/edziennik/dialogs/EventManualDialog.java index 6b5fca15..6654fa62 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/dialogs/EventManualDialog.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/dialogs/EventManualDialog.java @@ -425,7 +425,7 @@ public class EventManualDialog { return; this.app = _app; AsyncTask.execute(() -> { - this.profile = app.db.profileDao().getByIdNow(profileId); + this.profile = app.db.profileDao().getFullByIdNow(profileId); if (profile != null) { ((Activity) context).runOnUiThread(() -> { actualShow(editingEvent, defaultDate, defaultTime, dialogType); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/widgets/luckynumber/WidgetLuckyNumber.java b/app/src/main/java/pl/szczodrzynski/edziennik/widgets/luckynumber/WidgetLuckyNumber.java index 7c2b847c..df6bfefb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/widgets/luckynumber/WidgetLuckyNumber.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/widgets/luckynumber/WidgetLuckyNumber.java @@ -159,7 +159,7 @@ public class WidgetLuckyNumber extends AppWidgetProvider { } } - Profile profile = app.db.profileDao().getByIdNow(widgetConfig.profileId); + Profile profile = app.db.profileDao().getFullByIdNow(widgetConfig.profileId); IIcon icon = CommunityMaterial.Icon.cmd_emoticon_dead_outline; boolean noNumberText = false; if (profile == null) { From 2ae6d2a4a0532567367fc763e2b2b951fb7975de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 29 Sep 2019 17:22:44 +0200 Subject: [PATCH 012/691] [APIv2] Update LibrusTest --- .../szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt index a01bc4df..482ba67b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt @@ -49,7 +49,7 @@ class LibrusTest(val app: App) { app.startService(Intent(app, ApiService::class.java)) - val data = DataLibrus(app, profile, loginStore).apply { + /*val data = DataLibrus(app, profile, loginStore).apply { callback = object : ProgressCallback { override fun onProgress(progressStep: Int) { @@ -64,12 +64,12 @@ class LibrusTest(val app: App) { d(TAG, "Error "+error.getDetails(app)) } } - } + }*/ - LoginLibrus(data, LOGIN_METHOD_LIBRUS_MESSAGES) { + /*LoginLibrus(data, LOGIN_METHOD_LIBRUS_MESSAGES) { d(TAG, "Login succeeded.") d(TAG, "Profile data: ${data.profile?.studentData?.toString()}") d(TAG, "LoginStore data: ${data.loginStore.data}") - } + }*/ } } From 7da310167886dbba1b98f28b95bc87e37172b61f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 29 Sep 2019 20:18:21 +0200 Subject: [PATCH 013/691] Delete unused MaterialDrawer module --- MaterialDrawer/build.gradle | 59 - MaterialDrawer/gradle.properties | 4 - MaterialDrawer/src/main/AndroidManifest.xml | 2 - .../fonts/materialdrawerfont-font-v5.0.0.ttf | Bin 2104 -> 0 bytes .../materialdrawer/AccountHeader.java | 455 ---- .../materialdrawer/AccountHeaderBuilder.java | 1500 ------------- .../com/mikepenz/materialdrawer/Drawer.java | 1174 ---------- .../materialdrawer/DrawerBuilder.java | 1899 ----------------- .../mikepenz/materialdrawer/DrawerUtils.java | 447 ---- .../mikepenz/materialdrawer/MiniDrawer.java | 544 ----- .../materialdrawer/holder/BadgeStyle.java | 232 -- .../materialdrawer/holder/ColorHolder.java | 25 - .../materialdrawer/holder/DimenHolder.java | 34 - .../materialdrawer/holder/ImageHolder.java | 165 -- .../materialdrawer/holder/StringHolder.java | 16 - .../icons/MaterialDrawerFont.java | 145 -- .../interfaces/ICrossfader.java | 10 - .../interfaces/OnCheckedChangeListener.java | 19 - .../model/AbstractBadgeableDrawerItem.java | 107 - .../model/AbstractDrawerItem.java | 449 ---- .../model/AbstractSwitchableDrawerItem.java | 126 -- .../model/AbstractToggleableDrawerItem.java | 133 -- .../model/BaseDescribeableDrawerItem.java | 114 - .../materialdrawer/model/BaseDrawerItem.java | 356 --- .../materialdrawer/model/BaseViewHolder.java | 25 - .../model/ContainerDrawerItem.java | 149 -- .../model/DividerDrawerItem.java | 67 - .../model/ExpandableBadgeDrawerItem.java | 174 -- .../model/ExpandableDrawerItem.java | 134 -- .../materialdrawer/model/MiniDrawerItem.java | 193 -- .../model/MiniProfileDrawerItem.java | 168 -- .../model/PrimaryDrawerItem.java | 8 - .../model/ProfileDrawerItem.java | 365 ---- .../model/ProfileSettingDrawerItem.java | 318 --- .../model/SecondaryDrawerItem.java | 42 - .../model/SecondarySwitchDrawerItem.java | 42 - .../model/SecondaryToggleDrawerItem.java | 42 - .../model/SectionDrawerItem.java | 160 -- .../model/SwitchDrawerItem.java | 8 - .../model/ToggleDrawerItem.java | 8 - .../model/interfaces/Badgeable.java | 16 - .../model/interfaces/ColorfulBadgeable.java | 13 - .../model/interfaces/IDrawerItem.java | 48 - .../model/interfaces/IProfile.java | 42 - .../model/interfaces/Iconable.java | 19 - .../model/interfaces/Nameable.java | 16 - .../interfaces/OnPostBindViewListener.java | 16 - .../model/interfaces/Selectable.java | 10 - .../model/interfaces/Tagable.java | 10 - .../model/interfaces/Typefaceable.java | 12 - .../model/utils/BadgeDrawableBuilder.java | 44 - .../util/AbstractDrawerImageLoader.java | 34 - .../util/DrawerImageLoader.java | 94 - .../util/DrawerItemViewHelper.java | 95 - .../materialdrawer/util/DrawerUIUtils.java | 242 --- .../materialdrawer/util/KeyboardUtil.java | 103 - .../util/PressedEffectStateListDrawable.java | 51 - .../materialdrawer/view/BezelImageView.java | 349 --- .../res/drawable/material_drawer_badge.xml | 5 - .../drawable/material_drawer_circle_mask.xml | 20 - .../material_drawer_shadow_bottom.xml | 7 - .../material_drawer_shadow_left.9.png | Bin 15047 -> 0 bytes .../material_drawer_shadow_right.9.png | Bin 171 -> 0 bytes .../drawable/material_drawer_shadow_top.xml | 7 - .../src/main/res/layout/material_drawer.xml | 5 - .../layout/material_drawer_compact_header.xml | 114 - .../res/layout/material_drawer_fits_not.xml | 6 - .../res/layout/material_drawer_header.xml | 128 -- .../layout/material_drawer_item_container.xml | 7 - .../layout/material_drawer_item_divider.xml | 12 - .../material_drawer_item_expandable.xml | 79 - .../material_drawer_item_expandable_badge.xml | 108 - .../res/layout/material_drawer_item_mini.xml | 35 - .../material_drawer_item_mini_profile.xml | 19 - .../layout/material_drawer_item_primary.xml | 91 - .../layout/material_drawer_item_profile.xml | 75 - .../material_drawer_item_profile_setting.xml | 60 - .../layout/material_drawer_item_secondary.xml | 87 - .../material_drawer_item_secondary_switch.xml | 77 - .../material_drawer_item_secondary_toggle.xml | 78 - .../layout/material_drawer_item_section.xml | 24 - .../layout/material_drawer_item_switch.xml | 77 - .../layout/material_drawer_item_toggle.xml | 78 - .../layout/material_drawer_recycler_view.xml | 8 - .../res/layout/material_drawer_slider.xml | 25 - .../src/main/res/values-sw600dp/dimens.xml | 4 - MaterialDrawer/src/main/res/values/attrs.xml | 31 - MaterialDrawer/src/main/res/values/colors.xml | 46 - MaterialDrawer/src/main/res/values/dimens.xml | 76 - ...anslate_library_materialdrawer_strings.xml | 5 - MaterialDrawer/src/main/res/values/ids.xml | 27 - .../src/main/res/values/strings.xml | 5 - MaterialDrawer/src/main/res/values/styles.xml | 399 ---- 93 files changed, 12957 deletions(-) delete mode 100644 MaterialDrawer/build.gradle delete mode 100644 MaterialDrawer/gradle.properties delete mode 100644 MaterialDrawer/src/main/AndroidManifest.xml delete mode 100644 MaterialDrawer/src/main/assets/fonts/materialdrawerfont-font-v5.0.0.ttf delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/AccountHeader.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/AccountHeaderBuilder.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/Drawer.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/DrawerBuilder.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/DrawerUtils.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/MiniDrawer.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/BadgeStyle.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/ColorHolder.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/DimenHolder.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/ImageHolder.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/StringHolder.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/icons/MaterialDrawerFont.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/interfaces/ICrossfader.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/interfaces/OnCheckedChangeListener.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractBadgeableDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractSwitchableDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractToggleableDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseDescribeableDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseViewHolder.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ContainerDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/DividerDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ExpandableBadgeDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ExpandableDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/MiniDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/MiniProfileDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/PrimaryDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ProfileDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ProfileSettingDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondaryDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondarySwitchDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondaryToggleDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SectionDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SwitchDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ToggleDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Badgeable.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/ColorfulBadgeable.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/IDrawerItem.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/IProfile.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Iconable.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Nameable.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/OnPostBindViewListener.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Selectable.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Tagable.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Typefaceable.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/utils/BadgeDrawableBuilder.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/AbstractDrawerImageLoader.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerImageLoader.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerItemViewHelper.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerUIUtils.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/KeyboardUtil.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/PressedEffectStateListDrawable.java delete mode 100644 MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/view/BezelImageView.java delete mode 100644 MaterialDrawer/src/main/res/drawable/material_drawer_badge.xml delete mode 100644 MaterialDrawer/src/main/res/drawable/material_drawer_circle_mask.xml delete mode 100644 MaterialDrawer/src/main/res/drawable/material_drawer_shadow_bottom.xml delete mode 100644 MaterialDrawer/src/main/res/drawable/material_drawer_shadow_left.9.png delete mode 100644 MaterialDrawer/src/main/res/drawable/material_drawer_shadow_right.9.png delete mode 100644 MaterialDrawer/src/main/res/drawable/material_drawer_shadow_top.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_compact_header.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_fits_not.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_header.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_container.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_divider.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_expandable.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_expandable_badge.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_mini.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_mini_profile.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_primary.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_profile.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_profile_setting.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_secondary.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_secondary_switch.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_secondary_toggle.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_section.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_switch.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_item_toggle.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_recycler_view.xml delete mode 100644 MaterialDrawer/src/main/res/layout/material_drawer_slider.xml delete mode 100644 MaterialDrawer/src/main/res/values-sw600dp/dimens.xml delete mode 100644 MaterialDrawer/src/main/res/values/attrs.xml delete mode 100644 MaterialDrawer/src/main/res/values/colors.xml delete mode 100644 MaterialDrawer/src/main/res/values/dimens.xml delete mode 100644 MaterialDrawer/src/main/res/values/donottranslate_library_materialdrawer_strings.xml delete mode 100644 MaterialDrawer/src/main/res/values/ids.xml delete mode 100644 MaterialDrawer/src/main/res/values/strings.xml delete mode 100644 MaterialDrawer/src/main/res/values/styles.xml diff --git a/MaterialDrawer/build.gradle b/MaterialDrawer/build.gradle deleted file mode 100644 index f1f52f6c..00000000 --- a/MaterialDrawer/build.gradle +++ /dev/null @@ -1,59 +0,0 @@ -apply plugin: 'com.android.library' - -android { - compileSdkVersion rootProject.ext.compileSdkVersion - buildToolsVersion "28.0.3" - - defaultConfig { - minSdkVersion 14 - targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 6102 - versionName "6.1.2" - - resValue "string", "materialdrawer_lib_version", "6.1.2" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' - } - debugMinify { - debuggable = true - minifyEnabled = true - proguardFiles 'proguard-android.txt' - } - } - productFlavors { - } - lintOptions { - abortOnError false - } -} - -dependencies { - implementation "androidx.appcompat:appcompat:${androidXAppCompat}" - implementation "androidx.recyclerview:recyclerview:${androidXRecyclerView}" - implementation "androidx.annotation:annotation:1.0.2" - implementation "com.google.android.material:material:${googleMaterial}" - implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.15' - - // add the constraintLayout used to create the items and headers - implementation "androidx.constraintlayout:constraintlayout:1.1.3" - - // used to base on some backwards compatible themes - // contains util classes to support various android versions, and clean up code - // comes with the awesome "Holder"-Pattern - // https://github.com/mikepenz/Materialize - api 'com.mikepenz:materialize:1.2.0' - - // used to provide out of the box icon font support. simplifies development, - // and provides scalable icons. the core is very very light - // https://github.com/mikepenz/Android-Iconics - api "com.mikepenz:iconics-core:${iconics}" - - // used to fill the RecyclerView with the DrawerItems - // and provides single and multi selection, expandable items - // https://github.com/mikepenz/FastAdapter - api 'com.mikepenz:fastadapter:3.3.0' - api 'com.mikepenz:fastadapter-extensions-expandable:3.3.0' -} \ No newline at end of file diff --git a/MaterialDrawer/gradle.properties b/MaterialDrawer/gradle.properties deleted file mode 100644 index 86351b9f..00000000 --- a/MaterialDrawer/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -POM_NAME=MaterialDrawer Library -POM_DESCRIPTION=The flexible, easy to use, all in one drawer library for your Android project. -POM_ARTIFACT_ID=materialdrawer -POM_PACKAGING=aar \ No newline at end of file diff --git a/MaterialDrawer/src/main/AndroidManifest.xml b/MaterialDrawer/src/main/AndroidManifest.xml deleted file mode 100644 index 1182871d..00000000 --- a/MaterialDrawer/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/MaterialDrawer/src/main/assets/fonts/materialdrawerfont-font-v5.0.0.ttf b/MaterialDrawer/src/main/assets/fonts/materialdrawerfont-font-v5.0.0.ttf deleted file mode 100644 index f02fa65a5736b0305cd70b317bfcd6a1ac36b52f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2104 zcmeHHUuauZ82_DfZ~k^obhc|6XUN8IBaymgU5k)PCNpKspl&9LDBE3fo0g?XNK%@< z$)0>r6d#1Cph$gD@oC5&3s!|OHgOUrf-h1kx)&ezGHkEEbMB3HMTGU)lbmzD@Avz@ z@0|0Ub3X_GyYLzeR?7u#H=J8B)w|Nr~m#LpC&s)u_cW+Jsa)|nJ z)oM36KS2Eo=U}zAG-F?$jZ$A>&(7LbC0M#40rOAt6SD*YG2=(TH%|S)?0lyihq+1p zCF+4Wg@iOyPw~2@OgYP!=WZjy#13$d_9pK-^_~B-w-Qm93f-%KIoh6I} z9)qY^ojQUr70ad=QJbq;#5|3hVg)_a7@-!&D|iRX)J*sgU|vQb;m3YFghBlq;Au@F zgBT!dBFb?$N5yXE{2B@C2nh^Rdyv*XuIHy`i25Mc7}|yhiADHRd)PzE0Iedl344g- zo($tN@irqtUXtpj-g0O0JC2GF^{q|tdhTsGV13hAaaNEs*h#AF^jlI3DyypRCFjnD zw%W^Gy-qyAJQ#YHNt}fR8%?yafbQDYYu~QjSl@Iu97jiT`3h=ikzdsO_4N(S3+ET- z6KC1^*!jSD-+9lu6feY|iRWXZjJ*2 z;UT|7df~#UjTcT`SV-q9f>ql=4p1MlJ^T3*37xP{wdNF?Z_auCC)caDGCRs>&oXA!Jx z{j5n=p|3pLApW8!o0M;PvX3=t!jt{P-}K}#;v%BHXEQ|P02x=7tR7cA*&sgW$tLCB zJlV$zc-)iy#6R@pFvi89g4MBGb5`v{%UZNs`9{5i0xMC6wW!5t7AwvPN){G1l1GEN z>6GkNd#+JW9vM4~k|x?TR#Xy4FowfB3%fI~({^>CX0>pd8>zB7*0|f&&O)j4C#!bd zZZR#DqEPFst`MmC5|Uird5Gm*^cq%Rn`BG_7@q& B7xn-E diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/AccountHeader.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/AccountHeader.java deleted file mode 100644 index 0899fca3..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/AccountHeader.java +++ /dev/null @@ -1,455 +0,0 @@ -package com.mikepenz.materialdrawer; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Bundle; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import pl.droidsonroids.gif.GifDrawable; -import pl.droidsonroids.gif.GifImageView; - -import android.view.View; -import android.widget.ImageView; - -import com.mikepenz.materialdrawer.holder.ImageHolder; -import com.mikepenz.materialdrawer.model.interfaces.IProfile; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Created by mikepenz on 27.02.15. - */ -public class AccountHeader { - protected final static double NAVIGATION_DRAWER_ACCOUNT_ASPECT_RATIO = 9d / 16d; - - protected static final String BUNDLE_SELECTION_HEADER = "bundle_selection_header"; - - - protected final AccountHeaderBuilder mAccountHeaderBuilder; - - protected AccountHeader(AccountHeaderBuilder accountHeaderBuilder) { - this.mAccountHeaderBuilder = accountHeaderBuilder; - } - - /** - * the protected getter for the AccountHeaderBuilder - * - * @return the AccountHeaderBuilder - */ - protected AccountHeaderBuilder getAccountHeaderBuilder() { - return mAccountHeaderBuilder; - } - - /** - * Get the Root view for the Header - * - * @return - */ - public View getView() { - return mAccountHeaderBuilder.mAccountHeaderContainer; - } - - /** - * Set the drawer for the AccountHeader so we can use it for the select - * - * @param drawer - */ - public void setDrawer(Drawer drawer) { - mAccountHeaderBuilder.mDrawer = drawer; - } - - /** - * Returns the header background view so the dev can set everything on it - * - * @return - */ - public GifImageView getHeaderBackgroundView() { - return mAccountHeaderBuilder.mAccountHeaderBackground; - } - - /** - * set the background for the header via the ImageHolder class - * - * @param imageHolder - */ - public void setHeaderBackground(ImageHolder imageHolder) { - ImageHolder.applyTo(imageHolder, mAccountHeaderBuilder.mAccountHeaderBackground); - } - - /** - * Set the background for the Header - * - * @param headerBackground - */ - public void setBackground(Drawable headerBackground) { - mAccountHeaderBuilder.mAccountHeaderBackground.setImageDrawable(headerBackground); - } - - /** - * set the background for the header as file name - * - * @param headerBackgroundPath - * @return - */ - public void setBackground(String headerBackgroundPath) { - try { - if (headerBackgroundPath.endsWith(".gif")) { - setHeaderBackground(new ImageHolder(new GifDrawable(headerBackgroundPath))); - } - else { - setHeaderBackground(new ImageHolder(Uri.parse(headerBackgroundPath))); - } - - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Set the background for the Header as resource - * - * @param headerBackgroundRes - */ - public void setBackgroundRes(@DrawableRes int headerBackgroundRes) { - mAccountHeaderBuilder.mAccountHeaderBackground.setImageResource(headerBackgroundRes); - } - - /** - * Toggle the selection list (show or hide it) - * - * @param ctx - */ - public void toggleSelectionList(Context ctx) { - mAccountHeaderBuilder.toggleSelectionList(ctx); - } - - /** - * returns if the selection list is currently shown - * - * @return - */ - public boolean isSelectionListShown() { - return mAccountHeaderBuilder.mSelectionListShown; - } - - - /** - * set this to false if you want to hide the first line of the selection box in the header (first line would be the name) - * - * @param selectionFirstLineShown - */ - public void setSelectionFirstLineShown(boolean selectionFirstLineShown) { - mAccountHeaderBuilder.mSelectionFirstLineShown = selectionFirstLineShown; - mAccountHeaderBuilder.updateHeaderAndList(); - } - - /** - * set this to false if you want to hide the second line of the selection box in the header (second line would be the e-mail) - * - * @param selectionSecondLineShown - */ - public void setSelectionSecondLineShown(boolean selectionSecondLineShown) { - mAccountHeaderBuilder.mSelectionSecondLineShown = selectionSecondLineShown; - mAccountHeaderBuilder.updateHeaderAndList(); - } - - /** - * set this to define the first line in the selection area if there is no profile - * note this will block any values from profiles! - * - * @param selectionFirstLine - */ - public void setSelectionFirstLine(String selectionFirstLine) { - mAccountHeaderBuilder.mSelectionFirstLine = selectionFirstLine; - mAccountHeaderBuilder.updateHeaderAndList(); - } - - /** - * set this to define the second line in the selection area if there is no profile - * note this will block any values from profiles! - * - * @param selectionSecondLine - */ - public void setSelectionSecondLine(String selectionSecondLine) { - mAccountHeaderBuilder.mSelectionSecondLine = selectionSecondLine; - mAccountHeaderBuilder.updateHeaderAndList(); - } - - /** - * returns the current list of profiles set for this header - * - * @return - */ - public List getProfiles() { - return mAccountHeaderBuilder.mProfiles; - } - - /** - * Set a new list of profiles for the header - * - * @param profiles - */ - public void setProfiles(List profiles) { - mAccountHeaderBuilder.mProfiles = profiles; - mAccountHeaderBuilder.updateHeaderAndList(); - } - - /** - * Selects the given profile and sets it to the new active profile - * - * @param profile - */ - public void setActiveProfile(IProfile profile) { - setActiveProfile(profile, false); - } - - /** - * Selects the given profile and sets it to the new active profile - * - * @param profile - */ - public void setActiveProfile(IProfile profile, boolean fireOnProfileChanged) { - final boolean isCurrentSelectedProfile = mAccountHeaderBuilder.switchProfiles(profile); - //if the selectionList is shown we should also update the current selected profile in the list - if (mAccountHeaderBuilder.mDrawer != null && isSelectionListShown()) { - mAccountHeaderBuilder.mDrawer.setSelection(profile.getIdentifier(), false); - } - //fire the event if enabled and a listener is set - if (fireOnProfileChanged && mAccountHeaderBuilder.mOnAccountHeaderListener != null) { - mAccountHeaderBuilder.mOnAccountHeaderListener.onProfileChanged(null, profile, isCurrentSelectedProfile); - } - } - - /** - * Selects a profile by its identifier - * - * @param identifier - */ - public void setActiveProfile(long identifier) { - setActiveProfile(identifier, false); - } - - /** - * Selects a profile by its identifier - * - * @param identifier - */ - public void setActiveProfile(long identifier, boolean fireOnProfileChanged) { - if (mAccountHeaderBuilder.mProfiles != null) { - for (IProfile profile : mAccountHeaderBuilder.mProfiles) { - if (profile != null) { - if (profile.getIdentifier() == identifier) { - setActiveProfile(profile, fireOnProfileChanged); - return; - } - } - } - } - } - - /** - * get the current active profile - * - * @return - */ - public IProfile getActiveProfile() { - return mAccountHeaderBuilder.mCurrentProfile; - } - - - /** - * Helper method to update a profile using it's identifier - * - * @param newProfile - */ - public void updateProfile(@NonNull IProfile newProfile) { - updateProfileByIdentifier(newProfile); - } - - /** - * Helper method to update a profile using it's identifier - * - * @param newProfile - */ - @Deprecated - public void updateProfileByIdentifier(@NonNull IProfile newProfile) { - int found = getPositionByIdentifier(newProfile.getIdentifier()); - if (found > -1) { - mAccountHeaderBuilder.mProfiles.set(found, newProfile); - mAccountHeaderBuilder.updateHeaderAndList(); - } - } - - - /** - * Add new profiles to the existing list of profiles - * - * @param profiles - */ - public void addProfiles(@NonNull IProfile... profiles) { - if (mAccountHeaderBuilder.mProfiles == null) { - mAccountHeaderBuilder.mProfiles = new ArrayList<>(); - } - - Collections.addAll(mAccountHeaderBuilder.mProfiles, profiles); - - mAccountHeaderBuilder.updateHeaderAndList(); - } - - /** - * Add a new profile at a specific position to the list - * - * @param profile - * @param position - */ - public void addProfile(@NonNull IProfile profile, int position) { - if (mAccountHeaderBuilder.mProfiles == null) { - mAccountHeaderBuilder.mProfiles = new ArrayList<>(); - } - mAccountHeaderBuilder.mProfiles.add(position, profile); - - mAccountHeaderBuilder.updateHeaderAndList(); - } - - /** - * remove a profile from the given position - * - * @param position - */ - public void removeProfile(int position) { - if (mAccountHeaderBuilder.mProfiles != null && mAccountHeaderBuilder.mProfiles.size() > position) { - mAccountHeaderBuilder.mProfiles.remove(position); - } - - mAccountHeaderBuilder.updateHeaderAndList(); - } - - /** - * remove the profile with the given identifier - * - * @param identifier - */ - public void removeProfileByIdentifier(long identifier) { - int found = getPositionByIdentifier(identifier); - if (found > -1) { - mAccountHeaderBuilder.mProfiles.remove(found); - } - - mAccountHeaderBuilder.updateHeaderAndList(); - } - - /** - * try to remove the given profile - * - * @param profile - */ - public void removeProfile(@NonNull IProfile profile) { - removeProfileByIdentifier(profile.getIdentifier()); - } - - /** - * Clear the header - */ - public void clear() { - mAccountHeaderBuilder.mProfiles = null; - - //calculate the profiles to set - mAccountHeaderBuilder.calculateProfiles(); - - //process and build the profiles - mAccountHeaderBuilder.buildProfiles(); - } - - /** - * gets the position of a profile by it's identifier - * - * @param identifier - * @return - */ - private int getPositionByIdentifier(long identifier) { - int found = -1; - if (mAccountHeaderBuilder.mProfiles != null && identifier != -1) { - for (int i = 0; i < mAccountHeaderBuilder.mProfiles.size(); i++) { - if (mAccountHeaderBuilder.mProfiles.get(i) != null) { - if (mAccountHeaderBuilder.mProfiles.get(i).getIdentifier() == identifier) { - found = i; - break; - } - } - } - } - return found; - } - - /** - * add the values to the bundle for saveInstanceState - * - * @param savedInstanceState - * @return - */ - public Bundle saveInstanceState(Bundle savedInstanceState) { - if (savedInstanceState != null) { - savedInstanceState.putInt(BUNDLE_SELECTION_HEADER, mAccountHeaderBuilder.getCurrentSelection()); - } - return savedInstanceState; - } - - - public interface OnAccountHeaderListener { - /** - * the event when the profile changes - * - * @param view - * @param profile - * @return if the event was consumed - */ - boolean onProfileChanged(View view, IProfile profile, boolean current); - } - - public interface OnAccountHeaderItemLongClickListener { - /** - * the event when the profile item is longClicked inside the list - * - * @param view - * @param profile - * @param current - * @return if the event was consumed - */ - boolean onProfileLongClick(View view, IProfile profile, boolean current); - } - - public interface OnAccountHeaderProfileImageListener { - /** - * the event when the profile image is clicked - * - * @param view - * @param profile - * @return if the event was consumed - */ - boolean onProfileImageClick(View view, IProfile profile, boolean current); - - /** - * the event when the profile image is long clicked - * - * @param view - * @param profile - * @return if the event was consumed - */ - boolean onProfileImageLongClick(View view, IProfile profile, boolean current); - } - - public interface OnAccountHeaderSelectionViewClickListener { - /** - * the event when the user clicks the selection list under the profile icons - * - * @param view - * @param profile - * @return if the event was consumed - */ - boolean onClick(View view, IProfile profile); - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/AccountHeaderBuilder.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/AccountHeaderBuilder.java deleted file mode 100644 index bb447671..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/AccountHeaderBuilder.java +++ /dev/null @@ -1,1500 +0,0 @@ -package com.mikepenz.materialdrawer; - -import android.app.Activity; -import android.content.Context; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.text.TextUtils; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import com.mikepenz.iconics.IconicsDrawable; -import com.mikepenz.materialdrawer.holder.ColorHolder; -import com.mikepenz.materialdrawer.holder.DimenHolder; -import com.mikepenz.materialdrawer.holder.ImageHolder; -import com.mikepenz.materialdrawer.holder.StringHolder; -import com.mikepenz.materialdrawer.icons.MaterialDrawerFont; -import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; -import com.mikepenz.materialdrawer.model.interfaces.IProfile; -import com.mikepenz.materialdrawer.util.DrawerImageLoader; -import com.mikepenz.materialdrawer.util.DrawerUIUtils; -import com.mikepenz.materialdrawer.view.BezelImageView; -import com.mikepenz.materialize.util.UIUtils; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Stack; - -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.annotation.DimenRes; -import androidx.annotation.DrawableRes; -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.appcompat.content.res.AppCompatResources; -import androidx.constraintlayout.widget.Guideline; -import androidx.core.view.ViewCompat; -import pl.droidsonroids.gif.GifDrawable; -import pl.droidsonroids.gif.GifImageView; - -/** - * Created by mikepenz on 23.05.15. - */ -public class AccountHeaderBuilder { - // global references to views we need later - protected Guideline mStatusBarGuideline; - protected View mAccountHeader; - protected GifImageView mAccountHeaderBackground; - protected BezelImageView mCurrentProfileView; - protected ImageView mAccountSwitcherArrow; - protected TextView mCurrentProfileName; - protected TextView mCurrentProfileEmail; - protected BezelImageView mProfileFirstView; - protected BezelImageView mProfileSecondView; - protected BezelImageView mProfileThirdView; - - // global references to the profiles - protected IProfile mCurrentProfile; - protected IProfile mProfileFirst; - protected IProfile mProfileSecond; - protected IProfile mProfileThird; - - - // global stuff - protected boolean mSelectionListShown = false; - protected int mAccountHeaderTextSectionBackgroundResource = -1; - - // the activity to use - protected Activity mActivity; - - /** - * Pass the activity you use the drawer in ;) - * - * @param activity - * @return - */ - public AccountHeaderBuilder withActivity(@NonNull Activity activity) { - this.mActivity = activity; - return this; - } - - // defines if we use the compactStyle - protected boolean mCompactStyle = false; - - /** - * Defines if we should use the compact style for the header. - * - * @param compactStyle - * @return - */ - public AccountHeaderBuilder withCompactStyle(boolean compactStyle) { - this.mCompactStyle = compactStyle; - return this; - } - - // the typeface used for textViews within the AccountHeader - protected Typeface mTypeface; - - // the typeface used for name textView only. overrides mTypeface - protected Typeface mNameTypeface; - - // the typeface used for email textView only. overrides mTypeface - protected Typeface mEmailTypeface; - - /** - * Define the typeface which will be used for all textViews in the AccountHeader - * - * @param typeface - * @return - */ - public AccountHeaderBuilder withTypeface(@NonNull Typeface typeface) { - this.mTypeface = typeface; - return this; - } - - /** - * Define the typeface which will be used for name textView in the AccountHeader. - * Overrides typeface supplied to {@link AccountHeaderBuilder#withTypeface(android.graphics.Typeface)} - * - * @param typeface - * @return - * @see #withTypeface(android.graphics.Typeface) - */ - public AccountHeaderBuilder withNameTypeface(@NonNull Typeface typeface) { - this.mNameTypeface = typeface; - return this; - } - - /** - * Define the typeface which will be used for email textView in the AccountHeader. - * Overrides typeface supplied to {@link AccountHeaderBuilder#withTypeface(android.graphics.Typeface)} - * - * @param typeface - * @return - * @see #withTypeface(android.graphics.Typeface) - */ - public AccountHeaderBuilder withEmailTypeface(@NonNull Typeface typeface) { - this.mEmailTypeface = typeface; - return this; - } - - // set the account header height - protected DimenHolder mHeight; - - /** - * set the height for the header - * - * @param heightPx - * @return - */ - public AccountHeaderBuilder withHeightPx(int heightPx) { - this.mHeight = DimenHolder.fromPixel(heightPx); - return this; - } - - - /** - * set the height for the header - * - * @param heightDp - * @return - */ - public AccountHeaderBuilder withHeightDp(int heightDp) { - this.mHeight = DimenHolder.fromDp(heightDp); - return this; - } - - /** - * set the height for the header by resource - * - * @param heightRes - * @return - */ - public AccountHeaderBuilder withHeightRes(@DimenRes int heightRes) { - this.mHeight = DimenHolder.fromResource(heightRes); - return this; - } - - //the background color for the slider - protected ColorHolder mTextColor; - - /** - * set the background for the slider as color - * - * @param textColor - * @return - */ - public AccountHeaderBuilder withTextColor(@ColorInt int textColor) { - this.mTextColor = ColorHolder.fromColor(textColor); - return this; - } - - /** - * set the background for the slider as resource - * - * @param textColorRes - * @return - */ - public AccountHeaderBuilder withTextColorRes(@ColorRes int textColorRes) { - this.mTextColor = ColorHolder.fromColorRes(textColorRes); - return this; - } - - //the current selected profile is visible in the list - protected boolean mCurrentHiddenInList = false; - - /** - * hide the current selected profile from the list - * - * @param currentProfileHiddenInList - * @return - */ - public AccountHeaderBuilder withCurrentProfileHiddenInList(boolean currentProfileHiddenInList) { - mCurrentHiddenInList = currentProfileHiddenInList; - return this; - } - - //set to hide the first or second line - protected boolean mSelectionFirstLineShown = true; - protected boolean mSelectionSecondLineShown = true; - - /** - * set this to false if you want to hide the first line of the selection box in the header (first line would be the name) - * - * @param selectionFirstLineShown - * @return - * @deprecated replaced by {@link #withSelectionFirstLineShown} - */ - @Deprecated - public AccountHeaderBuilder withSelectionFistLineShown(boolean selectionFirstLineShown) { - this.mSelectionFirstLineShown = selectionFirstLineShown; - return this; - } - - /** - * set this to false if you want to hide the first line of the selection box in the header (first line would be the name) - * - * @param selectionFirstLineShown - * @return - */ - public AccountHeaderBuilder withSelectionFirstLineShown(boolean selectionFirstLineShown) { - this.mSelectionFirstLineShown = selectionFirstLineShown; - return this; - } - - /** - * set this to false if you want to hide the second line of the selection box in the header (second line would be the e-mail) - * - * @param selectionSecondLineShown - * @return - */ - public AccountHeaderBuilder withSelectionSecondLineShown(boolean selectionSecondLineShown) { - this.mSelectionSecondLineShown = selectionSecondLineShown; - return this; - } - - - //set one of these to define the text in the first or second line with in the account selector - protected String mSelectionFirstLine; - protected String mSelectionSecondLine; - - /** - * set this to define the first line in the selection area if there is no profile - * note this will block any values from profiles! - * - * @param selectionFirstLine - * @return - */ - public AccountHeaderBuilder withSelectionFirstLine(String selectionFirstLine) { - this.mSelectionFirstLine = selectionFirstLine; - return this; - } - - /** - * set this to define the second line in the selection area if there is no profile - * note this will block any values from profiles! - * - * @param selectionSecondLine - * @return - */ - public AccountHeaderBuilder withSelectionSecondLine(String selectionSecondLine) { - this.mSelectionSecondLine = selectionSecondLine; - return this; - } - - // set no divider below the header - protected boolean mPaddingBelowHeader = true; - - /** - * Set this to false if you want no padding below the Header - * - * @param paddingBelowHeader - * @return - */ - public AccountHeaderBuilder withPaddingBelowHeader(boolean paddingBelowHeader) { - this.mPaddingBelowHeader = paddingBelowHeader; - return this; - } - - // set no divider below the header - protected boolean mDividerBelowHeader = true; - - /** - * Set this to false if you want no divider below the Header - * - * @param dividerBelowHeader - * @return - */ - public AccountHeaderBuilder withDividerBelowHeader(boolean dividerBelowHeader) { - this.mDividerBelowHeader = dividerBelowHeader; - return this; - } - - // set non translucent statusBar mode - protected boolean mTranslucentStatusBar = true; - - /** - * Set or disable this if you use a translucent statusbar - * - * @param translucentStatusBar - * @return - */ - public AccountHeaderBuilder withTranslucentStatusBar(boolean translucentStatusBar) { - this.mTranslucentStatusBar = translucentStatusBar; - return this; - } - - //the background for the header - protected ImageHolder mHeaderBackground; - - /** - * set the background for the slider as color - * - * @param headerBackground - * @return - */ - public AccountHeaderBuilder withHeaderBackground(Drawable headerBackground) { - this.mHeaderBackground = new ImageHolder(headerBackground); - return this; - } - - /** - * set the background for the header as resource - * - * @param headerBackgroundRes - * @return - */ - public AccountHeaderBuilder withHeaderBackground(@DrawableRes int headerBackgroundRes) { - this.mHeaderBackground = new ImageHolder(headerBackgroundRes); - return this; - } - - /** - * set the background for the header as file name - * - * @param headerBackgroundPath - * @return - */ - public AccountHeaderBuilder withHeaderBackground(String headerBackgroundPath) { - try { - if (headerBackgroundPath.endsWith(".gif")) { - this.mHeaderBackground = new ImageHolder(new GifDrawable(headerBackgroundPath)); - } - else { - this.mHeaderBackground = new ImageHolder(Uri.parse(headerBackgroundPath)); - } - - } catch (IOException e) { - e.printStackTrace(); - } - return this; - } - - /** - * set the background for the header via the ImageHolder class - * - * @param headerBackground - * @return - */ - public AccountHeaderBuilder withHeaderBackground(ImageHolder headerBackground) { - this.mHeaderBackground = headerBackground; - return this; - } - - //background scale type - protected ImageView.ScaleType mHeaderBackgroundScaleType = null; - - /** - * define the ScaleType for the header background - * - * @param headerBackgroundScaleType - * @return - */ - public AccountHeaderBuilder withHeaderBackgroundScaleType(ImageView.ScaleType headerBackgroundScaleType) { - this.mHeaderBackgroundScaleType = headerBackgroundScaleType; - return this; - } - - //profile images in the header are shown or not - protected boolean mProfileImagesVisible = true; - - /** - * define if the profile images in the header are shown or not - * - * @param profileImagesVisible - * @return - */ - public AccountHeaderBuilder withProfileImagesVisible(boolean profileImagesVisible) { - this.mProfileImagesVisible = profileImagesVisible; - return this; - } - - //only the main profile image is visible - protected boolean mOnlyMainProfileImageVisible = false; - - /** - * define if only the main (current selected) profile image should be visible - * - * @param onlyMainProfileImageVisible - * @return - */ - public AccountHeaderBuilder withOnlyMainProfileImageVisible(boolean onlyMainProfileImageVisible) { - this.mOnlyMainProfileImageVisible = onlyMainProfileImageVisible; - return this; - } - - //show small profile images but hide MainProfileImage - protected boolean mOnlySmallProfileImagesVisible = false; - - /** - * define if only the small profile images should be visible - * - * @param onlySmallProfileImagesVisible - * @return - */ - public AccountHeaderBuilder withOnlySmallProfileImagesVisible(boolean onlySmallProfileImagesVisible) { - this.mOnlySmallProfileImagesVisible = onlySmallProfileImagesVisible; - return this; - } - - //close the drawer after a profile was clicked in the list - protected Boolean mCloseDrawerOnProfileListClick = null; - - /** - * define if the drawer should close if the user clicks on a profile item if the selection list is shown - * - * @param closeDrawerOnProfileListClick - * @return - */ - public AccountHeaderBuilder withCloseDrawerOnProfileListClick(boolean closeDrawerOnProfileListClick) { - this.mCloseDrawerOnProfileListClick = closeDrawerOnProfileListClick; - return this; - } - - //reset the drawer list to the main drawer list after the profile was clicked in the list - protected boolean mResetDrawerOnProfileListClick = true; - - /** - * define if the drawer selection list should be reseted after the user clicks on a profile item if the selection list is shown - * - * @param resetDrawerOnProfileListClick - * @return - */ - public AccountHeaderBuilder withResetDrawerOnProfileListClick(boolean resetDrawerOnProfileListClick) { - this.mResetDrawerOnProfileListClick = resetDrawerOnProfileListClick; - return this; - } - - // set the profile images clickable or not - protected boolean mProfileImagesClickable = true; - - /** - * enable or disable the profile images to be clickable - * - * @param profileImagesClickable - * @return - */ - public AccountHeaderBuilder withProfileImagesClickable(boolean profileImagesClickable) { - this.mProfileImagesClickable = profileImagesClickable; - return this; - } - - // set to use the alternative profile header switching - protected boolean mAlternativeProfileHeaderSwitching = false; - - /** - * enable the alternative profile header switching - * - * @param alternativeProfileHeaderSwitching - * @return - */ - public AccountHeaderBuilder withAlternativeProfileHeaderSwitching(boolean alternativeProfileHeaderSwitching) { - this.mAlternativeProfileHeaderSwitching = alternativeProfileHeaderSwitching; - return this; - } - - // enable 3 small header previews - protected boolean mThreeSmallProfileImages = false; - - /** - * enable the extended profile icon view with 3 small header images instead of two - * - * @param threeSmallProfileImages - * @return - */ - public AccountHeaderBuilder withThreeSmallProfileImages(boolean threeSmallProfileImages) { - this.mThreeSmallProfileImages = threeSmallProfileImages; - return this; - } - - //the delay which is waited before the drawer is closed - protected int mOnProfileClickDrawerCloseDelay = 100; - - /** - * Define the delay for the drawer close operation after a click. - * This is a small trick to improve the speed (and remove lag) if you open a new activity after a DrawerItem - * was selected. - * NOTE: Disable this by passing -1 - * - * @param onProfileClickDrawerCloseDelay the delay in MS (-1 to disable) - * @return - */ - public AccountHeaderBuilder withOnProfileClickDrawerCloseDelay(int onProfileClickDrawerCloseDelay) { - this.mOnProfileClickDrawerCloseDelay = onProfileClickDrawerCloseDelay; - return this; - } - - // the onAccountHeaderProfileImageListener to set - protected AccountHeader.OnAccountHeaderProfileImageListener mOnAccountHeaderProfileImageListener; - - /** - * set click / longClick listener for the header images - * - * @param onAccountHeaderProfileImageListener - * @return - */ - public AccountHeaderBuilder withOnAccountHeaderProfileImageListener(AccountHeader.OnAccountHeaderProfileImageListener onAccountHeaderProfileImageListener) { - this.mOnAccountHeaderProfileImageListener = onAccountHeaderProfileImageListener; - return this; - } - - // the onAccountHeaderSelectionListener to set - protected AccountHeader.OnAccountHeaderSelectionViewClickListener mOnAccountHeaderSelectionViewClickListener; - - /** - * set a onSelection listener for the selection box - * - * @param onAccountHeaderSelectionViewClickListener - * @return - */ - public AccountHeaderBuilder withOnAccountHeaderSelectionViewClickListener(AccountHeader.OnAccountHeaderSelectionViewClickListener onAccountHeaderSelectionViewClickListener) { - this.mOnAccountHeaderSelectionViewClickListener = onAccountHeaderSelectionViewClickListener; - return this; - } - - //set the selection list enabled if there is only a single profile - protected boolean mSelectionListEnabledForSingleProfile = true; - - /** - * enable or disable the selection list if there is only a single profile - * - * @param selectionListEnabledForSingleProfile - * @return - */ - public AccountHeaderBuilder withSelectionListEnabledForSingleProfile(boolean selectionListEnabledForSingleProfile) { - this.mSelectionListEnabledForSingleProfile = selectionListEnabledForSingleProfile; - return this; - } - - //set the selection enabled disabled - protected boolean mSelectionListEnabled = true; - - /** - * enable or disable the selection list - * - * @param selectionListEnabled - * @return - */ - public AccountHeaderBuilder withSelectionListEnabled(boolean selectionListEnabled) { - this.mSelectionListEnabled = selectionListEnabled; - return this; - } - - // the drawerLayout to use - protected View mAccountHeaderContainer; - - /** - * You can pass a custom view for the drawer lib. note this requires the same structure as the drawer.xml - * - * @param accountHeader - * @return - */ - public AccountHeaderBuilder withAccountHeader(@NonNull View accountHeader) { - this.mAccountHeaderContainer = accountHeader; - return this; - } - - /** - * You can pass a custom layout for the drawer lib. see the drawer.xml in layouts of this lib on GitHub - * - * @param resLayout - * @return - */ - public AccountHeaderBuilder withAccountHeader(@LayoutRes int resLayout) { - if (mActivity == null) { - throw new RuntimeException("please pass an activity first to use this call"); - } - - if (resLayout != -1) { - this.mAccountHeaderContainer = mActivity.getLayoutInflater().inflate(resLayout, null, false); - } else { - if (mCompactStyle) { - this.mAccountHeaderContainer = mActivity.getLayoutInflater().inflate(R.layout.material_drawer_compact_header, null, false); - } else { - this.mAccountHeaderContainer = mActivity.getLayoutInflater().inflate(R.layout.material_drawer_header, null, false); - } - } - - return this; - } - - // the profiles to display - protected List mProfiles; - - /** - * set the arrayList of DrawerItems for the drawer - * - * @param profiles - * @return - */ - public AccountHeaderBuilder withProfiles(@NonNull List profiles) { - if (mDrawer != null) { - mDrawer.mDrawerBuilder.idDistributor.checkIds(profiles); - } - this.mProfiles = profiles; - return this; - } - - /** - * add single ore more DrawerItems to the Drawer - * - * @param profiles - * @return - */ - public AccountHeaderBuilder addProfiles(@NonNull IProfile... profiles) { - if (this.mProfiles == null) { - this.mProfiles = new ArrayList<>(); - } - if (mDrawer != null) { - mDrawer.mDrawerBuilder.idDistributor.checkIds(profiles); - } - Collections.addAll(this.mProfiles, profiles); - - return this; - } - - // the click listener to be fired on profile or selection click - protected AccountHeader.OnAccountHeaderListener mOnAccountHeaderListener; - - /** - * add a listener for the accountHeader - * - * @param onAccountHeaderListener - * @return - */ - public AccountHeaderBuilder withOnAccountHeaderListener(AccountHeader.OnAccountHeaderListener onAccountHeaderListener) { - this.mOnAccountHeaderListener = onAccountHeaderListener; - return this; - } - - //the on long click listener to be fired on profile longClick inside the list - protected AccountHeader.OnAccountHeaderItemLongClickListener mOnAccountHeaderItemLongClickListener; - - /** - * the on long click listener to be fired on profile longClick inside the list - * - * @param onAccountHeaderItemLongClickListener - * @return - */ - public AccountHeaderBuilder withOnAccountHeaderItemLongClickListener(AccountHeader.OnAccountHeaderItemLongClickListener onAccountHeaderItemLongClickListener) { - this.mOnAccountHeaderItemLongClickListener = onAccountHeaderItemLongClickListener; - return this; - } - - // the drawer to set the AccountSwitcher for - protected Drawer mDrawer; - - /** - * @param drawer - * @return - */ - public AccountHeaderBuilder withDrawer(@NonNull Drawer drawer) { - this.mDrawer = drawer; - - //set the top padding to 0 as this would happen when the AccountHeader is created during Drawer build time - drawer.getRecyclerView().setPadding(drawer.getRecyclerView().getPaddingLeft(), 0, drawer.getRecyclerView().getPaddingRight(), drawer.getRecyclerView().getPaddingBottom()); - return this; - } - - // savedInstance to restore state - protected Bundle mSavedInstance; - - /** - * create the drawer with the values of a savedInstance - * - * @param savedInstance - * @return - */ - public AccountHeaderBuilder withSavedInstance(Bundle savedInstance) { - this.mSavedInstance = savedInstance; - return this; - } - - /** - * helper method to set the height for the header! - * - * @param height - */ - private void setHeaderHeight(int height) { - if (mAccountHeaderContainer != null) { - ViewGroup.LayoutParams params = mAccountHeaderContainer.getLayoutParams(); - if (params != null) { - params.height = height; - mAccountHeaderContainer.setLayoutParams(params); - } - - View accountHeader = mAccountHeaderContainer.findViewById(R.id.material_drawer_account_header); - if (accountHeader != null) { - // TODO why is this null? - params = accountHeader.getLayoutParams(); - if(params != null) { - params.height = height; - accountHeader.setLayoutParams(params); - } - } - - View accountHeaderBackground = mAccountHeaderContainer.findViewById(R.id.material_drawer_account_header_background); - if (accountHeaderBackground != null) { - params = accountHeaderBackground.getLayoutParams(); - params.height = height; - accountHeaderBackground.setLayoutParams(params); - } - } - } - - /** - * a small helper to handle the selectionView - * - * @param on - */ - private void handleSelectionView(IProfile profile, boolean on) { - if (on) { - if (Build.VERSION.SDK_INT >= 23) { - mAccountHeaderContainer.setForeground(AppCompatResources.getDrawable(mAccountHeaderContainer.getContext(), mAccountHeaderTextSectionBackgroundResource)); - } else { - // todo foreground thing? - } - mAccountHeaderContainer.setOnClickListener(onSelectionClickListener); - mAccountHeaderContainer.setTag(R.id.material_drawer_profile_header, profile); - } else { - if (Build.VERSION.SDK_INT >= 23) { - mAccountHeaderContainer.setForeground(null); - } else { - // TODO foreground reset - } - mAccountHeaderContainer.setOnClickListener(null); - } - } - - /** - * method to build the header view - * - * @return - */ - public AccountHeader build() { - // if the user has not set a accountHeader use the default one :D - if (mAccountHeaderContainer == null) { - withAccountHeader(-1); - } - - // get the header view within the container - mAccountHeader = mAccountHeaderContainer.findViewById(R.id.material_drawer_account_header); - mStatusBarGuideline = mAccountHeaderContainer.findViewById(R.id.material_drawer_statusbar_guideline); - - //the default min header height by default 148dp - int defaultHeaderMinHeight = mActivity.getResources().getDimensionPixelSize(R.dimen.material_drawer_account_header_height); - int statusBarHeight = UIUtils.getStatusBarHeight(mActivity, true); - - // handle the height for the header - int height; - if (mHeight != null) { - height = mHeight.asPixel(mActivity); - } else { - if (mCompactStyle) { - height = mActivity.getResources().getDimensionPixelSize(R.dimen.material_drawer_account_header_height_compact); - } else { - //calculate the header height by getting the optimal drawer width and calculating it * 9 / 16 - height = (int) (DrawerUIUtils.getOptimalDrawerWidth(mActivity) * AccountHeader.NAVIGATION_DRAWER_ACCOUNT_ASPECT_RATIO); - - //if we are lower than api 19 (>= 19 we have a translucentStatusBar) the height should be a bit lower - //probably even if we are non translucent on > 19 devices? - if (Build.VERSION.SDK_INT < 19) { - int tempHeight = height - statusBarHeight; - //if we are lower than api 19 we are not able to have a translucent statusBar so we remove the height of the statusBar from the padding - //to prevent display issues we only reduce the height if we still fit the required minHeight of 148dp (R.dimen.material_drawer_account_header_height) - //we remove additional 8dp from the defaultMinHeaderHeight as there is some buffer in the header and to prevent to large spacings - if (tempHeight > defaultHeaderMinHeight - UIUtils.convertDpToPixel(8, mActivity)) { - height = tempHeight; - } - } - } - } - - // handle everything if we have a translucent status bar which only is possible on API >= 19 - if (mTranslucentStatusBar && Build.VERSION.SDK_INT >= 21) { - mStatusBarGuideline.setGuidelineBegin(statusBarHeight); - - //in fact it makes no difference if we have a translucent statusBar or not. we want 9/16 just if we are not compact - if (mCompactStyle) { - height = height + statusBarHeight; - } else if ((height - statusBarHeight) <= defaultHeaderMinHeight) { - //if the height + statusBar of the header is lower than the required 148dp + statusBar we change the height to be able to display all the data - height = defaultHeaderMinHeight + statusBarHeight; - } - } - - //set the height for the header - setHeaderHeight(height); - - // get the background view - mAccountHeaderBackground = (GifImageView) mAccountHeaderContainer.findViewById(R.id.material_drawer_account_header_background); - // set the background - ImageHolder.applyTo(mHeaderBackground, mAccountHeaderBackground, DrawerImageLoader.Tags.ACCOUNT_HEADER.name()); - - if (mHeaderBackgroundScaleType != null) { - mAccountHeaderBackground.setScaleType(mHeaderBackgroundScaleType); - } - - // get the text color to use for the text section - int textColor = ColorHolder.color(mTextColor, mActivity, R.attr.material_drawer_header_selection_text, R.color.material_drawer_header_selection_text); - int subTextColor = ColorHolder.color(mTextColor, mActivity, R.attr.material_drawer_header_selection_subtext, R.color.material_drawer_header_selection_subtext); - - mAccountHeaderTextSectionBackgroundResource = UIUtils.getSelectableBackgroundRes(mActivity); - handleSelectionView(mCurrentProfile, true); - - // set the arrow :D - mAccountSwitcherArrow = mAccountHeaderContainer.findViewById(R.id.material_drawer_account_header_text_switcher); - mAccountSwitcherArrow.setImageDrawable(new IconicsDrawable(mActivity, MaterialDrawerFont.Icon.mdf_arrow_drop_down).sizeRes(R.dimen.material_drawer_account_header_dropdown).paddingRes(R.dimen.material_drawer_account_header_dropdown_padding).color(subTextColor)); - - //get the fields for the name - mCurrentProfileView = mAccountHeader.findViewById(R.id.material_drawer_account_header_current); - mCurrentProfileName = mAccountHeader.findViewById(R.id.material_drawer_account_header_name); - mCurrentProfileEmail = mAccountHeader.findViewById(R.id.material_drawer_account_header_email); - - //set the typeface for the AccountHeader - if (mNameTypeface != null) { - mCurrentProfileName.setTypeface(mNameTypeface); - } else if (mTypeface != null) { - mCurrentProfileName.setTypeface(mTypeface); - } - - if (mEmailTypeface != null) { - mCurrentProfileEmail.setTypeface(mEmailTypeface); - } else if (mTypeface != null) { - mCurrentProfileEmail.setTypeface(mTypeface); - } - - mCurrentProfileName.setTextColor(textColor); - mCurrentProfileEmail.setTextColor(subTextColor); - - mProfileFirstView = mAccountHeader.findViewById(R.id.material_drawer_account_header_small_first); - mProfileSecondView = mAccountHeader.findViewById(R.id.material_drawer_account_header_small_second); - mProfileThirdView = mAccountHeader.findViewById(R.id.material_drawer_account_header_small_third); - - //calculate the profiles to set - calculateProfiles(); - - //process and build the profiles - buildProfiles(); - - // try to restore all saved values again - if (mSavedInstance != null) { - int selection = mSavedInstance.getInt(AccountHeader.BUNDLE_SELECTION_HEADER, -1); - if (selection != -1) { - //predefine selection (should be the first element - if (mProfiles != null && (selection) > -1 && selection < mProfiles.size()) { - switchProfiles(mProfiles.get(selection)); - } - } - } - - //everything created. now set the header - if (mDrawer != null) { - mDrawer.setHeader(mAccountHeaderContainer, mPaddingBelowHeader, mDividerBelowHeader); - } - - //forget the reference to the activity - mActivity = null; - - return new AccountHeader(this); - } - - /** - * helper method to calculate the order of the profiles - */ - protected void calculateProfiles() { - if (mProfiles == null) { - mProfiles = new ArrayList<>(); - } - - if (mCurrentProfile == null) { - int setCount = 0; - int size = mProfiles.size(); - for (int i = 0; i < size; i++) { - if (mProfiles.size() > i && mProfiles.get(i).isSelectable()) { - if (setCount == 0 && (mCurrentProfile == null)) { - mCurrentProfile = mProfiles.get(i); - } else if (setCount == 1 && (mProfileFirst == null)) { - mProfileFirst = mProfiles.get(i); - } else if (setCount == 2 && (mProfileSecond == null)) { - mProfileSecond = mProfiles.get(i); - } else if (setCount == 3 && (mProfileThird == null)) { - mProfileThird = mProfiles.get(i); - } - setCount++; - } - } - - return; - } - - IProfile[] previousActiveProfiles = new IProfile[]{ - mCurrentProfile, - mProfileFirst, - mProfileSecond, - mProfileThird - }; - - IProfile[] newActiveProfiles = new IProfile[4]; - Stack unusedProfiles = new Stack<>(); - - // try to keep existing active profiles in the same positions - for (int i = 0; i < mProfiles.size(); i++) { - IProfile p = mProfiles.get(i); - if (p.isSelectable()) { - boolean used = false; - for (int j = 0; j < 4; j++) { - if (previousActiveProfiles[j] == p) { - newActiveProfiles[j] = p; - used = true; - break; - } - } - if (!used) { - unusedProfiles.push(p); - } - } - } - - Stack activeProfiles = new Stack<>(); - // try to fill the gaps with new available profiles - for (int i = 0; i < 4; i++) { - if (newActiveProfiles[i] != null) { - activeProfiles.push(newActiveProfiles[i]); - } else if (!unusedProfiles.isEmpty()) { - activeProfiles.push(unusedProfiles.pop()); - } - } - - Stack reversedActiveProfiles = new Stack<>(); - while (!activeProfiles.empty()) { - reversedActiveProfiles.push(activeProfiles.pop()); - } - - // reassign active profiles - if (reversedActiveProfiles.isEmpty()) { - mCurrentProfile = null; - } else { - mCurrentProfile = reversedActiveProfiles.pop(); - } - if (reversedActiveProfiles.isEmpty()) { - mProfileFirst = null; - } else { - mProfileFirst = reversedActiveProfiles.pop(); - } - if (reversedActiveProfiles.isEmpty()) { - mProfileSecond = null; - } else { - mProfileSecond = reversedActiveProfiles.pop(); - } - if (reversedActiveProfiles.isEmpty()) { - mProfileThird = null; - } else { - mProfileThird = reversedActiveProfiles.pop(); - } - } - - /** - * helper method to switch the profiles - * - * @param newSelection - * @return true if the new selection was the current profile - */ - protected boolean switchProfiles(IProfile newSelection) { - if (newSelection == null) { - return false; - } - if (mCurrentProfile == newSelection) { - return true; - } - - if (mAlternativeProfileHeaderSwitching) { - int prevSelection = -1; - if (mProfileFirst == newSelection) { - prevSelection = 1; - } else if (mProfileSecond == newSelection) { - prevSelection = 2; - } else if (mProfileThird == newSelection) { - prevSelection = 3; - } - - IProfile tmp = mCurrentProfile; - mCurrentProfile = newSelection; - - if (prevSelection == 1) { - mProfileFirst = tmp; - } else if (prevSelection == 2) { - mProfileSecond = tmp; - } else if (prevSelection == 3) { - mProfileThird = tmp; - } - } else { - if (mProfiles != null) { - ArrayList previousActiveProfiles = new ArrayList<>(Arrays.asList(mCurrentProfile, mProfileFirst, mProfileSecond, mProfileThird)); - - if (previousActiveProfiles.contains(newSelection)) { - int position = -1; - - for (int i = 0; i < 4; i++) { - if (previousActiveProfiles.get(i) == newSelection) { - position = i; - break; - } - } - - if (position != -1) { - previousActiveProfiles.remove(position); - previousActiveProfiles.add(0, newSelection); - - mCurrentProfile = previousActiveProfiles.get(0); - mProfileFirst = previousActiveProfiles.get(1); - mProfileSecond = previousActiveProfiles.get(2); - mProfileThird = previousActiveProfiles.get(3); - } - } else { - mProfileThird = mProfileSecond; - mProfileSecond = mProfileFirst; - mProfileFirst = mCurrentProfile; - mCurrentProfile = newSelection; - } - } - } - - //if we only show the small profile images we have to make sure the first (would be the current selected) profile is also shown - if (mOnlySmallProfileImagesVisible) { - mProfileThird = mProfileSecond; - mProfileSecond = mProfileFirst; - mProfileFirst = mCurrentProfile; - //mCurrentProfile = mProfileThird; - } - - buildProfiles(); - - return false; - } - - /** - * helper method to build the views for the ui - */ - protected void buildProfiles() { - mCurrentProfileView.setVisibility(View.GONE); - mAccountSwitcherArrow.setVisibility(View.GONE); - mProfileFirstView.setVisibility(View.GONE); - mProfileFirstView.setOnClickListener(null); - mProfileSecondView.setVisibility(View.GONE); - mProfileSecondView.setOnClickListener(null); - mProfileThirdView.setVisibility(View.GONE); - mProfileThirdView.setOnClickListener(null); - mCurrentProfileName.setText(""); - mCurrentProfileEmail.setText(""); - - handleSelectionView(mCurrentProfile, true); - - if (mCurrentProfile != null) { - if ((mProfileImagesVisible || mOnlyMainProfileImageVisible) && !mOnlySmallProfileImagesVisible) { - setImageOrPlaceholder(mCurrentProfileView, mCurrentProfile.getIcon()); - if (mProfileImagesClickable) { - mCurrentProfileView.setOnClickListener(onCurrentProfileClickListener); - mCurrentProfileView.setOnLongClickListener(onCurrentProfileLongClickListener); - mCurrentProfileView.disableTouchFeedback(false); - } else { - mCurrentProfileView.disableTouchFeedback(true); - } - mCurrentProfileView.setVisibility(View.VISIBLE); - - mCurrentProfileView.invalidate(); - } else if (mCompactStyle) { - mCurrentProfileView.setVisibility(View.GONE); - } - - handleSelectionView(mCurrentProfile, true); - mAccountSwitcherArrow.setVisibility(View.VISIBLE); - mCurrentProfileView.setTag(R.id.material_drawer_profile_header, mCurrentProfile); - - StringHolder.applyTo(mCurrentProfile.getName(), mCurrentProfileName); - StringHolder.applyTo(mCurrentProfile.getEmail(), mCurrentProfileEmail); - - if (mProfileFirst != null && mProfileImagesVisible && !mOnlyMainProfileImageVisible) { - setImageOrPlaceholder(mProfileFirstView, mProfileFirst.getIcon()); - mProfileFirstView.setTag(R.id.material_drawer_profile_header, mProfileFirst); - if (mProfileImagesClickable) { - mProfileFirstView.setOnClickListener(onProfileClickListener); - mProfileFirstView.setOnLongClickListener(onProfileLongClickListener); - mProfileFirstView.disableTouchFeedback(false); - } else { - mProfileFirstView.disableTouchFeedback(true); - } - mProfileFirstView.setVisibility(View.VISIBLE); - mProfileFirstView.invalidate(); - } - if (mProfileSecond != null && mProfileImagesVisible && !mOnlyMainProfileImageVisible) { - setImageOrPlaceholder(mProfileSecondView, mProfileSecond.getIcon()); - mProfileSecondView.setTag(R.id.material_drawer_profile_header, mProfileSecond); - if (mProfileImagesClickable) { - mProfileSecondView.setOnClickListener(onProfileClickListener); - mProfileSecondView.setOnLongClickListener(onProfileLongClickListener); - mProfileSecondView.disableTouchFeedback(false); - } else { - mProfileSecondView.disableTouchFeedback(true); - } - mProfileSecondView.setVisibility(View.VISIBLE); - mProfileSecondView.invalidate(); - } - if (mProfileThird != null && mThreeSmallProfileImages && mProfileImagesVisible && !mOnlyMainProfileImageVisible) { - setImageOrPlaceholder(mProfileThirdView, mProfileThird.getIcon()); - mProfileThirdView.setTag(R.id.material_drawer_profile_header, mProfileThird); - if (mProfileImagesClickable) { - mProfileThirdView.setOnClickListener(onProfileClickListener); - mProfileThirdView.setOnLongClickListener(onProfileLongClickListener); - mProfileThirdView.disableTouchFeedback(false); - } else { - mProfileThirdView.disableTouchFeedback(true); - } - mProfileThirdView.setVisibility(View.VISIBLE); - mProfileThirdView.invalidate(); - } - } else if (mProfiles != null && mProfiles.size() > 0) { - IProfile profile = mProfiles.get(0); - mAccountHeader.setTag(R.id.material_drawer_profile_header, profile); - handleSelectionView(mCurrentProfile, true); - mAccountSwitcherArrow.setVisibility(View.VISIBLE); - if (mCurrentProfile != null) { - StringHolder.applyTo(mCurrentProfile.getName(), mCurrentProfileName); - StringHolder.applyTo(mCurrentProfile.getEmail(), mCurrentProfileEmail); - } - } - - if (!mSelectionFirstLineShown) { - mCurrentProfileName.setVisibility(View.GONE); - } - if (!TextUtils.isEmpty(mSelectionFirstLine)) { - mCurrentProfileName.setText(mSelectionFirstLine); - } - if (!mSelectionSecondLineShown) { - mCurrentProfileEmail.setVisibility(View.GONE); - } - if (!TextUtils.isEmpty(mSelectionSecondLine)) { - mCurrentProfileEmail.setText(mSelectionSecondLine); - } - - //if we disabled the list - if (!mSelectionListEnabled || !mSelectionListEnabledForSingleProfile && mProfileFirst == null && (mProfiles == null || mProfiles.size() == 1)) { - mAccountSwitcherArrow.setVisibility(View.GONE); - handleSelectionView(null, false); - } - - //if we disabled the list but still have set a custom listener - if (mOnAccountHeaderSelectionViewClickListener != null) { - handleSelectionView(mCurrentProfile, true); - } - } - - /** - * small helper method to set an profile image or a placeholder - * - * @param iv - * @param imageHolder - */ - private void setImageOrPlaceholder(ImageView iv, ImageHolder imageHolder) { - //cancel previous started image loading processes - DrawerImageLoader.getInstance().cancelImage(iv); - //set the placeholder - iv.setImageDrawable(DrawerImageLoader.getInstance().getImageLoader().placeholder(iv.getContext(), DrawerImageLoader.Tags.PROFILE.name())); - //set the real image (probably also the uri) - ImageHolder.applyTo(imageHolder, iv, DrawerImageLoader.Tags.PROFILE.name()); - } - - /** - * onProfileClickListener to notify onClick on the current profile image - */ - private View.OnClickListener onCurrentProfileClickListener = new View.OnClickListener() { - @Override - public void onClick(final View v) { - onProfileImageClick(v, true); - } - }; - - /** - * onProfileClickListener to notify onClick on a profile image - */ - private View.OnClickListener onProfileClickListener = new View.OnClickListener() { - @Override - public void onClick(final View v) { - onProfileImageClick(v, false); - } - }; - - /** - * calls the mOnAccountHEaderProfileImageListener and continues with the actions afterwards - * - * @param v - * @param current - */ - private void onProfileImageClick(View v, boolean current) { - IProfile profile = (IProfile) v.getTag(R.id.material_drawer_profile_header); - - boolean consumed = false; - if (mOnAccountHeaderProfileImageListener != null) { - consumed = mOnAccountHeaderProfileImageListener.onProfileImageClick(v, profile, current); - } - - //if the event was already consumed by the click don't continue. note that this will also stop the profile change event - if (!consumed) { - onProfileClick(v, current); - } - } - - /** - * onProfileLongClickListener to call the onProfileImageLongClick on the current profile image - */ - private View.OnLongClickListener onCurrentProfileLongClickListener = new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (mOnAccountHeaderProfileImageListener != null) { - IProfile profile = (IProfile) v.getTag(R.id.material_drawer_profile_header); - return mOnAccountHeaderProfileImageListener.onProfileImageLongClick(v, profile, true); - } - return false; - } - }; - - /** - * onProfileLongClickListener to call the onProfileImageLongClick on a profile image - */ - private View.OnLongClickListener onProfileLongClickListener = new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (mOnAccountHeaderProfileImageListener != null) { - IProfile profile = (IProfile) v.getTag(R.id.material_drawer_profile_header); - return mOnAccountHeaderProfileImageListener.onProfileImageLongClick(v, profile, false); - } - return false; - } - }; - - protected void onProfileClick(View v, boolean current) { - final IProfile profile = (IProfile) v.getTag(R.id.material_drawer_profile_header); - switchProfiles(profile); - - //reset the drawer content - resetDrawerContent(v.getContext()); - - //notify the MiniDrawer about the clicked profile (only if one exists and is hooked to the Drawer - if (mDrawer != null && mDrawer.getDrawerBuilder() != null && mDrawer.getDrawerBuilder().mMiniDrawer != null) { - mDrawer.getDrawerBuilder().mMiniDrawer.onProfileClick(); - } - - //notify about the changed profile - boolean consumed = false; - if (mOnAccountHeaderListener != null) { - consumed = mOnAccountHeaderListener.onProfileChanged(v, profile, current); - } - - if (!consumed) { - if (mOnProfileClickDrawerCloseDelay > 0) { - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - if (mDrawer != null) { - mDrawer.closeDrawer(); - } - } - }, mOnProfileClickDrawerCloseDelay); - } else { - if (mDrawer != null) { - mDrawer.closeDrawer(); - } - } - } - } - - /** - * get the current selection - * - * @return - */ - protected int getCurrentSelection() { - if (mCurrentProfile != null && mProfiles != null) { - int i = 0; - for (IProfile profile : mProfiles) { - if (profile == mCurrentProfile) { - return i; - } - i++; - } - } - return -1; - } - - /** - * onSelectionClickListener to notify the onClick on the checkbox - */ - private View.OnClickListener onSelectionClickListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - boolean consumed = false; - if (mOnAccountHeaderSelectionViewClickListener != null) { - consumed = mOnAccountHeaderSelectionViewClickListener.onClick(v, (IProfile) v.getTag(R.id.material_drawer_profile_header)); - } - - if (mAccountSwitcherArrow.getVisibility() == View.VISIBLE && !consumed) { - toggleSelectionList(v.getContext()); - } - } - }; - - /** - * helper method to toggle the collection - * - * @param ctx - */ - protected void toggleSelectionList(Context ctx) { - if (mDrawer != null) { - //if we already show the list. reset everything instead - if (mDrawer.switchedDrawerContent()) { - resetDrawerContent(ctx); - mSelectionListShown = false; - } else { - //build and set the drawer selection list - buildDrawerSelectionList(); - - // update the arrow image within the drawer - mAccountSwitcherArrow.clearAnimation(); - ViewCompat.animate(mAccountSwitcherArrow).rotation(180).start(); - //mAccountSwitcherArrow.setImageDrawable(new IconicsDrawable(ctx, MaterialDrawerFont.Icon.mdf_arrow_drop_up).sizeRes(R.dimen.material_drawer_account_header_dropdown).paddingRes(R.dimen.material_drawer_account_header_dropdown_padding).color(ColorHolder.color(mTextColor, ctx, R.attr.material_drawer_header_selection_text, R.color.material_drawer_header_selection_text))); - mSelectionListShown = true; - } - } - } - - /** - * helper method to build and set the drawer selection list - */ - protected void buildDrawerSelectionList() { - int selectedPosition = -1; - int position = 0; - ArrayList profileDrawerItems = new ArrayList<>(); - if (mProfiles != null) { - for (IProfile profile : mProfiles) { - if (profile == mCurrentProfile) { - if (mCurrentHiddenInList) { - continue; - } else { - selectedPosition = mDrawer.mDrawerBuilder.getItemAdapter().getGlobalPosition(position); - } - } - if (profile instanceof IDrawerItem) { - ((IDrawerItem) profile).withSetSelected(false); - profileDrawerItems.add((IDrawerItem) profile); - } - position = position + 1; - } - } - mDrawer.switchDrawerContent(onDrawerItemClickListener, onDrawerItemLongClickListener, profileDrawerItems, selectedPosition); - } - - /** - * onDrawerItemClickListener to catch the selection for the new profile! - */ - private Drawer.OnDrawerItemClickListener onDrawerItemClickListener = new Drawer.OnDrawerItemClickListener() { - @Override - public boolean onItemClick(final View view, int position, final IDrawerItem drawerItem) { - final boolean isCurrentSelectedProfile; - if (drawerItem != null && drawerItem instanceof IProfile && drawerItem.isSelectable()) { - isCurrentSelectedProfile = switchProfiles((IProfile) drawerItem); - } else { - isCurrentSelectedProfile = false; - } - - if (mResetDrawerOnProfileListClick) { - mDrawer.setOnDrawerItemClickListener(null); - } - - //wrap the onSelection call and the reset stuff within a handler to prevent lag - if (mResetDrawerOnProfileListClick && mDrawer != null && view != null && view.getContext() != null) { - resetDrawerContent(view.getContext()); - } - - //notify the MiniDrawer about the clicked profile (only if one exists and is hooked to the Drawer - if (mDrawer != null && mDrawer.getDrawerBuilder() != null && mDrawer.getDrawerBuilder().mMiniDrawer != null) { - mDrawer.getDrawerBuilder().mMiniDrawer.onProfileClick(); - } - - boolean consumed = false; - if (drawerItem != null && drawerItem instanceof IProfile) { - if (mOnAccountHeaderListener != null) { - consumed = mOnAccountHeaderListener.onProfileChanged(view, (IProfile) drawerItem, isCurrentSelectedProfile); - } - } - - //if a custom behavior was chosen via the CloseDrawerOnProfileListClick then use this. else react on the result of the onProfileChanged listener - if (mCloseDrawerOnProfileListClick != null) { - consumed = consumed && !mCloseDrawerOnProfileListClick; - } - - //totally custom handling of the drawer behavior as otherwise the selection of the profile list is set to the Drawer - if (mDrawer != null && !consumed) { - //close the drawer after click - mDrawer.mDrawerBuilder.closeDrawerDelayed(); - } - - //consume the event to prevent setting the clicked item as selected in the already switched item list - return true; - } - }; - - /** - * onDrawerItemLongClickListener to catch the longClick for a profile - */ - private Drawer.OnDrawerItemLongClickListener onDrawerItemLongClickListener = new Drawer.OnDrawerItemLongClickListener() { - @Override - public boolean onItemLongClick(View view, int position, IDrawerItem drawerItem) { - //if a longClickListener was defined use it - if (mOnAccountHeaderItemLongClickListener != null) { - final boolean isCurrentSelectedProfile; - isCurrentSelectedProfile = drawerItem != null && drawerItem.isSelected(); - - if (drawerItem != null && drawerItem instanceof IProfile) { - return mOnAccountHeaderItemLongClickListener.onProfileLongClick(view, (IProfile) drawerItem, isCurrentSelectedProfile); - } - } - return false; - } - }; - - /** - * helper method to reset the drawer content - */ - private void resetDrawerContent(Context ctx) { - if (mDrawer != null) { - mDrawer.resetDrawerContent(); - } - - mAccountSwitcherArrow.clearAnimation(); - ViewCompat.animate(mAccountSwitcherArrow).rotation(0).start(); - //mAccountSwitcherArrow.setImageDrawable(new IconicsDrawable(ctx, MaterialDrawerFont.Icon.mdf_arrow_drop_down).sizeRes(R.dimen.material_drawer_account_header_dropdown).paddingRes(R.dimen.material_drawer_account_header_dropdown_padding).color(ColorHolder.color(mTextColor, ctx, R.attr.material_drawer_header_selection_text, R.color.material_drawer_header_selection_text))); - } - - /** - * small helper class to update the header and the list - */ - protected void updateHeaderAndList() { - //recalculate the profiles - calculateProfiles(); - //update the profiles in the header - buildProfiles(); - //if we currently show the list add the new item directly to it - if (mSelectionListShown) { - buildDrawerSelectionList(); - } - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/Drawer.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/Drawer.java deleted file mode 100644 index 91446f13..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/Drawer.java +++ /dev/null @@ -1,1174 +0,0 @@ -package com.mikepenz.materialdrawer; - -import android.app.Activity; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.core.util.Pair; -import androidx.drawerlayout.widget.DrawerLayout; -import androidx.appcompat.app.ActionBarDrawerToggle; -import androidx.recyclerview.widget.RecyclerView; -import androidx.appcompat.widget.Toolbar; -import pl.droidsonroids.gif.GifDrawable; - -import android.view.View; -import android.widget.AdapterView; -import android.widget.FrameLayout; - -import com.mikepenz.fastadapter.FastAdapter; -import com.mikepenz.fastadapter.adapters.ModelAdapter; -import com.mikepenz.fastadapter.expandable.ExpandableExtension; -import com.mikepenz.fastadapter.select.SelectExtension; -import com.mikepenz.materialdrawer.holder.DimenHolder; -import com.mikepenz.materialdrawer.holder.ImageHolder; -import com.mikepenz.materialdrawer.holder.StringHolder; -import com.mikepenz.materialdrawer.model.AbstractDrawerItem; -import com.mikepenz.materialdrawer.model.ContainerDrawerItem; -import com.mikepenz.materialdrawer.model.interfaces.Badgeable; -import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; -import com.mikepenz.materialdrawer.model.interfaces.Iconable; -import com.mikepenz.materialdrawer.model.interfaces.Nameable; -import com.mikepenz.materialize.Materialize; -import com.mikepenz.materialize.view.ScrimInsetsRelativeLayout; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * Created by mikepenz on 03.02.15. - */ -public class Drawer { - /** - * BUNDLE param to store the selection - */ - protected static final String BUNDLE_SELECTION = "_selection"; - protected static final String BUNDLE_SELECTION_APPENDED = "_selection_appended"; - protected static final String BUNDLE_STICKY_FOOTER_SELECTION = "bundle_sticky_footer_selection"; - protected static final String BUNDLE_STICKY_FOOTER_SELECTION_APPENDED = "bundle_sticky_footer_selection_appended"; - protected static final String BUNDLE_DRAWER_CONTENT_SWITCHED = "bundle_drawer_content_switched"; - protected static final String BUNDLE_DRAWER_CONTENT_SWITCHED_APPENDED = "bundle_drawer_content_switched_appended"; - - /** - * Per the design guidelines, you should show the drawer on launch until the user manually - * expands it. This shared preference tracks this. - */ - protected static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned"; - - /** - * Per the design guidelines, you should show the drawer on launch until the user manually - * expands it. This shared preference tracks this. - */ - protected static final String PREF_USER_OPENED_DRAWER_BY_DRAGGING = "navigation_drawer_dragged_open"; - - - protected final DrawerBuilder mDrawerBuilder; - private FrameLayout mContentView; - - /** - * the protected Constructor for the result - * - * @param drawerBuilder - */ - protected Drawer(DrawerBuilder drawerBuilder) { - this.mDrawerBuilder = drawerBuilder; - } - - /** - * the protected getter of the mDrawerBuilder - * only used internally to prevent the default behavior of some public methods - * - * @return - */ - protected DrawerBuilder getDrawerBuilder() { - return this.mDrawerBuilder; - } - - /** - * Get the DrawerLayout of the current drawer - * - * @return - */ - public DrawerLayout getDrawerLayout() { - return this.mDrawerBuilder.mDrawerLayout; - } - - /** - * Sets the toolbar which should be used in combination with the drawer - * This will handle the ActionBarDrawerToggle for you. - * Do not set this if you are in a sub activity and want to handle the back arrow on your own - * - * @param activity - * @param toolbar the toolbar which is used in combination with the drawer - */ - public void setToolbar(@NonNull Activity activity, @NonNull Toolbar toolbar) { - setToolbar(activity, toolbar, false); - } - - /** - * Sets the toolbar which should be used in combination with the drawer - * This will handle the ActionBarDrawerToggle for you. - * Do not set this if you are in a sub activity and want to handle the back arrow on your own - * - * @param activity - * @param toolbar the toolbar which is used in combination with the drawer - * @param recreateActionBarDrawerToggle defines if the ActionBarDrawerToggle needs to be recreated with the new set Toolbar - */ - public void setToolbar(@NonNull Activity activity, @NonNull Toolbar toolbar, boolean recreateActionBarDrawerToggle) { - this.mDrawerBuilder.mToolbar = toolbar; - this.mDrawerBuilder.handleDrawerNavigation(activity, recreateActionBarDrawerToggle); - } - - /** - * Add a custom ActionBarDrawerToggle which will be used in combination with this drawer. - * - * @param actionBarDrawerToggle - */ - public void setActionBarDrawerToggle(@NonNull ActionBarDrawerToggle actionBarDrawerToggle) { - this.mDrawerBuilder.mActionBarDrawerToggleEnabled = true; - this.mDrawerBuilder.mActionBarDrawerToggle = actionBarDrawerToggle; - this.mDrawerBuilder.handleDrawerNavigation(null, false); - } - - /** - * Open the drawer - */ - public void openDrawer() { - if (mDrawerBuilder.mDrawerLayout != null && mDrawerBuilder.mSliderLayout != null) { - mDrawerBuilder.mDrawerLayout.openDrawer(mDrawerBuilder.mDrawerGravity); - } - } - - /** - * close the drawer - */ - public void closeDrawer() { - if (mDrawerBuilder.mDrawerLayout != null) { - mDrawerBuilder.mDrawerLayout.closeDrawer(mDrawerBuilder.mDrawerGravity); - } - } - - /** - * Get the current state of the drawer. - * True if the drawer is currently open. - * - * @return - */ - public boolean isDrawerOpen() { - if (mDrawerBuilder.mDrawerLayout != null && mDrawerBuilder.mSliderLayout != null) { - return mDrawerBuilder.mDrawerLayout.isDrawerOpen(mDrawerBuilder.mDrawerGravity); - } - return false; - } - - - /** - * set the insetsFrameLayout to display the content in fullscreen - * under the statusBar and navigationBar - * - * @param fullscreen - */ - public void setFullscreen(boolean fullscreen) { - if (mDrawerBuilder.mMaterialize != null) { - mDrawerBuilder.mMaterialize.setFullscreen(fullscreen); - } - } - - /** - * get the Materialize object used to beautify your activity - * - * @return - */ - public Materialize getMaterialize() { - return mDrawerBuilder.mMaterialize; - } - - - /** - * gets the already generated MiniDrawer or creates a new one - * - * @return - */ - public MiniDrawer getMiniDrawer() { - if (mDrawerBuilder.mMiniDrawer == null) { - mDrawerBuilder.mMiniDrawer = new MiniDrawer().withDrawer(this).withAccountHeader(mDrawerBuilder.mAccountHeader); - } - return mDrawerBuilder.mMiniDrawer; - } - - /** - * get the slider layout of the current drawer. - * This is the layout containing the ListView - * - * @return - */ - public ScrimInsetsRelativeLayout getSlider() { - return mDrawerBuilder.mSliderLayout; - } - - /** - * get the container frameLayout of the current drawer - * - * @return - */ - public FrameLayout getContent() { - if (mContentView == null && this.mDrawerBuilder.mDrawerLayout != null) { - mContentView = (FrameLayout) this.mDrawerBuilder.mDrawerLayout.findViewById(R.id.content_layout); - } - return mContentView; - } - - /** - * get the listView of the current drawer - * - * @return - */ - public RecyclerView getRecyclerView() { - return mDrawerBuilder.mRecyclerView; - } - - /** - * get the FastAdapter of the current drawer - * - * @return - */ - public FastAdapter getAdapter() { - return mDrawerBuilder.mAdapter; - } - - /** - * get the HeaderAdapter of the current drawer - * - * @return - */ - public ModelAdapter getHeaderAdapter() { - return mDrawerBuilder.mHeaderAdapter; - } - - /** - * get the ItemAdapter of the current drawer - * - * @return - */ - public ModelAdapter getItemAdapter() { - return mDrawerBuilder.mItemAdapter; - } - - /** - * get the FooterAdapter of the current drawer - * - * @return - */ - public ModelAdapter getFooterAdapter() { - return mDrawerBuilder.mFooterAdapter; - } - - /** - * get the ExpandableExtension of the current drawer - * - * @return - */ - public ExpandableExtension getExpandableExtension() { - return mDrawerBuilder.mExpandableExtension; - } - - /** - * get all drawerItems of the current drawer - * - * @return - */ - public List getDrawerItems() { - return mDrawerBuilder.getItemAdapter().getAdapterItems(); - } - - /** - * get the Header View if set else NULL - * - * @return - */ - public View getHeader() { - return mDrawerBuilder.mHeaderView; - } - - public void setHeaderBackground(String headerBackgroundPath) { - if (headerBackgroundPath.endsWith(".gif")) { - try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - mDrawerBuilder.mAccountHeader.mAccountHeaderBuilder.mAccountHeaderBackground.setImageURI(null); - mDrawerBuilder.mAccountHeader.mAccountHeaderBuilder.mAccountHeaderBackground.setImageDrawable(new GifDrawable(headerBackgroundPath)); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - else { - mDrawerBuilder.mAccountHeader.mAccountHeaderBuilder.mAccountHeaderBackground.setImageURI(Uri.parse(headerBackgroundPath)); - } - } - - public void setHeaderBackground(int headerBackgroundRes) { - mDrawerBuilder.mAccountHeader.mAccountHeaderBuilder.mAccountHeaderBackground.setImageResource(headerBackgroundRes); - } - - - /** - * get the StickyHeader View if set else NULL - * - * @return - */ - public View getStickyHeader() { - return mDrawerBuilder.mStickyHeaderView; - } - - /** - * method to replace a previous set header - * - * @param view - */ - public void setHeader(@NonNull View view) { - setHeader(view, true, true); - } - - /** - * method to replace a previous set header - * - * @param view - * @param divider - */ - public void setHeader(@NonNull View view, boolean divider) { - setHeader(view, true, divider); - } - - /** - * method to replace a previous set header - * - * @param view - * @param padding - * @param divider - */ - public void setHeader(@NonNull View view, boolean padding, boolean divider) { - setHeader(view, padding, divider, null); - } - - /** - * method to replace a previous set header - * - * @param view - * @param padding - * @param divider - * @param height - */ - public void setHeader(@NonNull View view, boolean padding, boolean divider, DimenHolder height) { - mDrawerBuilder.getHeaderAdapter().clear(); - if (padding) { - mDrawerBuilder.getHeaderAdapter().add(new ContainerDrawerItem().withView(view).withDivider(divider).withHeight(height).withViewPosition(ContainerDrawerItem.Position.TOP)); - } else { - mDrawerBuilder.getHeaderAdapter().add(new ContainerDrawerItem().withView(view).withDivider(divider).withHeight(height).withViewPosition(ContainerDrawerItem.Position.NONE)); - } - //we need to set the padding so the header starts on top - mDrawerBuilder.mRecyclerView.setPadding(mDrawerBuilder.mRecyclerView.getPaddingLeft(), 0, mDrawerBuilder.mRecyclerView.getPaddingRight(), mDrawerBuilder.mRecyclerView.getPaddingBottom()); - } - - /** - * method to remove the header of the list - */ - public void removeHeader() { - mDrawerBuilder.getHeaderAdapter().clear(); - //possibly there should be also a reset of the padding so the first item starts below the toolbar - } - - /** - * get the Footer View if set else NULL - * - * @return - */ - public View getFooter() { - return mDrawerBuilder.mFooterView; - } - - /** - * get the StickyFooter View if set else NULL - * - * @return - */ - public View getStickyFooter() { - return mDrawerBuilder.mStickyFooterView; - } - - /** - * get the StickyFooter Shadow View if set else NULL - * - * @return - */ - private View getStickyFooterShadow() { - return mDrawerBuilder.mStickyFooterShadowView; - } - - /** - * get the ActionBarDrawerToggle - * - * @return - */ - public ActionBarDrawerToggle getActionBarDrawerToggle() { - return mDrawerBuilder.mActionBarDrawerToggle; - } - - /** - * sets the gravity for this drawer. - * - * @param gravity the gravity which is defined for the drawer - */ - public void setGravity(int gravity) { - DrawerLayout.LayoutParams params = (DrawerLayout.LayoutParams) getSlider().getLayoutParams(); - params.gravity = gravity; - getSlider().setLayoutParams(params); - mDrawerBuilder.mDrawerGravity = gravity; - } - - /** - * calculates the position of an drawerItem. searching by it's identifier - * - * @param drawerItem - * @return - */ - public int getPosition(@NonNull IDrawerItem drawerItem) { - return getPosition(drawerItem.getIdentifier()); - } - - /** - * calculates the position of an drawerItem. searching by it's identifier - * - * @param identifier - * @return - */ - public int getPosition(long identifier) { - return DrawerUtils.getPositionByIdentifier(mDrawerBuilder, identifier); - } - - /** - * returns the DrawerItem by the given identifier - * - * @param identifier - * @return - */ - public IDrawerItem getDrawerItem(long identifier) { - Pair res = getAdapter().getItemById(identifier); - if (res != null) { - return res.first; - } else { - return null; - } - } - - /** - * returns the found drawerItem by the given tag - * - * @param tag - * @return - */ - public IDrawerItem getDrawerItem(Object tag) { - return DrawerUtils.getDrawerItem(getDrawerItems(), tag); - } - - /** - * calculates the position of an drawerItem. searching by it's identifier - * - * @param drawerItem - * @return - */ - public int getStickyFooterPosition(@NonNull IDrawerItem drawerItem) { - return getStickyFooterPosition(drawerItem.getIdentifier()); - } - - /** - * calculates the position of an drawerItem inside the footer. searching by it's identfier - * - * @param identifier - * @return - */ - public int getStickyFooterPosition(long identifier) { - return DrawerUtils.getStickyFooterPositionByIdentifier(mDrawerBuilder, identifier); - } - - /** - * get the current position of the selected drawer element - * - * @return - */ - public int getCurrentSelectedPosition() { - return mDrawerBuilder.mAdapter.getSelections().size() == 0 ? -1 : mDrawerBuilder.mAdapter.getSelections().iterator().next(); - } - - /** - * get the current selected item identifier - * - * @return - */ - public long getCurrentSelection() { - IDrawerItem drawerItem = mDrawerBuilder.getDrawerItem(getCurrentSelectedPosition()); - if (drawerItem != null) { - return drawerItem.getIdentifier(); - } - return -1; - } - - /** - * get the current position of the selected sticky footer element - * - * @return - */ - public int getCurrentStickyFooterSelectedPosition() { - return mDrawerBuilder.mCurrentStickyFooterSelection; - } - - /** - * deselects all selected items - */ - public void deselect() { - getAdapter().deselect(); - } - - /** - * deselects the item with the given identifier - * - * @param identifier the identifier to search for - */ - public void deselect(long identifier) { - getAdapter().deselect(getPosition(identifier)); - } - - /** - * set the current selection in the drawer - * NOTE: This will trigger onDrawerItemSelected without a view! - * - * @param identifier the identifier to search for - */ - public void setSelection(long identifier) { - setSelection(identifier, true); - } - - /** - * set the current selection in the drawer - * NOTE: This will trigger onDrawerItemSelected without a view if you pass fireOnClick = true; - * - * @param identifier the identifier to search for - * @param fireOnClick true if the click listener should be called - */ - public void setSelection(long identifier, boolean fireOnClick) { - SelectExtension select = getAdapter().getExtension(SelectExtension.class); - if (select != null) { - select.deselect(); - select.selectByIdentifier(identifier, false, true); - - //we also have to call the general notify - Pair res = getAdapter().getItemById(identifier); - if (res != null) { - Integer position = res.second; - notifySelect(position != null ? position : -1, fireOnClick); - } - } - } - - /** - * set the current selection in the footer of the drawer - * NOTE: This will trigger onDrawerItemSelected without a view if you pass fireOnClick = true; - * - * @param identifier the identifier to search for - * @param fireOnClick true if the click listener should be called - */ - public void setStickyFooterSelection(long identifier, boolean fireOnClick) { - setStickyFooterSelectionAtPosition(getStickyFooterPosition(identifier), fireOnClick); - } - - /** - * set the current selection in the drawer - * NOTE: This will trigger onDrawerItemSelected without a view! - * - * @param drawerItem the drawerItem to select (this requires a set identifier) - */ - public void setSelection(@NonNull IDrawerItem drawerItem) { - setSelection(drawerItem.getIdentifier(), true); - } - - /** - * set the current selection in the drawer - * NOTE: This will trigger onDrawerItemSelected without a view if you pass fireOnClick = true; - * - * @param drawerItem the drawerItem to select (this requires a set identifier) - * @param fireOnClick true if the click listener should be called - */ - public void setSelection(@NonNull IDrawerItem drawerItem, boolean fireOnClick) { - setSelection(drawerItem.getIdentifier(), fireOnClick); - } - - /** - * set the current selection in the drawer - * NOTE: This will trigger onDrawerItemSelected without a view! - * - * @param position the position to select - */ - public boolean setSelectionAtPosition(int position) { - return setSelectionAtPosition(position, true); - } - - /* - * set the current selection in the drawer - * NOTE: this also deselects all other selections. if you do not want this. use the direct api of the adater .getAdapter().select(position, fireOnClick) - * NOTE: This will trigger onDrawerItemSelected without a view if you pass fireOnClick = true; - * - * @param position - * @param fireOnClick - * @return true if the event was consumed - */ - public boolean setSelectionAtPosition(int position, boolean fireOnClick) { - if (mDrawerBuilder.mRecyclerView != null) { - SelectExtension select = getAdapter().getExtension(SelectExtension.class); - if (select != null) { - select.deselect(); - select.select(position, false); - notifySelect(position, fireOnClick); - } - } - return false; - } - - private void notifySelect(int position, boolean fireOnClick) { - if (fireOnClick && position >= 0) { - IDrawerItem item = mDrawerBuilder.mAdapter.getItem(position); - - if (item instanceof AbstractDrawerItem && ((AbstractDrawerItem) item).getOnDrawerItemClickListener() != null) { - ((AbstractDrawerItem) item).getOnDrawerItemClickListener().onItemClick(null, position, item); - } - - if (mDrawerBuilder.mOnDrawerItemClickListener != null) { - mDrawerBuilder.mOnDrawerItemClickListener.onItemClick(null, position, item); - } - } - - //we set the selection on a normal item in the drawer so we have to deselect the items in the StickyDrawer - mDrawerBuilder.resetStickyFooterSelection(); - } - - /** - * set the current selection in the footer of the drawer - * NOTE: This will trigger onDrawerItemSelected without a view! - * - * @param position the position to select - */ - public void setStickyFooterSelectionAtPosition(int position) { - setStickyFooterSelectionAtPosition(position, true); - } - - /** - * set the current selection in the footer of the drawer - * NOTE: This will trigger onDrawerItemSelected without a view if you pass fireOnClick = true; - * - * @param position - * @param fireOnClick - */ - public void setStickyFooterSelectionAtPosition(int position, boolean fireOnClick) { - DrawerUtils.setStickyFooterSelection(mDrawerBuilder, position, fireOnClick); - } - - /** - * update a specific drawer item :D - * automatically identified by its id - * - * @param drawerItem - */ - public void updateItem(@NonNull IDrawerItem drawerItem) { - updateItemAtPosition(drawerItem, getPosition(drawerItem)); - } - - /** - * update the badge for a specific drawerItem - * identified by its id - * - * @param identifier - * @param badge - */ - public void updateBadge(long identifier, StringHolder badge) { - IDrawerItem drawerItem = getDrawerItem(identifier); - if (drawerItem instanceof Badgeable) { - Badgeable badgeable = (Badgeable) drawerItem; - badgeable.withBadge(badge); - updateItem((IDrawerItem) badgeable); - } - } - - /** - * update the name for a specific drawerItem - * identified by its id - * - * @param identifier - * @param name - */ - public void updateName(long identifier, StringHolder name) { - IDrawerItem drawerItem = getDrawerItem(identifier); - if (drawerItem instanceof Nameable) { - Nameable pdi = (Nameable) drawerItem; - pdi.withName(name); - updateItem((IDrawerItem) pdi); - } - } - - /** - * update the name for a specific drawerItem - * identified by its id - * - * @param identifier - * @param image - */ - public void updateIcon(long identifier, ImageHolder image) { - IDrawerItem drawerItem = getDrawerItem(identifier); - if (drawerItem instanceof Iconable) { - Iconable pdi = (Iconable) drawerItem; - pdi.withIcon(image); - updateItem((IDrawerItem) pdi); - } - } - - /** - * Update a drawerItem at a specific position - * - * @param drawerItem - * @param position - */ - public void updateItemAtPosition(@NonNull IDrawerItem drawerItem, int position) { - if (mDrawerBuilder.checkDrawerItem(position, false)) { - mDrawerBuilder.getItemAdapter().set(position, drawerItem); - } - } - - /** - * Add a drawerItem at the end - * - * @param drawerItem - */ - public void addItem(@NonNull IDrawerItem drawerItem) { - mDrawerBuilder.getItemAdapter().add(drawerItem); - } - - /** - * Add a drawerItem at a specific position - * - * @param drawerItem - * @param position - */ - public void addItemAtPosition(@NonNull IDrawerItem drawerItem, int position) { - mDrawerBuilder.getItemAdapter().add(position, drawerItem); - } - - /** - * Set a drawerItem at a specific position - * - * @param drawerItem - * @param position - */ - public void setItemAtPosition(@NonNull IDrawerItem drawerItem, int position) { - mDrawerBuilder.getItemAdapter().add(position, drawerItem); - } - - /** - * Remove a drawerItem at a specific position - * - * @param position - */ - public void removeItemByPosition(int position) { - if (mDrawerBuilder.checkDrawerItem(position, false)) { - mDrawerBuilder.getItemAdapter().remove(position); - } - } - - /** - * Remove a drawerItem by the identifier - * - * @param identifier - */ - public void removeItem(long identifier) { - getItemAdapter().removeByIdentifier(identifier); - } - - /** - * remove a list of drawerItems by ther identifiers - * - * @param identifiers - */ - public void removeItems(long... identifiers) { - if (identifiers != null) { - for (long identifier : identifiers) { - removeItem(identifier); - } - } - } - - /** - * Removes all items from drawer - */ - public void removeAllItems() { - mDrawerBuilder.getItemAdapter().clear(); - } - - /** - * add new Items to the current DrawerItem List - * - * @param drawerItems - */ - public void addItems(@NonNull IDrawerItem... drawerItems) { - mDrawerBuilder.getItemAdapter().add(drawerItems); - } - - /** - * add new items to the current DrawerItem list at a specific position - * - * @param position - * @param drawerItems - */ - public void addItemsAtPosition(int position, @NonNull IDrawerItem... drawerItems) { - mDrawerBuilder.getItemAdapter().add(position, drawerItems); - } - - /** - * Replace the current DrawerItems with a new ArrayList of items - * - * @param drawerItems - */ - public void setItems(@NonNull List drawerItems) { - setItems(drawerItems, false); - } - - /** - * replace the current DrawerItems with the new ArrayList. - * - * @param drawerItems - * @param switchedItems - */ - private void setItems(@NonNull List drawerItems, boolean switchedItems) { - //if we are currently at a switched list set the new reference - if (originalDrawerItems != null && !switchedItems) { - originalDrawerItems = drawerItems; - } - mDrawerBuilder.getItemAdapter().setNewList(drawerItems); - } - - /** - * update a specific footerDrawerItem :D - * automatically identified by it's id - * - * @param drawerItem - */ - public void updateStickyFooterItem(@NonNull IDrawerItem drawerItem) { - updateStickyFooterItemAtPosition(drawerItem, getStickyFooterPosition(drawerItem)); - } - - /** - * update a footerDrawerItem at a specific position - * - * @param drawerItem - * @param position - */ - public void updateStickyFooterItemAtPosition(@NonNull IDrawerItem drawerItem, int position) { - if (mDrawerBuilder.mStickyDrawerItems != null && mDrawerBuilder.mStickyDrawerItems.size() > position) { - mDrawerBuilder.mStickyDrawerItems.set(position, drawerItem); - } - - DrawerUtils.rebuildStickyFooterView(mDrawerBuilder); - } - - - /** - * Add a footerDrawerItem at the end - * - * @param drawerItem - */ - public void addStickyFooterItem(@NonNull IDrawerItem drawerItem) { - if (mDrawerBuilder.mStickyDrawerItems == null) { - mDrawerBuilder.mStickyDrawerItems = new ArrayList<>(); - } - mDrawerBuilder.mStickyDrawerItems.add(drawerItem); - - DrawerUtils.rebuildStickyFooterView(mDrawerBuilder); - } - - /** - * Add a footerDrawerItem at a specific position - * - * @param drawerItem - * @param position - */ - public void addStickyFooterItemAtPosition(@NonNull IDrawerItem drawerItem, int position) { - if (mDrawerBuilder.mStickyDrawerItems == null) { - mDrawerBuilder.mStickyDrawerItems = new ArrayList<>(); - } - mDrawerBuilder.mStickyDrawerItems.add(position, drawerItem); - - DrawerUtils.rebuildStickyFooterView(mDrawerBuilder); - } - - /** - * Set a footerDrawerItem at a specific position - * - * @param drawerItem - * @param position - */ - public void setStickyFooterItemAtPosition(@NonNull IDrawerItem drawerItem, int position) { - if (mDrawerBuilder.mStickyDrawerItems != null && mDrawerBuilder.mStickyDrawerItems.size() > position) { - mDrawerBuilder.mStickyDrawerItems.set(position, drawerItem); - } - - DrawerUtils.rebuildStickyFooterView(mDrawerBuilder); - } - - - /** - * Remove a footerDrawerItem at a specific position - * - * @param position - */ - public void removeStickyFooterItemAtPosition(int position) { - if (mDrawerBuilder.mStickyDrawerItems != null && mDrawerBuilder.mStickyDrawerItems.size() > position) { - mDrawerBuilder.mStickyDrawerItems.remove(position); - } - - DrawerUtils.rebuildStickyFooterView(mDrawerBuilder); - } - - /** - * Removes all footerItems from drawer - */ - public void removeAllStickyFooterItems() { - if (mDrawerBuilder.mStickyDrawerItems != null) { - mDrawerBuilder.mStickyDrawerItems.clear(); - } - if (mDrawerBuilder.mStickyFooterView != null) { - mDrawerBuilder.mStickyFooterView.setVisibility(View.GONE); - } - } - - /** - * setter for the OnDrawerItemClickListener - * - * @param onDrawerItemClickListener - */ - public void setOnDrawerItemClickListener(OnDrawerItemClickListener onDrawerItemClickListener) { - mDrawerBuilder.mOnDrawerItemClickListener = onDrawerItemClickListener; - } - - public void setOnDrawerNavigationListener(OnDrawerNavigationListener onDrawerNavigationListener) { //WBE - mDrawerBuilder.mOnDrawerNavigationListener = onDrawerNavigationListener; - } - - /** - * method to get the OnDrawerItemClickListener - * - * @return - */ - public OnDrawerItemClickListener getOnDrawerItemClickListener() { - return mDrawerBuilder.mOnDrawerItemClickListener; - } - - /** - * method to get the OnDrawerNavigationListener - * - * @return - */ - public OnDrawerNavigationListener getOnDrawerNavigationListener() { //WBE - return mDrawerBuilder.mOnDrawerNavigationListener; - } - - /** - * setter for the OnDrawerItemLongClickListener - * - * @param onDrawerItemLongClickListener - */ - public void setOnDrawerItemLongClickListener(OnDrawerItemLongClickListener onDrawerItemLongClickListener) { - mDrawerBuilder.mOnDrawerItemLongClickListener = onDrawerItemLongClickListener; - } - - /** - * method to get the OnDrawerItemLongClickListener - * - * @return - */ - public OnDrawerItemLongClickListener getOnDrawerItemLongClickListener() { - return mDrawerBuilder.mOnDrawerItemLongClickListener; - } - - //variables to store and remember the original list of the drawer - private Drawer.OnDrawerItemClickListener originalOnDrawerItemClickListener; - private Drawer.OnDrawerItemLongClickListener originalOnDrawerItemLongClickListener; - private List originalDrawerItems; - private Bundle originalDrawerState; - - /** - * information if the current drawer content is switched by alternative content (profileItems) - * - * @return - */ - public boolean switchedDrawerContent() { - return !(originalOnDrawerItemClickListener == null && originalDrawerItems == null && originalDrawerState == null); - } - - /** - * get the original list of drawerItems - * - * @return - */ - public List getOriginalDrawerItems() { - return originalDrawerItems; - } - - /** - * method to switch the drawer content to new elements - * - * @param onDrawerItemClickListener - * @param drawerItems - * @param drawerSelection - */ - public void switchDrawerContent(@NonNull OnDrawerItemClickListener onDrawerItemClickListener, OnDrawerItemLongClickListener onDrawerItemLongClickListener, @NonNull List drawerItems, int drawerSelection) { - //just allow a single switched drawer - if (!switchedDrawerContent()) { - //save out previous values - originalOnDrawerItemClickListener = getOnDrawerItemClickListener(); - originalOnDrawerItemLongClickListener = getOnDrawerItemLongClickListener(); - originalDrawerState = getAdapter().saveInstanceState(new Bundle()); - mDrawerBuilder.mExpandableExtension.collapse(false); - originalDrawerItems = getDrawerItems(); - } - - //set the new items - setOnDrawerItemClickListener(onDrawerItemClickListener); - setOnDrawerItemLongClickListener(onDrawerItemLongClickListener); - setItems(drawerItems, true); - setSelectionAtPosition(drawerSelection, false); - - if (!mDrawerBuilder.mKeepStickyItemsVisible) { - //hide stickyFooter and it's shadow - if (getStickyFooter() != null) { - getStickyFooter().setVisibility(View.GONE); - } - if (getStickyFooterShadow() != null) { - getStickyFooterShadow().setVisibility(View.GONE); - } - } - } - - /** - * helper method to reset to the original drawerContent - */ - public void resetDrawerContent() { - if (switchedDrawerContent()) { - //set the new items - setOnDrawerItemClickListener(originalOnDrawerItemClickListener); - setOnDrawerItemLongClickListener(originalOnDrawerItemLongClickListener); - setItems(originalDrawerItems, true); - getAdapter().withSavedInstanceState(originalDrawerState); - //remove the references - originalOnDrawerItemClickListener = null; - originalOnDrawerItemLongClickListener = null; - originalDrawerItems = null; - originalDrawerState = null; - - //if we switch back scroll back to the top - mDrawerBuilder.mRecyclerView.smoothScrollToPosition(0); - - //show the stickyFooter and it's shadow again - if (getStickyFooter() != null) { - getStickyFooter().setVisibility(View.VISIBLE); - } - if (getStickyFooterShadow() != null) { - getStickyFooterShadow().setVisibility(View.VISIBLE); - } - - //if we currently show the accountHeader selection list make sure to reset this attr - if (mDrawerBuilder.mAccountHeader != null && mDrawerBuilder.mAccountHeader.mAccountHeaderBuilder != null) { - mDrawerBuilder.mAccountHeader.mAccountHeaderBuilder.mSelectionListShown = false; - } - } - } - - /** - * add the values to the bundle for saveInstanceState - * - * @param savedInstanceState - * @return - */ - public Bundle saveInstanceState(Bundle savedInstanceState) { - if (savedInstanceState != null) { - if (!mDrawerBuilder.mAppended) { - savedInstanceState = mDrawerBuilder.mAdapter.saveInstanceState(savedInstanceState, BUNDLE_SELECTION); - savedInstanceState.putInt(BUNDLE_STICKY_FOOTER_SELECTION, mDrawerBuilder.mCurrentStickyFooterSelection); - savedInstanceState.putBoolean(BUNDLE_DRAWER_CONTENT_SWITCHED, switchedDrawerContent()); - } else { - savedInstanceState = mDrawerBuilder.mAdapter.saveInstanceState(savedInstanceState, BUNDLE_SELECTION_APPENDED); - savedInstanceState.putInt(BUNDLE_STICKY_FOOTER_SELECTION_APPENDED, mDrawerBuilder.mCurrentStickyFooterSelection); - savedInstanceState.putBoolean(BUNDLE_DRAWER_CONTENT_SWITCHED_APPENDED, switchedDrawerContent()); - } - } - return savedInstanceState; - } - - - public interface OnDrawerNavigationListener { - /** - * @param clickedView - * @return true if the event was consumed - */ - boolean onNavigationClickListener(View clickedView); - } - - public interface OnDrawerItemClickListener { - /** - * @param view - * @param position - * @param drawerItem - * @return true if the event was consumed - */ - boolean onItemClick(View view, int position, IDrawerItem drawerItem); - } - - public interface OnDrawerItemLongClickListener { - /** - * @param view - * @param position - * @param drawerItem - * @return true if the event was consumed - */ - boolean onItemLongClick(View view, int position, IDrawerItem drawerItem); - } - - public interface OnDrawerListener { - /** - * @param drawerView - */ - void onDrawerOpened(View drawerView); - - /** - * @param drawerView - */ - void onDrawerClosed(View drawerView); - - /** - * @param drawerView - * @param slideOffset - */ - void onDrawerSlide(View drawerView, float slideOffset); - } - - public interface OnDrawerItemSelectedListener { - /** - * @param parent - * @param view - * @param position - * @param id - * @param drawerItem - */ - void onItemSelected(AdapterView parent, View view, int position, long id, IDrawerItem drawerItem); - - /** - * @param parent - */ - void onNothingSelected(AdapterView parent); - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/DrawerBuilder.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/DrawerBuilder.java deleted file mode 100644 index 6959ae48..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/DrawerBuilder.java +++ /dev/null @@ -1,1899 +0,0 @@ -package com.mikepenz.materialdrawer; - -import android.app.Activity; -import android.content.SharedPreferences; -import android.content.res.Configuration; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.preference.PreferenceManager; -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.annotation.DimenRes; -import androidx.annotation.DrawableRes; -import androidx.annotation.IdRes; -import androidx.annotation.LayoutRes; -import androidx.annotation.MenuRes; -import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; -import androidx.core.view.GravityCompat; -import androidx.core.view.ViewCompat; -import androidx.drawerlayout.widget.DrawerLayout; -import androidx.appcompat.app.ActionBarDrawerToggle; -import androidx.appcompat.view.SupportMenuInflater; -import androidx.appcompat.view.menu.MenuBuilder; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.appcompat.widget.Toolbar; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.LinearLayout; - -import com.mikepenz.fastadapter.FastAdapter; -import com.mikepenz.fastadapter.IAdapter; -import com.mikepenz.fastadapter.IAdapterExtension; -import com.mikepenz.fastadapter.IExpandable; -import com.mikepenz.fastadapter.IItemAdapter; -import com.mikepenz.fastadapter.adapters.ItemAdapter; -import com.mikepenz.fastadapter.adapters.ModelAdapter; -import com.mikepenz.fastadapter.expandable.ExpandableExtension; -import com.mikepenz.fastadapter.listeners.OnClickListener; -import com.mikepenz.fastadapter.listeners.OnLongClickListener; -import com.mikepenz.fastadapter.utils.DefaultIdDistributor; -import com.mikepenz.fastadapter.utils.DefaultIdDistributorImpl; -import com.mikepenz.iconics.utils.Utils; -import com.mikepenz.materialdrawer.holder.DimenHolder; -import com.mikepenz.materialdrawer.model.AbstractDrawerItem; -import com.mikepenz.materialdrawer.model.DividerDrawerItem; -import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; -import com.mikepenz.materialdrawer.model.SecondaryDrawerItem; -import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; -import com.mikepenz.materialdrawer.model.interfaces.Selectable; -import com.mikepenz.materialdrawer.util.DrawerUIUtils; -import com.mikepenz.materialize.Materialize; -import com.mikepenz.materialize.MaterializeBuilder; -import com.mikepenz.materialize.util.UIUtils; -import com.mikepenz.materialize.view.ScrimInsetsRelativeLayout; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * Created by mikepenz on 23.05.15. - */ -public class DrawerBuilder { - - // some internal vars - // variable to check if a builder is only used once - protected boolean mUsed = false; - protected int mCurrentStickyFooterSelection = -1; - protected boolean mAppended = false; - - // the activity to use - protected Activity mActivity; - protected RecyclerView.LayoutManager mLayoutManager; - protected ViewGroup mRootView; - protected Materialize mMaterialize; - public final DefaultIdDistributor idDistributor = new DefaultIdDistributorImpl(); - - /** - * default constructor - */ - public DrawerBuilder() { - getAdapter(); - } - - /** - * Construct a Drawer by passing the activity to use for the generation - * - * @param activity current activity which will contain the drawer - */ - public DrawerBuilder(@NonNull Activity activity) { - this.mRootView = (ViewGroup) activity.findViewById(android.R.id.content); - this.mActivity = activity; - this.mLayoutManager = new LinearLayoutManager(mActivity); - getAdapter(); - } - - /** - * Sets the activity which will be generated for the generation - * The activity is required and will be used to inflate the content in. - * After generation it is set to null to prevent a memory leak. - * - * @param activity current activity which will contain the drawer - */ - public DrawerBuilder withActivity(@NonNull Activity activity) { - this.mRootView = (ViewGroup) activity.findViewById(android.R.id.content); - this.mActivity = activity; - this.mLayoutManager = new LinearLayoutManager(mActivity); - return this; - } - - /** - * Sets the rootView which will host the DrawerLayout - * The content of this view will be extracted and added as the new content inside the drawerLayout - * - * @param rootView a view which will get switched out by the DrawerLayout and added as its child - */ - public DrawerBuilder withRootView(@NonNull ViewGroup rootView) { - this.mRootView = rootView; - - //disable the translucent statusBar we don't need it - withTranslucentStatusBar(false); - - return this; - } - - /** - * Sets the rootView which will host the DrawerLayout - * The content of this view will be extracted and added as the new content inside the drawerLayout - * - * @param rootViewRes the id of a view which will get switched out by the DrawerLayout and added as its child - */ - public DrawerBuilder withRootView(@IdRes int rootViewRes) { - if (mActivity == null) { - throw new RuntimeException("please pass an activity first to use this call"); - } - - return withRootView((ViewGroup) mActivity.findViewById(rootViewRes)); - } - - // set non translucent statusBar mode - protected boolean mTranslucentStatusBar = true; - - /** - * Sets that the view which hosts the DrawerLayout should have a translucent statusBar - * This is true by default, so it's possible to display the drawer under the statusBar - * - * @param translucentStatusBar sets whether the statusBar is transparent (and the drawer is displayed under it) or not - */ - public DrawerBuilder withTranslucentStatusBar(boolean translucentStatusBar) { - this.mTranslucentStatusBar = translucentStatusBar; - return this; - } - - // set if we want to display the specific Drawer below the statusBar - protected Boolean mDisplayBelowStatusBar; - - /** - * Sets that the slider of this Drawer should be displayed below the statusBar even with a translucentStatusBar - * - * @param displayBelowStatusBar sets wheter the slider of the drawer is displayed below the statusBar or not - */ - public DrawerBuilder withDisplayBelowStatusBar(boolean displayBelowStatusBar) { - this.mDisplayBelowStatusBar = displayBelowStatusBar; - return this; - } - - //defines if we want a inner shadow (used in with the MiniDrawer) - private boolean mInnerShadow = false; - - /** - * sets if the drawer should show an inner shadow or not - * - * @param innerShadow sets wheter the drawer should display an inner shadow or not - * @return - */ - public DrawerBuilder withInnerShadow(boolean innerShadow) { - this.mInnerShadow = innerShadow; - return this; - } - - // the toolbar of the activity - protected Toolbar mToolbar; - - /** - * Sets the toolbar which should be used in combination with the drawer - * This will handle the ActionBarDrawerToggle for you. - * Do not set this if you are in a sub activity and want to handle the back arrow on your own - * - * @param toolbar the toolbar which is used in combination with the drawer - */ - public DrawerBuilder withToolbar(@NonNull Toolbar toolbar) { - this.mToolbar = toolbar; - return this; - } - - // set non translucent NavigationBar mode - protected boolean mTranslucentNavigationBar = false; - - /** - * Set to true if you use a translucent NavigationBar - * - * @param translucentNavigationBar - * @return - */ - public DrawerBuilder withTranslucentNavigationBar(boolean translucentNavigationBar) { - this.mTranslucentNavigationBar = translucentNavigationBar; - - //if we disable the translucentNavigationBar it should be disabled at all - if (!translucentNavigationBar) { - this.mTranslucentNavigationBarProgrammatically = false; - } - - return this; - } - - // set to disable the translucent statusBar Programmatically - protected boolean mTranslucentNavigationBarProgrammatically = false; - - /** - * set this to true if you want a translucent navigation bar. - * - * @param translucentNavigationBarProgrammatically - * @return - */ - public DrawerBuilder withTranslucentNavigationBarProgrammatically(boolean translucentNavigationBarProgrammatically) { - this.mTranslucentNavigationBarProgrammatically = translucentNavigationBarProgrammatically; - //if we enable the programmatically translucent navigationBar we want also the normal navigationBar behavior - if (translucentNavigationBarProgrammatically) { - this.mTranslucentNavigationBar = true; - } - return this; - } - - - // set non translucent NavigationBar mode - protected boolean mFullscreen = false; - - /** - * Set to true if the used theme has a translucent statusBar - * and navigationBar and you want to manage the padding on your own. - * - * @param fullscreen - * @return - */ - public DrawerBuilder withFullscreen(boolean fullscreen) { - this.mFullscreen = fullscreen; - - if (fullscreen) { - withTranslucentStatusBar(true); - withTranslucentNavigationBar(false); - } - - return this; - } - - // set to no systemUI visible mode - protected boolean mSystemUIHidden = false; - - /** - * Set to true if you use your app in complete fullscreen mode - * with hidden statusBar and navigationBar - * - * @param systemUIHidden - * @return - */ - public DrawerBuilder withSystemUIHidden(boolean systemUIHidden) { - this.mSystemUIHidden = systemUIHidden; - - if (systemUIHidden) { - withFullscreen(systemUIHidden); - } - - return this; - } - - - // a custom view to be used instead of everything else - protected View mCustomView; - - /** - * Pass a custom view if you need a completely custom drawer - * content - * - * @param customView - * @return - */ - public DrawerBuilder withCustomView(@NonNull View customView) { - this.mCustomView = customView; - return this; - } - - // the drawerLayout to use - protected DrawerLayout mDrawerLayout; - protected ScrimInsetsRelativeLayout mSliderLayout; - - /** - * Pass a custom DrawerLayout which will be used. - * NOTE: This requires the same structure as the drawer.xml - * - * @param drawerLayout - * @return - */ - public DrawerBuilder withDrawerLayout(@NonNull DrawerLayout drawerLayout) { - this.mDrawerLayout = drawerLayout; - return this; - } - - /** - * Pass a custom DrawerLayout Resource which will be used. - * NOTE: This requires the same structure as the drawer.xml - * - * @param resLayout - * @return - */ - public DrawerBuilder withDrawerLayout(@LayoutRes int resLayout) { - if (mActivity == null) { - throw new RuntimeException("please pass an activity first to use this call"); - } - - if (resLayout != -1) { - this.mDrawerLayout = (DrawerLayout) mActivity.getLayoutInflater().inflate(resLayout, mRootView, false); - } else { - if (Build.VERSION.SDK_INT < 21) { - this.mDrawerLayout = (DrawerLayout) mActivity.getLayoutInflater().inflate(R.layout.material_drawer_fits_not, mRootView, false); - } else { - this.mDrawerLayout = (DrawerLayout) mActivity.getLayoutInflater().inflate(R.layout.material_drawer, mRootView, false); - } - } - - return this; - } - - //the background color for the slider - protected int mSliderBackgroundColor = 0; - protected int mSliderBackgroundColorRes = -1; - protected Drawable mSliderBackgroundDrawable = null; - protected int mSliderBackgroundDrawableRes = -1; - - /** - * Set the background color for the Slider. - * This is the view containing the list. - * - * @param sliderBackgroundColor - * @return - */ - public DrawerBuilder withSliderBackgroundColor(@ColorInt int sliderBackgroundColor) { - this.mSliderBackgroundColor = sliderBackgroundColor; - return this; - } - - /** - * Set the background color for the Slider from a Resource. - * This is the view containing the list. - * - * @param sliderBackgroundColorRes - * @return - */ - public DrawerBuilder withSliderBackgroundColorRes(@ColorRes int sliderBackgroundColorRes) { - this.mSliderBackgroundColorRes = sliderBackgroundColorRes; - return this; - } - - - /** - * Set the background drawable for the Slider. - * This is the view containing the list. - * - * @param sliderBackgroundDrawable - * @return - */ - public DrawerBuilder withSliderBackgroundDrawable(@NonNull Drawable sliderBackgroundDrawable) { - this.mSliderBackgroundDrawable = sliderBackgroundDrawable; - return this; - } - - - /** - * Set the background drawable for the Slider from a Resource. - * This is the view containing the list. - * - * @param sliderBackgroundDrawableRes - * @return - */ - public DrawerBuilder withSliderBackgroundDrawableRes(@DrawableRes int sliderBackgroundDrawableRes) { - this.mSliderBackgroundDrawableRes = sliderBackgroundDrawableRes; - return this; - } - - //the width of the drawer - protected int mDrawerWidth = -1; - - /** - * Set the DrawerBuilder width with a pixel value - * - * @param drawerWidthPx - * @return - */ - public DrawerBuilder withDrawerWidthPx(int drawerWidthPx) { - this.mDrawerWidth = drawerWidthPx; - return this; - } - - /** - * Set the DrawerBuilder width with a dp value - * - * @param drawerWidthDp - * @return - */ - public DrawerBuilder withDrawerWidthDp(int drawerWidthDp) { - if (mActivity == null) { - throw new RuntimeException("please pass an activity first to use this call"); - } - - this.mDrawerWidth = Utils.convertDpToPx(mActivity, drawerWidthDp); - return this; - } - - /** - * Set the DrawerBuilder width with a dimension resource - * - * @param drawerWidthRes - * @return - */ - public DrawerBuilder withDrawerWidthRes(@DimenRes int drawerWidthRes) { - if (mActivity == null) { - throw new RuntimeException("please pass an activity first to use this call"); - } - - this.mDrawerWidth = mActivity.getResources().getDimensionPixelSize(drawerWidthRes); - return this; - } - - //the gravity of the drawer - protected Integer mDrawerGravity = GravityCompat.START; - - /** - * Set the gravity for the drawer. START, LEFT | RIGHT, END - * - * @param gravity - * @return - */ - public DrawerBuilder withDrawerGravity(int gravity) { - this.mDrawerGravity = gravity; - return this; - } - - //the account selection header to use - protected AccountHeader mAccountHeader; - protected boolean mAccountHeaderSticky = false; - - /** - * Add a AccountSwitcherHeader which will be used in this drawer instance. - * NOTE: This will overwrite any set headerView. - * - * @param accountHeader - * @return - */ - public DrawerBuilder withAccountHeader(@NonNull AccountHeader accountHeader) { - return withAccountHeader(accountHeader, false); - } - - /** - * Add a AccountSwitcherHeader which will be used in this drawer instance. Pass true if it should be sticky - * NOTE: This will overwrite any set headerView or stickyHeaderView (depends on the boolean). - * - * @param accountHeader - * @param accountHeaderSticky - * @return - */ - public DrawerBuilder withAccountHeader(@NonNull AccountHeader accountHeader, boolean accountHeaderSticky) { - this.mAccountHeader = accountHeader; - this.mAccountHeaderSticky = accountHeaderSticky; - return this; - } - - // enable/disable the actionBarDrawerToggle animation - protected boolean mAnimateActionBarDrawerToggle = false; - - /** - * Set this to true if you want the ActionBarDrawerToggle to be animated. - * NOTE: This will only work if the built in ActionBarDrawerToggle is used. - * Enable it by setting withActionBarDrawerToggle to true - * - * @param actionBarDrawerToggleAnimated - * @return - */ - public DrawerBuilder withActionBarDrawerToggleAnimated(boolean actionBarDrawerToggleAnimated) { - this.mAnimateActionBarDrawerToggle = actionBarDrawerToggleAnimated; - return this; - } - - - // enable the drawer toggle / if withActionBarDrawerToggle we will autoGenerate it - protected boolean mActionBarDrawerToggleEnabled = true; - - /** - * Set this to false if you don't need the included ActionBarDrawerToggle - * - * @param actionBarDrawerToggleEnabled - * @return - */ - public DrawerBuilder withActionBarDrawerToggle(boolean actionBarDrawerToggleEnabled) { - this.mActionBarDrawerToggleEnabled = actionBarDrawerToggleEnabled; - return this; - } - - // drawer toggle - protected ActionBarDrawerToggle mActionBarDrawerToggle; - - /** - * Add a custom ActionBarDrawerToggle which will be used in combination with this drawer. - * - * @param actionBarDrawerToggle - * @return - */ - public DrawerBuilder withActionBarDrawerToggle(@NonNull ActionBarDrawerToggle actionBarDrawerToggle) { - this.mActionBarDrawerToggleEnabled = true; - this.mActionBarDrawerToggle = actionBarDrawerToggle; - return this; - } - - // defines if the drawer should scroll to top after click - protected boolean mScrollToTopAfterClick = false; - - /** - * defines if the drawer should scroll to top after click - * - * @param scrollToTopAfterClick - * @return - */ - public DrawerBuilder withScrollToTopAfterClick(boolean scrollToTopAfterClick) { - this.mScrollToTopAfterClick = scrollToTopAfterClick; - return this; - } - - - // header view - protected View mHeaderView; - protected boolean mHeaderDivider = true; - protected boolean mHeaderPadding = true; - protected DimenHolder mHeiderHeight = null; - - /** - * Add a header to the DrawerBuilder ListView. This can be any view - * - * @param headerView - * @return - */ - public DrawerBuilder withHeader(@NonNull View headerView) { - this.mHeaderView = headerView; - return this; - } - - /** - * Add a header to the DrawerBuilder ListView defined by a resource. - * - * @param headerViewRes - * @return - */ - public DrawerBuilder withHeader(@LayoutRes int headerViewRes) { - if (mActivity == null) { - throw new RuntimeException("please pass an activity first to use this call"); - } - - if (headerViewRes != -1) { - //i know there should be a root, bit i got none here - this.mHeaderView = mActivity.getLayoutInflater().inflate(headerViewRes, null, false); - } - - return this; - } - - /** - * Set this to false if you don't need the divider below the header - * - * @param headerDivider - * @return - */ - public DrawerBuilder withHeaderDivider(boolean headerDivider) { - this.mHeaderDivider = headerDivider; - return this; - } - - /** - * Set this to false if you don't need the padding below the header - * - * @param headerPadding - * @return - */ - public DrawerBuilder withHeaderPadding(boolean headerPadding) { - this.mHeaderPadding = headerPadding; - return this; - } - - /** - * Sets the header height for the header provided via `withHeader()` - * - * @param headerHeight the DimenHolder with the height we want to set for the header - * @return - */ - public DrawerBuilder withHeaderHeight(DimenHolder headerHeight) { - this.mHeiderHeight = headerHeight; - return this; - } - - // sticky view - protected View mStickyHeaderView; - // shadow shown on the top of the sticky header - protected boolean mStickyHeaderShadow = true; - - /** - * Add a sticky header below the DrawerBuilder ListView. This can be any view - * - * @param stickyHeader - * @return - */ - public DrawerBuilder withStickyHeader(@NonNull View stickyHeader) { - this.mStickyHeaderView = stickyHeader; - return this; - } - - /** - * Add a sticky header below the DrawerBuilder ListView defined by a resource. - * - * @param stickyHeaderRes - * @return - */ - public DrawerBuilder withStickyHeader(@LayoutRes int stickyHeaderRes) { - if (mActivity == null) { - throw new RuntimeException("please pass an activity first to use this call"); - } - - if (stickyHeaderRes != -1) { - //i know there should be a root, bit i got none here - this.mStickyHeaderView = mActivity.getLayoutInflater().inflate(stickyHeaderRes, null, false); - } - - return this; - } - - /** - * Set this to false if you don't want the shadow below the sticky header - * - * @param stickyHeaderShadow - * @return - */ - public DrawerBuilder withStickyHeaderShadow(boolean stickyHeaderShadow) { - this.mStickyHeaderShadow = stickyHeaderShadow; - return this; - } - - // footer view - protected View mFooterView; - protected boolean mFooterDivider = true; - protected boolean mFooterClickable = false; - - /** - * Add a footer to the DrawerBuilder ListView. This can be any view - * - * @param footerView - * @return - */ - public DrawerBuilder withFooter(@NonNull View footerView) { - this.mFooterView = footerView; - return this; - } - - /** - * Add a footer to the DrawerBuilder ListView defined by a resource. - * - * @param footerViewRes - * @return - */ - public DrawerBuilder withFooter(@LayoutRes int footerViewRes) { - if (mActivity == null) { - throw new RuntimeException("please pass an activity first to use this call"); - } - - if (footerViewRes != -1) { - //i know there should be a root, bit i got none here - this.mFooterView = mActivity.getLayoutInflater().inflate(footerViewRes, null, false); - } - - return this; - } - - /** - * Set this to true if you want the footer to be clickable - * - * @param footerClickable - * @return - */ - public DrawerBuilder withFooterClickable(boolean footerClickable) { - this.mFooterClickable = footerClickable; - return this; - } - - /** - * Set this to false if you don't need the divider above the footer - * - * @param footerDivider - * @return - */ - public DrawerBuilder withFooterDivider(boolean footerDivider) { - this.mFooterDivider = footerDivider; - return this; - } - - // sticky view - protected ViewGroup mStickyFooterView; - // divider shown on top of the sticky footer - protected boolean mStickyFooterDivider = false; - // sticky view - protected View mStickyFooterShadowView; - // shadow shown on the top of the sticky footer - protected boolean mStickyFooterShadow = true; - - /** - * Add a sticky footer below the DrawerBuilder ListView. This can be any view - * - * @param stickyFooter - * @return - */ - public DrawerBuilder withStickyFooter(@NonNull ViewGroup stickyFooter) { - this.mStickyFooterView = stickyFooter; - return this; - } - - /** - * Add a sticky footer below the DrawerBuilder ListView defined by a resource. - * - * @param stickyFooterRes - * @return - */ - public DrawerBuilder withStickyFooter(@LayoutRes int stickyFooterRes) { - if (mActivity == null) { - throw new RuntimeException("please pass an activity first to use this call"); - } - - if (stickyFooterRes != -1) { - //i know there should be a root, bit i got none here - this.mStickyFooterView = (ViewGroup) mActivity.getLayoutInflater().inflate(stickyFooterRes, null, false); - } - - return this; - } - - /** - * Set this to true if you want the divider above the sticky footer - * - * @param stickyFooterDivider - * @return - */ - public DrawerBuilder withStickyFooterDivider(boolean stickyFooterDivider) { - this.mStickyFooterDivider = stickyFooterDivider; - return this; - } - - /** - * Set this to false if you don't want the shadow on top of the sticky footer - * - * @param stickyFooterShadow - * @return - */ - public DrawerBuilder withStickyFooterShadow(boolean stickyFooterShadow) { - this.mStickyFooterShadow = stickyFooterShadow; - return this; - } - - // fire onClick after build - protected boolean mFireInitialOnClick = false; - - /** - * Set this to true if you love to get an initial onClick event after the build method is called - * - * @param fireOnInitialOnClick - * @return - */ - public DrawerBuilder withFireOnInitialOnClick(boolean fireOnInitialOnClick) { - this.mFireInitialOnClick = fireOnInitialOnClick; - return this; - } - - // if multiSelection is possible - protected boolean mMultiSelect = false; - - /** - * set this to true if you want to enable multiSelect mode inside the drawer. Note - * you will have to programmatically deselect if you want to remove all selections! - * You can disable this at a later time via .getAdapter().withMultiSelect(false) - * You can also modify all other settings of the FastAdapter via this method - * - * @param multiSelect true if multiSelect is enabled (default: false) - * @return this - */ - public DrawerBuilder withMultiSelect(boolean multiSelect) { - this.mMultiSelect = multiSelect; - return this; - } - - // item to select - protected int mSelectedItemPosition = 0; - - /** - * Set this to the index of the item, you would love to select upon start - * - * @param selectedItemPosition - * @return - */ - public DrawerBuilder withSelectedItemByPosition(int selectedItemPosition) { - this.mSelectedItemPosition = selectedItemPosition; - return this; - } - - // item to select - protected long mSelectedItemIdentifier = 0; - - /** - * Set this to the identifier of the item, you would love to select upon start - * - * @param selectedItemIdentifier - * @return - */ - public DrawerBuilder withSelectedItem(long selectedItemIdentifier) { - this.mSelectedItemIdentifier = selectedItemIdentifier; - return this; - } - - // an RecyclerView to use within the drawer :D - protected RecyclerView mRecyclerView; - - /** - * Define a custom RecyclerView which will be used in the drawer - * NOTE: this is not recommended - * - * @param recyclerView - * @return - */ - public DrawerBuilder withRecyclerView(@NonNull RecyclerView recyclerView) { - this.mRecyclerView = recyclerView; - return this; - } - - // if the adapter should enable hasStableIds to improve performance and allow animations - protected boolean mHasStableIds = false; - - /** - * define this if you want enable hasStableIds for the adapter which is generated. - * WARNING: only use this if you have set an identifer for all of your items else this could cause - * many weird things - * - * @param hasStableIds - * @return - */ - public DrawerBuilder withHasStableIds(boolean hasStableIds) { - this.mHasStableIds = hasStableIds; - if (mAdapter != null) { - mAdapter.setHasStableIds(hasStableIds); - } - return this; - } - - // an adapter to use for the list - protected FastAdapter mAdapter; - protected ModelAdapter mHeaderAdapter = new ItemAdapter<>().withIdDistributor(idDistributor); - protected ModelAdapter mItemAdapter = new ItemAdapter<>().withIdDistributor(idDistributor); - protected ModelAdapter mFooterAdapter = new ItemAdapter<>().withIdDistributor(idDistributor); - protected ExpandableExtension mExpandableExtension = new ExpandableExtension<>(); - - /** - * Define a custom Adapter which will be used in the drawer - * NOTE: this is not recommender - * WARNING: if you do this after adding items you will loose those! - * - * @param adapter the FastAdapter to use with this drawer - * @return this - */ - public DrawerBuilder withAdapter(@NonNull FastAdapter adapter) { - this.mAdapter = adapter; - //we have to rewrap as a different FastAdapter was provided - adapter.addAdapter(0, mHeaderAdapter); - adapter.addAdapter(1, mItemAdapter); - adapter.addAdapter(2, mFooterAdapter); - adapter.addExtension(mExpandableExtension); - return this; - } - - /** - * get the adapter (null safe) - * - * @return the FastAdapter used with this drawer - */ - protected FastAdapter getAdapter() { - if (mAdapter == null) { - mAdapter = FastAdapter.with(Arrays.asList(mHeaderAdapter, mItemAdapter, mFooterAdapter), Arrays.>asList(mExpandableExtension)); - mAdapter.withSelectable(true); - mAdapter.withMultiSelect(false); - mAdapter.withAllowDeselection(false); - mAdapter.setHasStableIds(mHasStableIds); - } - return mAdapter; - } - - protected IItemAdapter getItemAdapter() { - return mItemAdapter; - } - - protected IItemAdapter getHeaderAdapter() { - return mHeaderAdapter; - } - - protected IItemAdapter getFooterAdapter() { - return mFooterAdapter; - } - - // Defines a Adapter which wraps the main Adapter used in the RecyclerView to allow extended navigation and other stuff - protected RecyclerView.Adapter mAdapterWrapper; - - /** - * Defines a Adapter which wraps the main Adapter used in the RecyclerView to allow extended navigation and other stuff - * - * @param adapterWrapper - * @return - */ - public DrawerBuilder withAdapterWrapper(@NonNull RecyclerView.Adapter adapterWrapper) { - if (mAdapter == null) { - throw new RuntimeException("this adapter has to be set in conjunction to a normal adapter which is used inside this wrapper adapter"); - } - this.mAdapterWrapper = adapterWrapper; - return this; - } - - - //defines the itemAnimator to be used in conjunction with the RecyclerView - protected RecyclerView.ItemAnimator mItemAnimator = new DefaultItemAnimator(); - - /** - * defines the itemAnimator to be used in conjunction with the RecyclerView - * - * @param itemAnimator - * @return - */ - public DrawerBuilder withItemAnimator(RecyclerView.ItemAnimator itemAnimator) { - mItemAnimator = itemAnimator; - return this; - } - - /** - * Set the initial List of IDrawerItems for the Drawer - * - * @param drawerItems - * @return - */ - public DrawerBuilder withDrawerItems(@NonNull List drawerItems) { - this.getItemAdapter().set(drawerItems); - return this; - } - - /** - * Add a initial DrawerItem or a DrawerItem Array for the Drawer - * - * @param drawerItems - * @return - */ - public DrawerBuilder addDrawerItems(@NonNull IDrawerItem... drawerItems) { - this.getItemAdapter().add(drawerItems); - return this; - } - - // defines if we want to keep the sticky items visible, upon switching to the profiles - protected boolean mKeepStickyItemsVisible = false; - - /** - * Toggles if the sticky footer should stay visible upon switching to the profile list - * **WARNING** using this with stickyDrawerItems can lead to the selection not being updated correctly. Use with care - * - * @param keepStickyItemsVisible true if the sticky footer should stay visible - * @return this - */ - public DrawerBuilder withKeepStickyItemsVisible(boolean keepStickyItemsVisible) { - this.mKeepStickyItemsVisible = keepStickyItemsVisible; - return this; - } - - // always visible list in drawer - protected List mStickyDrawerItems = new ArrayList<>(); - - /** - * Set the initial List of IDrawerItems for the StickyDrawerFooter - * - * @param stickyDrawerItems - * @return - */ - public DrawerBuilder withStickyDrawerItems(@NonNull List stickyDrawerItems) { - this.mStickyDrawerItems = stickyDrawerItems; - return this; - } - - /** - * Add a initial DrawerItem or a DrawerItem Array for the StickyDrawerFooter - * - * @param stickyDrawerItems - * @return - */ - public DrawerBuilder addStickyDrawerItems(@NonNull IDrawerItem... stickyDrawerItems) { - if (this.mStickyDrawerItems == null) { - this.mStickyDrawerItems = new ArrayList<>(); - } - - Collections.addAll(this.mStickyDrawerItems, stickyDrawerItems); - - return this; - } - - /** - * Inflates the DrawerItems from a menu.xml - * - * @param menuRes - * @return - */ - public DrawerBuilder inflateMenu(@MenuRes int menuRes) { - MenuInflater menuInflater = new SupportMenuInflater(mActivity); - MenuBuilder mMenu = new MenuBuilder(mActivity); - - menuInflater.inflate(menuRes, mMenu); - - addMenuItems(mMenu, false); - - return this; - } - - /** - * helper method to init the drawerItems from a menu - * - * @param mMenu - * @param subMenu - */ - private void addMenuItems(Menu mMenu, boolean subMenu) { - int groupId = R.id.material_drawer_menu_default_group; - for (int i = 0; i < mMenu.size(); i++) { - MenuItem mMenuItem = mMenu.getItem(i); - IDrawerItem iDrawerItem; - if (!subMenu && mMenuItem.getGroupId() != groupId && mMenuItem.getGroupId() != 0) { - groupId = mMenuItem.getGroupId(); - iDrawerItem = new DividerDrawerItem(); - getItemAdapter().add(iDrawerItem); - } - if (mMenuItem.hasSubMenu()) { - iDrawerItem = new PrimaryDrawerItem() - .withName(mMenuItem.getTitle().toString()) - .withIcon(mMenuItem.getIcon()) - .withIdentifier(mMenuItem.getItemId()) - .withEnabled(mMenuItem.isEnabled()) - .withSelectable(false); - getItemAdapter().add(iDrawerItem); - addMenuItems(mMenuItem.getSubMenu(), true); - } else if (mMenuItem.getGroupId() != 0 || subMenu) { - iDrawerItem = new SecondaryDrawerItem() - .withName(mMenuItem.getTitle().toString()) - .withIcon(mMenuItem.getIcon()) - .withIdentifier(mMenuItem.getItemId()) - .withEnabled(mMenuItem.isEnabled()); - getItemAdapter().add(iDrawerItem); - } else { - iDrawerItem = new PrimaryDrawerItem() - .withName(mMenuItem.getTitle().toString()) - .withIcon(mMenuItem.getIcon()) - .withIdentifier(mMenuItem.getItemId()) - .withEnabled(mMenuItem.isEnabled()); - getItemAdapter().add(iDrawerItem); - } - } - } - - // close drawer on click - protected boolean mCloseOnClick = true; - - /** - * Set this to false if the drawer should stay opened after an item was clicked - * - * @param closeOnClick - * @return this - */ - public DrawerBuilder withCloseOnClick(boolean closeOnClick) { - this.mCloseOnClick = closeOnClick; - return this; - } - - // delay drawer close to prevent lag - protected int mDelayOnDrawerClose = 50; - - /** - * Define the delay for the drawer close operation after a click. - * This is a small trick to improve the speed (and remove lag) if you open a new activity after a DrawerItem - * was selected. - * NOTE: Disable this by passing -1 - * - * @param delayOnDrawerClose the delay in MS (-1 to disable) - * @return this - */ - public DrawerBuilder withDelayOnDrawerClose(int delayOnDrawerClose) { - this.mDelayOnDrawerClose = delayOnDrawerClose; - return this; - } - - // delay drawer click event to prevent lag (you should either choose DelayOnDrawerClose or this) - protected int mDelayDrawerClickEvent = 0; - - /** - * Define the delay for the drawer click event after a click. - * This can be used to improve performance and prevent lag, especially when you switch fragments inside the listener. - * This will ignore the boolean value you can return in the listener, as the listener is called after the drawer was closed. - * NOTE: Disable this to pass -1 - * - * @param delayDrawerClickEvent -1 to disable - * @return this - */ - public DrawerBuilder withDelayDrawerClickEvent(int delayDrawerClickEvent) { - this.mDelayDrawerClickEvent = delayDrawerClickEvent; - return this; - } - - // onDrawerListener - protected Drawer.OnDrawerListener mOnDrawerListener; - - /** - * Define a OnDrawerListener for this Drawer - * - * @param onDrawerListener - * @return this - */ - public DrawerBuilder withOnDrawerListener(@NonNull Drawer.OnDrawerListener onDrawerListener) { - this.mOnDrawerListener = onDrawerListener; - return this; - } - - // onDrawerItemClickListeners - protected Drawer.OnDrawerItemClickListener mOnDrawerItemClickListener; - - /** - * Define a OnDrawerItemClickListener for this Drawer - * - * @param onDrawerItemClickListener - * @return - */ - public DrawerBuilder withOnDrawerItemClickListener(@NonNull Drawer.OnDrawerItemClickListener onDrawerItemClickListener) { - this.mOnDrawerItemClickListener = onDrawerItemClickListener; - return this; - } - - // onDrawerItemClickListeners - protected Drawer.OnDrawerItemLongClickListener mOnDrawerItemLongClickListener; - - /** - * Define a OnDrawerItemLongClickListener for this Drawer - * - * @param onDrawerItemLongClickListener - * @return - */ - public DrawerBuilder withOnDrawerItemLongClickListener(@NonNull Drawer.OnDrawerItemLongClickListener onDrawerItemLongClickListener) { - this.mOnDrawerItemLongClickListener = onDrawerItemLongClickListener; - return this; - } - - // onDrawerListener - protected Drawer.OnDrawerNavigationListener mOnDrawerNavigationListener; - - /** - * Define a OnDrawerNavigationListener for this Drawer - * - * @param onDrawerNavigationListener - * @return this - */ - public DrawerBuilder withOnDrawerNavigationListener(@NonNull Drawer.OnDrawerNavigationListener onDrawerNavigationListener) { - this.mOnDrawerNavigationListener = onDrawerNavigationListener; - return this; - } - - //show the drawer on the first launch to show the user its there - protected boolean mShowDrawerOnFirstLaunch = false; - - /** - * define if the DrawerBuilder is shown on the first launch - * - * @param showDrawerOnFirstLaunch - * @return - */ - public DrawerBuilder withShowDrawerOnFirstLaunch(boolean showDrawerOnFirstLaunch) { - this.mShowDrawerOnFirstLaunch = showDrawerOnFirstLaunch; - return this; - } - - //show the drawer on launch to show the user its there, keep doing it until the user has dragged it open once - protected boolean mShowDrawerUntilDraggedOpened = false; - - /** - * define if the DrawerBuilder is shown until the user has dragged it open once - * - * @param showDrawerUntilDraggedOpened - * @return DrawerBuilder - */ - public DrawerBuilder withShowDrawerUntilDraggedOpened(boolean showDrawerUntilDraggedOpened) { - mShowDrawerUntilDraggedOpened = showDrawerUntilDraggedOpened; - return this; - } - - //also generate the MiniDrawer for this Drawer - protected boolean mGenerateMiniDrawer = false; - protected MiniDrawer mMiniDrawer = null; - - /** - * define if the DrawerBuilder should also generate a MiniDrawer for th - * - * @param generateMiniDrawer - * @return - */ - public DrawerBuilder withGenerateMiniDrawer(boolean generateMiniDrawer) { - this.mGenerateMiniDrawer = generateMiniDrawer; - return this; - } - - - // savedInstance to restore state - protected Bundle mSavedInstance; - - /** - * Set the Bundle (savedInstance) which is passed by the activity. - * No need to null-check everything is handled automatically - * - * @param savedInstance - * @return - */ - public DrawerBuilder withSavedInstance(Bundle savedInstance) { - this.mSavedInstance = savedInstance; - return this; - } - - // shared preferences to use for integrated functions - protected SharedPreferences mSharedPreferences; - - /** - * Set the {@link SharedPreferences} to use for the `showDrawerOnFirstLaunch` or the `ShowDrawerUntilDraggedOpened` - * - * @param sharedPreferences SharedPreference to use - * @return this - */ - public DrawerBuilder withSharedPreferences(SharedPreferences sharedPreferences) { - this.mSharedPreferences = sharedPreferences; - return this; - } - - /** - * helper method to handle when the drawer should be shown on launch - */ - private void handleShowOnLaunch() { - //check if it should be shown on launch (and we have a drawerLayout) - if (mActivity != null && mDrawerLayout != null) { - if (mShowDrawerOnFirstLaunch || mShowDrawerUntilDraggedOpened) { - final SharedPreferences preferences = mSharedPreferences != null ? mSharedPreferences : PreferenceManager.getDefaultSharedPreferences(mActivity); - - if (mShowDrawerOnFirstLaunch && !preferences.getBoolean(Drawer.PREF_USER_LEARNED_DRAWER, false)) { - //if it was not shown yet - //open the drawer - mDrawerLayout.openDrawer(mSliderLayout); - - //save that it showed up once ;) - SharedPreferences.Editor editor = preferences.edit(); - editor.putBoolean(Drawer.PREF_USER_LEARNED_DRAWER, true); - editor.apply(); - - } else if (mShowDrawerUntilDraggedOpened && !preferences.getBoolean(Drawer.PREF_USER_OPENED_DRAWER_BY_DRAGGING, false)) { - // open the drawer since the user has not dragged it open yet - mDrawerLayout.openDrawer(mSliderLayout); - - // add a listener to detect dragging - mDrawerLayout.addDrawerListener(new DrawerLayout.SimpleDrawerListener() { - boolean hasBeenDragged = false; - - @Override - public void onDrawerStateChanged(int newState) { - if (newState == DrawerLayout.STATE_DRAGGING) { - // save that the user was dragging - hasBeenDragged = true; - - } else if (newState == DrawerLayout.STATE_IDLE) { - // check if the user was dragging and if that resulted in an open drawer - if (hasBeenDragged && mDrawerLayout.isDrawerOpen(mDrawerGravity)) { - // Save that the user has dragged it open - SharedPreferences.Editor editor = preferences.edit(); - editor.putBoolean(Drawer.PREF_USER_OPENED_DRAWER_BY_DRAGGING, true); - editor.apply(); - } else { - // reset the drag boolean - hasBeenDragged = false; - } - } - } - }); - } - } - } - } - - /** - * Build and add the DrawerBuilder to your activity - * - * @return - */ - public Drawer build() { - if (mUsed) { - throw new RuntimeException("you must not reuse a DrawerBuilder builder"); - } - if (mActivity == null) { - throw new RuntimeException("please pass an activity"); - } - - //set that this builder was used. now you have to create a new one - mUsed = true; - - // if the user has not set a drawerLayout use the default one :D - if (mDrawerLayout == null) { - withDrawerLayout(-1); - } - - //some new Materialize magic ;) - mMaterialize = new MaterializeBuilder() - .withActivity(mActivity) - .withRootView(mRootView) - .withFullscreen(mFullscreen) - .withSystemUIHidden(mSystemUIHidden) - .withUseScrimInsetsLayout(false) - .withTransparentStatusBar(mTranslucentStatusBar) - .withTranslucentNavigationBarProgrammatically(mTranslucentNavigationBarProgrammatically) - .withContainer(mDrawerLayout) - .build(); - - //handle the navigation stuff of the ActionBarDrawerToggle and the drawer in general - handleDrawerNavigation(mActivity, false); - - //build the view which will be set to the drawer - Drawer result = buildView(); - - //define id for the sliderLayout - mSliderLayout.setId(R.id.material_drawer_slider_layout); - // add the slider to the drawer - mDrawerLayout.addView(mSliderLayout, 1); - - return result; - } - - /** - * Build and add the DrawerBuilder to your activity - * - * @return - */ - public Drawer buildForFragment() { - if (mUsed) { - throw new RuntimeException("you must not reuse a DrawerBuilder builder"); - } - if (mActivity == null) { - throw new RuntimeException("please pass an activity"); - } - if (mRootView == null) { - throw new RuntimeException("please pass the view which should host the DrawerLayout"); - } - - //set that this builder was used. now you have to create a new one - mUsed = true; - - // if the user has not set a drawerLayout use the default one :D - if (mDrawerLayout == null) { - withDrawerLayout(-1); - } - - //set the drawer here... - - View originalContentView = mRootView.getChildAt(0); - - boolean alreadyInflated = originalContentView.getId() == R.id.materialize_root; - - //only add the new layout if it wasn't done before - if (!alreadyInflated) { - // remove the contentView - mRootView.removeView(originalContentView); - } else { - //if it was already inflated we have to clean up again - mRootView.removeAllViews(); - } - - //create the layoutParams to use for the contentView - FrameLayout.LayoutParams layoutParamsContentView = new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - ); - - //add the drawer - mRootView.addView(mDrawerLayout, layoutParamsContentView); - - //set the id so we can check if it was already inflated - mDrawerLayout.setId(R.id.materialize_root); - - //handle the navigation stuff of the ActionBarDrawerToggle and the drawer in general - handleDrawerNavigation(mActivity, false); - - //build the view which will be set to the drawer - Drawer result = buildView(); - - // add the slider to the drawer - mDrawerLayout.addView(originalContentView, 0); - - //define id for the sliderLayout - mSliderLayout.setId(R.id.material_drawer_slider_layout); - // add the slider to the drawer - mDrawerLayout.addView(mSliderLayout, 1); - - return result; - } - - /** - * handles the different logics for the Drawer Navigation Listeners / Indications (ActionBarDrawertoggle) - */ - protected void handleDrawerNavigation(Activity activity, boolean recreateActionBarDrawerToggle) { - //set the navigationOnClickListener - final View.OnClickListener toolbarNavigationListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - boolean handled = false; - - if (mOnDrawerNavigationListener != null && (mActionBarDrawerToggle != null && !mActionBarDrawerToggle.isDrawerIndicatorEnabled())) { - handled = mOnDrawerNavigationListener.onNavigationClickListener(v); - } - if (!handled) { - if (mDrawerLayout.isDrawerOpen(mDrawerGravity)) { - mDrawerLayout.closeDrawer(mDrawerGravity); - } else { - mDrawerLayout.openDrawer(mDrawerGravity); - } - } - } - }; - - if (recreateActionBarDrawerToggle) { - mActionBarDrawerToggle = null; - } - - // create the ActionBarDrawerToggle if not set and enabled and if we have a toolbar - if (mActionBarDrawerToggleEnabled && mActionBarDrawerToggle == null && mToolbar != null) { - this.mActionBarDrawerToggle = new ActionBarDrawerToggle(activity, mDrawerLayout, mToolbar, R.string.material_drawer_open, R.string.material_drawer_close) { - @Override - public void onDrawerOpened(View drawerView) { - if (mOnDrawerListener != null) { - mOnDrawerListener.onDrawerOpened(drawerView); - } - super.onDrawerOpened(drawerView); - } - - @Override - public void onDrawerClosed(View drawerView) { - if (mOnDrawerListener != null) { - mOnDrawerListener.onDrawerClosed(drawerView); - } - super.onDrawerClosed(drawerView); - } - - @Override - public void onDrawerSlide(View drawerView, float slideOffset) { - if (mOnDrawerListener != null) { - mOnDrawerListener.onDrawerSlide(drawerView, slideOffset); - } - - if (!mAnimateActionBarDrawerToggle) { - super.onDrawerSlide(drawerView, 0); - } else { - super.onDrawerSlide(drawerView, slideOffset); - } - } - }; - this.mActionBarDrawerToggle.syncState(); - } - - //if we got a toolbar set a toolbarNavigationListener - //we also have to do this after setting the ActionBarDrawerToggle as this will overwrite this - if (mToolbar != null) { - this.mToolbar.setNavigationOnClickListener(toolbarNavigationListener); - } - - //handle the ActionBarDrawerToggle - if (mActionBarDrawerToggle != null) { - mActionBarDrawerToggle.setToolbarNavigationClickListener(toolbarNavigationListener); - mDrawerLayout.addDrawerListener(mActionBarDrawerToggle); - } else { - mDrawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() { - @Override - public void onDrawerSlide(View drawerView, float slideOffset) { - if (mOnDrawerListener != null) { - mOnDrawerListener.onDrawerSlide(drawerView, slideOffset); - } - } - - @Override - public void onDrawerOpened(View drawerView) { - if (mOnDrawerListener != null) { - mOnDrawerListener.onDrawerOpened(drawerView); - } - } - - @Override - public void onDrawerClosed(View drawerView) { - if (mOnDrawerListener != null) { - mOnDrawerListener.onDrawerClosed(drawerView); - } - } - - @Override - public void onDrawerStateChanged(int newState) { - - } - }); - } - } - - /** - * build the drawers content only. This will still return a Result object, but only with the content set. No inflating of a DrawerLayout. - * - * @return Result object with only the content set - */ - public Drawer buildView() { - // get the slider view - mSliderLayout = (ScrimInsetsRelativeLayout) mActivity.getLayoutInflater().inflate(R.layout.material_drawer_slider, mDrawerLayout, false); - mSliderLayout.setBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(mActivity, R.attr.material_drawer_background, R.color.material_drawer_background)); - // get the layout params - DrawerLayout.LayoutParams params = (DrawerLayout.LayoutParams) mSliderLayout.getLayoutParams(); - if (params != null) { - // if we've set a custom gravity set it - params.gravity = mDrawerGravity; - // if this is a drawer from the right, change the margins :D - params = DrawerUtils.processDrawerLayoutParams(this, params); - // set the new layout params - mSliderLayout.setLayoutParams(params); - } - - //create the content - createContent(); - - //create the result object - Drawer result = new Drawer(this); - //set the drawer for the accountHeader if set - if (mAccountHeader != null) { - mAccountHeader.setDrawer(result); - } - - //toggle selection list if we were previously on the account list - if (mSavedInstance != null && mSavedInstance.getBoolean(Drawer.BUNDLE_DRAWER_CONTENT_SWITCHED, false)) { - mAccountHeader.toggleSelectionList(mActivity); - } - - //handle if the drawer should be shown on launch - handleShowOnLaunch(); - - //we only want to hook a Drawer to the MiniDrawer if it is the main drawer, not the appended one - if (!mAppended && mGenerateMiniDrawer) { - // if we should create a MiniDrawer we have to do this now - mMiniDrawer = new MiniDrawer().withDrawer(result).withAccountHeader(mAccountHeader); - } - - //forget the reference to the activity - mActivity = null; - - return result; - } - - /** - * Call this method to append a new DrawerBuilder to a existing Drawer. - * - * @param result the Drawer.Result of an existing Drawer - * @return - */ - public Drawer append(@NonNull Drawer result) { - if (mUsed) { - throw new RuntimeException("you must not reuse a DrawerBuilder builder"); - } - if (mDrawerGravity == null) { - throw new RuntimeException("please set the gravity for the drawer"); - } - - //set that this builder was used. now you have to create a new one - mUsed = true; - mAppended = true; - - //get the drawer layout from the previous drawer - mDrawerLayout = result.getDrawerLayout(); - - // get the slider view - mSliderLayout = (ScrimInsetsRelativeLayout) mActivity.getLayoutInflater().inflate(R.layout.material_drawer_slider, mDrawerLayout, false); - mSliderLayout.setBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(mActivity, R.attr.material_drawer_background, R.color.material_drawer_background)); - // get the layout params - DrawerLayout.LayoutParams params = (DrawerLayout.LayoutParams) mSliderLayout.getLayoutParams(); - // set the gravity of this drawerGravity - params.gravity = mDrawerGravity; - // if this is a drawer from the right, change the margins :D - params = DrawerUtils.processDrawerLayoutParams(this, params); - // set the new params - mSliderLayout.setLayoutParams(params); - //define id for the sliderLayout - mSliderLayout.setId(R.id.material_drawer_slider_layout); - // add the slider to the drawer - mDrawerLayout.addView(mSliderLayout, 1); - - //create the content - createContent(); - - //create the result object - Drawer appendedResult = new Drawer(this); - - //toggle selection list if we were previously on the account list - if (mSavedInstance != null && mSavedInstance.getBoolean(Drawer.BUNDLE_DRAWER_CONTENT_SWITCHED_APPENDED, false)) { - mAccountHeader.toggleSelectionList(mActivity); - } - - //forget the reference to the activity - mActivity = null; - - return appendedResult; - } - - /** - * the helper method to create the content for the drawer - */ - private void createContent() { - //if we have a customView use this - if (mCustomView != null) { - LinearLayout.LayoutParams contentParams = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - ); - contentParams.weight = 1f; - mSliderLayout.addView(mCustomView, contentParams); - return; - } - - //set the shadow for the drawer - if (Build.VERSION.SDK_INT < 21 && mDrawerLayout != null) { - if (ViewCompat.getLayoutDirection(mRootView) == ViewCompat.LAYOUT_DIRECTION_LTR) { - mDrawerLayout.setDrawerShadow(mDrawerGravity == GravityCompat.START ? R.drawable.material_drawer_shadow_right : R.drawable.material_drawer_shadow_left, mDrawerGravity); - } else { - mDrawerLayout.setDrawerShadow(mDrawerGravity == GravityCompat.START ? R.drawable.material_drawer_shadow_left : R.drawable.material_drawer_shadow_right, mDrawerGravity); - } - } - - // if we have an adapter (either by defining a custom one or the included one add a list :D - View contentView; - if (mRecyclerView == null) { - contentView = LayoutInflater.from(mActivity).inflate(R.layout.material_drawer_recycler_view, mSliderLayout, false); - mRecyclerView = (RecyclerView) contentView.findViewById(R.id.material_drawer_recycler_view); - //set the itemAnimator - mRecyclerView.setItemAnimator(mItemAnimator); - //some style improvements on older devices - mRecyclerView.setFadingEdgeLength(0); - - //set the drawing cache background to the same color as the slider to improve performance - //mRecyclerView.setDrawingCacheBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(mActivity, R.attr.material_drawer_background, R.color.material_drawer_background)); - mRecyclerView.setClipToPadding(false); - //additional stuff - mRecyclerView.setLayoutManager(mLayoutManager); - - int paddingTop = 0; - if ((mDisplayBelowStatusBar == null || mDisplayBelowStatusBar) && !mSystemUIHidden) { - paddingTop = UIUtils.getStatusBarHeight(mActivity); - } - int paddingBottom = 0; - int orientation = mActivity.getResources().getConfiguration().orientation; - if (((mTranslucentNavigationBar || mFullscreen) && Build.VERSION.SDK_INT >= 21) && !mSystemUIHidden - && (orientation == Configuration.ORIENTATION_PORTRAIT - || (orientation == Configuration.ORIENTATION_LANDSCAPE - && DrawerUIUtils.isSystemBarOnBottom(mActivity)))) { - paddingBottom = UIUtils.getNavigationBarHeight(mActivity); - } - - mRecyclerView.setPadding(0, paddingTop, 0, paddingBottom); - } else { - contentView = mRecyclerView; - } - - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - ); - params.weight = 1f; - mSliderLayout.addView(contentView, params); - - if (mInnerShadow) { - View innerShadow = mSliderLayout.findViewById(R.id.material_drawer_inner_shadow); - innerShadow.setVisibility(View.VISIBLE); - innerShadow.bringToFront(); - if (mDrawerGravity == GravityCompat.START) { - innerShadow.setBackgroundResource(R.drawable.material_drawer_shadow_left); - } else { - innerShadow.setBackgroundResource(R.drawable.material_drawer_shadow_right); - } - } - - // set the background - if (mSliderBackgroundColor != 0) { - mSliderLayout.setBackgroundColor(mSliderBackgroundColor); - } else if (mSliderBackgroundColorRes != -1) { - mSliderLayout.setBackgroundColor(ContextCompat.getColor(mActivity, mSliderBackgroundColorRes)); - } else if (mSliderBackgroundDrawable != null) { - UIUtils.setBackground(mSliderLayout, mSliderBackgroundDrawable); - } else if (mSliderBackgroundDrawableRes != -1) { - UIUtils.setBackground(mSliderLayout, mSliderBackgroundDrawableRes); - } - - //handle the header - DrawerUtils.handleHeaderView(this); - - //handle the footer - DrawerUtils.handleFooterView(this, new View.OnClickListener() { - @Override - public void onClick(View v) { - IDrawerItem drawerItem = (IDrawerItem) v.getTag(R.id.material_drawer_item); - DrawerUtils.onFooterDrawerItemClick(DrawerBuilder.this, drawerItem, v, true); - } - }); - - //if MultiSelect is possible - mAdapter.withMultiSelect(mMultiSelect); - if (mMultiSelect) { - mAdapter.withSelectOnLongClick(false); - mAdapter.withAllowDeselection(true); - } - - //set the adapter on the listView - if (mAdapterWrapper == null) { - mRecyclerView.setAdapter(mAdapter); - } else { - mRecyclerView.setAdapter(mAdapterWrapper); - } - - //predefine selection (should be the first element - if (mSelectedItemPosition == 0 && mSelectedItemIdentifier != 0L) { - mSelectedItemPosition = DrawerUtils.getPositionByIdentifier(this, mSelectedItemIdentifier); - } - if (mHeaderView != null && mSelectedItemPosition == 0) { - mSelectedItemPosition = 1; - } - mAdapter.deselect(); - mAdapter.select(mSelectedItemPosition); - - // add the onDrawerItemClickListener if set - mAdapter.withOnClickListener(new OnClickListener() { - @Override - public boolean onClick(final View view, IAdapter adapter, final IDrawerItem item, final int position) { - if (!(item != null && item instanceof Selectable && !item.isSelectable())) { - resetStickyFooterSelection(); - mCurrentStickyFooterSelection = -1; - } - - //call the listener - boolean consumed = false; - - //call the item specific listener - if (item instanceof AbstractDrawerItem && ((AbstractDrawerItem) item).getOnDrawerItemClickListener() != null) { - consumed = ((AbstractDrawerItem) item).getOnDrawerItemClickListener().onItemClick(view, position, item); - } - - //call the drawer listener - if (mOnDrawerItemClickListener != null) { - if (mDelayDrawerClickEvent > 0) { - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - mOnDrawerItemClickListener.onItemClick(view, position, item); - } - }, mDelayDrawerClickEvent); - } else { - consumed = mOnDrawerItemClickListener.onItemClick(view, position, item); - } - } - - //we have to notify the miniDrawer if existing, and if the event was not consumed yet - if (!consumed && mMiniDrawer != null) { - consumed = mMiniDrawer.onItemClick(item); - } - - //if we were a expandable item we consume the event closing makes no sense - if (item instanceof IExpandable && ((IExpandable) item).getSubItems() != null) { - //we consume the event and want no further handling - return true; - } - - - if (!consumed) { - //close the drawer after click - closeDrawerDelayed(); - } - - return consumed; - } - }); - // add the onDrawerItemLongClickListener if set - mAdapter.withOnLongClickListener(new OnLongClickListener() { - @Override - public boolean onLongClick(View view, IAdapter adapter, final IDrawerItem item, final int position) { - if (mOnDrawerItemLongClickListener != null) { - return mOnDrawerItemLongClickListener.onItemLongClick(view, position, getDrawerItem(position)); - } - return false; - } - }); - - if (mRecyclerView != null) { - mRecyclerView.scrollToPosition(0); - } - - // try to restore all saved values again - if (mSavedInstance != null) { - if (!mAppended) { - mAdapter.deselect(); - mAdapter.withSavedInstanceState(mSavedInstance, Drawer.BUNDLE_SELECTION); - DrawerUtils.setStickyFooterSelection(this, mSavedInstance.getInt(Drawer.BUNDLE_STICKY_FOOTER_SELECTION, -1), null); - } else { - mAdapter.deselect(); - mAdapter.withSavedInstanceState(mSavedInstance, Drawer.BUNDLE_SELECTION_APPENDED); - DrawerUtils.setStickyFooterSelection(this, mSavedInstance.getInt(Drawer.BUNDLE_STICKY_FOOTER_SELECTION_APPENDED, -1), null); - } - } - - // call initial onClick event to allow the dev to init the first view - if (mFireInitialOnClick && mOnDrawerItemClickListener != null) { - int selection = mAdapter.getSelections().size() == 0 ? -1 : mAdapter.getSelections().iterator().next(); - mOnDrawerItemClickListener.onItemClick(null, selection, getDrawerItem(selection)); - } - } - - /** - * helper method to close the drawer delayed - */ - protected void closeDrawerDelayed() { - if (mCloseOnClick && mDrawerLayout != null) { - if (mDelayOnDrawerClose > -1) { - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - mDrawerLayout.closeDrawers(); - - if (mScrollToTopAfterClick) { - mRecyclerView.smoothScrollToPosition(0); - } - } - }, mDelayOnDrawerClose); - } else { - mDrawerLayout.closeDrawers(); - } - } - } - - /** - * get the drawerItem at a specific position - * - * @param position - * @return - */ - protected IDrawerItem getDrawerItem(int position) { - return (IDrawerItem) getAdapter().getItem(position); - } - - /** - * check if the item is within the bounds of the list - * - * @param position - * @param includeOffset - * @return - */ - protected boolean checkDrawerItem(int position, boolean includeOffset) { - return getAdapter().getItem(position) != null; - } - - /** - * simple helper method to reset the selection of the sticky footer - */ - protected void resetStickyFooterSelection() { - if (mStickyFooterView instanceof LinearLayout) { - for (int i = 0; i < (mStickyFooterView).getChildCount(); i++) { - (mStickyFooterView).getChildAt(i).setActivated(false); - (mStickyFooterView).getChildAt(i).setSelected(false); - } - } - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/DrawerUtils.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/DrawerUtils.java deleted file mode 100644 index 87f1f746..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/DrawerUtils.java +++ /dev/null @@ -1,447 +0,0 @@ -package com.mikepenz.materialdrawer; - -import android.content.Context; -import android.os.Build; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; - -import com.mikepenz.materialdrawer.model.AbstractDrawerItem; -import com.mikepenz.materialdrawer.model.ContainerDrawerItem; -import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; -import com.mikepenz.materialdrawer.model.interfaces.Selectable; -import com.mikepenz.materialdrawer.util.DrawerUIUtils; -import com.mikepenz.materialize.util.UIUtils; - -import java.util.List; - -import androidx.drawerlayout.widget.DrawerLayout; - -/** - * Created by mikepenz on 23.05.15. - */ -class DrawerUtils { - /** - * helper method to handle the onClick of the footer - * - * @param drawer - * @param drawerItem - * @param v - * @param fireOnClick true if we should call the listener, false if not, null to not call the listener and not close the drawer - */ - public static void onFooterDrawerItemClick(DrawerBuilder drawer, IDrawerItem drawerItem, View v, Boolean fireOnClick) { - boolean checkable = !(drawerItem != null && drawerItem instanceof Selectable && !drawerItem.isSelectable()); - if (checkable) { - drawer.resetStickyFooterSelection(); - - v.setActivated(true); - v.setSelected(true); - - //remove the selection in the list - drawer.getAdapter().deselect(); - - //find the position of the clicked footer item - if (drawer.mStickyFooterView != null && drawer.mStickyFooterView instanceof LinearLayout) { - LinearLayout footer = (LinearLayout) drawer.mStickyFooterView; - for (int i = 0; i < footer.getChildCount(); i++) { - if (footer.getChildAt(i) == v) { - drawer.mCurrentStickyFooterSelection = i; - break; - } - } - } - } - - - if (fireOnClick != null) { - boolean consumed = false; - - if (fireOnClick) { - if (drawerItem instanceof AbstractDrawerItem && ((AbstractDrawerItem) drawerItem).getOnDrawerItemClickListener() != null) { - consumed = ((AbstractDrawerItem) drawerItem).getOnDrawerItemClickListener().onItemClick(v, -1, drawerItem); - } - - if (drawer.mOnDrawerItemClickListener != null) { - consumed = drawer.mOnDrawerItemClickListener.onItemClick(v, -1, drawerItem); - } - } - - if (!consumed) { - //close the drawer after click - drawer.closeDrawerDelayed(); - } - } - } - - /** - * helper method to set the selection of the footer - * - * @param drawer - * @param position - * @param fireOnClick - */ - public static void setStickyFooterSelection(DrawerBuilder drawer, int position, Boolean fireOnClick) { - if (position > -1) { - if (drawer.mStickyFooterView != null && drawer.mStickyFooterView instanceof LinearLayout) { - LinearLayout footer = (LinearLayout) drawer.mStickyFooterView; - if (drawer.mStickyFooterDivider) { - position = position + 1; - } - if (footer.getChildCount() > position && position >= 0) { - IDrawerItem drawerItem = (IDrawerItem) footer.getChildAt(position).getTag(R.id.material_drawer_item); - onFooterDrawerItemClick(drawer, drawerItem, footer.getChildAt(position), fireOnClick); - } - } - } - } - - /** - * calculates the position of an drawerItem. searching by it's identifier - * - * @param identifier - * @return - */ - public static int getPositionByIdentifier(DrawerBuilder drawer, long identifier) { - if (identifier != -1) { - for (int i = 0; i < drawer.getAdapter().getItemCount(); i++) { - if (drawer.getAdapter().getItem(i).getIdentifier() == identifier) { - return i; - } - } - } - - return -1; - } - - /** - * gets the drawerItem with the specific identifier from a drawerItem list - * - * @param drawerItems - * @param identifier - * @return - */ - public static IDrawerItem getDrawerItem(List drawerItems, long identifier) { - if (identifier != -1) { - for (IDrawerItem drawerItem : drawerItems) { - if (drawerItem.getIdentifier() == identifier) { - return drawerItem; - } - } - } - return null; - } - - /** - * gets the drawerItem by a defined tag from a drawerItem list - * - * @param drawerItems - * @param tag - * @return - */ - public static IDrawerItem getDrawerItem(List drawerItems, Object tag) { - if (tag != null) { - for (IDrawerItem drawerItem : drawerItems) { - if (tag.equals(drawerItem.getTag())) { - return drawerItem; - } - } - } - return null; - } - - /** - * calculates the position of an drawerItem inside the footer. searching by it's identifier - * - * @param identifier - * @return - */ - public static int getStickyFooterPositionByIdentifier(DrawerBuilder drawer, long identifier) { - if (identifier != -1) { - if (drawer.mStickyFooterView != null && drawer.mStickyFooterView instanceof LinearLayout) { - LinearLayout footer = (LinearLayout) drawer.mStickyFooterView; - - int shadowOffset = 0; - for (int i = 0; i < footer.getChildCount(); i++) { - Object o = footer.getChildAt(i).getTag(R.id.material_drawer_item); - - //count up the shadowOffset to return the correct position of the given item - if (o == null && drawer.mStickyFooterDivider) { - shadowOffset = shadowOffset + 1; - } - - if (o != null && o instanceof IDrawerItem && ((IDrawerItem) o).getIdentifier() == identifier) { - return i - shadowOffset; - } - } - } - } - - return -1; - } - - /** - * helper method to handle the headerView - * - * @param drawer - */ - public static void handleHeaderView(DrawerBuilder drawer) { - //use the AccountHeader if set - if (drawer.mAccountHeader != null) { - if (drawer.mAccountHeaderSticky) { - drawer.mStickyHeaderView = drawer.mAccountHeader.getView(); - } else { - drawer.mHeaderView = drawer.mAccountHeader.getView(); - drawer.mHeaderDivider = drawer.mAccountHeader.mAccountHeaderBuilder.mDividerBelowHeader; - drawer.mHeaderPadding = drawer.mAccountHeader.mAccountHeaderBuilder.mPaddingBelowHeader; - } - } - - //sticky header view - if (drawer.mStickyHeaderView != null) { - //add the sticky footer view and align it to the bottom - RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT); - layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, 1); - drawer.mStickyHeaderView.setId(R.id.material_drawer_sticky_header); - drawer.mSliderLayout.addView(drawer.mStickyHeaderView, 0, layoutParams); - - //now align the recyclerView below the stickyFooterView ;) - RelativeLayout.LayoutParams layoutParamsListView = (RelativeLayout.LayoutParams) drawer.mRecyclerView.getLayoutParams(); - layoutParamsListView.addRule(RelativeLayout.BELOW, R.id.material_drawer_sticky_header); - drawer.mRecyclerView.setLayoutParams(layoutParamsListView); - - //set a background color or the elevation will not work - drawer.mStickyHeaderView.setBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(drawer.mActivity, R.attr.material_drawer_background, R.color.material_drawer_background)); - - if (drawer.mStickyHeaderShadow) { - //add a shadow - if (Build.VERSION.SDK_INT >= 21) { - drawer.mStickyHeaderView.setElevation(UIUtils.convertDpToPixel(4, drawer.mActivity)); - } else { - View view = new View(drawer.mActivity); - view.setBackgroundResource(R.drawable.material_drawer_shadow_bottom); - drawer.mSliderLayout.addView(view, RelativeLayout.LayoutParams.MATCH_PARENT, (int) UIUtils.convertDpToPixel(4, drawer.mActivity)); - //now align the shadow below the stickyHeader ;) - RelativeLayout.LayoutParams lps = (RelativeLayout.LayoutParams) view.getLayoutParams(); - lps.addRule(RelativeLayout.BELOW, R.id.material_drawer_sticky_header); - view.setLayoutParams(lps); - } - } - - //remove the padding of the recyclerView again we have the header on top of it - drawer.mRecyclerView.setPadding(0, 0, 0, 0); - } - - // set the header (do this before the setAdapter because some devices will crash else - if (drawer.mHeaderView != null) { - if (drawer.mRecyclerView == null) { - throw new RuntimeException("can't use a headerView without a recyclerView"); - } - - if (drawer.mHeaderPadding) { - drawer.getHeaderAdapter().add(new ContainerDrawerItem().withView(drawer.mHeaderView).withHeight(drawer.mHeiderHeight).withDivider(drawer.mHeaderDivider).withViewPosition(ContainerDrawerItem.Position.TOP)); - } else { - drawer.getHeaderAdapter().add(new ContainerDrawerItem().withView(drawer.mHeaderView).withHeight(drawer.mHeiderHeight).withDivider(drawer.mHeaderDivider).withViewPosition(ContainerDrawerItem.Position.NONE)); - } - //set the padding on the top to 0 - drawer.mRecyclerView.setPadding(drawer.mRecyclerView.getPaddingLeft(), 0, drawer.mRecyclerView.getPaddingRight(), drawer.mRecyclerView.getPaddingBottom()); - } - } - - /** - * small helper to rebuild the FooterView - * - * @param drawer - */ - public static void rebuildStickyFooterView(final DrawerBuilder drawer) { - if (drawer.mSliderLayout != null) { - if (drawer.mStickyFooterView != null) { - drawer.mStickyFooterView.removeAllViews(); - - //create the divider - if (drawer.mStickyFooterDivider) { - addStickyFooterDivider(drawer.mStickyFooterView.getContext(), drawer.mStickyFooterView); - } - - //fill the footer with items - DrawerUtils.fillStickyDrawerItemFooter(drawer, drawer.mStickyFooterView, new View.OnClickListener() { - @Override - public void onClick(View v) { - IDrawerItem drawerItem = (IDrawerItem) v.getTag(R.id.material_drawer_item); - com.mikepenz.materialdrawer.DrawerUtils.onFooterDrawerItemClick(drawer, drawerItem, v, true); - } - }); - - drawer.mStickyFooterView.setVisibility(View.VISIBLE); - } else { - //there was no footer yet. now just create one - DrawerUtils.handleFooterView(drawer, new View.OnClickListener() { - @Override - public void onClick(View v) { - IDrawerItem drawerItem = (IDrawerItem) v.getTag(R.id.material_drawer_item); - DrawerUtils.onFooterDrawerItemClick(drawer, drawerItem, v, true); - } - }); - } - - setStickyFooterSelection(drawer, drawer.mCurrentStickyFooterSelection, false); - } - } - - /** - * helper method to handle the footerView - * - * @param drawer - */ - public static void handleFooterView(DrawerBuilder drawer, View.OnClickListener onClickListener) { - Context ctx = drawer.mSliderLayout.getContext(); - - //use the StickyDrawerItems if set - if (drawer.mStickyDrawerItems != null && drawer.mStickyDrawerItems.size() > 0) { - drawer.mStickyFooterView = DrawerUtils.buildStickyDrawerItemFooter(ctx, drawer, onClickListener); - } - - //sticky footer view - if (drawer.mStickyFooterView != null) { - //add the sticky footer view and align it to the bottom - RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT); - layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 1); - drawer.mStickyFooterView.setId(R.id.material_drawer_sticky_footer); - drawer.mSliderLayout.addView(drawer.mStickyFooterView, layoutParams); - - if ((drawer.mTranslucentNavigationBar || drawer.mFullscreen) && Build.VERSION.SDK_INT >= 19) { - drawer.mStickyFooterView.setPadding(0, 0, 0, UIUtils.getNavigationBarHeight(ctx)); - } - - //now align the recyclerView above the stickyFooterView ;) - RelativeLayout.LayoutParams layoutParamsListView = (RelativeLayout.LayoutParams) drawer.mRecyclerView.getLayoutParams(); - layoutParamsListView.addRule(RelativeLayout.ABOVE, R.id.material_drawer_sticky_footer); - drawer.mRecyclerView.setLayoutParams(layoutParamsListView); - - //handle shadow on top of the sticky footer - if (drawer.mStickyFooterShadow) { - drawer.mStickyFooterShadowView = new View(ctx); - drawer.mStickyFooterShadowView.setBackgroundResource(R.drawable.material_drawer_shadow_top); - drawer.mSliderLayout.addView(drawer.mStickyFooterShadowView, RelativeLayout.LayoutParams.MATCH_PARENT, ctx.getResources().getDimensionPixelSize(R.dimen.material_drawer_sticky_footer_elevation)); - //now align the shadow below the stickyHeader ;) - RelativeLayout.LayoutParams lps = (RelativeLayout.LayoutParams) drawer.mStickyFooterShadowView.getLayoutParams(); - lps.addRule(RelativeLayout.ABOVE, R.id.material_drawer_sticky_footer); - drawer.mStickyFooterShadowView.setLayoutParams(lps); - } - - //remove the padding of the recyclerView again we have the footer below it - drawer.mRecyclerView.setPadding(drawer.mRecyclerView.getPaddingLeft(), drawer.mRecyclerView.getPaddingTop(), drawer.mRecyclerView.getPaddingRight(), ctx.getResources().getDimensionPixelSize(R.dimen.material_drawer_padding)); - } - - // set the footer (do this before the setAdapter because some devices will crash else - if (drawer.mFooterView != null) { - if (drawer.mRecyclerView == null) { - throw new RuntimeException("can't use a footerView without a recyclerView"); - } - - if (drawer.mFooterDivider) { - drawer.getFooterAdapter().add(new ContainerDrawerItem().withView(drawer.mFooterView).withViewPosition(ContainerDrawerItem.Position.BOTTOM)); - } else { - drawer.getFooterAdapter().add(new ContainerDrawerItem().withView(drawer.mFooterView).withViewPosition(ContainerDrawerItem.Position.NONE)); - } - } - } - - - /** - * build the sticky footer item view - * - * @return - */ - public static ViewGroup buildStickyDrawerItemFooter(Context ctx, DrawerBuilder drawer, View.OnClickListener onClickListener) { - //create the container view - final LinearLayout linearLayout = new LinearLayout(ctx); - linearLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - linearLayout.setOrientation(LinearLayout.VERTICAL); - //set the background color to the drawer background color (if it has alpha the shadow won't be visible) - linearLayout.setBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(ctx, R.attr.material_drawer_background, R.color.material_drawer_background)); - - //create the divider - if (drawer.mStickyFooterDivider) { - addStickyFooterDivider(ctx, linearLayout); - } - - fillStickyDrawerItemFooter(drawer, linearLayout, onClickListener); - - return linearLayout; - } - - /** - * adds the shadow to the stickyFooter - * - * @param ctx - * @param footerView - */ - private static void addStickyFooterDivider(Context ctx, ViewGroup footerView) { - LinearLayout divider = new LinearLayout(ctx); - LinearLayout.LayoutParams dividerParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - divider.setMinimumHeight((int) UIUtils.convertDpToPixel(1, ctx)); - divider.setOrientation(LinearLayout.VERTICAL); - divider.setBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(ctx, R.attr.material_drawer_divider, R.color.material_drawer_divider)); - footerView.addView(divider, dividerParams); - } - - /** - * helper method to fill the sticky footer with it's elements - * - * @param drawer - * @param container - * @param onClickListener - */ - public static void fillStickyDrawerItemFooter(DrawerBuilder drawer, ViewGroup container, View.OnClickListener onClickListener) { - //add all drawer items - for (IDrawerItem drawerItem : drawer.mStickyDrawerItems) { - View view = drawerItem.generateView(container.getContext(), container); - view.setTag(drawerItem); - - if (drawerItem.isEnabled()) { - //UIUtils.setBackground(view, UIUtils.getSelectableBackground(container.getContext(), selected_color, drawerItem.isSelectedBackgroundAnimated())); - view.setOnClickListener(onClickListener); - } - - container.addView(view); - - //for android API 17 --> Padding not applied via xml - DrawerUIUtils.setDrawerVerticalPadding(view); - } - //and really. don't ask about this. it won't set the padding if i don't set the padding for the container - container.setPadding(0, 0, 0, 0); - } - - - /** - * helper to extend the layoutParams of the drawer - * - * @param params - * @return - */ - public static DrawerLayout.LayoutParams processDrawerLayoutParams(DrawerBuilder drawer, DrawerLayout.LayoutParams params) { - if (params != null) { - if (drawer.mDrawerGravity != null && (drawer.mDrawerGravity == Gravity.RIGHT || drawer.mDrawerGravity == Gravity.END)) { - params.rightMargin = 0; - if (Build.VERSION.SDK_INT >= 17) { - params.setMarginEnd(0); - } - - params.leftMargin = drawer.mActivity.getResources().getDimensionPixelSize(R.dimen.material_drawer_margin); - if (Build.VERSION.SDK_INT >= 17) { - params.setMarginEnd(drawer.mActivity.getResources().getDimensionPixelSize(R.dimen.material_drawer_margin)); - } - } - - if (drawer.mDrawerWidth > -1) { - params.width = drawer.mDrawerWidth; - } else { - params.width = DrawerUIUtils.getOptimalDrawerWidth(drawer.mActivity); - } - } - - return params; - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/MiniDrawer.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/MiniDrawer.java deleted file mode 100644 index fc5a33f0..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/MiniDrawer.java +++ /dev/null @@ -1,544 +0,0 @@ -package com.mikepenz.materialdrawer; - -import android.content.Context; -import android.content.res.Configuration; -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; - -import com.mikepenz.fastadapter.FastAdapter; -import com.mikepenz.fastadapter.IAdapter; -import com.mikepenz.fastadapter.adapters.ItemAdapter; -import com.mikepenz.fastadapter.listeners.OnClickListener; -import com.mikepenz.fastadapter.listeners.OnLongClickListener; -import com.mikepenz.materialdrawer.interfaces.ICrossfader; -import com.mikepenz.materialdrawer.model.MiniDrawerItem; -import com.mikepenz.materialdrawer.model.MiniProfileDrawerItem; -import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; -import com.mikepenz.materialdrawer.model.ProfileDrawerItem; -import com.mikepenz.materialdrawer.model.SecondaryDrawerItem; -import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; -import com.mikepenz.materialdrawer.model.interfaces.IProfile; -import com.mikepenz.materialize.util.UIUtils; - -import java.util.List; - -/** - * Created by mikepenz on 15.07.15. - * Don't count this for real yet. it's just a quick try on creating a Gmail like panel - */ -public class MiniDrawer { - public static final int PROFILE = 1; - public static final int ITEM = 2; - - private LinearLayout mContainer; - private RecyclerView mRecyclerView; - protected FastAdapter mAdapter; - protected ItemAdapter mItemAdapter; - - private Drawer mDrawer; - - /** - * Provide the Drawer which will be used as dataSource for the drawerItems - * - * @param drawer - * @return - */ - public MiniDrawer withDrawer(@NonNull Drawer drawer) { - this.mDrawer = drawer; - return this; - } - - private AccountHeader mAccountHeader; - - /** - * Provide the AccountHeader which will be used as the dataSource for the profiles - * - * @param accountHeader - * @return - */ - public MiniDrawer withAccountHeader(@NonNull AccountHeader accountHeader) { - this.mAccountHeader = accountHeader; - return this; - } - - private ICrossfader mCrossFader; - - /** - * Provide the Crossfader implementation which is used with this MiniDrawer - * - * @param crossFader - * @return - */ - public MiniDrawer withCrossFader(@NonNull ICrossfader crossFader) { - this.mCrossFader = crossFader; - return this; - } - - private boolean mInnerShadow = false; - - /** - * set to true if you want to show the innerShadow on the MiniDrawer - * - * @param innerShadow - * @return - */ - public MiniDrawer withInnerShadow(boolean innerShadow) { - this.mInnerShadow = innerShadow; - return this; - } - - private boolean mInRTL = false; - - /** - * set to true if you want the MiniDrawer in RTL mode - * - * @param inRTL - * @return - */ - public MiniDrawer withInRTL(boolean inRTL) { - this.mInRTL = inRTL; - return this; - } - - private boolean mIncludeSecondaryDrawerItems = false; - - /** - * set to true if you also want to display secondaryDrawerItems - * - * @param includeSecondaryDrawerItems - * @return - */ - public MiniDrawer withIncludeSecondaryDrawerItems(boolean includeSecondaryDrawerItems) { - this.mIncludeSecondaryDrawerItems = includeSecondaryDrawerItems; - return this; - } - - private boolean mEnableSelectedMiniDrawerItemBackground = false; - - /** - * set to true if you want to display the background for the miniDrawerItem - * - * @param enableSelectedMiniDrawerItemBackground - * @return - */ - public MiniDrawer withEnableSelectedMiniDrawerItemBackground(boolean enableSelectedMiniDrawerItemBackground) { - this.mEnableSelectedMiniDrawerItemBackground = enableSelectedMiniDrawerItemBackground; - return this; - } - - private boolean mEnableProfileClick = true; - - /** - * set to false if you do not want the profile image to toggle to the normal drawers profile selection - * - * @param enableProfileClick - * @return this - */ - public MiniDrawer withEnableProfileClick(boolean enableProfileClick) { - this.mEnableProfileClick = enableProfileClick; - return this; - } - - private OnMiniDrawerItemClickListener mOnMiniDrawerItemClickListener; - - /** - * Define the onMiniDrawerItemClickListener called before any logic in the MiniDrawer is run, allows you to intercept the default behavior - * - * @param onMiniDrawerItemClickListener - * @return this - */ - public MiniDrawer withOnMiniDrawerItemClickListener(OnMiniDrawerItemClickListener onMiniDrawerItemClickListener) { - this.mOnMiniDrawerItemClickListener = onMiniDrawerItemClickListener; - return this; - } - - - private OnClickListener mOnMiniDrawerItemOnClickListener; - - /** - * Define an onClickListener for the MiniDrawer item adapter. WARNING: this will completely overwrite the default behavior - * You may want to check the `OnMiniDrawerItemClickListener` (withOnMiniDrawerItemClickListener) which just hooks into the default behavior - * - * @param onMiniDrawerItemOnClickListener - * @return this - */ - public MiniDrawer withOnMiniDrawerItemOnClickListener(OnClickListener onMiniDrawerItemOnClickListener) { - this.mOnMiniDrawerItemOnClickListener = onMiniDrawerItemOnClickListener; - return this; - } - - - private OnLongClickListener mOnMiniDrawerItemLongClickListener; - - /** - * Define an onLongClickListener for the MiniDrawer item adapter - * - * @param onMiniDrawerItemLongClickListener - * @return - */ - public MiniDrawer withOnMiniDrawerItemLongClickListener(OnLongClickListener onMiniDrawerItemLongClickListener) { - this.mOnMiniDrawerItemLongClickListener = onMiniDrawerItemLongClickListener; - return this; - } - - /** - * get the RecyclerView of this MiniDrawer - * - * @return - */ - public RecyclerView getRecyclerView() { - return mRecyclerView; - } - - /** - * get the FastAdapter of this MiniDrawer - * - * @return - */ - public FastAdapter getAdapter() { - return mAdapter; - } - - /** - * get the ItemAdapter of this MiniDrawer - * - * @return - */ - public ItemAdapter getItemAdapter() { - return mItemAdapter; - } - - /** - * get the Drawer used to fill this MiniDrawer - * - * @return - */ - public Drawer getDrawer() { - return mDrawer; - } - - /** - * get the AccountHeader used to fill the this MiniDrawer - * - * @return - */ - public AccountHeader getAccountHeader() { - return mAccountHeader; - } - - /** - * get the Crossfader used for this MiniDrawer - * - * @return - */ - public ICrossfader getCrossFader() { - return mCrossFader; - } - - - /** - * the defined FastAdapter.OnClickListener which completely replaces the original behavior - * - * @return - */ - public OnClickListener getOnMiniDrawerItemOnClickListener() { - return mOnMiniDrawerItemOnClickListener; - } - - /** - * @return - */ - public OnLongClickListener getOnMiniDrawerItemLongClickListener() { - return mOnMiniDrawerItemLongClickListener; - } - - - /** - * generates a MiniDrawerItem from a IDrawerItem - * - * @param drawerItem - * @return - */ - public IDrawerItem generateMiniDrawerItem(IDrawerItem drawerItem) { - if (drawerItem instanceof SecondaryDrawerItem) { - if (drawerItem.isExcludeFromMiniDrawer()) { - return null; - } - return mIncludeSecondaryDrawerItems ? new MiniDrawerItem((SecondaryDrawerItem) drawerItem).withEnableSelectedBackground(mEnableSelectedMiniDrawerItemBackground).withSelectedBackgroundAnimated(false) : null; - } else if (drawerItem instanceof PrimaryDrawerItem) { - if (drawerItem.isExcludeFromMiniDrawer()) { - return null; - } - return new MiniDrawerItem((PrimaryDrawerItem) drawerItem).withEnableSelectedBackground(mEnableSelectedMiniDrawerItemBackground).withSelectedBackgroundAnimated(false); - } else if (drawerItem instanceof ProfileDrawerItem) { - if (drawerItem.isExcludeFromMiniDrawer()) { - return null; - } - MiniProfileDrawerItem mpdi = new MiniProfileDrawerItem((ProfileDrawerItem) drawerItem); - mpdi.withEnabled(mEnableProfileClick); - return mpdi; - } - return null; - } - - /** - * gets the type of a IDrawerItem - * - * @param drawerItem - * @return - */ - public int getMiniDrawerType(IDrawerItem drawerItem) { - if (drawerItem instanceof MiniProfileDrawerItem) { - return PROFILE; - } else if (drawerItem instanceof MiniDrawerItem) { - return ITEM; - } - return -1; - } - - /** - * build the MiniDrawer - * - * @param ctx - * @return - */ - public View build(Context ctx) { - mContainer = new LinearLayout(ctx); - if (mInnerShadow) { - if (!mInRTL) { - mContainer.setBackgroundResource(R.drawable.material_drawer_shadow_left); - } else { - mContainer.setBackgroundResource(R.drawable.material_drawer_shadow_right); - } - } - - //create and append recyclerView - mRecyclerView = new RecyclerView(ctx); - mContainer.addView(mRecyclerView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - - //set the itemAnimator - mRecyclerView.setItemAnimator(new DefaultItemAnimator()); - //some style improvements on older devices - mRecyclerView.setFadingEdgeLength(0); - //set the drawing cache background to the same color as the slider to improve performance - //mRecyclerView.setDrawingCacheBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(mActivity, R.attr.material_drawer_background, R.color.material_drawer_background)); - mRecyclerView.setClipToPadding(false); - //additional stuff - mRecyclerView.setLayoutManager(new LinearLayoutManager(ctx)); - //adapter - mItemAdapter = new ItemAdapter<>(); - mAdapter = FastAdapter.with(mItemAdapter); - mAdapter.withSelectable(true); - mAdapter.withAllowDeselection(false); - mRecyclerView.setAdapter(mAdapter); - - //if the activity with the drawer should be fullscreen add the padding for the statusbar - if (mDrawer != null && mDrawer.mDrawerBuilder != null && (mDrawer.mDrawerBuilder.mFullscreen || mDrawer.mDrawerBuilder.mTranslucentStatusBar)) { - mRecyclerView.setPadding(mRecyclerView.getPaddingLeft(), UIUtils.getStatusBarHeight(ctx), mRecyclerView.getPaddingRight(), mRecyclerView.getPaddingBottom()); - } - - //if the activity with the drawer should be fullscreen add the padding for the navigationBar - if (mDrawer != null && mDrawer.mDrawerBuilder != null && (mDrawer.mDrawerBuilder.mFullscreen || mDrawer.mDrawerBuilder.mTranslucentNavigationBar) && ctx.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - mRecyclerView.setPadding(mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(), mRecyclerView.getPaddingRight(), UIUtils.getNavigationBarHeight(ctx)); - } - - //set the adapter with the items - createItems(); - - return mContainer; - } - - /** - * call this method to trigger the onProfileClick on the MiniDrawer - */ - public void onProfileClick() { - //crossfade if we are cross faded - if (mCrossFader != null) { - if (mCrossFader.isCrossfaded()) { - mCrossFader.crossfade(); - } - } - - //update the current profile - if (mAccountHeader != null) { - IProfile profile = mAccountHeader.getActiveProfile(); - if (profile instanceof IDrawerItem) { - mItemAdapter.set(0, generateMiniDrawerItem((IDrawerItem) profile)); - } - } - } - - /** - * call this method to trigger the onItemClick on the MiniDrawer - * - * @param selectedDrawerItem - * @return - */ - public boolean onItemClick(IDrawerItem selectedDrawerItem) { - //We only need to clear if the new item is selectable - if (selectedDrawerItem.isSelectable()) { - //crossfade if we are cross faded - if (mCrossFader != null) { - if (mCrossFader.isCrossfaded()) { - mCrossFader.crossfade(); - } - } - //update everything - setSelection(selectedDrawerItem.getIdentifier()); - - return false; - } else { - return true; - } - } - - /** - * set the selection of the MiniDrawer - * - * @param identifier the identifier of the item which should be selected (-1 for none) - */ - public void setSelection(long identifier) { - if (identifier == -1) { - mAdapter.deselect(); - } - int count = mAdapter.getItemCount(); - for (int i = 0; i < count; i++) { - IDrawerItem item = mAdapter.getItem(i); - if (item.getIdentifier() == identifier && !item.isSelected()) { - mAdapter.deselect(); - mAdapter.select(i); - } - } - } - - /** - * update a MiniDrawerItem (after updating the main Drawer) via its identifier - * - * @param identifier the identifier of the item which was updated - */ - public void updateItem(long identifier) { - if (mDrawer != null && mAdapter != null && mItemAdapter.getAdapterItems() != null && identifier != -1) { - IDrawerItem drawerItem = DrawerUtils.getDrawerItem(getDrawerItems(), identifier); - for (int i = 0; i < mItemAdapter.getAdapterItems().size(); i++) { - if (mItemAdapter.getAdapterItems().get(i).getIdentifier() == drawerItem.getIdentifier()) { - IDrawerItem miniDrawerItem = generateMiniDrawerItem(drawerItem); - if (miniDrawerItem != null) { - mItemAdapter.set(i, miniDrawerItem); - } - } - } - } - } - - /** - * creates the items for the MiniDrawer - */ - public void createItems() { - mItemAdapter.clear(); - - int profileOffset = 0; - if (mAccountHeader != null && mAccountHeader.getAccountHeaderBuilder().mProfileImagesVisible) { - IProfile profile = mAccountHeader.getActiveProfile(); - if (profile instanceof IDrawerItem) { - mItemAdapter.add(generateMiniDrawerItem((IDrawerItem) profile)); - profileOffset = 1; - } - } - - int select = -1; - if (mDrawer != null) { - if (getDrawerItems() != null) { - //migrate to miniDrawerItems - int length = getDrawerItems().size(); - - int position = 0; - for (int i = 0; i < length; i++) { - IDrawerItem miniDrawerItem = generateMiniDrawerItem(getDrawerItems().get(i)); - if (miniDrawerItem != null) { - if (miniDrawerItem.isSelected()) { - select = position; - } - mItemAdapter.add(miniDrawerItem); - position = position + 1; - } - } - - if (select >= 0) { - //+1 because of the profile - mAdapter.select(select + profileOffset); - } - } - } - - //listener - if (mOnMiniDrawerItemOnClickListener != null) { - mAdapter.withOnClickListener(mOnMiniDrawerItemOnClickListener); - } else { - mAdapter.withOnClickListener(new OnClickListener() { - @Override - public boolean onClick(View v, IAdapter adapter, final IDrawerItem item, final int position) { - int type = getMiniDrawerType(item); - - //if a listener is defined and we consume the event return - if (mOnMiniDrawerItemClickListener != null && mOnMiniDrawerItemClickListener.onItemClick(v, position, item, type)) { - return false; - } - - if (type == ITEM) { - //fire the onClickListener also if the specific drawerItem is not Selectable - if (item.isSelectable()) { - //make sure we are on the original drawerItemList - if (mAccountHeader != null && mAccountHeader.isSelectionListShown()) { - mAccountHeader.toggleSelectionList(v.getContext()); - } - IDrawerItem drawerItem = mDrawer.getDrawerItem(item.getIdentifier()); - if (drawerItem != null && !drawerItem.isSelected()) { - //set the selection - mDrawer.setSelection(item, true); - } - } else if (mDrawer.getOnDrawerItemClickListener() != null) { - //get the original `DrawerItem` from the Drawer as this one will contain all information - mDrawer.getOnDrawerItemClickListener().onItemClick(v, position, DrawerUtils.getDrawerItem(getDrawerItems(), item.getIdentifier())); - } - } else if (type == PROFILE) { - if (mAccountHeader != null && !mAccountHeader.isSelectionListShown()) { - mAccountHeader.toggleSelectionList(v.getContext()); - } - if (mCrossFader != null) { - mCrossFader.crossfade(); - } - } - return false; - } - }); - } - mAdapter.withOnLongClickListener(mOnMiniDrawerItemLongClickListener); - mRecyclerView.scrollToPosition(0); - } - - /** - * returns always the original drawerItems and not the switched content - * - * @return - */ - private List getDrawerItems() { - return mDrawer.getOriginalDrawerItems() != null ? mDrawer.getOriginalDrawerItems() : mDrawer.getDrawerItems(); - } - - - public interface OnMiniDrawerItemClickListener { - /** - * @param view - * @param position - * @param drawerItem - * @param type either MiniDrawer.PROFILE or MiniDrawer.ITEM - * @return true if the event was consumed - */ - boolean onItemClick(View view, int position, IDrawerItem drawerItem, int type); - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/BadgeStyle.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/BadgeStyle.java deleted file mode 100644 index 7a754a47..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/BadgeStyle.java +++ /dev/null @@ -1,232 +0,0 @@ -package com.mikepenz.materialdrawer.holder; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.drawable.Drawable; -import android.widget.TextView; - -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.model.utils.BadgeDrawableBuilder; - -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.annotation.DimenRes; -import androidx.annotation.Dimension; -import androidx.annotation.DrawableRes; -import androidx.core.view.ViewCompat; - -import static androidx.annotation.Dimension.DP; -import static androidx.annotation.Dimension.PX; - -/** - * Class to allow defining a BadgeStyle for the `BadgeDrawerItem` - */ -public class BadgeStyle { - private int mGradientDrawable = R.drawable.material_drawer_badge; - private Drawable mBadgeBackground; - private ColorHolder mColor; - private ColorHolder mColorPressed; - private ColorHolder mTextColor; - private ColorStateList mTextColorStateList; - private DimenHolder mCorners; - private DimenHolder mPaddingTopBottom = DimenHolder.fromDp(2); //2 looks best - private DimenHolder mPaddingLeftRight = DimenHolder.fromDp(3); //3 looks best - private DimenHolder mMinWidth = DimenHolder.fromDp(20); //20 looks nice - - public int getGradientDrawable() { - return mGradientDrawable; - } - - public BadgeStyle withGradientDrawable(@DrawableRes int gradientDrawable) { - this.mGradientDrawable = gradientDrawable; - this.mBadgeBackground = null; - return this; - } - - public Drawable getBadgeBackground() { - return mBadgeBackground; - } - - public BadgeStyle withBadgeBackground(Drawable badgeBackground) { - this.mBadgeBackground = badgeBackground; - this.mGradientDrawable = -1; - return this; - } - - public ColorHolder getColor() { - return mColor; - } - - public BadgeStyle withColor(@ColorInt int color) { - this.mColor = ColorHolder.fromColor(color); - return this; - } - - public BadgeStyle withColorRes(@ColorRes int color) { - this.mColor = ColorHolder.fromColorRes(color); - return this; - } - - public ColorHolder getColorPressed() { - return mColorPressed; - } - - public BadgeStyle withColorPressed(@ColorInt int colorPressed) { - this.mColorPressed = ColorHolder.fromColor(colorPressed); - return this; - } - - public BadgeStyle withColorPressedRes(@ColorRes int colorPressed) { - this.mColorPressed = ColorHolder.fromColorRes(colorPressed); - return this; - } - - public ColorHolder getTextColor() { - return mTextColor; - } - - public BadgeStyle withTextColor(@ColorInt int textColor) { - this.mTextColor = ColorHolder.fromColor(textColor); - return this; - } - - public BadgeStyle withTextColorRes(@ColorRes int textColor) { - this.mTextColor = ColorHolder.fromColorRes(textColor); - return this; - } - - public BadgeStyle withTextColorStateList(ColorStateList textColorStateList) { - this.mTextColor = null; - this.mTextColorStateList = textColorStateList; - return this; - } - - public DimenHolder getCorners() { - return mCorners; - } - - public BadgeStyle withCorners(@Dimension(unit = PX) int cornersPx) { - this.mCorners = DimenHolder.fromPixel(cornersPx); - return this; - } - - public BadgeStyle withCornersDp(@Dimension(unit = DP) int corners) { - this.mCorners = DimenHolder.fromDp(corners); - return this; - } - - public BadgeStyle withCorners(DimenHolder corners) { - this.mCorners = corners; - return this; - } - - public DimenHolder getPaddingLeftRight() { - return mPaddingLeftRight; - } - - public BadgeStyle withPaddingLeftRightPx(@Dimension(unit = PX) int paddingLeftRight) { - this.mPaddingLeftRight = DimenHolder.fromPixel(paddingLeftRight); - return this; - } - - public BadgeStyle withPaddingLeftRightDp(@Dimension(unit = DP) int paddingLeftRight) { - this.mPaddingLeftRight = DimenHolder.fromDp(paddingLeftRight); - return this; - } - - public BadgeStyle withPaddingLeftRightRes(@DimenRes int paddingLeftRight) { - this.mPaddingLeftRight = DimenHolder.fromResource(paddingLeftRight); - return this; - } - - public DimenHolder getPaddingTopBottom() { - return mPaddingTopBottom; - } - - public BadgeStyle withPaddingTopBottomPx(@Dimension(unit = PX) int paddingTopBottom) { - this.mPaddingTopBottom = DimenHolder.fromPixel(paddingTopBottom); - return this; - } - - public BadgeStyle withPaddingTopBottomDp(@Dimension(unit = DP) int paddingTopBottom) { - this.mPaddingTopBottom = DimenHolder.fromDp(paddingTopBottom); - return this; - } - - public BadgeStyle withPaddingTopBottomRes(@DimenRes int paddingTopBottom) { - this.mPaddingTopBottom = DimenHolder.fromResource(paddingTopBottom); - return this; - } - - public BadgeStyle withPadding(@Dimension(unit = PX) int padding) { - this.mPaddingLeftRight = DimenHolder.fromPixel(padding); - this.mPaddingTopBottom = DimenHolder.fromPixel(padding); - return this; - } - - public BadgeStyle withPadding(DimenHolder padding) { - this.mPaddingLeftRight = padding; - this.mPaddingTopBottom = padding; - return this; - } - - public DimenHolder getMinWidth() { - return mMinWidth; - } - - public BadgeStyle withMinWidth(@Dimension(unit = PX) int minWidth) { - this.mMinWidth = DimenHolder.fromPixel(minWidth); - return this; - } - - public BadgeStyle withMinWidth(DimenHolder minWidth) { - this.mMinWidth = minWidth; - return this; - } - - public BadgeStyle() { - } - - public BadgeStyle(@ColorInt int color, @ColorInt int colorPressed) { - this.mColor = ColorHolder.fromColor(color); - this.mColorPressed = ColorHolder.fromColor(colorPressed); - } - - public BadgeStyle(@DrawableRes int gradientDrawable, @ColorInt int color, @ColorInt int colorPressed, @ColorInt int textColor) { - this.mGradientDrawable = gradientDrawable; - this.mColor = ColorHolder.fromColor(color); - this.mColorPressed = ColorHolder.fromColor(colorPressed); - this.mTextColor = ColorHolder.fromColor(textColor); - } - - public void style(TextView badgeTextView) { - style(badgeTextView, null); - } - - public void style(TextView badgeTextView, ColorStateList colorStateList) { - Context ctx = badgeTextView.getContext(); - //set background for badge - if (mBadgeBackground == null) { - ViewCompat.setBackground(badgeTextView, new BadgeDrawableBuilder(this).build(ctx)); - } else { - ViewCompat.setBackground(badgeTextView, mBadgeBackground); - } - - //set the badge text color - if (mTextColor != null) { - ColorHolder.applyToOr(mTextColor, badgeTextView, null); - } else if (mTextColorStateList != null) { - badgeTextView.setTextColor(mTextColorStateList); - } else if (colorStateList != null) { - badgeTextView.setTextColor(colorStateList); - } - - //set the padding - int paddingLeftRight = mPaddingLeftRight.asPixel(ctx); - int paddingTopBottom = mPaddingTopBottom.asPixel(ctx); - badgeTextView.setPadding(paddingLeftRight, paddingTopBottom, paddingLeftRight, paddingTopBottom); - - //set the min width - badgeTextView.setMinWidth(mMinWidth.asPixel(ctx)); - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/ColorHolder.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/ColorHolder.java deleted file mode 100644 index 60f577f0..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/ColorHolder.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.mikepenz.materialdrawer.holder; - -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; - -/** - * Created by mikepenz on 13.07.15. - */ -public class ColorHolder extends com.mikepenz.materialize.holder.ColorHolder { - public ColorHolder() { - } - - public static ColorHolder fromColorRes(@ColorRes int colorRes) { - ColorHolder colorHolder = new ColorHolder(); - colorHolder.setColorRes(colorRes); - return colorHolder; - } - - public static ColorHolder fromColor(@ColorInt int colorInt) { - ColorHolder colorHolder = new ColorHolder(); - colorHolder.setColorInt(colorInt); - return colorHolder; - } - -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/DimenHolder.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/DimenHolder.java deleted file mode 100644 index c946e5dc..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/DimenHolder.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.mikepenz.materialdrawer.holder; - -import androidx.annotation.DimenRes; -import androidx.annotation.Dimension; - -import static androidx.annotation.Dimension.DP; -import static androidx.annotation.Dimension.PX; - -/** - * Created by mikepenz on 13.07.15. - */ -public class DimenHolder extends com.mikepenz.materialize.holder.DimenHolder { - public DimenHolder() { - - } - - public static DimenHolder fromPixel(@Dimension(unit = PX) int pixel) { - DimenHolder dimenHolder = new DimenHolder(); - dimenHolder.setPixel(pixel); - return dimenHolder; - } - - public static DimenHolder fromDp(@Dimension(unit = DP) int dp) { - DimenHolder dimenHolder = new DimenHolder(); - dimenHolder.setDp(dp); - return dimenHolder; - } - - public static DimenHolder fromResource(@DimenRes int resource) { - DimenHolder dimenHolder = new DimenHolder(); - dimenHolder.setResource(resource); - return dimenHolder; - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/ImageHolder.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/ImageHolder.java deleted file mode 100644 index 02f3a035..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/ImageHolder.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.mikepenz.materialdrawer.holder; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import androidx.annotation.DrawableRes; -import androidx.appcompat.content.res.AppCompatResources; -import android.view.View; -import android.widget.ImageView; - -import com.mikepenz.iconics.IconicsDrawable; -import com.mikepenz.iconics.typeface.IIcon; -import com.mikepenz.materialdrawer.util.DrawerImageLoader; - -import java.io.FileNotFoundException; -import java.io.InputStream; - -/** - * Created by mikepenz on 13.07.15. - */ - -public class ImageHolder extends com.mikepenz.materialize.holder.ImageHolder { - private IIcon mIIcon; - - public ImageHolder(String url) { - super(url); - } - - public ImageHolder(Uri uri) { - super(uri); - } - - public ImageHolder(Drawable icon) { - super(icon); - } - - public ImageHolder(Bitmap bitmap) { - super(bitmap); - } - - public ImageHolder(@DrawableRes int iconRes) { - super(iconRes); - } - - public ImageHolder(IIcon iicon) { - super((Bitmap) null); - this.mIIcon = iicon; - } - - public IIcon getIIcon() { - return mIIcon; - } - - public void setIIcon(IIcon mIIcon) { - this.mIIcon = mIIcon; - } - - /** - * sets an existing image to the imageView - * - * @param imageView - * @param tag used to identify imageViews and define different placeholders - * @return true if an image was set - */ - @Override - public boolean applyTo(ImageView imageView, String tag) { - if (getUri() != null) { - boolean consumed = DrawerImageLoader.getInstance().setImage(imageView, getUri(), tag); - if (!consumed) { - imageView.setImageURI(getUri()); - } - } else if (getIcon() != null) { - imageView.setImageDrawable(getIcon()); - } else if (getBitmap() != null) { - imageView.setImageBitmap(getBitmap()); - } else if (getIconRes() != -1) { - imageView.setImageResource(getIconRes()); - } else if (mIIcon != null) { - imageView.setImageDrawable(new IconicsDrawable(imageView.getContext(), mIIcon).actionBar()); - } else { - imageView.setImageBitmap(null); - return false; - } - return true; - } - - /** - * this only handles Drawables - * - * @param ctx - * @param iconColor - * @param tint - * @return - */ - public Drawable decideIcon(Context ctx, int iconColor, boolean tint, int paddingDp) { - Drawable icon = getIcon(); - - if (mIIcon != null) { - icon = new IconicsDrawable(ctx, mIIcon).color(iconColor).sizeDp(24).paddingDp(paddingDp); - } else if (getIconRes() != -1) { - icon = AppCompatResources.getDrawable(ctx, getIconRes()); - } else if (getUri() != null) { - try { - InputStream inputStream = ctx.getContentResolver().openInputStream(getUri()); - icon = Drawable.createFromStream(inputStream, getUri().toString()); - } catch (FileNotFoundException e) { - //no need to handle this - } - } - - //if we got an icon AND we have auto tinting enabled AND it is no IIcon, tint it ;) - if (icon != null && tint && mIIcon == null) { - icon = icon.mutate(); - icon.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN); - } - - return icon; - } - - /** - * a small static helper which catches nulls for us - * - * @param imageHolder - * @param ctx - * @param iconColor - * @param tint - * @return - */ - public static Drawable decideIcon(ImageHolder imageHolder, Context ctx, int iconColor, boolean tint, int paddingDp) { - if (imageHolder == null) { - return null; - } else { - return imageHolder.decideIcon(ctx, iconColor, tint, paddingDp); - } - } - - /** - * decides which icon to apply or hide this view - * - * @param imageHolder - * @param imageView - * @param iconColor - * @param tint - * @param paddingDp - */ - public static void applyDecidedIconOrSetGone(ImageHolder imageHolder, ImageView imageView, int iconColor, boolean tint, int paddingDp) { - if (imageHolder != null && imageView != null) { - Drawable drawable = ImageHolder.decideIcon(imageHolder, imageView.getContext(), iconColor, tint, paddingDp); - if (drawable != null) { - imageView.setImageDrawable(drawable); - imageView.setVisibility(View.VISIBLE); - } else if (imageHolder.getBitmap() != null) { - imageView.setImageBitmap(imageHolder.getBitmap()); - imageView.setVisibility(View.VISIBLE); - } else { - imageView.setVisibility(View.GONE); - } - } else if (imageView != null) { - imageView.setVisibility(View.GONE); - } - } - -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/StringHolder.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/StringHolder.java deleted file mode 100644 index 49a4a356..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/holder/StringHolder.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.mikepenz.materialdrawer.holder; - -import androidx.annotation.StringRes; - -/** - * Created by mikepenz on 13.07.15. - */ -public class StringHolder extends com.mikepenz.materialize.holder.StringHolder { - public StringHolder(CharSequence text) { - super(text); - } - - public StringHolder(@StringRes int textRes) { - super(textRes); - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/icons/MaterialDrawerFont.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/icons/MaterialDrawerFont.java deleted file mode 100644 index 1e8edc50..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/icons/MaterialDrawerFont.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.mikepenz.materialdrawer.icons; - -import android.content.Context; -import android.graphics.Typeface; - -import com.mikepenz.iconics.typeface.IIcon; -import com.mikepenz.iconics.typeface.ITypeface; - -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedList; - -/** - * Created by mikepenz on 01.11.14. - */ -public class MaterialDrawerFont implements ITypeface { - private static final String TTF_FILE = "materialdrawerfont-font-v5.0.0.ttf"; - - private static Typeface typeface = null; - - private static HashMap mChars; - - @Override - public IIcon getIcon(String key) { - return Icon.valueOf(key); - } - - @Override - public HashMap getCharacters() { - if (mChars == null) { - HashMap aChars = new HashMap(); - for (Icon v : Icon.values()) { - aChars.put(v.name(), v.character); - } - mChars = aChars; - } - - return mChars; - } - - @Override - public String getMappingPrefix() { - return "mdf"; - } - - @Override - public String getFontName() { - return "MaterialDrawerFont"; - } - - @Override - public String getVersion() { - return "5.0.0"; - } - - @Override - public int getIconCount() { - return mChars.size(); - } - - @Override - public Collection getIcons() { - Collection icons = new LinkedList(); - - for (Icon value : Icon.values()) { - icons.add(value.name()); - } - - return icons; - } - - - @Override - public String getAuthor() { - return ""; - } - - @Override - public String getUrl() { - return ""; - } - - @Override - public String getDescription() { - return ""; - } - - @Override - public String getLicense() { - return ""; - } - - @Override - public String getLicenseUrl() { - return ""; - } - - @Override - public Typeface getTypeface(Context context) { - if (typeface == null) { - try { - typeface = Typeface.createFromAsset(context.getAssets(), "fonts/" + TTF_FILE); - } catch (Exception e) { - return null; - } - } - return typeface; - } - - public enum Icon implements IIcon { - mdf_arrow_drop_down('\ue5c5'), - mdf_arrow_drop_up('\ue5c7'), - mdf_expand_less('\ue5ce'), - mdf_expand_more('\ue5cf'), - mdf_person('\ue7fd'); - - char character; - - Icon(char character) { - this.character = character; - } - - public String getFormattedName() { - return "{" + name() + "}"; - } - - public char getCharacter() { - return character; - } - - public String getName() { - return name(); - } - - // remember the typeface so we can use it later - private static ITypeface typeface; - - public ITypeface getTypeface() { - if (typeface == null) { - typeface = new MaterialDrawerFont(); - } - return typeface; - } - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/interfaces/ICrossfader.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/interfaces/ICrossfader.java deleted file mode 100644 index 3ee11bfa..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/interfaces/ICrossfader.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.mikepenz.materialdrawer.interfaces; - -/** - * Created by mikepenz on 18.07.15. - */ -public interface ICrossfader { - void crossfade(); - - boolean isCrossfaded(); -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/interfaces/OnCheckedChangeListener.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/interfaces/OnCheckedChangeListener.java deleted file mode 100644 index b34e5373..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/interfaces/OnCheckedChangeListener.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.mikepenz.materialdrawer.interfaces; - -import android.widget.CompoundButton; - -import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; - -/** - * Interface definition for a callback to be invoked when the checked state - * of a compound button changed. - */ -public interface OnCheckedChangeListener { - /** - * Called when the checked state of a compound button has changed. - * - * @param buttonView The compound button view whose state has changed. - * @param isChecked The new checked state of buttonView. - */ - void onCheckedChanged(IDrawerItem drawerItem, CompoundButton buttonView, boolean isChecked); -} \ No newline at end of file diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractBadgeableDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractBadgeableDrawerItem.java deleted file mode 100644 index 170fa8e9..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractBadgeableDrawerItem.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import androidx.annotation.LayoutRes; -import androidx.annotation.StringRes; -import android.view.View; -import android.widget.TextView; -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.holder.BadgeStyle; -import com.mikepenz.materialdrawer.holder.StringHolder; -import com.mikepenz.materialdrawer.model.interfaces.ColorfulBadgeable; - -import java.util.List; - -/** - * Created by mikepenz on 03.02.15. - */ -public abstract class AbstractBadgeableDrawerItem extends BaseDescribeableDrawerItem implements ColorfulBadgeable { - protected StringHolder mBadge; - protected BadgeStyle mBadgeStyle = new BadgeStyle(); - - @Override - public Item withBadge(StringHolder badge) { - this.mBadge = badge; - return (Item) this; - } - - @Override - public Item withBadge(String badge) { - this.mBadge = new StringHolder(badge); - return (Item) this; - } - - @Override - public Item withBadge(@StringRes int badgeRes) { - this.mBadge = new StringHolder(badgeRes); - return (Item) this; - } - - @Override - public Item withBadgeStyle(BadgeStyle badgeStyle) { - this.mBadgeStyle = badgeStyle; - return (Item) this; - } - - public StringHolder getBadge() { - return mBadge; - } - - public BadgeStyle getBadgeStyle() { - return mBadgeStyle; - } - - @Override - public int getType() { - return R.id.material_drawer_item_primary;/*"PRIMARY_ITEM"*/ - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_primary; - } - - @Override - public void bindView(ViewHolder viewHolder, List payloads) { - super.bindView(viewHolder, payloads); - - Context ctx = viewHolder.itemView.getContext(); - //bind the basic view parts - bindViewHelper(viewHolder); - - //set the text for the badge or hide - boolean badgeVisible = StringHolder.applyToOrHide(mBadge, viewHolder.badge); - //style the badge if it is visible - if (badgeVisible) { - mBadgeStyle.style(viewHolder.badge, getTextColorStateList(getColor(ctx), getSelectedTextColor(ctx))); - viewHolder.badgeContainer.setVisibility(View.VISIBLE); - } else { - viewHolder.badgeContainer.setVisibility(View.GONE); - } - - //define the typeface for our textViews - if (getTypeface() != null) { - viewHolder.badge.setTypeface(getTypeface()); - } - - //call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required) - onPostBindView(this, viewHolder.itemView); - } - - @Override - public ViewHolder getViewHolder(View v) { - return new ViewHolder(v); - } - - public static class ViewHolder extends BaseViewHolder { - private View badgeContainer; - private TextView badge; - - public ViewHolder(View view) { - super(view); - this.badgeContainer = view.findViewById(R.id.material_drawer_badge_container); - this.badge = (TextView) view.findViewById(R.id.material_drawer_badge); - } - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractDrawerItem.java deleted file mode 100644 index eb5f7c43..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractDrawerItem.java +++ /dev/null @@ -1,449 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.mikepenz.materialdrawer.Drawer; -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; -import com.mikepenz.materialdrawer.model.interfaces.OnPostBindViewListener; -import com.mikepenz.materialdrawer.model.interfaces.Selectable; -import com.mikepenz.materialdrawer.model.interfaces.Tagable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import androidx.annotation.CallSuper; -import androidx.recyclerview.widget.RecyclerView; - -/** - * Created by mikepenz on 14.07.15. - */ -public abstract class AbstractDrawerItem implements IDrawerItem, Selectable, Tagable { - - protected boolean mExcludeFromMiniDrawer = false; - - @Override - public boolean isExcludeFromMiniDrawer() { - return mExcludeFromMiniDrawer; - } - - // the identifier for this item - protected long mIdentifier = -1; - - /** - * set the identifier of this item - * - * @param identifier - * @return - */ - public T withIdentifier(long identifier) { - this.mIdentifier = identifier; - return (T) this; - } - - /** - * returns the identifier of this item - * -1 is the default not set state - * - * @return - */ - @Override - public long getIdentifier() { - return mIdentifier; - } - - // the tag for this item - protected Object mTag; - - /** - * set the tag of this item - * - * @param object - * @return - */ - public T withTag(Object object) { - this.mTag = object; - return (T) this; - } - - /** - * @return the tag of this item - */ - @Override - public Object getTag() { - return mTag; - } - - // defines if this item is enabled - protected boolean mEnabled = true; - - /** - * set if this item is enabled - * - * @param enabled true if this item is enabled - * @return - */ - public T withEnabled(boolean enabled) { - this.mEnabled = enabled; - return (T) this; - } - - /** - * @return if this item is enabled - */ - @Override - public boolean isEnabled() { - return mEnabled; - } - - // defines if the item is selected - protected boolean mSelected = false; - - /** - * set if this item is selected - * - * @param selected true if this item is selected - * @return - */ - @Override - public T withSetSelected(boolean selected) { - this.mSelected = selected; - return (T) this; - } - - /** - * @return if this item is selected - */ - @Override - public boolean isSelected() { - return mSelected; - } - - // defines if this item is selectable - protected boolean mSelectable = true; - - /** - * set if this item is selectable - * - * @param selectable true if this item is selectable - * @return - */ - @Override - public T withSelectable(boolean selectable) { - this.mSelectable = selectable; - return (T) this; - } - - /** - * @return if this item is selectable - */ - @Override - public boolean isSelectable() { - return mSelectable; - } - - // defines if the item's background' change should be animated when it is (de)selected - protected boolean mSelectedBackgroundAnimated = true; - - /** - * set if this item is selectable - * - * @param selectedBackgroundAnimated true if this item's background should fade when it is (de) selected - * @return - */ - public T withSelectedBackgroundAnimated(boolean selectedBackgroundAnimated) { - this.mSelectedBackgroundAnimated = selectedBackgroundAnimated; - return (T) this; - } - - /** - * @return if this item is selectable - */ - public boolean isSelectedBackgroundAnimated() { - return mSelectedBackgroundAnimated; - } - - public Drawer.OnDrawerItemClickListener mOnDrawerItemClickListener = null; - - public Drawer.OnDrawerItemClickListener getOnDrawerItemClickListener() { - return mOnDrawerItemClickListener; - } - - /** - * this listener is called when an item is clicked in the drawer. - * WARNING: don't overwrite this in the Switch / Toggle drawerItems if you want the toggle / switch to be selected - * if the item is clicked and the item is not selectable. - * - * @param onDrawerItemClickListener - * @return - */ - public T withOnDrawerItemClickListener(Drawer.OnDrawerItemClickListener onDrawerItemClickListener) { - this.mOnDrawerItemClickListener = onDrawerItemClickListener; - return (T) this; - } - - protected OnPostBindViewListener mOnPostBindViewListener = null; - - public OnPostBindViewListener getOnPostBindViewListener() { - return mOnPostBindViewListener; - } - - /** - * add this listener and hook in if you want to modify a drawerItems view without creating a custom drawer item - * - * @param onPostBindViewListener - * @return - */ - public T withPostOnBindViewListener(OnPostBindViewListener onPostBindViewListener) { - this.mOnPostBindViewListener = onPostBindViewListener; - return (T) this; - } - - /** - * is called after bindView to allow some post creation setps - * - * @param drawerItem the drawerItem which is bound to the view - * @param view the currently view which will be bound - */ - public void onPostBindView(IDrawerItem drawerItem, View view) { - if (mOnPostBindViewListener != null) { - mOnPostBindViewListener.onBindView(drawerItem, view); - } - } - - // the parent of this item - private IDrawerItem mParent; - - /** - * @return the parent of this item - */ - @Override - public IDrawerItem getParent() { - return mParent; - } - - /** - * the parent for this item - * - * @param parent it's parent - * @return this - */ - @Override - public IDrawerItem withParent(IDrawerItem parent) { - this.mParent = parent; - return this; - } - - // the subItems to expand for this item - protected List mSubItems; - - /** - * a list of subItems - * **WARNING** Make sure the subItems provided already have identifiers - * - * @param subItems - * @return - */ - public T withSubItems(List subItems) { - this.mSubItems = subItems; - for (IDrawerItem subItem : subItems) { - subItem.withParent(this); - } - return (T) this; - } - - /** - * an array of subItems - * **WARNING** Make sure the subItems provided already have identifiers - * - * @param subItems - * @return - */ - public T withSubItems(IDrawerItem... subItems) { - if (mSubItems == null) { - mSubItems = new ArrayList<>(); - } - for (IDrawerItem subItem : subItems) { - subItem.withParent(this); - } - Collections.addAll(mSubItems, subItems); - return (T) this; - } - - /** - * @return the subItems for this item - */ - @Override - public List getSubItems() { - return mSubItems; - } - - //if the this item is currently expanded - private boolean mExpanded = false; - - /** - * @param expanded defines if this item is now expanded or not - * @return this - */ - @Override - public T withIsExpanded(boolean expanded) { - mExpanded = expanded; - return (T) this; - } - - /** - * @return if this item is currently expaneded - */ - @Override - public boolean isExpanded() { - return mExpanded; - } - - - /** - * overwrite this method and return true if the item should auto expand on click, false if you want to disable this - * - * @return true if this item should auto expand in the adapter - */ - @Override - public boolean isAutoExpanding() { - return true; - } - - /** - * generates a view by the defined LayoutRes - * - * @param ctx - * @return - */ - @Override - public View generateView(Context ctx) { - VH viewHolder = getViewHolder(LayoutInflater.from(ctx).inflate(getLayoutRes(), null, false)); - bindView(viewHolder, Collections.emptyList()); - return viewHolder.itemView; - } - - /** - * generates a view by the defined LayoutRes and pass the LayoutParams from the parent - * - * @param ctx - * @param parent - * @return - */ - @Override - public View generateView(Context ctx, ViewGroup parent) { - VH viewHolder = getViewHolder(LayoutInflater.from(ctx).inflate(getLayoutRes(), parent, false)); - bindView(viewHolder, Collections.emptyList()); - return viewHolder.itemView; - } - - @CallSuper - @Override - public void bindView(VH holder, List payloads) { - holder.itemView.setTag(R.id.material_drawer_item, this); - } - - /** - * called when the view is unbound - * - * @param holder - */ - @Override - public void unbindView(VH holder) { - holder.itemView.clearAnimation(); - } - - /** - * View got attached to the window - * - * @param holder - */ - @Override - public void attachToWindow(VH holder) { - - } - - /** - * View got detached from the window - * - * @param holder - */ - @Override - public void detachFromWindow(VH holder) { - - } - - /** - * is called when the ViewHolder is in a transient state. return true if you want to reuse - * that view anyways - * - * @param holder the viewHolder for the view which failed to recycle - * @return true if we want to recycle anyways (false - it get's destroyed) - */ - @Override - public boolean failedToRecycle(VH holder) { - return false; - } - - /** - * This method returns the ViewHolder for our item, using the provided View. - * - * @param parent - * @return the ViewHolder for this Item - */ - @Override - public VH getViewHolder(ViewGroup parent) { - return getViewHolder(LayoutInflater.from(parent.getContext()).inflate(getLayoutRes(), parent, false)); - } - - /** - * This method returns the ViewHolder for our item, using the provided View. - * - * @param v - * @return the ViewHolder for this Item - */ - public abstract VH getViewHolder(View v); - - /** - * If this item equals to the given identifier - * - * @param id - * @return - */ - @Override - public boolean equals(long id) { - return id == mIdentifier; - } - - public boolean equals(int id) { - return id == mIdentifier; - } - - /** - * If this item equals to the given object - * - * @param o - * @return - */ - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AbstractDrawerItem that = (AbstractDrawerItem) o; - return mIdentifier == that.mIdentifier; - } - - /** - * the hashCode implementation - * - * @return - */ - @Override - public int hashCode() { - return Long.valueOf(mIdentifier).hashCode(); - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractSwitchableDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractSwitchableDrawerItem.java deleted file mode 100644 index a41ed90a..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractSwitchableDrawerItem.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import androidx.annotation.LayoutRes; -import androidx.appcompat.widget.SwitchCompat; -import android.view.View; -import android.widget.CompoundButton; - -import com.mikepenz.materialdrawer.Drawer; -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.interfaces.OnCheckedChangeListener; -import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; - -import java.util.List; - -/** - * Created by mikepenz on 03.02.15. - */ -public abstract class AbstractSwitchableDrawerItem extends BaseDescribeableDrawerItem { - - private boolean switchEnabled = true; - - private boolean checked = false; - private OnCheckedChangeListener onCheckedChangeListener = null; - - public Item withChecked(boolean checked) { - this.checked = checked; - return (Item) this; - } - - public Item withSwitchEnabled(boolean switchEnabled) { - this.switchEnabled = switchEnabled; - return (Item) this; - } - - public Item withOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) { - this.onCheckedChangeListener = onCheckedChangeListener; - return (Item) this; - } - - public Item withCheckable(boolean checkable) { - return withSelectable(checkable); - } - - public boolean isChecked() { - return checked; - } - - public boolean isSwitchEnabled() { - return switchEnabled; - } - - public OnCheckedChangeListener getOnCheckedChangeListener() { - return onCheckedChangeListener; - } - - @Override - public int getType() { - return R.id.material_drawer_item_primary_switch; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_switch; - } - - @Override - public void bindView(final ViewHolder viewHolder, List payloads) { - super.bindView(viewHolder, payloads); - - //bind the basic view parts - bindViewHelper(viewHolder); - - //handle the switch - viewHolder.switchView.setOnCheckedChangeListener(null); - viewHolder.switchView.setChecked(checked); - viewHolder.switchView.setOnCheckedChangeListener(checkedChangeListener); - viewHolder.switchView.setEnabled(switchEnabled); - - //add a onDrawerItemClickListener here to be able to check / uncheck if the drawerItem can't be selected - withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() { - @Override - public boolean onItemClick(View view, int position, IDrawerItem drawerItem) { - if (!isSelectable()) { - checked = !checked; - viewHolder.switchView.setChecked(checked); - } - - return false; - } - }); - - //call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required) - onPostBindView(this, viewHolder.itemView); - } - - @Override - public ViewHolder getViewHolder(View v) { - return new ViewHolder(v); - } - - public static class ViewHolder extends BaseViewHolder { - private SwitchCompat switchView; - - private ViewHolder(View view) { - super(view); - this.switchView = (SwitchCompat) view.findViewById(R.id.material_drawer_switch); - } - } - - private CompoundButton.OnCheckedChangeListener checkedChangeListener = new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (isEnabled()) { - checked = isChecked; - if (getOnCheckedChangeListener() != null) { - getOnCheckedChangeListener().onCheckedChanged(AbstractSwitchableDrawerItem.this, buttonView, isChecked); - } - } else { - buttonView.setOnCheckedChangeListener(null); - buttonView.setChecked(!isChecked); - buttonView.setOnCheckedChangeListener(checkedChangeListener); - } - } - }; -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractToggleableDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractToggleableDrawerItem.java deleted file mode 100644 index eaccfd47..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/AbstractToggleableDrawerItem.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import androidx.annotation.LayoutRes; -import android.view.View; -import android.widget.CompoundButton; -import android.widget.ToggleButton; - -import com.mikepenz.materialdrawer.Drawer; -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.interfaces.OnCheckedChangeListener; -import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; - -import java.util.List; - -/** - * Created by mikepenz on 03.02.15. - */ -public class AbstractToggleableDrawerItem extends BaseDescribeableDrawerItem { - private boolean toggleEnabled = true; - - private boolean checked = false; - private OnCheckedChangeListener onCheckedChangeListener = null; - - public Item withChecked(boolean checked) { - this.checked = checked; - return (Item) this; - } - - public Item withToggleEnabled(boolean toggleEnabled) { - this.toggleEnabled = toggleEnabled; - return (Item) this; - } - - public Item withOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) { - this.onCheckedChangeListener = onCheckedChangeListener; - return (Item) this; - } - - public boolean isChecked() { - return checked; - } - - public void setChecked(boolean checked) { - this.checked = checked; - } - - public boolean isToggleEnabled() { - return toggleEnabled; - } - - public void setToggleEnabled(boolean toggleEnabled) { - this.toggleEnabled = toggleEnabled; - } - - public OnCheckedChangeListener getOnCheckedChangeListener() { - return onCheckedChangeListener; - } - - public void setOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) { - this.onCheckedChangeListener = onCheckedChangeListener; - } - - @Override - public int getType() { - return R.id.material_drawer_item_primary_toggle; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_toggle; - } - - @Override - public void bindView(final ViewHolder viewHolder, List payloads) { - super.bindView(viewHolder, payloads); - - //bind the basic view parts - bindViewHelper(viewHolder); - - //handle the toggle - viewHolder.toggle.setOnCheckedChangeListener(null); - viewHolder.toggle.setChecked(checked); - viewHolder.toggle.setOnCheckedChangeListener(checkedChangeListener); - viewHolder.toggle.setEnabled(toggleEnabled); - - //add a onDrawerItemClickListener here to be able to check / uncheck if the drawerItem can't be selected - withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() { - @Override - public boolean onItemClick(View view, int position, IDrawerItem drawerItem) { - if (!isSelectable()) { - checked = !checked; - viewHolder.toggle.setChecked(checked); - } - - return false; - } - }); - - //call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required) - onPostBindView(this, viewHolder.itemView); - } - - @Override - public ViewHolder getViewHolder(View v) { - return new ViewHolder(v); - } - - public static class ViewHolder extends BaseViewHolder { - private ToggleButton toggle; - - private ViewHolder(View view) { - super(view); - this.toggle = (ToggleButton) view.findViewById(R.id.material_drawer_toggle); - } - } - - private CompoundButton.OnCheckedChangeListener checkedChangeListener = new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (isEnabled()) { - checked = isChecked; - if (getOnCheckedChangeListener() != null) { - getOnCheckedChangeListener().onCheckedChanged(AbstractToggleableDrawerItem.this, buttonView, isChecked); - } - } else { - buttonView.setOnCheckedChangeListener(null); - buttonView.setChecked(!isChecked); - buttonView.setOnCheckedChangeListener(checkedChangeListener); - } - } - }; -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseDescribeableDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseDescribeableDrawerItem.java deleted file mode 100644 index da6c1799..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseDescribeableDrawerItem.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.drawable.Drawable; - -import com.mikepenz.materialdrawer.holder.ColorHolder; -import com.mikepenz.materialdrawer.holder.ImageHolder; -import com.mikepenz.materialdrawer.holder.StringHolder; -import com.mikepenz.materialdrawer.util.DrawerUIUtils; - -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.annotation.StringRes; - -import static com.mikepenz.materialdrawer.util.DrawerUIUtils.themeDrawerItem; - -/** - * Created by mikepenz on 03.02.15. - */ -public abstract class BaseDescribeableDrawerItem extends BaseDrawerItem { - private StringHolder description; - private ColorHolder descriptionTextColor; - - @Override - public boolean isExcludeFromMiniDrawer() { - return this.excludeFromMiniDrawer; - } - - public T withDescription(String description) { - this.description = new StringHolder(description); - return (T) this; - } - - public T withDescription(@StringRes int descriptionRes) { - this.description = new StringHolder(descriptionRes); - return (T) this; - } - - public T withDescriptionTextColor(@ColorInt int color) { - this.descriptionTextColor = ColorHolder.fromColor(color); - return (T) this; - } - - public T withDescriptionTextColorRes(@ColorRes int colorRes) { - this.descriptionTextColor = ColorHolder.fromColorRes(colorRes); - return (T) this; - } - - public StringHolder getDescription() { - return description; - } - - public ColorHolder getDescriptionTextColor() { - return descriptionTextColor; - } - - /** - * a helper method to have the logic for all secondaryDrawerItems only once - * - * @param viewHolder - */ - protected void bindViewHelper(BaseViewHolder viewHolder) { - Context ctx = viewHolder.itemView.getContext(); - - //set the identifier from the drawerItem here. It can be used to run tests - viewHolder.itemView.setId(hashCode()); - - //set the item selected if it is - viewHolder.itemView.setSelected(isSelected()); - - //set the item enabled if it is - viewHolder.itemView.setEnabled(isEnabled()); - - //get the correct color for the background - int selectedColor = getSelectedColor(ctx); - //get the correct color for the text - int color = getColor(ctx); - ColorStateList selectedTextColor = getTextColorStateList(color, getSelectedTextColor(ctx)); - //get the correct color for the icon - int iconColor = getIconColor(ctx); - int selectedIconColor = getSelectedIconColor(ctx); - - //set the background for the item - themeDrawerItem(ctx, viewHolder.view, selectedColor, isSelectedBackgroundAnimated()); - //set the text for the name - StringHolder.applyTo(this.getName(), viewHolder.name); - //set the text for the description or hide - StringHolder.applyToOrHide(this.getDescription(), viewHolder.description); - - //set the colors for textViews - viewHolder.name.setTextColor(selectedTextColor); - //set the description text color - ColorHolder.applyToOr(getDescriptionTextColor(), viewHolder.description, selectedTextColor); - - //define the typeface for our textViews - if (getTypeface() != null) { - viewHolder.name.setTypeface(getTypeface()); - viewHolder.description.setTypeface(getTypeface()); - } - - //get the drawables for our icon and set it - Drawable icon = ImageHolder.decideIcon(getIcon(), ctx, iconColor, isIconTinted(), 1); - if (icon != null) { - Drawable selectedIcon = ImageHolder.decideIcon(getSelectedIcon(), ctx, selectedIconColor, isIconTinted(), 1); - ImageHolder.applyMultiIconTo(icon, iconColor, selectedIcon, selectedIconColor, isIconTinted(), viewHolder.icon); - } else { - ImageHolder.applyDecidedIconOrSetGone(getIcon(), viewHolder.icon, iconColor, isIconTinted(), 1); - } - - //for android API 17 --> Padding not applied via xml - DrawerUIUtils.setDrawerVerticalPadding(viewHolder.view, level); - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseDrawerItem.java deleted file mode 100644 index 0550f2cb..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseDrawerItem.java +++ /dev/null @@ -1,356 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.util.Pair; - -import com.mikepenz.iconics.typeface.IIcon; -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.holder.ColorHolder; -import com.mikepenz.materialdrawer.holder.ImageHolder; -import com.mikepenz.materialdrawer.holder.StringHolder; -import com.mikepenz.materialdrawer.model.interfaces.Iconable; -import com.mikepenz.materialdrawer.model.interfaces.Nameable; -import com.mikepenz.materialdrawer.model.interfaces.Tagable; -import com.mikepenz.materialdrawer.model.interfaces.Typefaceable; -import com.mikepenz.materialdrawer.util.DrawerUIUtils; - -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.annotation.DrawableRes; -import androidx.annotation.StringRes; -import androidx.recyclerview.widget.RecyclerView; - -import static com.mikepenz.materialdrawer.util.DrawerUIUtils.getBooleanStyleable; - -/** - * Created by mikepenz on 03.02.15. - */ -public abstract class BaseDrawerItem extends AbstractDrawerItem implements Nameable, Iconable, Tagable, Typefaceable { - protected ImageHolder icon; - protected ImageHolder selectedIcon; - protected StringHolder name; - - protected boolean iconTinted = false; - - protected ColorHolder selectedColor; - protected ColorHolder textColor; - protected ColorHolder selectedTextColor; - protected ColorHolder disabledTextColor; - - protected ColorHolder iconColor; - protected ColorHolder selectedIconColor; - protected ColorHolder disabledIconColor; - - protected Typeface typeface = null; - - protected Pair colorStateList; - - protected int level = 1; - - protected boolean excludeFromMiniDrawer = false; - - public T withIcon(ImageHolder icon) { - this.icon = icon; - return (T) this; - } - - public T withIcon(Drawable icon) { - this.icon = new ImageHolder(icon); - return (T) this; - } - - public T withIcon(@DrawableRes int iconRes) { - this.icon = new ImageHolder(iconRes); - return (T) this; - } - - public T withSelectedIcon(Drawable selectedIcon) { - this.selectedIcon = new ImageHolder(selectedIcon); - return (T) this; - } - - public T withSelectedIcon(@DrawableRes int selectedIconRes) { - this.selectedIcon = new ImageHolder(selectedIconRes); - return (T) this; - } - - public T withIcon(IIcon iicon) { - this.icon = new ImageHolder(iicon); - //if we are on api 21 or higher we use the IconicsDrawable for selection too and color it with the correct color - //else we use just the one drawable and enable tinting on press - if (Build.VERSION.SDK_INT >= 21) { - this.selectedIcon = new ImageHolder(iicon); - } else { - this.withIconTintingEnabled(true); - } - - return (T) this; - } - - public T withName(StringHolder name) { - this.name = name; - return (T) this; - } - - public T withName(String name) { - this.name = new StringHolder(name); - return (T) this; - } - - public T withName(@StringRes int nameRes) { - this.name = new StringHolder(nameRes); - return (T) this; - } - - public T withSelectedColor(@ColorInt int selectedColor) { - this.selectedColor = ColorHolder.fromColor(selectedColor); - return (T) this; - } - - public T withSelectedColorRes(@ColorRes int selectedColorRes) { - this.selectedColor = ColorHolder.fromColorRes(selectedColorRes); - return (T) this; - } - - public T withTextColor(@ColorInt int textColor) { - this.textColor = ColorHolder.fromColor(textColor); - return (T) this; - } - - public T withTextColorRes(@ColorRes int textColorRes) { - this.textColor = ColorHolder.fromColorRes(textColorRes); - return (T) this; - } - - public T withSelectedTextColor(@ColorInt int selectedTextColor) { - this.selectedTextColor = ColorHolder.fromColor(selectedTextColor); - return (T) this; - } - - public T withSelectedTextColorRes(@ColorRes int selectedColorRes) { - this.selectedTextColor = ColorHolder.fromColorRes(selectedColorRes); - return (T) this; - } - - public T withDisabledTextColor(@ColorInt int disabledTextColor) { - this.disabledTextColor = ColorHolder.fromColor(disabledTextColor); - return (T) this; - } - - public T withDisabledTextColorRes(@ColorRes int disabledTextColorRes) { - this.disabledTextColor = ColorHolder.fromColorRes(disabledTextColorRes); - return (T) this; - } - - public T withIconColor(@ColorInt int iconColor) { - this.iconColor = ColorHolder.fromColor(iconColor); - return (T) this; - } - - public T withIconColorRes(@ColorRes int iconColorRes) { - this.iconColor = ColorHolder.fromColorRes(iconColorRes); - return (T) this; - } - - public T withSelectedIconColor(@ColorInt int selectedIconColor) { - this.selectedIconColor = ColorHolder.fromColor(selectedIconColor); - return (T) this; - } - - public T withSelectedIconColorRes(@ColorRes int selectedColorRes) { - this.selectedIconColor = ColorHolder.fromColorRes(selectedColorRes); - return (T) this; - } - - public T withDisabledIconColor(@ColorInt int disabledIconColor) { - this.disabledIconColor = ColorHolder.fromColor(disabledIconColor); - return (T) this; - } - - public T withDisabledIconColorRes(@ColorRes int disabledIconColorRes) { - this.disabledIconColor = ColorHolder.fromColorRes(disabledIconColorRes); - return (T) this; - } - - /** - * will tint the icon with the default (or set) colors - * (default and selected state) - * - * @param iconTintingEnabled - * @return - */ - public T withIconTintingEnabled(boolean iconTintingEnabled) { - this.iconTinted = iconTintingEnabled; - return (T) this; - } - - @Deprecated - public T withIconTinted(boolean iconTinted) { - this.iconTinted = iconTinted; - return (T) this; - } - - /** - * for backwards compatibility - withIconTinted.. - * - * @param iconTinted - * @return - */ - @Deprecated - public T withTintSelectedIcon(boolean iconTinted) { - return withIconTintingEnabled(iconTinted); - } - - public T withTypeface(Typeface typeface) { - this.typeface = typeface; - return (T) this; - } - - public T withLevel(int level) { - this.level = level; - return (T) this; - } - - public T withExcludeFromMiniDrawer(boolean excludeFromMiniDrawer) { - this.excludeFromMiniDrawer = excludeFromMiniDrawer; - return (T) this; - } - - - public ColorHolder getSelectedColor() { - return selectedColor; - } - - public ColorHolder getTextColor() { - return textColor; - } - - public ColorHolder getSelectedTextColor() { - return selectedTextColor; - } - - public ColorHolder getDisabledTextColor() { - return disabledTextColor; - } - - public boolean isIconTinted() { - return iconTinted; - } - - public ImageHolder getIcon() { - return icon; - } - - public ImageHolder getSelectedIcon() { - return selectedIcon; - } - - public StringHolder getName() { - return name; - } - - public ColorHolder getDisabledIconColor() { - return disabledIconColor; - } - - public ColorHolder getSelectedIconColor() { - return selectedIconColor; - } - - public ColorHolder getIconColor() { - return iconColor; - } - - public Typeface getTypeface() { - return typeface; - } - - public int getLevel() { - return level; - } - - /** - * helper method to decide for the correct color - * - * @param ctx - * @return - */ - protected int getSelectedColor(Context ctx) { - if (getBooleanStyleable(ctx, R.styleable.MaterialDrawer_material_drawer_legacy_style, false)) { - return ColorHolder.color(getSelectedColor(), ctx, R.attr.material_drawer_selected_legacy, R.color.material_drawer_selected_legacy); - } else { - return ColorHolder.color(getSelectedColor(), ctx, R.attr.material_drawer_selected, R.color.material_drawer_selected); - } - } - - /** - * helper method to decide for the correct color - * - * @param ctx - * @return - */ - protected int getColor(Context ctx) { - int color; - if (this.isEnabled()) { - color = ColorHolder.color(getTextColor(), ctx, R.attr.material_drawer_primary_text, R.color.material_drawer_primary_text); - } else { - color = ColorHolder.color(getDisabledTextColor(), ctx, R.attr.material_drawer_hint_text, R.color.material_drawer_hint_text); - } - return color; - } - - /** - * helper method to decide for the correct color - * - * @param ctx - * @return - */ - protected int getSelectedTextColor(Context ctx) { - return ColorHolder.color(getSelectedTextColor(), ctx, R.attr.material_drawer_selected_text, R.color.material_drawer_selected_text); - } - - /** - * helper method to decide for the correct color - * - * @param ctx - * @return - */ - public int getIconColor(Context ctx) { - int iconColor; - if (this.isEnabled()) { - iconColor = ColorHolder.color(getIconColor(), ctx, R.attr.material_drawer_primary_icon, R.color.material_drawer_primary_icon); - } else { - iconColor = ColorHolder.color(getDisabledIconColor(), ctx, R.attr.material_drawer_hint_icon, R.color.material_drawer_hint_icon); - } - return iconColor; - } - - /** - * helper method to decide for the correct color - * - * @param ctx - * @return - */ - protected int getSelectedIconColor(Context ctx) { - return ColorHolder.color(getSelectedIconColor(), ctx, R.attr.material_drawer_selected_text, R.color.material_drawer_selected_text); - } - - /** - * helper to get the ColorStateList for the text and remembering it so we do not have to recreate it all the time - * - * @param color - * @param selectedTextColor - * @return - */ - protected ColorStateList getTextColorStateList(@ColorInt int color, @ColorInt int selectedTextColor) { - if (colorStateList == null || color + selectedTextColor != colorStateList.first) { - colorStateList = new Pair<>(color + selectedTextColor, DrawerUIUtils.getTextColorStateList(color, selectedTextColor)); - } - - return colorStateList.second; - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseViewHolder.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseViewHolder.java deleted file mode 100644 index a49e3a4d..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/BaseViewHolder.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.mikepenz.materialdrawer.R; - -import androidx.recyclerview.widget.RecyclerView; - -public class BaseViewHolder extends RecyclerView.ViewHolder { - protected View view; - protected ImageView icon; - protected TextView name; - protected TextView description; - - public BaseViewHolder(View view) { - super(view); - - this.view = view; - this.icon = view.findViewById(R.id.material_drawer_icon); - this.name = view.findViewById(R.id.material_drawer_name); - this.description = view.findViewById(R.id.material_drawer_description); - } -} \ No newline at end of file diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ContainerDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ContainerDrawerItem.java deleted file mode 100644 index c636dbf8..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ContainerDrawerItem.java +++ /dev/null @@ -1,149 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import androidx.annotation.LayoutRes; -import androidx.recyclerview.widget.RecyclerView; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; - -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.holder.DimenHolder; -import com.mikepenz.materialize.util.UIUtils; - -import java.util.List; - -/** - * Created by mikepenz on 03.02.15. - */ -public class ContainerDrawerItem extends AbstractDrawerItem { - - private DimenHolder mHeight; - - public ContainerDrawerItem withHeight(DimenHolder height) { - mHeight = height; - return this; - } - - public DimenHolder getHeight() { - return mHeight; - } - - private View mView; - - public ContainerDrawerItem withView(View view) { - this.mView = view; - return this; - } - - public View getView() { - return mView; - } - - public enum Position { - TOP, - BOTTOM, - NONE; - } - - private Position mViewPosition = Position.TOP; - - public ContainerDrawerItem withViewPosition(Position position) { - this.mViewPosition = position; - return this; - } - - private boolean mDivider = true; - - public ContainerDrawerItem withDivider(boolean divider) { - this.mDivider = divider; - return this; - } - - public Position getViewPosition() { - return mViewPosition; - } - - @Override - public int getType() { - return R.id.material_drawer_item_container; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_container; - } - - @Override - public void bindView(ViewHolder viewHolder, List payloads) { - super.bindView(viewHolder, payloads); - - Context ctx = viewHolder.itemView.getContext(); - - //set the identifier from the drawerItem here. It can be used to run tests - viewHolder.itemView.setId(hashCode()); - - //define how the divider should look like - viewHolder.view.setEnabled(false); - - //make sure our view is not used in another parent - if (mView.getParent() != null) { - ((ViewGroup) mView.getParent()).removeView(mView); - } - - //set the height - int height = ViewGroup.LayoutParams.WRAP_CONTENT; - if (mHeight != null) { - RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) viewHolder.view.getLayoutParams(); - height = mHeight.asPixel(ctx); - lp.height = height; - viewHolder.view.setLayoutParams(lp); - } - - //make sure the header view is empty - ((ViewGroup) viewHolder.view).removeAllViews(); - - int dividerHeight = 0; - if (mDivider) { - dividerHeight = 1; - } - - View divider = new View(ctx); - divider.setMinimumHeight(dividerHeight); - divider.setBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(ctx, R.attr.material_drawer_divider, R.color.material_drawer_divider)); - - LinearLayout.LayoutParams dividerParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) UIUtils.convertDpToPixel(dividerHeight, ctx)); - LinearLayout.LayoutParams viewParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, mHeight != null ? height - (int) UIUtils.convertDpToPixel(dividerHeight, ctx) : height); - - //depending on the position we add the view - if (mViewPosition == Position.TOP) { - ((ViewGroup) viewHolder.view).addView(mView, viewParams); - dividerParams.bottomMargin = ctx.getResources().getDimensionPixelSize(R.dimen.material_drawer_padding); - ((ViewGroup) viewHolder.view).addView(divider, dividerParams); - } else if (mViewPosition == Position.BOTTOM) { - dividerParams.topMargin = ctx.getResources().getDimensionPixelSize(R.dimen.material_drawer_padding); - ((ViewGroup) viewHolder.view).addView(divider, dividerParams); - ((ViewGroup) viewHolder.view).addView(mView, viewParams); - } else { - ((ViewGroup) viewHolder.view).addView(mView, viewParams); - } - - //call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required) - onPostBindView(this, viewHolder.itemView); - } - - @Override - public ViewHolder getViewHolder(View v) { - return new ViewHolder(v); - } - - public static class ViewHolder extends RecyclerView.ViewHolder { - private View view; - - private ViewHolder(View view) { - super(view); - this.view = view; - } - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/DividerDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/DividerDrawerItem.java deleted file mode 100644 index e7429d1e..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/DividerDrawerItem.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import androidx.annotation.LayoutRes; -import androidx.core.view.ViewCompat; -import androidx.recyclerview.widget.RecyclerView; -import android.view.View; - -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialize.util.UIUtils; - -import java.util.List; - -/** - * Created by mikepenz on 03.02.15. - */ -public class DividerDrawerItem extends AbstractDrawerItem { - @Override - public int getType() { - return R.id.material_drawer_item_divider; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_divider; - } - - @Override - public void bindView(ViewHolder viewHolder, List payloads) { - super.bindView(viewHolder, payloads); - - Context ctx = viewHolder.itemView.getContext(); - - //set the identifier from the drawerItem here. It can be used to run tests - viewHolder.itemView.setId(hashCode()); - - //define how the divider should look like - viewHolder.view.setClickable(false); - viewHolder.view.setEnabled(false); - viewHolder.view.setMinimumHeight(1); - ViewCompat.setImportantForAccessibility(viewHolder.view, - ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO); - - //set the color for the divider - viewHolder.divider.setBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(ctx, R.attr.material_drawer_divider, R.color.material_drawer_divider)); - - //call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required) - onPostBindView(this, viewHolder.itemView); - } - - @Override - public ViewHolder getViewHolder(View v) { - return new ViewHolder(v); - } - - public static class ViewHolder extends RecyclerView.ViewHolder { - private View view; - private View divider; - - private ViewHolder(View view) { - super(view); - this.view = view; - this.divider = view.findViewById(R.id.material_drawer_divider); - } - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ExpandableBadgeDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ExpandableBadgeDrawerItem.java deleted file mode 100644 index f481a741..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ExpandableBadgeDrawerItem.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import android.graphics.Color; -import androidx.annotation.LayoutRes; -import androidx.annotation.StringRes; -import androidx.core.view.ViewCompat; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.mikepenz.iconics.IconicsDrawable; -import com.mikepenz.materialdrawer.Drawer; -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.holder.BadgeStyle; -import com.mikepenz.materialdrawer.holder.ColorHolder; -import com.mikepenz.materialdrawer.holder.StringHolder; -import com.mikepenz.materialdrawer.icons.MaterialDrawerFont; -import com.mikepenz.materialdrawer.model.interfaces.ColorfulBadgeable; -import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; - -import java.util.List; - -/** - * Created by mikepenz on 03.02.15. - * NOTE: The arrow will just animate (and rotate) on APIs higher than 11 as the ViewCompat will skip this on API 10 - */ -public class ExpandableBadgeDrawerItem extends BaseDescribeableDrawerItem - implements ColorfulBadgeable { - - private Drawer.OnDrawerItemClickListener mOnDrawerItemClickListener; - - protected ColorHolder arrowColor; - - protected int arrowRotationAngleStart = 0; - - protected int arrowRotationAngleEnd = 180; - - protected StringHolder mBadge; - protected BadgeStyle mBadgeStyle = new BadgeStyle(); - - @Override - public int getType() { - return R.id.material_drawer_item_expandable_badge; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_expandable_badge; - } - - @Override - public void bindView(ExpandableBadgeDrawerItem.ViewHolder viewHolder, List payloads) { - super.bindView(viewHolder, payloads); - - Context ctx = viewHolder.itemView.getContext(); - //bind the basic view parts - bindViewHelper(viewHolder); - - //set the text for the badge or hide - boolean badgeVisible = StringHolder.applyToOrHide(mBadge, viewHolder.badge); - //style the badge if it is visible - if (true) { - mBadgeStyle.style(viewHolder.badge, getTextColorStateList(getColor(ctx), getSelectedTextColor(ctx))); - viewHolder.badgeContainer.setVisibility(View.VISIBLE); - } else { - viewHolder.badgeContainer.setVisibility(View.GONE); - } - - //define the typeface for our textViews - if (getTypeface() != null) { - viewHolder.badge.setTypeface(getTypeface()); - } - - //make sure all animations are stopped - if (viewHolder.arrow.getDrawable() instanceof IconicsDrawable) { - ((IconicsDrawable) viewHolder.arrow.getDrawable()).color(this.arrowColor != null ? this.arrowColor.color(ctx) : getIconColor(ctx)); - } - viewHolder.arrow.clearAnimation(); - if (!isExpanded()) { - viewHolder.arrow.setRotation(this.arrowRotationAngleStart); - } else { - viewHolder.arrow.setRotation(this.arrowRotationAngleEnd); - } - - //call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required) - onPostBindView(this, viewHolder.itemView); - } - - @Override - public ExpandableBadgeDrawerItem withOnDrawerItemClickListener(Drawer.OnDrawerItemClickListener onDrawerItemClickListener) { - mOnDrawerItemClickListener = onDrawerItemClickListener; - return this; - } - - @Override - public Drawer.OnDrawerItemClickListener getOnDrawerItemClickListener() { - return mOnArrowDrawerItemClickListener; - } - - /** - * our internal onDrawerItemClickListener which will handle the arrow animation - */ - private Drawer.OnDrawerItemClickListener mOnArrowDrawerItemClickListener = new Drawer.OnDrawerItemClickListener() { - @Override - public boolean onItemClick(View view, int position, IDrawerItem drawerItem) { - if (drawerItem instanceof AbstractDrawerItem && drawerItem.isEnabled()) { - if (((AbstractDrawerItem) drawerItem).getSubItems() != null) { - if (((AbstractDrawerItem) drawerItem).isExpanded()) { - ViewCompat.animate(view.findViewById(R.id.material_drawer_arrow)).rotation(ExpandableBadgeDrawerItem.this.arrowRotationAngleEnd).start(); - } else { - ViewCompat.animate(view.findViewById(R.id.material_drawer_arrow)) - .rotation(ExpandableBadgeDrawerItem.this.arrowRotationAngleStart) - .start(); - } - } - } - - return mOnDrawerItemClickListener != null && mOnDrawerItemClickListener.onItemClick(view, position, drawerItem); - } - }; - - @Override - public ExpandableBadgeDrawerItem withBadge(StringHolder badge) { - this.mBadge = badge; - return (ExpandableBadgeDrawerItem) this; - } - - @Override - public ExpandableBadgeDrawerItem withBadge(String badge) { - this.mBadge = new StringHolder(badge); - return (ExpandableBadgeDrawerItem) this; - } - - @Override - public ExpandableBadgeDrawerItem withBadge(@StringRes int badgeRes) { - this.mBadge = new StringHolder(badgeRes); - return (ExpandableBadgeDrawerItem) this; - } - - @Override - public ExpandableBadgeDrawerItem withBadgeStyle(BadgeStyle badgeStyle) { - this.mBadgeStyle = badgeStyle; - return (ExpandableBadgeDrawerItem) this; - } - - public StringHolder getBadge() { - return mBadge; - } - - public BadgeStyle getBadgeStyle() { - return mBadgeStyle; - } - - @Override - public ViewHolder getViewHolder(View v) { - return new ViewHolder(v); - } - - public static class ViewHolder extends BaseViewHolder { - public ImageView arrow; - public View badgeContainer; - public TextView badge; - - public ViewHolder(View view) { - super(view); - badgeContainer = view.findViewById(R.id.material_drawer_badge_container); - badge = view.findViewById(R.id.material_drawer_badge); - arrow = view.findViewById(R.id.material_drawer_arrow); - arrow.setImageDrawable(new IconicsDrawable(view.getContext(), MaterialDrawerFont.Icon.mdf_expand_more).sizeDp(16).paddingDp(2).color(Color.BLACK)); - } - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ExpandableDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ExpandableDrawerItem.java deleted file mode 100644 index f457d131..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ExpandableDrawerItem.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import android.graphics.Color; -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.annotation.LayoutRes; -import androidx.core.view.ViewCompat; -import android.view.View; -import android.widget.ImageView; - -import com.mikepenz.iconics.IconicsDrawable; -import com.mikepenz.materialdrawer.Drawer; -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.holder.ColorHolder; -import com.mikepenz.materialdrawer.icons.MaterialDrawerFont; -import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; - -import java.util.List; - -/** - * Created by mikepenz on 03.02.15. - * NOTE: The arrow will just animate (and rotate) on APIs higher than 11 as the ViewCompat will skip this on API 10 - */ -public class ExpandableDrawerItem extends BaseDescribeableDrawerItem { - - private Drawer.OnDrawerItemClickListener mOnDrawerItemClickListener; - - protected ColorHolder arrowColor; - - protected int arrowRotationAngleStart = 0; - - protected int arrowRotationAngleEnd = 180; - - public ExpandableDrawerItem withArrowColor(@ColorInt int arrowColor) { - this.arrowColor = ColorHolder.fromColor(arrowColor); - return this; - } - - public ExpandableDrawerItem withArrowColorRes(@ColorRes int arrowColorRes) { - this.arrowColor = ColorHolder.fromColorRes(arrowColorRes); - return this; - } - - public ExpandableDrawerItem withArrowRotationAngleStart(int angle) { - this.arrowRotationAngleStart = angle; - return this; - } - - public ExpandableDrawerItem withArrowRotationAngleEnd(int angle) { - this.arrowRotationAngleEnd = angle; - return this; - } - - @Override - public int getType() { - return R.id.material_drawer_item_expandable; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_expandable; - } - - @Override - public ExpandableDrawerItem withOnDrawerItemClickListener(Drawer.OnDrawerItemClickListener onDrawerItemClickListener) { - mOnDrawerItemClickListener = onDrawerItemClickListener; - return this; - } - - @Override - public Drawer.OnDrawerItemClickListener getOnDrawerItemClickListener() { - return mOnArrowDrawerItemClickListener; - } - - /** - * our internal onDrawerItemClickListener which will handle the arrow animation - */ - private Drawer.OnDrawerItemClickListener mOnArrowDrawerItemClickListener = new Drawer.OnDrawerItemClickListener() { - @Override - public boolean onItemClick(View view, int position, IDrawerItem drawerItem) { - if (drawerItem instanceof AbstractDrawerItem && drawerItem.isEnabled()) { - if (((AbstractDrawerItem) drawerItem).getSubItems() != null) { - if (((AbstractDrawerItem) drawerItem).isExpanded()) { - ViewCompat.animate(view.findViewById(R.id.material_drawer_arrow)).rotation(ExpandableDrawerItem.this.arrowRotationAngleEnd).start(); - } else { - ViewCompat.animate(view.findViewById(R.id.material_drawer_arrow)).rotation(ExpandableDrawerItem.this.arrowRotationAngleStart).start(); - } - } - } - - return mOnDrawerItemClickListener != null && mOnDrawerItemClickListener.onItemClick(view, position, drawerItem); - } - }; - - @Override - public void bindView(ViewHolder viewHolder, List payloads) { - super.bindView(viewHolder, payloads); - - Context ctx = viewHolder.itemView.getContext(); - //bind the basic view parts - bindViewHelper(viewHolder); - - //make sure all animations are stopped - if (viewHolder.arrow.getDrawable() instanceof IconicsDrawable) { - ((IconicsDrawable) viewHolder.arrow.getDrawable()).color(this.arrowColor != null ? this.arrowColor.color(ctx) : getIconColor(ctx)); - } - viewHolder.arrow.clearAnimation(); - if (!isExpanded()) { - viewHolder.arrow.setRotation(this.arrowRotationAngleStart); - } else { - viewHolder.arrow.setRotation(this.arrowRotationAngleEnd); - } - - //call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required) - onPostBindView(this, viewHolder.itemView); - } - - @Override - public ViewHolder getViewHolder(View v) { - return new ViewHolder(v); - } - - public static class ViewHolder extends BaseViewHolder { - public ImageView arrow; - - public ViewHolder(View view) { - super(view); - arrow = view.findViewById(R.id.material_drawer_arrow); - arrow.setImageDrawable(new IconicsDrawable(view.getContext(), MaterialDrawerFont.Icon.mdf_expand_more).sizeDp(16).paddingDp(2).color(Color.BLACK)); - } - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/MiniDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/MiniDrawerItem.java deleted file mode 100644 index 6303b2bb..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/MiniDrawerItem.java +++ /dev/null @@ -1,193 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.holder.BadgeStyle; -import com.mikepenz.materialdrawer.holder.DimenHolder; -import com.mikepenz.materialdrawer.holder.ImageHolder; -import com.mikepenz.materialdrawer.holder.StringHolder; - -import java.util.List; - -import androidx.annotation.DimenRes; -import androidx.annotation.LayoutRes; -import androidx.recyclerview.widget.RecyclerView; - -import static com.mikepenz.materialdrawer.util.DrawerUIUtils.themeDrawerItem; - -/** - * Created by mikepenz on 03.02.15. - */ -public class MiniDrawerItem extends BaseDrawerItem { - private StringHolder mBadge; - private BadgeStyle mBadgeStyle = new BadgeStyle(); - - private boolean mEnableSelectedBackground = false; - protected DimenHolder mCustomHeight; - - public MiniDrawerItem() { - - } - - public MiniDrawerItem(PrimaryDrawerItem primaryDrawerItem) { - this.mIdentifier = primaryDrawerItem.mIdentifier; - this.mTag = primaryDrawerItem.mTag; - - this.mBadge = primaryDrawerItem.mBadge; - this.mBadgeStyle = primaryDrawerItem.mBadgeStyle; - - this.mEnabled = primaryDrawerItem.mEnabled; - this.mSelectable = primaryDrawerItem.mSelectable; - this.mSelected = primaryDrawerItem.mSelected; - - this.icon = primaryDrawerItem.icon; - this.selectedIcon = primaryDrawerItem.selectedIcon; - - this.iconTinted = primaryDrawerItem.iconTinted; - this.selectedColor = primaryDrawerItem.selectedColor; - - this.iconColor = primaryDrawerItem.iconColor; - this.selectedIconColor = primaryDrawerItem.selectedIconColor; - this.disabledIconColor = primaryDrawerItem.disabledIconColor; - } - - public MiniDrawerItem(SecondaryDrawerItem secondaryDrawerItem) { - this.mIdentifier = secondaryDrawerItem.mIdentifier; - this.mTag = secondaryDrawerItem.mTag; - - this.mBadge = secondaryDrawerItem.mBadge; - this.mBadgeStyle = secondaryDrawerItem.mBadgeStyle; - - this.mEnabled = secondaryDrawerItem.mEnabled; - this.mSelectable = secondaryDrawerItem.mSelectable; - this.mSelected = secondaryDrawerItem.mSelected; - - this.icon = secondaryDrawerItem.icon; - this.selectedIcon = secondaryDrawerItem.selectedIcon; - - this.iconTinted = secondaryDrawerItem.iconTinted; - this.selectedColor = secondaryDrawerItem.selectedColor; - - this.iconColor = secondaryDrawerItem.iconColor; - this.selectedIconColor = secondaryDrawerItem.selectedIconColor; - this.disabledIconColor = secondaryDrawerItem.disabledIconColor; - } - - - public MiniDrawerItem withCustomHeightRes(@DimenRes int customHeightRes) { - this.mCustomHeight = DimenHolder.fromResource(customHeightRes); - return this; - } - - public MiniDrawerItem withCustomHeightDp(int customHeightDp) { - this.mCustomHeight = DimenHolder.fromDp(customHeightDp); - return this; - } - - public MiniDrawerItem withCustomHeightPx(int customHeightPx) { - this.mCustomHeight = DimenHolder.fromPixel(customHeightPx); - return this; - } - - public MiniDrawerItem withCustomHeight(DimenHolder customHeight) { - this.mCustomHeight = customHeight; - return this; - } - - public MiniDrawerItem withEnableSelectedBackground(boolean enableSelectedBackground) { - this.mEnableSelectedBackground = enableSelectedBackground; - return this; - } - - @Override - public int getType() { - return R.id.material_drawer_item_mini; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_mini; - } - - @Override - public void bindView(ViewHolder viewHolder, List payloads) { - super.bindView(viewHolder, payloads); - - Context ctx = viewHolder.itemView.getContext(); - - //set a different height for this item - if (mCustomHeight != null) { - RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) viewHolder.itemView.getLayoutParams(); - lp.height = mCustomHeight.asPixel(ctx); - viewHolder.itemView.setLayoutParams(lp); - } - - //set the identifier from the drawerItem here. It can be used to run tests - viewHolder.itemView.setId(hashCode()); - - //set the item enabled if it is - viewHolder.itemView.setEnabled(isEnabled()); - - //set the item selected if it is - viewHolder.itemView.setSelected(isSelected()); - - // - viewHolder.itemView.setTag(this); - - //get the correct color for the icon - int iconColor = getIconColor(ctx); - int selectedIconColor = getSelectedIconColor(ctx); - - if (mEnableSelectedBackground) { - //get the correct color for the background - int selectedColor = getSelectedColor(ctx); - //set the background for the item - themeDrawerItem(ctx, viewHolder.view, selectedColor, isSelectedBackgroundAnimated()); - } - - //set the text for the badge or hide - boolean badgeVisible = StringHolder.applyToOrHide(mBadge, viewHolder.badge); - //style the badge if it is visible - if (badgeVisible) { - mBadgeStyle.style(viewHolder.badge); - } - - //get the drawables for our icon and set it - Drawable icon = ImageHolder.decideIcon(getIcon(), ctx, iconColor, isIconTinted(), 1); - Drawable selectedIcon = ImageHolder.decideIcon(getSelectedIcon(), ctx, selectedIconColor, isIconTinted(), 1); - ImageHolder.applyMultiIconTo(icon, iconColor, selectedIcon, selectedIconColor, isIconTinted(), viewHolder.icon); - - //for android API 17 --> Padding not applied via xml - int verticalPadding = ctx.getResources().getDimensionPixelSize(R.dimen.material_drawer_padding); - int topBottomPadding = ctx.getResources().getDimensionPixelSize(R.dimen.material_mini_drawer_item_padding); - viewHolder.itemView.setPadding(verticalPadding, topBottomPadding, verticalPadding, topBottomPadding); - - //call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required) - onPostBindView(this, viewHolder.itemView); - } - - @Override - public ViewHolder getViewHolder(View v) { - return new ViewHolder(v); - } - - public static class ViewHolder extends RecyclerView.ViewHolder { - private View view; - private ImageView icon; - private TextView badge; - - public ViewHolder(View view) { - super(view); - - this.view = view; - this.icon = (ImageView) view.findViewById(R.id.material_drawer_icon); - this.badge = (TextView) view.findViewById(R.id.material_drawer_badge); - } - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/MiniProfileDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/MiniProfileDrawerItem.java deleted file mode 100644 index c4a09653..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/MiniProfileDrawerItem.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import androidx.annotation.DimenRes; -import androidx.annotation.DrawableRes; -import androidx.annotation.LayoutRes; -import androidx.recyclerview.widget.RecyclerView; -import android.view.View; -import android.widget.ImageView; - -import com.mikepenz.iconics.typeface.IIcon; -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.holder.DimenHolder; -import com.mikepenz.materialdrawer.holder.ImageHolder; -import com.mikepenz.materialdrawer.holder.StringHolder; -import com.mikepenz.materialdrawer.model.interfaces.IProfile; - -import java.util.List; - -/** - * Created by mikepenz on 03.02.15. - */ -public class MiniProfileDrawerItem extends AbstractDrawerItem implements IProfile { - protected ImageHolder icon; - - protected DimenHolder customHeight; - - public MiniProfileDrawerItem() { - withSelectable(false); - } - - public MiniProfileDrawerItem(ProfileDrawerItem profile) { - this.icon = profile.icon; - this.mEnabled = profile.mEnabled; - withSelectable(false); - } - - @Override - public MiniProfileDrawerItem withName(CharSequence name) { - return null; - } - - @Override - public StringHolder getName() { - return null; - } - - @Override - public MiniProfileDrawerItem withEmail(String email) { - return null; - } - - @Override - public StringHolder getEmail() { - return null; - } - - @Override - public MiniProfileDrawerItem withIcon(Drawable icon) { - this.icon = new ImageHolder(icon); - return this; - } - - @Override - public MiniProfileDrawerItem withIcon(@DrawableRes int iconRes) { - this.icon = new ImageHolder(iconRes); - return this; - } - - @Override - public MiniProfileDrawerItem withIcon(Bitmap iconBitmap) { - this.icon = new ImageHolder(iconBitmap); - return this; - } - - @Override - public MiniProfileDrawerItem withIcon(String url) { - this.icon = new ImageHolder(url); - return this; - } - - @Override - public MiniProfileDrawerItem withIcon(Uri uri) { - this.icon = new ImageHolder(uri); - return this; - } - - @Override - public MiniProfileDrawerItem withIcon(IIcon icon) { - this.icon = new ImageHolder(icon); - return this; - } - - public MiniProfileDrawerItem withCustomHeightRes(@DimenRes int customHeightRes) { - this.customHeight = DimenHolder.fromResource(customHeightRes); - return this; - } - - public MiniProfileDrawerItem withCustomHeightDp(int customHeightDp) { - this.customHeight = DimenHolder.fromDp(customHeightDp); - return this; - } - - public MiniProfileDrawerItem withCustomHeightPx(int customHeightPx) { - this.customHeight = DimenHolder.fromPixel(customHeightPx); - return this; - } - - public MiniProfileDrawerItem withCustomHeight(DimenHolder customHeight) { - this.customHeight = customHeight; - return this; - } - - @Override - public ImageHolder getIcon() { - return icon; - } - - @Override - public int getType() { - return R.id.material_drawer_item_mini_profile; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_mini_profile; - } - - @Override - public void bindView(ViewHolder viewHolder, List payloads) { - super.bindView(viewHolder, payloads); - - if (customHeight != null) { - RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) viewHolder.itemView.getLayoutParams(); - lp.height = customHeight.asPixel(viewHolder.itemView.getContext()); - viewHolder.itemView.setLayoutParams(lp); - } - - //set the identifier from the drawerItem here. It can be used to run tests - viewHolder.itemView.setId(hashCode()); - - //set the item enabled if it is - viewHolder.itemView.setEnabled(isEnabled()); - - //set the icon - ImageHolder.applyToOrSetInvisible(getIcon(), viewHolder.icon); - - //call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required) - onPostBindView(this, viewHolder.itemView); - } - - @Override - public ViewHolder getViewHolder(View v) { - return new ViewHolder(v); - } - - public static class ViewHolder extends RecyclerView.ViewHolder { - private ImageView icon; - - public ViewHolder(View view) { - super(view); - this.icon = (ImageView) view.findViewById(R.id.material_drawer_icon); - } - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/PrimaryDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/PrimaryDrawerItem.java deleted file mode 100644 index accf6f41..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/PrimaryDrawerItem.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -/** - * Created by mikepenz on 03.02.15. - */ -public class PrimaryDrawerItem extends AbstractBadgeableDrawerItem { - -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ProfileDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ProfileDrawerItem.java deleted file mode 100644 index 492d19af..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ProfileDrawerItem.java +++ /dev/null @@ -1,365 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Bitmap; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.util.Pair; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.mikepenz.iconics.typeface.IIcon; -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.holder.ColorHolder; -import com.mikepenz.materialdrawer.holder.ImageHolder; -import com.mikepenz.materialdrawer.holder.StringHolder; -import com.mikepenz.materialdrawer.model.interfaces.IProfile; -import com.mikepenz.materialdrawer.model.interfaces.Tagable; -import com.mikepenz.materialdrawer.model.interfaces.Typefaceable; -import com.mikepenz.materialdrawer.util.DrawerImageLoader; -import com.mikepenz.materialdrawer.util.DrawerUIUtils; - -import java.util.List; - -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.annotation.DrawableRes; -import androidx.annotation.LayoutRes; -import androidx.annotation.StringRes; -import androidx.recyclerview.widget.RecyclerView; - -import static com.mikepenz.materialdrawer.util.DrawerUIUtils.getBooleanStyleable; -import static com.mikepenz.materialdrawer.util.DrawerUIUtils.themeDrawerItem; - -/** - * Created by mikepenz on 03.02.15. - */ -public class ProfileDrawerItem extends AbstractDrawerItem implements IProfile, Tagable, Typefaceable { - protected boolean nameShown = false; - - protected ImageHolder icon; - - protected StringHolder name; - protected StringHolder email; - - protected ColorHolder selectedColor; - protected ColorHolder textColor; - protected ColorHolder selectedTextColor; - protected ColorHolder disabledTextColor; - - protected Typeface typeface = null; - - @Override - public ProfileDrawerItem withIcon(Drawable icon) { - this.icon = new ImageHolder(icon); - return this; - } - - @Override - public ProfileDrawerItem withIcon(@DrawableRes int iconRes) { - this.icon = new ImageHolder(iconRes); - return this; - } - - @Override - public ProfileDrawerItem withIcon(Bitmap iconBitmap) { - this.icon = new ImageHolder(iconBitmap); - return this; - } - - @Override - public ProfileDrawerItem withIcon(IIcon icon) { - this.icon = new ImageHolder(icon); - return this; - } - - @Override - public ProfileDrawerItem withIcon(String url) { - this.icon = new ImageHolder(url); - return this; - } - - @Override - public ProfileDrawerItem withIcon(Uri uri) { - this.icon = new ImageHolder(uri); - return this; - } - - public ProfileDrawerItem withName(CharSequence name) { - this.name = new StringHolder(name); - return this; - } - - public ProfileDrawerItem withName(@StringRes int nameRes) { - this.name = new StringHolder(nameRes); - return this; - } - - public ProfileDrawerItem withEmail(String email) { - this.email = new StringHolder(email); - return this; - } - - public ProfileDrawerItem withEmail(@StringRes int emailRes) { - this.email = new StringHolder(emailRes); - return this; - } - - /** - * Whether to show the profile name in the account switcher. - * - * @param nameShown show name in switcher - * @return the {@link ProfileDrawerItem} - */ - public ProfileDrawerItem withNameShown(boolean nameShown) { - this.nameShown = nameShown; - return this; - } - - public ProfileDrawerItem withSelectedColor(@ColorInt int selectedColor) { - this.selectedColor = ColorHolder.fromColor(selectedColor); - return this; - } - - public ProfileDrawerItem withSelectedColorRes(@ColorRes int selectedColorRes) { - this.selectedColor = ColorHolder.fromColorRes(selectedColorRes); - return this; - } - - public ProfileDrawerItem withTextColor(@ColorInt int textColor) { - this.textColor = ColorHolder.fromColor(textColor); - return this; - } - - public ProfileDrawerItem withTextColorRes(@ColorRes int textColorRes) { - this.textColor = ColorHolder.fromColorRes(textColorRes); - return this; - } - - public ProfileDrawerItem withSelectedTextColor(@ColorInt int selectedTextColor) { - this.selectedTextColor = ColorHolder.fromColor(selectedTextColor); - return this; - } - - public ProfileDrawerItem withSelectedTextColorRes(@ColorRes int selectedColorRes) { - this.selectedTextColor = ColorHolder.fromColorRes(selectedColorRes); - return this; - } - - public ProfileDrawerItem withDisabledTextColor(@ColorInt int disabledTextColor) { - this.disabledTextColor = ColorHolder.fromColor(disabledTextColor); - return this; - } - - public ProfileDrawerItem withDisabledTextColorRes(@ColorRes int disabledTextColorRes) { - this.disabledTextColor = ColorHolder.fromColorRes(disabledTextColorRes); - return this; - } - - public ProfileDrawerItem withTypeface(Typeface typeface) { - this.typeface = typeface; - return this; - } - - public boolean isNameShown() { - return nameShown; - } - - public ColorHolder getSelectedColor() { - return selectedColor; - } - - public ColorHolder getTextColor() { - return textColor; - } - - public ColorHolder getSelectedTextColor() { - return selectedTextColor; - } - - public ColorHolder getDisabledTextColor() { - return disabledTextColor; - } - - @Override - public Typeface getTypeface() { - return typeface; - } - - public ImageHolder getIcon() { - return icon; - } - - @Override - public StringHolder getName() { - return name; - } - - public StringHolder getEmail() { - return email; - } - - @Override - public int getType() { - return R.id.material_drawer_item_profile; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_profile; - } - - @Override - public void bindView(ViewHolder viewHolder, List payloads) { - super.bindView(viewHolder, payloads); - - Context ctx = viewHolder.itemView.getContext(); - - //set the identifier from the drawerItem here. It can be used to run tests - viewHolder.itemView.setId(hashCode()); - - //set the item enabled if it is - viewHolder.itemView.setEnabled(isEnabled()); - - //set the item selected if it is - viewHolder.itemView.setSelected(isSelected()); - - //get the correct color for the background - int selectedColor = getSelectedColor(ctx); - //get the correct color for the text - int color = getColor(ctx); - int secondaryColor = getSecondaryColor(ctx); - int selectedTextColor = getSelectedTextColor(ctx); - - //set the background for the item - themeDrawerItem(ctx, viewHolder.view, selectedColor, isSelectedBackgroundAnimated()); - - if (nameShown) { - viewHolder.name.setVisibility(View.VISIBLE); - StringHolder.applyTo(this.getName(), viewHolder.name); - } else { - viewHolder.name.setVisibility(View.GONE); - } - //the MaterialDrawer follows the Google Apps. those only show the e-mail - //within the profile switcher. The problem this causes some confusion for - //some developers. And if you only set the name, the item would be empty - //so here's a small fallback which will prevent this issue of empty items ;) - if (!nameShown && this.getEmail() == null && this.getName() != null) { - StringHolder.applyTo(this.getName(), viewHolder.email); - } else { - StringHolder.applyTo(this.getEmail(), viewHolder.email); - } - - if (getTypeface() != null) { - viewHolder.name.setTypeface(getTypeface()); - viewHolder.email.setTypeface(getTypeface()); - } - - if (nameShown) { - viewHolder.name.setTextColor(getTextColorStateList(color, selectedTextColor)); - } - viewHolder.email.setTextColor(getTextColorStateList(secondaryColor, selectedTextColor)); - - //cancel previous started image loading processes - DrawerImageLoader.getInstance().cancelImage(viewHolder.profileIcon); - //set the icon - ImageHolder.applyToOrSetInvisible(getIcon(), viewHolder.profileIcon, DrawerImageLoader.Tags.PROFILE_DRAWER_ITEM.name()); - - //for android API 17 --> Padding not applied via xml - DrawerUIUtils.setDrawerVerticalPadding(viewHolder.view); - - //call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required) - onPostBindView(this, viewHolder.itemView); - } - - @Override - public ViewHolder getViewHolder(View v) { - return new ViewHolder(v); - } - - public static class ViewHolder extends RecyclerView.ViewHolder { - private View view; - private ImageView profileIcon; - private TextView name; - private TextView email; - - private ViewHolder(View view) { - super(view); - this.view = view; - this.profileIcon = view.findViewById(R.id.material_drawer_profileIcon); - this.name = view.findViewById(R.id.material_drawer_name); - this.email = view.findViewById(R.id.material_drawer_email); - } - } - - /** - * helper method to decide for the correct color - * - * @param ctx - * @return - */ - protected int getSelectedColor(Context ctx) { - if (getBooleanStyleable(ctx, R.styleable.MaterialDrawer_material_drawer_legacy_style, false)) { - return ColorHolder.color(getSelectedColor(), ctx, R.attr.material_drawer_selected_legacy, R.color.material_drawer_selected_legacy); - } else { - return ColorHolder.color(getSelectedColor(), ctx, R.attr.material_drawer_selected, R.color.material_drawer_selected); - } - } - - /** - * helper method to decide for the correct color - * - * @param ctx - * @return - */ - protected int getColor(Context ctx) { - int color; - if (this.isEnabled()) { - color = ColorHolder.color(getTextColor(), ctx, R.attr.material_drawer_primary_text, R.color.material_drawer_primary_text); - } else { - color = ColorHolder.color(getDisabledTextColor(), ctx, R.attr.material_drawer_hint_text, R.color.material_drawer_hint_text); - } - return color; - } - - protected int getSecondaryColor(Context ctx) { - int color; - if (this.isEnabled()) { - color = ColorHolder.color(getTextColor(), ctx, R.attr.material_drawer_secondary_text, R.color.material_drawer_secondary_text); - } else { - color = ColorHolder.color(getDisabledTextColor(), ctx, R.attr.material_drawer_hint_text, R.color.material_drawer_hint_text); - } - return color; - } - - /** - * helper method to decide for the correct color - * - * @param ctx - * @return - */ - protected int getSelectedTextColor(Context ctx) { - return ColorHolder.color(getSelectedTextColor(), ctx, R.attr.material_drawer_selected_text, R.color.material_drawer_selected_text); - } - - protected Pair colorStateList; - - /** - * helper to get the ColorStateList for the text and remembering it so we do not have to recreate it all the time - * - * @param color - * @param selectedTextColor - * @return - */ - protected ColorStateList getTextColorStateList(@ColorInt int color, @ColorInt int selectedTextColor) { - if (colorStateList == null || color + selectedTextColor != colorStateList.first) { - colorStateList = new Pair<>(color + selectedTextColor, DrawerUIUtils.getTextColorStateList(color, selectedTextColor)); - } - - return colorStateList.second; - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ProfileSettingDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ProfileSettingDrawerItem.java deleted file mode 100644 index 12a8013c..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ProfileSettingDrawerItem.java +++ /dev/null @@ -1,318 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.mikepenz.iconics.typeface.IIcon; -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.holder.ColorHolder; -import com.mikepenz.materialdrawer.holder.ImageHolder; -import com.mikepenz.materialdrawer.holder.StringHolder; -import com.mikepenz.materialdrawer.model.interfaces.IProfile; -import com.mikepenz.materialdrawer.model.interfaces.Tagable; -import com.mikepenz.materialdrawer.model.interfaces.Typefaceable; -import com.mikepenz.materialdrawer.util.DrawerUIUtils; -import com.mikepenz.materialize.util.UIUtils; - -import java.util.List; - -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.annotation.DrawableRes; -import androidx.annotation.LayoutRes; -import androidx.annotation.StringRes; -import androidx.core.view.ViewCompat; -import androidx.recyclerview.widget.RecyclerView; - -import static com.mikepenz.materialdrawer.util.DrawerUIUtils.getBooleanStyleable; - -/** - * Created by mikepenz on 03.02.15. - */ -public class ProfileSettingDrawerItem extends AbstractDrawerItem implements IProfile, Tagable, Typefaceable { - private ImageHolder icon; - - private StringHolder name; - private StringHolder description; - - private boolean iconTinted = false; - - private ColorHolder selectedColor; - private ColorHolder textColor; - private ColorHolder iconColor; - private ColorHolder descriptionTextColor; - - private Typeface typeface = null; - - private boolean selectable = false; - - @Override - public ProfileSettingDrawerItem withIcon(Drawable icon) { - this.icon = new ImageHolder(icon); - return this; - } - - @Override - public ProfileSettingDrawerItem withIcon(@DrawableRes int iconRes) { - this.icon = new ImageHolder(iconRes); - return this; - } - - @Override - public ProfileSettingDrawerItem withIcon(Bitmap icon) { - this.icon = new ImageHolder(icon); - return this; - } - - @Override - public ProfileSettingDrawerItem withIcon(IIcon iicon) { - this.icon = new ImageHolder(iicon); - return this; - } - - @Override - public ProfileSettingDrawerItem withIcon(String url) { - this.icon = new ImageHolder(url); - return this; - } - - @Override - public ProfileSettingDrawerItem withIcon(Uri uri) { - this.icon = new ImageHolder(uri); - return this; - } - - public ProfileSettingDrawerItem withName(CharSequence name) { - this.name = new StringHolder(name); - return this; - } - - public ProfileSettingDrawerItem withName(@StringRes int nameRes) { - this.name = new StringHolder(nameRes); - return this; - } - - public ProfileSettingDrawerItem withDescription(String description) { - this.description = new StringHolder(description); - return this; - } - - public ProfileSettingDrawerItem withDescription(@StringRes int descriptionRes) { - this.description = new StringHolder(descriptionRes); - return this; - } - - //NOTE we reuse the IProfile here to allow custom items within the AccountSwitcher. There is an alias method withDescription for this - public ProfileSettingDrawerItem withEmail(String email) { - this.description = new StringHolder(email); - return this; - } - - public ProfileSettingDrawerItem withSelectedColor(@ColorInt int selectedColor) { - this.selectedColor = ColorHolder.fromColor(selectedColor); - return this; - } - - public ProfileSettingDrawerItem withSelectedColorRes(@ColorRes int selectedColorRes) { - this.selectedColor = ColorHolder.fromColorRes(selectedColorRes); - return this; - } - - public ProfileSettingDrawerItem withTextColor(@ColorInt int textColor) { - this.textColor = ColorHolder.fromColor(textColor); - return this; - } - - public ProfileSettingDrawerItem withTextColorRes(@ColorRes int textColorRes) { - this.textColor = ColorHolder.fromColorRes(textColorRes); - return this; - } - - public ProfileSettingDrawerItem withDescriptionTextColor(@ColorInt int descriptionColor) { - this.descriptionTextColor = ColorHolder.fromColor(descriptionColor); - return this; - } - - public ProfileSettingDrawerItem withDescriptionTextColorRes(@ColorRes int descriptionColorRes) { - this.descriptionTextColor = ColorHolder.fromColorRes(descriptionColorRes); - return this; - } - - public ProfileSettingDrawerItem withIconColor(@ColorInt int iconColor) { - this.iconColor = ColorHolder.fromColor(iconColor); - return this; - } - - public ProfileSettingDrawerItem withIconColorRes(@ColorRes int iconColorRes) { - this.iconColor = ColorHolder.fromColorRes(iconColorRes); - return this; - } - - public ProfileSettingDrawerItem withTypeface(Typeface typeface) { - this.typeface = typeface; - return this; - } - - public ProfileSettingDrawerItem withIconTinted(boolean iconTinted) { - this.iconTinted = iconTinted; - return this; - } - - public ColorHolder getSelectedColor() { - return selectedColor; - } - - public ColorHolder getTextColor() { - return textColor; - } - - public ColorHolder getDescriptionTextColor() { - return descriptionTextColor; - } - - public ColorHolder getIconColor() { - return iconColor; - } - - - public ImageHolder getIcon() { - return icon; - } - - public boolean isIconTinted() { - return iconTinted; - } - - public void setIconTinted(boolean iconTinted) { - this.iconTinted = iconTinted; - } - - @Override - public Typeface getTypeface() { - return typeface; - } - - @Override - public StringHolder getName() { - return name; - } - - public StringHolder getEmail() { - return description; - } - - public StringHolder getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = new StringHolder(description); - } - - @Override - public boolean isSelectable() { - return selectable; - } - - public ProfileSettingDrawerItem withSelectable(boolean selectable) { - this.selectable = selectable; - return this; - } - - @Override - public int getType() { - return R.id.material_drawer_item_profile_setting; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_profile_setting; - } - - @Override - public void bindView(ViewHolder viewHolder, List payloads) { - super.bindView(viewHolder, payloads); - - //get the context - Context ctx = viewHolder.itemView.getContext(); - - //set the identifier from the drawerItem here. It can be used to run tests - viewHolder.itemView.setId(hashCode()); - - //set the item enabled if it is - viewHolder.itemView.setEnabled(isEnabled()); - - //set the item selected if it is - viewHolder.itemView.setSelected(isSelected()); - - //get the correct color for the background - int selectedColor = getSelectedColor(ctx); - //get the correct color for the text - int color = ColorHolder.color(getTextColor(), ctx, R.attr.material_drawer_primary_text, R.color.material_drawer_primary_text); - int iconColor = ColorHolder.color(getIconColor(), ctx, R.attr.material_drawer_primary_icon, R.color.material_drawer_primary_icon); - int descriptionColor = ColorHolder.color(getDescriptionTextColor(), ctx, R.attr.material_drawer_primary_text, R.color.material_drawer_primary_text); - - ViewCompat.setBackground(viewHolder.view, UIUtils.getSelectableBackground(ctx, selectedColor, isSelectedBackgroundAnimated())); - - StringHolder.applyTo(this.getName(), viewHolder.name); - viewHolder.name.setTextColor(color); - - StringHolder.applyToOrHide(this.getDescription(), viewHolder.description); - viewHolder.description.setTextColor(descriptionColor); - - if (getTypeface() != null) { - viewHolder.name.setTypeface(getTypeface()); - viewHolder.description.setTypeface(getTypeface()); - } - - //set the correct icon - ImageHolder.applyDecidedIconOrSetGone(icon, viewHolder.icon, iconColor, isIconTinted(), 2); - - //for android API 17 --> Padding not applied via xml - DrawerUIUtils.setDrawerVerticalPadding(viewHolder.view); - - //call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required) - onPostBindView(this, viewHolder.itemView); - } - - /** - * helper method to decide for the correct color - * - * @param ctx - * @return - */ - protected int getSelectedColor(Context ctx) { - if (getBooleanStyleable(ctx, R.styleable.MaterialDrawer_material_drawer_legacy_style, false)) { - return ColorHolder.color(getSelectedColor(), ctx, R.attr.material_drawer_selected_legacy, R.color.material_drawer_selected_legacy); - } else { - return ColorHolder.color(getSelectedColor(), ctx, R.attr.material_drawer_selected, R.color.material_drawer_selected); - } - } - - @Override - public ViewHolder getViewHolder(View v) { - return new ViewHolder(v); - } - - public static class ViewHolder extends RecyclerView.ViewHolder { - private View view; - private ImageView icon; - private TextView name; - private TextView description; - - private ViewHolder(View view) { - super(view); - this.view = view; - this.icon = (ImageView) view.findViewById(R.id.material_drawer_icon); - this.name = (TextView) view.findViewById(R.id.material_drawer_name); - this.description = (TextView) view.findViewById(R.id.material_drawer_description); - } - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondaryDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondaryDrawerItem.java deleted file mode 100644 index 8f22290b..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondaryDrawerItem.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import androidx.annotation.LayoutRes; - -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.holder.ColorHolder; - -/** - * Created by mikepenz on 03.02.15. - */ -public class SecondaryDrawerItem extends AbstractBadgeableDrawerItem { - - @Override - public int getType() { - return R.id.material_drawer_item_secondary; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_secondary; - } - - /** - * helper method to decide for the correct color - * OVERWRITE to get the correct secondary color - * - * @param ctx - * @return - */ - @Override - protected int getColor(Context ctx) { - int color; - if (this.isEnabled()) { - color = ColorHolder.color(getTextColor(), ctx, R.attr.material_drawer_secondary_text, R.color.material_drawer_secondary_text); - } else { - color = ColorHolder.color(getDisabledTextColor(), ctx, R.attr.material_drawer_hint_text, R.color.material_drawer_hint_text); - } - return color; - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondarySwitchDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondarySwitchDrawerItem.java deleted file mode 100644 index 22b5862a..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondarySwitchDrawerItem.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import androidx.annotation.LayoutRes; - -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.holder.ColorHolder; - -/** - * Created by mikepenz on 03.02.15. - */ -public class SecondarySwitchDrawerItem extends AbstractSwitchableDrawerItem { - - @Override - public int getType() { - return R.id.material_drawer_item_secondary_switch; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_secondary_switch; - } - - /** - * helper method to decide for the correct color - * OVERWRITE to get the correct secondary color - * - * @param ctx - * @return - */ - @Override - protected int getColor(Context ctx) { - int color; - if (this.isEnabled()) { - color = ColorHolder.color(getTextColor(), ctx, R.attr.material_drawer_secondary_text, R.color.material_drawer_secondary_text); - } else { - color = ColorHolder.color(getDisabledTextColor(), ctx, R.attr.material_drawer_hint_text, R.color.material_drawer_hint_text); - } - return color; - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondaryToggleDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondaryToggleDrawerItem.java deleted file mode 100644 index 30024be3..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SecondaryToggleDrawerItem.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import androidx.annotation.LayoutRes; - -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.holder.ColorHolder; - -/** - * Created by mikepenz on 03.02.15. - */ -public class SecondaryToggleDrawerItem extends AbstractToggleableDrawerItem { - - @Override - public int getType() { - return R.id.material_drawer_item_secondary_toggle; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_secondary_toggle; - } - - /** - * helper method to decide for the correct color - * OVERWRITE to get the correct secondary color - * - * @param ctx - * @return - */ - @Override - protected int getColor(Context ctx) { - int color; - if (this.isEnabled()) { - color = ColorHolder.color(getTextColor(), ctx, R.attr.material_drawer_secondary_text, R.color.material_drawer_secondary_text); - } else { - color = ColorHolder.color(getDisabledTextColor(), ctx, R.attr.material_drawer_hint_text, R.color.material_drawer_hint_text); - } - return color; - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SectionDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SectionDrawerItem.java deleted file mode 100644 index 4a336024..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SectionDrawerItem.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -import android.content.Context; -import android.graphics.Typeface; -import androidx.annotation.LayoutRes; -import androidx.annotation.StringRes; -import androidx.recyclerview.widget.RecyclerView; -import android.view.View; -import android.widget.TextView; - -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.holder.ColorHolder; -import com.mikepenz.materialdrawer.holder.StringHolder; -import com.mikepenz.materialdrawer.model.interfaces.Nameable; -import com.mikepenz.materialdrawer.model.interfaces.Typefaceable; -import com.mikepenz.materialize.util.UIUtils; - -import java.util.List; - -/** - * Created by mikepenz on 03.02.15. - */ -public class SectionDrawerItem extends AbstractDrawerItem implements Nameable, Typefaceable { - - private StringHolder name; - private boolean divider = true; - - private ColorHolder textColor; - - private Typeface typeface = null; - - public SectionDrawerItem withName(StringHolder name) { - this.name = name; - return this; - } - - public SectionDrawerItem withName(String name) { - this.name = new StringHolder(name); - return this; - } - - public SectionDrawerItem withName(@StringRes int nameRes) { - this.name = new StringHolder(nameRes); - return this; - } - - public SectionDrawerItem withDivider(boolean divider) { - this.divider = divider; - return this; - } - - public SectionDrawerItem withTextColor(int textColor) { - this.textColor = ColorHolder.fromColor(textColor); - return this; - } - - public SectionDrawerItem withTextColorRes(int textColorRes) { - this.textColor = ColorHolder.fromColorRes(textColorRes); - return this; - } - - public SectionDrawerItem withTypeface(Typeface typeface) { - this.typeface = typeface; - return this; - } - - public boolean hasDivider() { - return divider; - } - - public ColorHolder getTextColor() { - return textColor; - } - - public StringHolder getName() { - return name; - } - - @Override - public boolean isEnabled() { - return false; - } - - @Override - public boolean isSelected() { - return false; - } - - @Override - public int getType() { - return R.id.material_drawer_item_section; - } - - @Override - @LayoutRes - public int getLayoutRes() { - return R.layout.material_drawer_item_section; - } - - @Override - public Typeface getTypeface() { - return typeface; - } - - @Override - public void bindView(ViewHolder viewHolder, List payloads) { - super.bindView(viewHolder, payloads); - - Context ctx = viewHolder.itemView.getContext(); - - //set the identifier from the drawerItem here. It can be used to run tests - viewHolder.itemView.setId(hashCode()); - - //define this item to be not clickable nor enabled - viewHolder.view.setClickable(false); - viewHolder.view.setEnabled(false); - - //define the text color - viewHolder.name.setTextColor(ColorHolder.color(getTextColor(), ctx, R.attr.material_drawer_secondary_text, R.color.material_drawer_secondary_text)); - - //set the text for the name - StringHolder.applyTo(this.getName(), viewHolder.name); - - //define the typeface for our textViews - if (getTypeface() != null) { - viewHolder.name.setTypeface(getTypeface()); - } - - //hide the divider if we do not need one - if (this.hasDivider()) { - viewHolder.divider.setVisibility(View.VISIBLE); - } else { - viewHolder.divider.setVisibility(View.GONE); - } - - //set the color for the divider - viewHolder.divider.setBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(ctx, R.attr.material_drawer_divider, R.color.material_drawer_divider)); - - //call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required) - onPostBindView(this, viewHolder.itemView); - } - - @Override - public ViewHolder getViewHolder(View v) { - return new ViewHolder(v); - } - - public static class ViewHolder extends RecyclerView.ViewHolder { - private View view; - private View divider; - private TextView name; - - private ViewHolder(View view) { - super(view); - this.view = view; - this.divider = view.findViewById(R.id.material_drawer_divider); - this.name = (TextView) view.findViewById(R.id.material_drawer_name); - } - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SwitchDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SwitchDrawerItem.java deleted file mode 100644 index f6ffe57a..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/SwitchDrawerItem.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -/** - * Created by mikepenz on 03.02.15. - */ -public class SwitchDrawerItem extends AbstractSwitchableDrawerItem { - -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ToggleDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ToggleDrawerItem.java deleted file mode 100644 index 407dc43c..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/ToggleDrawerItem.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.mikepenz.materialdrawer.model; - -/** - * Created by mikepenz on 03.02.15. - */ -public class ToggleDrawerItem extends AbstractToggleableDrawerItem { - -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Badgeable.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Badgeable.java deleted file mode 100644 index 9a1b5ec3..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Badgeable.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.mikepenz.materialdrawer.model.interfaces; - -import com.mikepenz.materialdrawer.holder.StringHolder; - -/** - * Created by mikepenz on 03.02.15. - */ -public interface Badgeable { - T withBadge(String badge); - - T withBadge(int badgeRes); - - T withBadge(StringHolder badgeRes); - - StringHolder getBadge(); -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/ColorfulBadgeable.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/ColorfulBadgeable.java deleted file mode 100644 index 84e7418c..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/ColorfulBadgeable.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.mikepenz.materialdrawer.model.interfaces; - -import com.mikepenz.materialdrawer.holder.BadgeStyle; - -/** - * Created by mikepenz on 03.02.15. - */ -public interface ColorfulBadgeable extends Badgeable { - T withBadgeStyle(BadgeStyle badgeStyle); - - BadgeStyle getBadgeStyle(); - -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/IDrawerItem.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/IDrawerItem.java deleted file mode 100644 index 35dc0d07..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/IDrawerItem.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.mikepenz.materialdrawer.model.interfaces; - -import android.content.Context; -import androidx.recyclerview.widget.RecyclerView; -import android.view.View; -import android.view.ViewGroup; - -import com.mikepenz.fastadapter.IExpandable; -import com.mikepenz.fastadapter.IItem; -import com.mikepenz.fastadapter.ISubItem; - -import java.util.List; - -/** - * Created by mikepenz on 03.02.15. - */ -public interface IDrawerItem extends IItem, IExpandable, ISubItem { - - Object getTag(); - - boolean isEnabled(); - - boolean isSelected(); - - T withSetSelected(boolean selected); - - boolean isSelectable(); - - boolean isExcludeFromMiniDrawer(); - - T withSelectable(boolean selectable); - - int getType(); - - int getLayoutRes(); - - View generateView(Context ctx); - - View generateView(Context ctx, ViewGroup parent); - - VH getViewHolder(ViewGroup parent); - - void unbindView(VH holder); - - void bindView(VH holder, List payloads); - - boolean equals(long id); -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/IProfile.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/IProfile.java deleted file mode 100644 index bb1edc64..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/IProfile.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.mikepenz.materialdrawer.model.interfaces; - -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import androidx.annotation.DrawableRes; - -import com.mikepenz.fastadapter.IIdentifyable; -import com.mikepenz.iconics.typeface.IIcon; -import com.mikepenz.materialdrawer.holder.ImageHolder; -import com.mikepenz.materialdrawer.holder.StringHolder; - -/** - * Created by mikepenz on 03.02.15. - */ -public interface IProfile extends IIdentifyable { - T withName(CharSequence name); - - StringHolder getName(); - - T withEmail(String email); - - StringHolder getEmail(); - - T withIcon(Drawable icon); - - T withIcon(Bitmap bitmap); - - T withIcon(@DrawableRes int iconRes); - - T withIcon(String url); - - T withIcon(Uri uri); - - T withIcon(IIcon icon); - - ImageHolder getIcon(); - - T withSelectable(boolean selectable); - - boolean isSelectable(); -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Iconable.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Iconable.java deleted file mode 100644 index e574dc9c..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Iconable.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.mikepenz.materialdrawer.model.interfaces; - -import android.graphics.drawable.Drawable; - -import com.mikepenz.iconics.typeface.IIcon; -import com.mikepenz.materialdrawer.holder.ImageHolder; - -/** - * Created by mikepenz on 03.02.15. - */ -public interface Iconable { - T withIcon(Drawable icon); - - T withIcon(IIcon iicon); - - T withIcon(ImageHolder icon); - - ImageHolder getIcon(); -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Nameable.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Nameable.java deleted file mode 100644 index 8193fd6f..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Nameable.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.mikepenz.materialdrawer.model.interfaces; - -import com.mikepenz.materialdrawer.holder.StringHolder; - -/** - * Created by mikepenz on 03.02.15. - */ -public interface Nameable { - T withName(String name); - - T withName(int nameRes); - - T withName(StringHolder name); - - StringHolder getName(); -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/OnPostBindViewListener.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/OnPostBindViewListener.java deleted file mode 100644 index b360a4dd..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/OnPostBindViewListener.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.mikepenz.materialdrawer.model.interfaces; - -import android.view.View; - -/** - * Created by mikepenz on 21.08.15. - */ -public interface OnPostBindViewListener { - /** - * allows you to hook in the BindView method and modify the view after binding - * - * @param drawerItem the drawerItem used for this view - * @param view the view which will be set - */ - void onBindView(IDrawerItem drawerItem, View view); -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Selectable.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Selectable.java deleted file mode 100644 index 6ed9a800..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Selectable.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.mikepenz.materialdrawer.model.interfaces; - -/** - * Created by mikepenz on 03.02.15. - */ -public interface Selectable { - T withSelectable(boolean selectable); - - boolean isSelectable(); -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Tagable.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Tagable.java deleted file mode 100644 index 7ecf8c12..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Tagable.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.mikepenz.materialdrawer.model.interfaces; - -/** - * Created by mikepenz on 03.02.15. - */ -public interface Tagable { - T withTag(Object tag); - - Object getTag(); -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Typefaceable.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Typefaceable.java deleted file mode 100644 index beba2cea..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/interfaces/Typefaceable.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.mikepenz.materialdrawer.model.interfaces; - -import android.graphics.Typeface; - -/** - * Created by mikepenz on 03.02.15. - */ -public interface Typefaceable { - T withTypeface(Typeface typeface); - - Typeface getTypeface(); -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/utils/BadgeDrawableBuilder.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/utils/BadgeDrawableBuilder.java deleted file mode 100644 index a132624c..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/model/utils/BadgeDrawableBuilder.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.mikepenz.materialdrawer.model.utils; - -import android.content.Context; -import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.StateListDrawable; -import androidx.appcompat.content.res.AppCompatResources; -import android.util.StateSet; - -import com.mikepenz.materialdrawer.holder.BadgeStyle; -import com.mikepenz.materialdrawer.holder.ColorHolder; - -/** - * Created by mikepenz on 02.07.15. - */ -public class BadgeDrawableBuilder { - private BadgeStyle mStyle; - - public BadgeDrawableBuilder(BadgeStyle style) { - this.mStyle = style; - } - - public StateListDrawable build(Context ctx) { - StateListDrawable stateListDrawable = new StateListDrawable(); - GradientDrawable normal = (GradientDrawable) AppCompatResources.getDrawable(ctx, mStyle.getGradientDrawable()); - GradientDrawable selected = (GradientDrawable) normal.getConstantState().newDrawable().mutate(); - - ColorHolder.applyToOrTransparent(mStyle.getColor(), ctx, normal); - if (mStyle.getColorPressed() == null) { - ColorHolder.applyToOrTransparent(mStyle.getColor(), ctx, selected); - } else { - ColorHolder.applyToOrTransparent(mStyle.getColorPressed(), ctx, selected); - } - - if (mStyle.getCorners() != null) { - normal.setCornerRadius(mStyle.getCorners().asPixel(ctx)); - selected.setCornerRadius(mStyle.getCorners().asPixel(ctx)); - } - - stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, selected); - stateListDrawable.addState(StateSet.WILD_CARD, normal); - - return stateListDrawable; - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/AbstractDrawerImageLoader.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/AbstractDrawerImageLoader.java deleted file mode 100644 index efd0f069..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/AbstractDrawerImageLoader.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.mikepenz.materialdrawer.util; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.util.Log; -import android.widget.ImageView; - -public abstract class AbstractDrawerImageLoader implements DrawerImageLoader.IDrawerImageLoader { - @Override - public void set(ImageView imageView, Uri uri, Drawable placeholder) { - } - - @Override - public void set(ImageView imageView, Uri uri, Drawable placeholder, String tag) { - //for backwards compatibility call the method without tag too - set(imageView, uri, placeholder); - //this won't do anything - Log.i("MaterialDrawer", "You have not specified a ImageLoader implementation through the DrawerImageLoader.init() method, or you are still overriding the deprecated method set(ImageView iv, Uri u, Drawable d) instead of set(ImageView iv, Uri u, Drawable d, String tag)"); - } - @Override - public void cancel(ImageView imageView) { - } - - @Override - public Drawable placeholder(Context ctx) { - return DrawerUIUtils.getPlaceHolder(ctx); - } - - @Override - public Drawable placeholder(Context ctx, String tag) { - return placeholder(ctx); - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerImageLoader.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerImageLoader.java deleted file mode 100644 index 1c46a611..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerImageLoader.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.mikepenz.materialdrawer.util; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.widget.ImageView; - -/** - * Created by mikepenz on 24.03.15. - */ -public class DrawerImageLoader { - public enum Tags { - PROFILE, - PROFILE_DRAWER_ITEM, - ACCOUNT_HEADER - } - - private static DrawerImageLoader SINGLETON = null; - - private IDrawerImageLoader imageLoader; - private boolean mHandleAllUris = false; - - private DrawerImageLoader(IDrawerImageLoader loaderImpl) { - imageLoader = loaderImpl; - } - - public static DrawerImageLoader init(IDrawerImageLoader loaderImpl) { - SINGLETON = new DrawerImageLoader(loaderImpl); - return SINGLETON; - } - - public static DrawerImageLoader getInstance() { - if (SINGLETON == null) { - SINGLETON = new DrawerImageLoader(new AbstractDrawerImageLoader() { - }); - } - return SINGLETON; - } - - public DrawerImageLoader withHandleAllUris(boolean handleAllUris) { - this.mHandleAllUris = handleAllUris; - return this; - } - - /** - * @param imageView - * @param uri - * @param tag - * @return false if not consumed - */ - public boolean setImage(ImageView imageView, Uri uri, String tag) { - //if we do not handle all uris and are not http or https we keep the original behavior - if (mHandleAllUris || "http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) { - if (imageLoader != null) { - Drawable placeHolder = imageLoader.placeholder(imageView.getContext(), tag); - imageLoader.set(imageView, uri, placeHolder, tag); - } - return true; - } - return false; - } - - public void cancelImage(ImageView imageView) { - if (imageLoader != null) { - imageLoader.cancel(imageView); - } - } - - public IDrawerImageLoader getImageLoader() { - return imageLoader; - } - - public void setImageLoader(IDrawerImageLoader imageLoader) { - this.imageLoader = imageLoader; - } - - public interface IDrawerImageLoader { - @Deprecated - void set(ImageView imageView, Uri uri, Drawable placeholder); - - void set(ImageView imageView, Uri uri, Drawable placeholder, String tag); - - void cancel(ImageView imageView); - - Drawable placeholder(Context ctx); - - /** - * @param ctx - * @param tag current possible tags: "profile", "profileDrawerItem", "accountHeader" - * @return - */ - Drawable placeholder(Context ctx, String tag); - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerItemViewHelper.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerItemViewHelper.java deleted file mode 100644 index 3a28fe6f..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerItemViewHelper.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.mikepenz.materialdrawer.util; - -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; - -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; -import com.mikepenz.materialize.util.UIUtils; - -import java.util.ArrayList; -import java.util.Collections; - -/** - * Created by mikepenz on 27.03.15. - */ -public class DrawerItemViewHelper { - - private Context mContext; - - public DrawerItemViewHelper(Context context) { - this.mContext = context; - } - - private ArrayList mDrawerItems = new ArrayList<>(); - - public DrawerItemViewHelper withDrawerItems(ArrayList drawerItems) { - this.mDrawerItems = drawerItems; - return this; - } - - public DrawerItemViewHelper withDrawerItems(IDrawerItem... drawerItems) { - Collections.addAll(this.mDrawerItems, drawerItems); - return this; - } - - private boolean mDivider = true; - - public DrawerItemViewHelper withDivider(boolean divider) { - this.mDivider = divider; - return this; - } - - private OnDrawerItemClickListener mOnDrawerItemClickListener = null; - - public DrawerItemViewHelper withOnDrawerItemClickListener(OnDrawerItemClickListener onDrawerItemClickListener) { - mOnDrawerItemClickListener = onDrawerItemClickListener; - return this; - } - - public View build() { - //create the container view - LinearLayout linearLayout = new LinearLayout(mContext); - linearLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - linearLayout.setOrientation(LinearLayout.VERTICAL); - - //create the divider - if (mDivider) { - LinearLayout divider = new LinearLayout(mContext); - divider.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - divider.setMinimumHeight((int) UIUtils.convertDpToPixel(1, mContext)); - divider.setOrientation(LinearLayout.VERTICAL); - divider.setBackgroundColor(UIUtils.getThemeColorFromAttrOrRes(mContext, R.attr.material_drawer_divider, R.color.material_drawer_divider)); - linearLayout.addView(divider); - } - - //add all drawer items - for (IDrawerItem drawerItem : mDrawerItems) { - View view = drawerItem.generateView(mContext); - view.setTag(drawerItem); - - if (drawerItem.isEnabled()) { - view.setBackgroundResource(UIUtils.getSelectableBackgroundRes(mContext)); - view.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mOnDrawerItemClickListener != null) { - mOnDrawerItemClickListener.onItemClick(v, (IDrawerItem) v.getTag(R.id.material_drawer_item)); - } - } - }); - } - - linearLayout.addView(view); - } - - return linearLayout; - } - - - public interface OnDrawerItemClickListener { - public void onItemClick(View view, IDrawerItem drawerItem); - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerUIUtils.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerUIUtils.java deleted file mode 100644 index faa1494b..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/DrawerUIUtils.java +++ /dev/null @@ -1,242 +0,0 @@ -package com.mikepenz.materialdrawer.util; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.Configuration; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.InsetDrawable; -import android.graphics.drawable.RippleDrawable; -import android.graphics.drawable.StateListDrawable; -import android.os.Build; -import android.util.DisplayMetrics; -import android.view.View; -import android.view.WindowManager; - -import androidx.annotation.StyleableRes; -import androidx.core.view.ViewCompat; - -import com.mikepenz.iconics.IconicsDrawable; -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.icons.MaterialDrawerFont; -import com.mikepenz.materialize.util.UIUtils; - -/** - * Created by mikepenz on 15.03.14. - */ -@SuppressLint("InlinedApi") -public class DrawerUIUtils { - - /** - * Get the boolean value of a given styleable. - * - * @param ctx - * @param styleable - * @param def - * @return - */ - public static boolean getBooleanStyleable(Context ctx, @StyleableRes int styleable, boolean def) { - TypedArray ta = ctx.getTheme().obtainStyledAttributes(R.styleable.MaterialDrawer); - return ta.getBoolean(styleable, def); - } - - /** - * Util method to theme the drawer item view's background (and foreground if possible) - * - * @param ctx the context to use - * @param view the view to theme - * @param selected_color the selected color to use - * @param animate true if we want to animate the StateListDrawable - */ - public static void themeDrawerItem(Context ctx, View view, int selected_color, boolean animate) { - boolean legacyStyle = getBooleanStyleable(ctx, R.styleable.MaterialDrawer_material_drawer_legacy_style, false); - - Drawable selected; - Drawable unselected; - - if (legacyStyle) { - // Material 1.0 styling - selected = new ColorDrawable(selected_color); - unselected = UIUtils.getSelectableBackground(ctx); - } else { - // Material 2.0 styling - int cornerRadius = ctx.getResources().getDimensionPixelSize(R.dimen.material_drawer_item_corner_radius); - int paddingTopBottom = ctx.getResources().getDimensionPixelSize(R.dimen.material_drawer_item_background_padding_top_bottom); - int paddingStartEnd = ctx.getResources().getDimensionPixelSize(R.dimen.material_drawer_item_background_padding_start_end); - - // define normal selected background - GradientDrawable gradientDrawable = new GradientDrawable(); - gradientDrawable.setColor(selected_color); - gradientDrawable.setCornerRadius(cornerRadius); - selected = new InsetDrawable(gradientDrawable, paddingStartEnd, paddingTopBottom, paddingStartEnd, paddingTopBottom); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // define mask for ripple - GradientDrawable gradientMask = new GradientDrawable(); - gradientMask.setColor(Color.BLACK); - gradientMask.setCornerRadius(cornerRadius); - Drawable mask = new InsetDrawable(gradientMask, paddingStartEnd, paddingTopBottom, paddingStartEnd, paddingTopBottom); - - unselected = new RippleDrawable(new ColorStateList(new int[][]{new int[]{}}, new int[]{UIUtils.getThemeColor(ctx, R.attr.colorControlHighlight)}), null, mask); - } else { - // define touch drawable - GradientDrawable touchDrawable = new GradientDrawable(); - touchDrawable.setColor(UIUtils.getThemeColor(ctx, R.attr.colorControlHighlight)); - touchDrawable.setCornerRadius(cornerRadius); - Drawable touchInsetDrawable = new InsetDrawable(touchDrawable, paddingStartEnd, paddingTopBottom, paddingStartEnd, paddingTopBottom); - - StateListDrawable unselectedStates = new StateListDrawable(); - //if possible and wanted we enable animating across states - if (animate) { - int duration = ctx.getResources().getInteger(android.R.integer.config_shortAnimTime); - unselectedStates.setEnterFadeDuration(duration); - unselectedStates.setExitFadeDuration(duration); - } - unselectedStates.addState(new int[]{android.R.attr.state_pressed}, touchInsetDrawable); - unselectedStates.addState(new int[]{}, new ColorDrawable(Color.TRANSPARENT)); - unselected = unselectedStates; - } - } - - StateListDrawable states = new StateListDrawable(); - - //if possible and wanted we enable animating across states - if (animate) { - int duration = ctx.getResources().getInteger(android.R.integer.config_shortAnimTime); - states.setEnterFadeDuration(duration); - states.setExitFadeDuration(duration); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - states.addState(new int[]{android.R.attr.state_selected}, selected); - states.addState(new int[]{}, new ColorDrawable(Color.TRANSPARENT)); - - ViewCompat.setBackground(view, states); - view.setForeground(unselected); - } else { - states.addState(new int[]{android.R.attr.state_selected}, selected); - states.addState(new int[]{}, unselected); - - ViewCompat.setBackground(view, states); - } - } - - /** - * helper to create a colorStateList for the text - * - * @param text_color - * @param selected_text_color - * @return - */ - public static ColorStateList getTextColorStateList(int text_color, int selected_text_color) { - return new ColorStateList( - new int[][]{ - new int[]{android.R.attr.state_selected}, - new int[]{} - }, - new int[]{ - selected_text_color, - text_color - } - ); - } - - /** - * helper to create a stateListDrawable for the icon - * - * @param icon - * @param selectedIcon - * @return - */ - public static StateListDrawable getIconStateList(Drawable icon, Drawable selectedIcon) { - StateListDrawable iconStateListDrawable = new StateListDrawable(); - iconStateListDrawable.addState(new int[]{android.R.attr.state_selected}, selectedIcon); - iconStateListDrawable.addState(new int[]{}, icon); - return iconStateListDrawable; - } - - /** - * helper to create a StateListDrawable for the drawer item background - * - * @param selected_color - * @return - */ - public static StateListDrawable getDrawerItemBackground(int selected_color) { - ColorDrawable clrActive = new ColorDrawable(selected_color); - StateListDrawable states = new StateListDrawable(); - states.addState(new int[]{android.R.attr.state_selected}, clrActive); - return states; - } - - /** - * helper to calculate the optimal drawer width - * - * @param context - * @return - */ - public static int getOptimalDrawerWidth(Context context) { - int possibleMinDrawerWidth = UIUtils.getScreenWidth(context) - UIUtils.getActionBarHeight(context); - int maxDrawerWidth = context.getResources().getDimensionPixelSize(R.dimen.material_drawer_width); - return Math.min(possibleMinDrawerWidth, maxDrawerWidth); - } - - - /** - * helper method to get a person placeHolder drawable - * - * @param ctx - * @return - */ - public static Drawable getPlaceHolder(Context ctx) { - return new IconicsDrawable(ctx, MaterialDrawerFont.Icon.mdf_person).colorRes(R.color.accent).backgroundColorRes(R.color.primary).sizeDp(56).paddingDp(16); - } - - /** - * helper to set the vertical padding to the DrawerItems - * this is required because on API Level 17 the padding is ignored which is set via the XML - * - * @param v - */ - public static void setDrawerVerticalPadding(View v) { - int verticalPadding = v.getContext().getResources().getDimensionPixelSize(R.dimen.material_drawer_vertical_padding); - v.setPadding(verticalPadding, 0, verticalPadding, 0); - } - - /** - * helper to set the vertical padding including the extra padding for deeper item hirachy level to the DrawerItems - * this is required because on API Level 17 the padding is ignored which is set via the XML - * - * @param v - * @param level - */ - public static void setDrawerVerticalPadding(View v, int level) { - int verticalPadding = v.getContext().getResources().getDimensionPixelSize(R.dimen.material_drawer_vertical_padding); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - v.setPaddingRelative(verticalPadding * level, 0, verticalPadding, 0); - } else { - v.setPadding(verticalPadding * level, 0, verticalPadding, 0); - } - } - - /** - * helper to check if the system bar is on the bottom of the screen - * - * @param ctx - * @return - */ - public static boolean isSystemBarOnBottom(Context ctx) { - WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE); - DisplayMetrics metrics = new DisplayMetrics(); - wm.getDefaultDisplay().getMetrics(metrics); - Configuration cfg = ctx.getResources().getConfiguration(); - boolean canMove = (metrics.widthPixels != metrics.heightPixels && - cfg.smallestScreenWidthDp < 600); - - return (!canMove || metrics.widthPixels < metrics.heightPixels); - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/KeyboardUtil.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/KeyboardUtil.java deleted file mode 100644 index 6ee03fad..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/KeyboardUtil.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2015 Mike Penz All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.mikepenz.materialdrawer.util; - -import android.app.Activity; -import android.graphics.Rect; -import android.os.Build; -import android.view.View; -import android.view.ViewTreeObserver; -import android.view.inputmethod.InputMethodManager; - -/** - * Created by mikepenz on 14.03.15. - * This class implements a hack to change the layout padding on bottom if the keyboard is shown - * to allow long lists with editTextViews - * Basic idea for this solution found here: http://stackoverflow.com/a/9108219/325479 - * @deprecated Do not use this anymore, the MaterialDrawer uses the `fitsSystemWindows` now correctly so it should not be required. (it would only be required for cases with the fullscreen flag) - */ -@Deprecated -public class KeyboardUtil { - private View decorView; - private View contentView; - - public KeyboardUtil(Activity act, View contentView) { - this.decorView = act.getWindow().getDecorView(); - this.contentView = contentView; - - //only required on newer android versions. it was working on API level 19 - if (Build.VERSION.SDK_INT >= 19) { - decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); - } - } - - public void enable() { - if (Build.VERSION.SDK_INT >= 19) { - decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); - } - } - - public void disable() { - if (Build.VERSION.SDK_INT >= 19) { - decorView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener); - } - } - - - //a small helper to allow showing the editText focus - ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - Rect r = new Rect(); - //r will be populated with the coordinates of your view that area still visible. - decorView.getWindowVisibleDisplayFrame(r); - - //get screen height and calculate the difference with the useable area from the r - int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels; - int diff = height - r.bottom; - - //if it could be a keyboard add the padding to the view - if (diff != 0) { - // if the use-able screen height differs from the total screen height we assume that it shows a keyboard now - //check if the padding is 0 (if yes set the padding for the keyboard) - if (contentView.getPaddingBottom() != diff) { - //set the padding of the contentView for the keyboard - contentView.setPadding(0, 0, 0, diff); - } - } else { - //check if the padding is != 0 (if yes reset the padding) - if (contentView.getPaddingBottom() != 0) { - //reset the padding of the contentView - contentView.setPadding(0, 0, 0, 0); - } - } - } - }; - - - /** - * Helper to hide the keyboard - * - * @param act - */ - public static void hideKeyboard(Activity act) { - if (act != null && act.getCurrentFocus() != null) { - InputMethodManager inputMethodManager = (InputMethodManager) act.getSystemService(Activity.INPUT_METHOD_SERVICE); - inputMethodManager.hideSoftInputFromWindow(act.getCurrentFocus().getWindowToken(), 0); - } - } -} diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/PressedEffectStateListDrawable.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/PressedEffectStateListDrawable.java deleted file mode 100644 index 546b6c32..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/util/PressedEffectStateListDrawable.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.mikepenz.materialdrawer.util; - -import android.annotation.SuppressLint; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.StateListDrawable; - -/** - * http://stackoverflow.com/questions/7979440/android-cloning-a-drawable-in-order-to-make-a-statelistdrawable-with-filters - * http://stackoverflow.com/users/2075875/malachiasz - */ - -@SuppressLint("InlinedApi") -public class PressedEffectStateListDrawable extends StateListDrawable { - - private int color; - private int selectionColor; - - public PressedEffectStateListDrawable(Drawable drawable, int color, int selectionColor) { - super(); - - drawable = drawable.mutate(); - - addState(new int[]{android.R.attr.state_selected}, drawable); - addState(new int[]{}, drawable); - - this.color = color; - this.selectionColor = selectionColor; - } - - @Override - protected boolean onStateChange(int[] states) { - boolean isStatePressedInArray = false; - for (int state : states) { - if (state == android.R.attr.state_selected) { - isStatePressedInArray = true; - } - } - if (isStatePressedInArray) { - super.setColorFilter(selectionColor, PorterDuff.Mode.SRC_IN); - } else { - super.setColorFilter(color, PorterDuff.Mode.SRC_IN); - } - return super.onStateChange(states); - } - - @Override - public boolean isStateful() { - return true; - } -} \ No newline at end of file diff --git a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/view/BezelImageView.java b/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/view/BezelImageView.java deleted file mode 100644 index 502f9df9..00000000 --- a/MaterialDrawer/src/main/java/com/mikepenz/materialdrawer/view/BezelImageView.java +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright 2014 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * The original is from Google you can find here: - * https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/widget/BezelImageView.java - * - * Modified and improved with additional functionality by Mike Penz - */ - -package com.mikepenz.materialdrawer.view; - -import android.annotation.TargetApi; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.ColorFilter; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.Outline; -import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.PorterDuffXfermode; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Build; - -import androidx.appcompat.widget.AppCompatImageView; -import androidx.core.view.ViewCompat; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewOutlineProvider; -import android.widget.ImageView; - -import com.mikepenz.materialdrawer.R; -import com.mikepenz.materialdrawer.util.DrawerImageLoader; - - -/** - * An {@link android.widget.ImageView} that draws its contents inside a mask and draws a border - * drawable on top. This is useful for applying a beveled look to image contents, but is also - * flexible enough for use with other desired aesthetics. - */ -public class BezelImageView extends AppCompatImageView { - private Paint mBlackPaint; - private Paint mMaskedPaint; - - private Rect mBounds; - private RectF mBoundsF; - - private Drawable mMaskDrawable; - private boolean mDrawCircularShadow = true; - - private ColorMatrixColorFilter mDesaturateColorFilter; - - private int mSelectorAlpha = 150; - private int mSelectorColor; - private ColorFilter mSelectorFilter; - - private boolean mCacheValid = false; - private Bitmap mCacheBitmap; - private int mCachedWidth; - private int mCachedHeight; - - private boolean isPressed = false; - private boolean isSelected; - - public BezelImageView(Context context) { - this(context, null); - } - - public BezelImageView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public BezelImageView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - // Attribute initialization - final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BezelImageView, defStyle, R.style.BezelImageView); - - mMaskDrawable = a.getDrawable(R.styleable.BezelImageView_biv_maskDrawable); - if (mMaskDrawable != null) { - mMaskDrawable.setCallback(this); - } - - mDrawCircularShadow = a.getBoolean(R.styleable.BezelImageView_biv_drawCircularShadow, true); - - mSelectorColor = a.getColor(R.styleable.BezelImageView_biv_selectorOnPress, 0); - - a.recycle(); - - // Other initialization - mBlackPaint = new Paint(); - mBlackPaint.setColor(0xff000000); - - mMaskedPaint = new Paint(); - mMaskedPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); - - // Always want a cache allocated. - mCacheBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - - // Create a desaturate color filter for pressed state. - ColorMatrix cm = new ColorMatrix(); - cm.setSaturation(0); - mDesaturateColorFilter = new ColorMatrixColorFilter(cm); - - //create a selectorFilter if we already have a color - if (mSelectorColor != 0) { - this.mSelectorFilter = new PorterDuffColorFilter(Color.argb(mSelectorAlpha, Color.red(mSelectorColor), Color.green(mSelectorColor), Color.blue(mSelectorColor)), PorterDuff.Mode.SRC_ATOP); - } - } - - @Override - protected void onSizeChanged(int w, int h, int old_w, int old_h) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (mDrawCircularShadow) { - setOutlineProvider(new CustomOutline(w, h)); - } - } - } - - @TargetApi(21) - private class CustomOutline extends ViewOutlineProvider { - - int width; - int height; - - CustomOutline(int width, int height) { - this.width = width; - this.height = height; - } - - @Override - public void getOutline(View view, Outline outline) { - outline.setOval(0, 0, width, height); - } - } - - @Override - protected boolean setFrame(int l, int t, int r, int b) { - final boolean changed = super.setFrame(l, t, r, b); - mBounds = new Rect(0, 0, r - l, b - t); - mBoundsF = new RectF(mBounds); - - if (mMaskDrawable != null) { - mMaskDrawable.setBounds(mBounds); - } - - if (changed) { - mCacheValid = false; - } - - return changed; - } - - @Override - protected void onDraw(Canvas canvas) { - if (mBounds == null) { - return; - } - - int width = mBounds.width(); - int height = mBounds.height(); - - if (width == 0 || height == 0) { - return; - } - - if (!mCacheValid || width != mCachedWidth || height != mCachedHeight || isSelected != isPressed) { - // Need to redraw the cache - if (width == mCachedWidth && height == mCachedHeight) { - // Have a correct-sized bitmap cache already allocated. Just erase it. - mCacheBitmap.eraseColor(0); - } else { - // Allocate a new bitmap with the correct dimensions. - mCacheBitmap.recycle(); - //noinspection AndroidLintDrawAllocation - mCacheBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - mCachedWidth = width; - mCachedHeight = height; - } - - Canvas cacheCanvas = new Canvas(mCacheBitmap); - if (mMaskDrawable != null) { - int sc = cacheCanvas.save(); - mMaskDrawable.draw(cacheCanvas); - if (isSelected) { - if (mSelectorFilter != null) { - mMaskedPaint.setColorFilter(mSelectorFilter); - } else { - mMaskedPaint.setColorFilter(mDesaturateColorFilter); - - } - } else { - mMaskedPaint.setColorFilter(null); - } - cacheCanvas.saveLayer(mBoundsF, mMaskedPaint, Canvas.ALL_SAVE_FLAG); - super.onDraw(cacheCanvas); - cacheCanvas.restoreToCount(sc); - } else if (isSelected) { - int sc = cacheCanvas.save(); - cacheCanvas.drawRect(0, 0, mCachedWidth, mCachedHeight, mBlackPaint); - if (mSelectorFilter != null) { - mMaskedPaint.setColorFilter(mSelectorFilter); - } else { - mMaskedPaint.setColorFilter(mDesaturateColorFilter); - } - cacheCanvas.saveLayer(mBoundsF, mMaskedPaint, Canvas.ALL_SAVE_FLAG); - super.onDraw(cacheCanvas); - cacheCanvas.restoreToCount(sc); - } else { - super.onDraw(cacheCanvas); - } - } - - // Draw from cache - canvas.drawBitmap(mCacheBitmap, mBounds.left, mBounds.top, null); - - //remember the previous press state - isPressed = isPressed(); - } - - - @Override - public boolean dispatchTouchEvent(MotionEvent event) { - // Check for clickable state and do nothing if disabled - if (!this.isClickable()) { - this.isSelected = false; - return super.onTouchEvent(event); - } - - // Set selected state based on Motion Event - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - this.isSelected = true; - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_SCROLL: - case MotionEvent.ACTION_OUTSIDE: - case MotionEvent.ACTION_CANCEL: - this.isSelected = false; - break; - } - - // Redraw image and return super type - this.invalidate(); - return super.dispatchTouchEvent(event); - } - - @Override - protected void drawableStateChanged() { - super.drawableStateChanged(); - if (mMaskDrawable != null && mMaskDrawable.isStateful()) { - mMaskDrawable.setState(getDrawableState()); - } - if (isDuplicateParentStateEnabled()) { - ViewCompat.postInvalidateOnAnimation(this); - } - } - - @Override - public void invalidateDrawable(Drawable who) { - if (who == mMaskDrawable) { - invalidate(); - } else { - super.invalidateDrawable(who); - } - } - - @Override - protected boolean verifyDrawable(Drawable who) { - return who == mMaskDrawable || super.verifyDrawable(who); - } - - - /** - * Sets the color of the selector to be draw over the - * CircularImageView. Be sure to provide some opacity. - * - * @param selectorColor The color (including alpha) to set for the selector overlay. - */ - public void setSelectorColor(int selectorColor) { - this.mSelectorColor = selectorColor; - this.mSelectorFilter = new PorterDuffColorFilter(Color.argb(mSelectorAlpha, Color.red(mSelectorColor), Color.green(mSelectorColor), Color.blue(mSelectorColor)), PorterDuff.Mode.SRC_ATOP); - this.invalidate(); - } - - - @Override - public void setImageDrawable(Drawable drawable) { - super.setImageDrawable(drawable); - } - - @Override - public void setImageResource(int resId) { - super.setImageResource(resId); - } - - @Override - public void setImageBitmap(Bitmap bm) { - super.setImageBitmap(bm); - } - - @Override - public void setImageURI(Uri uri) { - if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) { - DrawerImageLoader.getInstance().setImage(this, uri, null); - } else { - super.setImageURI(uri); - } - } - - private ColorMatrixColorFilter mTempDesaturateColorFilter; - private ColorFilter mTempSelectorFilter; - - public void disableTouchFeedback(boolean disable) { - if (disable) { - mTempDesaturateColorFilter = this.mDesaturateColorFilter; - mTempSelectorFilter = this.mSelectorFilter; - this.mSelectorFilter = null; - this.mDesaturateColorFilter = null; - } else { - if (mTempDesaturateColorFilter != null) { - this.mDesaturateColorFilter = mTempDesaturateColorFilter; - } - if (mTempSelectorFilter != null) { - this.mSelectorFilter = mTempSelectorFilter; - } - } - } -} diff --git a/MaterialDrawer/src/main/res/drawable/material_drawer_badge.xml b/MaterialDrawer/src/main/res/drawable/material_drawer_badge.xml deleted file mode 100644 index a5175915..00000000 --- a/MaterialDrawer/src/main/res/drawable/material_drawer_badge.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/drawable/material_drawer_circle_mask.xml b/MaterialDrawer/src/main/res/drawable/material_drawer_circle_mask.xml deleted file mode 100644 index b0f0dc8b..00000000 --- a/MaterialDrawer/src/main/res/drawable/material_drawer_circle_mask.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/drawable/material_drawer_shadow_bottom.xml b/MaterialDrawer/src/main/res/drawable/material_drawer_shadow_bottom.xml deleted file mode 100644 index 178b097e..00000000 --- a/MaterialDrawer/src/main/res/drawable/material_drawer_shadow_bottom.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/drawable/material_drawer_shadow_left.9.png b/MaterialDrawer/src/main/res/drawable/material_drawer_shadow_left.9.png deleted file mode 100644 index 1b5763a86c86df9b4592ade1a37c9b7812ad5941..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15047 zcmeI3Pl()99LH0tVpl7L9t2Neil}HNFMnn-i8E8`PU|i>?Xn%|_GI5CZ+1dwl9*&W zvkSu7B3SU`sa}*G)PvTO;6bH`co30-h=_Rcs0fNE>PcUcnMw9HvyVk9US44S{NDHd ze!t}Ne!rJoa&_hOlZO``SP%r^@bXe?mHysKKkD6g(eK`!mPLOK1xx26LAd8?@;P99 z`q>RZFn@77Yw=q9Da-Z;W$gItq&yk~bhaSWkB5jd9(KemZ@#aE(WYGWv5@T^R=zEbhYLwEr7X6qfSw#{ut%_zLRU99s<^ug!51p>H z+B!8(N3BMw7sr7m%fsQYJk-j5xFIX1Y05~IRaK%hq-e{F@ksKbV_A?%oEC}f&<$eO z_rxSFUiUZSMyZq}njYip8U)isUNmM$Das=p$VwT>^PC)e>Jw~+{q)ovTPA%nAYL3% zKV`08(DP$I>iK)5oXUG9rfbq}Pjk;b-@#yR>L@;Wi6R&yo$DEOwgMurlE~i-ZF2Gw zy_93~u0D3V`Qq$~lqh?iI~loqxFt&FmX<=cLUnqr)?!F-?1vrS?>Dn5TbUpf7Z=m2 z5+813+w~HzkJM%8l&^RmkF;=1nzSM-5;7!I>nMg*QE5>=ic|}sLXd1Gh|%^Px4X3~ zhN^Z{#X^Rqsl^zDnJ@iXdT$4z&DH;5c~8+AD!EX5AWVzL(17+8BEL}%x)ls{WVklVByE>6f?C6q$%^ETc zy`~T|h1hNv-Dg6#e0wwL7aBU-S{&b|;q%kd4OFdFQAIKoO_LPGs7WPQMgU8Aa-Sl5lo zwaf`KlWNJ0XuWO~>X=q@y0@Jr&+ZMeoB|d2})sIlUtNUJDFA$PCH1c%QItn=4}32(vkhmbiPqR^pNQWk=!a$*P;wv&$C^N zGIY&7pZ46isgARO;euCkAI-M-v-FXVG^>@0R;kNy7<#O@Bhp`MxpXg%l08MCnNlAp z%ym4UJ60%=+_4~x9kZn>Je;Lnn2pj$Kl;=uPaivH8g#mo-OfMx72VFLAQn#ofUr%D zi_Hh992Wq>HaRXfAE0tv00`UUxY&Gv%5ec8Y?I?+^8qTy1%R+kj*HC)s2mpn!ZtZB zHXopJTmT5$| ze1OVv0U&IX<6`pxD#rzYuuYDO%?GF)7XZRGIW9IIpmJOQ2;1bi*nEJ>aRDG~ljCCZ z0V>A@fUr%Di_Hh992Wq>HaRXfAE0tv00`UUxY&Gv%5ec8Y?Fy=A%BsRc=YYfA$?8r zj&E09q_232_R?xw5Vju_gqg2*YDdwN3MK#^PxkRU;5qnpr;&lx*xaSUH<&_*N*oOUVGu|Q_sD9>%*Ocwddb@ x?|||1m*~|u{&;0)eB9XSA9H{E=!?gNGe?h{d-Kq**J)^B`Q+)=J5OA={5Kr{Mce=Y diff --git a/MaterialDrawer/src/main/res/drawable/material_drawer_shadow_right.9.png b/MaterialDrawer/src/main/res/drawable/material_drawer_shadow_right.9.png deleted file mode 100644 index 224cc4ff43a29c546ae50c654c20c58c2f4cdb75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^JV0#3!3HEVSgovp6icy_X9x!n)NrJ90QsB+9+AZi z4BVX{%xHe{^je^xucwP+h)3t!Yl?gg4m>OeUfNFiFU`@U5Xr+-Y2h+^&Fz!7ypldm za#4)fkaYOplLMWvd)czen2t>^YG&h=GRe5G;NW`))xgl;_^?U481>l1!)FR53IYvg N@O1TaS?83{1OO&fGZ_E? diff --git a/MaterialDrawer/src/main/res/drawable/material_drawer_shadow_top.xml b/MaterialDrawer/src/main/res/drawable/material_drawer_shadow_top.xml deleted file mode 100644 index a8dbbb49..00000000 --- a/MaterialDrawer/src/main/res/drawable/material_drawer_shadow_top.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer.xml b/MaterialDrawer/src/main/res/layout/material_drawer.xml deleted file mode 100644 index 857b8a2f..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer.xml +++ /dev/null @@ -1,5 +0,0 @@ - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_compact_header.xml b/MaterialDrawer/src/main/res/layout/material_drawer_compact_header.xml deleted file mode 100644 index fea09e69..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_compact_header.xml +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_fits_not.xml b/MaterialDrawer/src/main/res/layout/material_drawer_fits_not.xml deleted file mode 100644 index 0fa02287..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_fits_not.xml +++ /dev/null @@ -1,6 +0,0 @@ - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_header.xml b/MaterialDrawer/src/main/res/layout/material_drawer_header.xml deleted file mode 100644 index 04ae7d80..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_header.xml +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_container.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_container.xml deleted file mode 100644 index 087928db..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_container.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_divider.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_divider.xml deleted file mode 100644 index 2409ff9d..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_divider.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_expandable.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_expandable.xml deleted file mode 100644 index e4d19a5f..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_expandable.xml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_expandable_badge.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_expandable_badge.xml deleted file mode 100644 index 0b8ef1f2..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_expandable_badge.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_mini.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_mini.xml deleted file mode 100644 index fd5b3afb..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_mini.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_mini_profile.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_mini_profile.xml deleted file mode 100644 index 539d1ddf..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_mini_profile.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_primary.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_primary.xml deleted file mode 100644 index 15244f47..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_primary.xml +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_profile.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_profile.xml deleted file mode 100644 index efe87870..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_profile.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_profile_setting.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_profile_setting.xml deleted file mode 100644 index c03f9718..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_profile_setting.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_secondary.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_secondary.xml deleted file mode 100644 index da51fc8d..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_secondary.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_secondary_switch.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_secondary_switch.xml deleted file mode 100644 index 03b1736e..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_secondary_switch.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_secondary_toggle.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_secondary_toggle.xml deleted file mode 100644 index b94919c9..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_secondary_toggle.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_section.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_section.xml deleted file mode 100644 index 86530a5d..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_section.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_switch.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_switch.xml deleted file mode 100644 index af15a74e..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_switch.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_item_toggle.xml b/MaterialDrawer/src/main/res/layout/material_drawer_item_toggle.xml deleted file mode 100644 index ea5779a0..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_item_toggle.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_recycler_view.xml b/MaterialDrawer/src/main/res/layout/material_drawer_recycler_view.xml deleted file mode 100644 index 9f23362f..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_recycler_view.xml +++ /dev/null @@ -1,8 +0,0 @@ - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/layout/material_drawer_slider.xml b/MaterialDrawer/src/main/res/layout/material_drawer_slider.xml deleted file mode 100644 index a903073d..00000000 --- a/MaterialDrawer/src/main/res/layout/material_drawer_slider.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/values-sw600dp/dimens.xml b/MaterialDrawer/src/main/res/values-sw600dp/dimens.xml deleted file mode 100644 index e8811a92..00000000 --- a/MaterialDrawer/src/main/res/values-sw600dp/dimens.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - 24dp - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/values/attrs.xml b/MaterialDrawer/src/main/res/values/attrs.xml deleted file mode 100644 index 2f769a02..00000000 --- a/MaterialDrawer/src/main/res/values/attrs.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/values/colors.xml b/MaterialDrawer/src/main/res/values/colors.xml deleted file mode 100644 index 42ef4d3b..00000000 --- a/MaterialDrawer/src/main/res/values/colors.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - @color/materialize_primary - @color/materialize_primary_dark - @color/materialize_primary_light - @color/materialize_accent - - - @color/md_white_1000 - - @color/md_light_primary_text - - @color/md_light_secondary - @color/md_light_secondary - @color/md_light_disabled - @color/md_light_disabled - @color/md_light_dividers - - #1F2196F3 - #E8E8E8 - @color/material_drawer_primary - - @color/material_drawer_primary_text - @color/material_drawer_secondary_text - - - @color/md_dark_background - - @color/md_dark_primary_text - - @color/md_dark_primary_icon - @color/md_dark_secondary - @color/md_dark_disabled - @color/md_dark_disabled - @color/md_dark_dividers - - #1F2196F3 - #202020 - @color/material_drawer_primary - - - @color/material_drawer_dark_primary_text - - @color/material_drawer_dark_secondary_text - diff --git a/MaterialDrawer/src/main/res/values/dimens.xml b/MaterialDrawer/src/main/res/values/dimens.xml deleted file mode 100644 index bc3fb3ef..00000000 --- a/MaterialDrawer/src/main/res/values/dimens.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - 320dp - - 0dp - - 8dp - - 16dp - - - 4dp - - - 160dp - 72dp - 16dp - 4dp - 56dp - 40dp - 56dp - 20sp - 13sp - 80dp - 22dp - 5dp - 18dp - 56dp - - - 8dp - 4dp - 8dp - 4dp - - - 48dp - 56dp - 12dp - 32dp - 14sp - 14sp - 12sp - - 42dp - 56dp - 12dp - 36dp - 14sp - 12sp - - 72dp - 40dp - 48dp - 16dp - 8dp - 14sp - 14sp - - 24dp - - 72dp - 4dp - 8dp - 56dp - 16dp - 12sp - 40dp - 16dp - - - 14sp - diff --git a/MaterialDrawer/src/main/res/values/donottranslate_library_materialdrawer_strings.xml b/MaterialDrawer/src/main/res/values/donottranslate_library_materialdrawer_strings.xml deleted file mode 100644 index b730bb0c..00000000 --- a/MaterialDrawer/src/main/res/values/donottranslate_library_materialdrawer_strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/MaterialDrawer/src/main/res/values/ids.xml b/MaterialDrawer/src/main/res/values/ids.xml deleted file mode 100644 index e9cce5b7..00000000 --- a/MaterialDrawer/src/main/res/values/ids.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MaterialDrawer/src/main/res/values/strings.xml b/MaterialDrawer/src/main/res/values/strings.xml deleted file mode 100644 index c26a8b87..00000000 --- a/MaterialDrawer/src/main/res/values/strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - Open - Close - diff --git a/MaterialDrawer/src/main/res/values/styles.xml b/MaterialDrawer/src/main/res/values/styles.xml deleted file mode 100644 index b4e22ebb..00000000 --- a/MaterialDrawer/src/main/res/values/styles.xml +++ /dev/null @@ -1,399 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From d2f06a256fab5d82a3327d2140198f88f4bf4d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 1 Oct 2019 07:03:12 +0200 Subject: [PATCH 014/691] [APIv2] Update API, add endpoint choosing algorithm --- app/debug/output.json | 1 - .../pl/szczodrzynski/edziennik/api/v2/Api.kt | 1 - .../edziennik/api/v2/Constants.kt | 3 + .../edziennik/api/v2/Endpoints.kt | 49 ++--- .../edziennik/api/v2/librus/Librus.kt | 98 ++++++++-- .../edziennik/api/v2/librus/LibrusTest.kt | 11 +- .../edziennik/api/v2/librus/data/LibrusApi.kt | 167 +++++++++--------- .../api/v2/librus/data/LibrusApiMe.kt | 6 +- .../api/v2/librus/login/LoginLibrus.kt | 7 +- .../api/v2/librus/login/LoginLibrusApi.kt | 27 ++- .../v2/librus/login/LoginLibrusMessages.kt | 13 +- .../api/v2/librus/login/LoginLibrusPortal.kt | 46 +++-- .../v2/librus/login/LoginLibrusSynergia.kt | 38 ++-- .../v2/librus/login/SynergiaTokenExtractor.kt | 31 +++- .../edziennik/api/v2/models/ApiError.kt | 3 +- .../edziennik/api/v2/models/Data.kt | 13 +- .../edziennik/api/v2/models/Endpoint.kt | 9 +- app/src/main/res/values/strings.xml | 4 + settings.gradle | 2 +- 19 files changed, 346 insertions(+), 183 deletions(-) delete mode 100644 app/debug/output.json diff --git a/app/debug/output.json b/app/debug/output.json deleted file mode 100644 index db263901..00000000 --- a/app/debug/output.json +++ /dev/null @@ -1 +0,0 @@ -[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":2600,"versionName":"2.6","enabled":true,"outputFile":"Edziennik_2600_debug.apk","fullName":"debug","baseName":"debug"},"path":"Edziennik_2600_debug.apk","properties":{}}] \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Api.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Api.kt index 97060c70..4af2e0e5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Api.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Api.kt @@ -16,6 +16,5 @@ open class Api(open val data: Data) { Crashlytics.logException(e) } - data.callback.onError(null, error) } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index 4992b519..fa133eda 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -4,6 +4,9 @@ package pl.szczodrzynski.edziennik.api.v2 +const val GET = 0 +const val POST = 1 + const val LIBRUS_USER_AGENT = "Dalvik/2.1.0 Android LibrusMobileApp" const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0" const val LIBRUS_CLIENT_ID = "wmSyUMo8llDAs4y9tJVYY92oyZ6h4lAt7KCuy0Gv" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt index 940fbf57..4279ff02 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt @@ -54,7 +54,7 @@ val endpoints = listOf( Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TIMETABLE, listOf( ENDPOINT_LIBRUS_API_TIMETABLES, ENDPOINT_LIBRUS_API_SUBSTITUTIONS - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_AGENDA, listOf( ENDPOINT_LIBRUS_API_EVENTS, ENDPOINT_LIBRUS_API_EVENT_TYPES, @@ -62,7 +62,7 @@ val endpoints = listOf( ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS, ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS, ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( ENDPOINT_LIBRUS_API_NORMAL_GC, ENDPOINT_LIBRUS_API_POINT_GC, @@ -76,58 +76,65 @@ val endpoints = listOf( ENDPOINT_LIBRUS_API_TEXT_GRADES, ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES, ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf( ENDPOINT_LIBRUS_API_HOMEWORK - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_NOTICES, listOf( ENDPOINT_LIBRUS_API_NOTICES - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_ATTENDANCES, listOf( ENDPOINT_LIBRUS_API_ATTENDANCE, ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_ANNOUNCEMENTS, listOf( ENDPOINT_LIBRUS_API_ANNOUNCEMENTS - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf( ENDPOINT_LIBRUS_API_ME - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SCHOOL_INFO, listOf( ENDPOINT_LIBRUS_API_SCHOOLS, ENDPOINT_LIBRUS_API_UNITS - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_CLASS_INFO, listOf( ENDPOINT_LIBRUS_API_CLASSES - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TEAM_INFO, listOf( ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_LUCKY_NUMBER, listOf( ENDPOINT_LIBRUS_API_LUCKY_NUMBER - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_TEACHERS, listOf( ENDPOINT_LIBRUS_API_USERS - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_SUBJECTS, listOf( ENDPOINT_LIBRUS_API_SUBJECTS - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_CLASSROOMS, listOf( ENDPOINT_LIBRUS_API_CLASSROOMS - ), LOGIN_METHOD_LIBRUS_API), + ), listOf(LOGIN_METHOD_LIBRUS_API)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf( ENDPOINT_LIBRUS_SYNERGIA_INFO - ), LOGIN_METHOD_LIBRUS_SYNERGIA), + ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)), Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_NUMBER, listOf( ENDPOINT_LIBRUS_SYNERGIA_INFO - ), LOGIN_METHOD_LIBRUS_SYNERGIA), - Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( + ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)), + /*Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( ENDPOINT_LIBRUS_SYNERGIA_GRADES - ), LOGIN_METHOD_LIBRUS_SYNERGIA), + ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)),*/ - Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_INBOX, listOf(ENDPOINT_LIBRUS_MESSAGES_RECEIVED), LOGIN_METHOD_LIBRUS_MESSAGES), - Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_OUTBOX, listOf(ENDPOINT_LIBRUS_MESSAGES_SENT), LOGIN_METHOD_LIBRUS_MESSAGES) + + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( + ENDPOINT_LIBRUS_API_NORMAL_GC, + ENDPOINT_LIBRUS_API_NORMAL_GRADES, + ENDPOINT_LIBRUS_SYNERGIA_GRADES + ), listOf(LOGIN_METHOD_LIBRUS_API, LOGIN_METHOD_LIBRUS_SYNERGIA)), + + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_INBOX, listOf(ENDPOINT_LIBRUS_MESSAGES_RECEIVED), listOf(LOGIN_METHOD_LIBRUS_MESSAGES)), + Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_OUTBOX, listOf(ENDPOINT_LIBRUS_MESSAGES_SENT), listOf(LOGIN_METHOD_LIBRUS_MESSAGES)) ) /* diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index 6289f6b2..523df841 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -5,18 +5,28 @@ package pl.szczodrzynski.edziennik.api.v2.librus import android.content.Context +import android.util.Log import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.AppError +import pl.szczodrzynski.edziennik.api.Edziennik import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 +import pl.szczodrzynski.edziennik.api.v2.endpoints import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.api.v2.models.Endpoint import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile import pl.szczodrzynski.edziennik.datamodels.ProfileFull +import kotlin.math.max class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { + companion object { + const val TAG = "Librus" + } val internalErrorList = mutableListOf() val data: DataLibrus @@ -29,43 +39,97 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va } override fun sync(featureIds: List) { + val possibleLoginMethods = data.loginMethods.toMutableList() + for (loginMethod in librusLoginMethods) { + if (loginMethod.isPossible(profile, loginStore)) + possibleLoginMethods += loginMethod.loginMethodId + } + + var highestLoginMethod = 0 + var targetEndpointList = mutableListOf() + + for (featureId in featureIds) { + endpoints.filter { it.featureId == featureId }.forEach { endpoint -> + if (possibleLoginMethods.containsAll(endpoint.requiredLoginMethods)) { + targetEndpointList.add(endpoint) + highestLoginMethod = max(highestLoginMethod, endpoint.requiredLoginMethods.max() ?: 0) + } + } + } + + targetEndpointList = targetEndpointList + .sortedWith(compareBy(Endpoint::featureId, Endpoint::priority)) + .distinctBy { it.featureId } + .toMutableList() + + Log.d(TAG, targetEndpointList.toString()) + + /* + + INPUT: [ + FEATURE_GRADES, + FEATURE_STUDENT_INFO, + FEATURE_STUDENT_NUMBER + ] + + OUTPUT: [ + Endpoint(loginType=2, + featureId=FEATURE_GRADES, endpointIds=[ + ENDPOINT_LIBRUS_API_NORMAL_GC, + ENDPOINT_LIBRUS_API_NORMAL_GRADES, + ENDPOINT_LIBRUS_SYNERGIA_GRADES + ], requiredLoginMethods=[ + LOGIN_METHOD_LIBRUS_API, + LOGIN_METHOD_LIBRUS_SYNERGIA + ]), + Endpoint(loginType=2, + featureId=FEATURE_STUDENT_INFO, endpointIds=[ + ENDPOINT_LIBRUS_API_ME + ], requiredLoginMethods=[ + LOGIN_METHOD_LIBRUS_API + ]), + Endpoint(loginType=2, + featureId=FEATURE_STUDENT_NUMBER, endpointIds=[ + ENDPOINT_LIBRUS_SYNERGIA_INFO + ], requiredLoginMethods=[ + LOGIN_METHOD_LIBRUS_SYNERGIA + ]) + ] + + */ } override fun getMessage(messageId: Int) { } - private fun wrapCallback(callback: SyncCallback): SyncCallback { - return object : SyncCallback { - override fun onSuccess(activityContext: Context?, profileFull: ProfileFull?) { - callback.onSuccess(activityContext, profileFull) + private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { + return object : EdziennikCallback { + override fun onCompleted() { + callback.onCompleted() } - override fun onProgress(progressStep: Int) { - callback.onProgress(progressStep) + override fun onProgress(step: Int) { + callback.onProgress(step) } - override fun onActionStarted(stringResId: Int) { - callback.onActionStarted(stringResId) + override fun onStartProgress(stringRes: Int) { + callback.onStartProgress(stringRes) } - override fun onLoginFirst(profileList: MutableList?, loginStore: LoginStore?) { - callback.onLoginFirst(profileList, loginStore) - } - - override fun onError(activityContext: Context?, error: AppError) { - when (error.errorCode) { + override fun onError(apiError: ApiError) { + when (apiError.errorCode) { in internalErrorList -> { // finish immediately if the same error occurs twice during the same sync - callback.onError(activityContext, error) + callback.onError(apiError) } CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> { - internalErrorList.add(error.errorCode) + internalErrorList.add(apiError.errorCode) loginStore.removeLoginData("refreshToken") // force a clean login //loginLibrus() } - else -> callback.onError(activityContext, error) + else -> callback.onError(apiError) } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt index 482ba67b..c6c94b80 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt @@ -11,9 +11,11 @@ import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.AppError import pl.szczodrzynski.edziennik.api.interfaces.ProgressCallback import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrus import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusPortal +import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile import pl.szczodrzynski.edziennik.utils.Utils.d @@ -47,7 +49,14 @@ class LibrusTest(val app: App) { fun go() { - app.startService(Intent(app, ApiService::class.java)) + Librus(app, profile, loginStore, object : EdziennikCallback { + override fun onCompleted() {} + override fun onError(apiError: ApiError) {} + override fun onProgress(step: Int) {} + override fun onStartProgress(stringRes: Int) {} + }).sync(listOf(FEATURE_GRADES, FEATURE_STUDENT_INFO, FEATURE_STUDENT_NUMBER)) + + //app.startService(Intent(app, ApiService::class.java)) /*val data = DataLibrus(app, profile, loginStore).apply { callback = object : ProgressCallback { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt index c95dc929..46e172ab 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt @@ -9,106 +9,101 @@ import com.google.gson.JsonObject import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response import im.wangchao.mhttp.callback.JsonCallbackHandler +import io.fabric.sdk.android.services.network.HttpRequest.post import pl.szczodrzynski.edziennik.api.AppError import pl.szczodrzynski.edziennik.api.AppError.CODE_MAINTENANCE import pl.szczodrzynski.edziennik.api.AppError.CODE_OTHER -import pl.szczodrzynski.edziennik.api.v2.Api -import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED -import pl.szczodrzynski.edziennik.api.v2.LIBRUS_API_URL -import pl.szczodrzynski.edziennik.api.v2.LIBRUS_USER_AGENT +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusSynergia +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.utils.Utils.d +import java.lang.Exception +import java.net.HttpURLConnection import java.net.HttpURLConnection.* -open class LibrusApi(override val data: DataLibrus) : Api(data) { +open class LibrusApi(open val data: DataLibrus) { companion object { const val TAG = "LibrusApi" } - fun apiRequest(endpoint: String, callback: (json: JsonObject?) -> Unit) { - d(TAG, "Requesting $LIBRUS_API_URL/$endpoint") + + fun apiGet(tag: String, endpoint: String, method: Int = GET, payload: JsonObject? = null, onSuccess: (json: JsonObject?) -> Unit) { + + val callback = object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response?) { + if (json == null && response?.parserErrorBody == null) { + data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY) + .withResponse(response)) + return + } + val error = if (response?.code() == 200) null else + json.getString("Code") ?: + json.getString("Message") ?: + response?.parserErrorBody + error?.let { code -> + when (code) { + "TokenIsExpired" -> ERROR_LIBRUS_API_TOKEN_EXPIRED + "Insufficient scopes" -> ERROR_LIBRUS_API_INSUFFICIENT_SCOPES + "Request is denied" -> ERROR_LIBRUS_API_REQUEST_DENIED + "Resource not found" -> ERROR_LIBRUS_API_RESOURCE_NOT_FOUND + "NotFound" -> ERROR_LIBRUS_API_DATA_NOT_FOUND + "AccessDeny" -> when (json.getString("Message")) { + "Student timetable is not public" -> ERROR_LIBRUS_API_TIMETABLE_NOT_PUBLIC + else -> ERROR_LIBRUS_API_RESOURCE_ACCESS_DENIED + } + "LuckyNumberIsNotActive" -> ERROR_LIBRUS_API_LUCKY_NUMBER_NOT_ACTIVE + "NotesIsNotActive" -> ERROR_LIBRUS_API_NOTES_NOT_ACTIVE + "InvalidRequest" -> ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS + "Nieprawidłowy węzeł." -> ERROR_LIBRUS_API_INCORRECT_ENDPOINT + else -> ERROR_LIBRUS_API_OTHER + }.let { errorCode -> + data.error(ApiError(tag, errorCode) + .withApiResponse(json) + .withResponse(response)) + return + } + } + + if (json == null) { + data.error(ApiError(tag, ERROR_RESPONSE_EMPTY) + .withResponse(response)) + return + } + + try { + onSuccess(json) + } catch (e: Exception) { + data.error(ApiError(tag, EXCEPTION_LIBRUS_API_REQUEST) + .withResponse(response) + .withThrowable(e) + .withApiResponse(json)) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + // TODO add hotfix for Classrooms 500 + data.error(ApiError(tag, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) + } + } + Request.builder() - .url(if (data.fakeLogin) "http://szkolny.eu/librus/api/$endpoint" else "$LIBRUS_API_URL/$endpoint") + .url("$LIBRUS_API_URL/$endpoint") .userAgent(LIBRUS_USER_AGENT) .addHeader("Authorization", "Bearer ${data.apiAccessToken}") - .get() + .apply { + when (method) { + GET -> get() + POST -> post() + } + if (payload != null) + setJsonBody(payload) + } + .allowErrorCode(HTTP_BAD_REQUEST) .allowErrorCode(HTTP_FORBIDDEN) .allowErrorCode(HTTP_UNAUTHORIZED) - .allowErrorCode(HTTP_BAD_REQUEST) - .callback(object : JsonCallbackHandler() { - override fun onSuccess(json: JsonObject?, response: Response) { - if (json == null) { - if (response.parserErrorBody != null && response.parserErrorBody == "Nieprawidłowy węzeł.") { - callback(null) - return - } - finishWithError(AppError(TAG, 453, CODE_MAINTENANCE, response)) - return - } - if (json.get("Status") != null) { - val message = json.get("Message") - val code = json.get("Code") - d(TAG, "apiRequest Error " + json.get("Status").asString + " " + (if (message == null) "" else message.asString) + " " + (if (code == null) "" else code.asString) + "\n\n" + response.request().url().toString()) - if (message != null && message !is JsonNull && message.asString == "Student timetable is not public") { - try { - callback(null) - } catch (e: NullPointerException) { - e.printStackTrace() - d(TAG, "apiRequest exception " + e.message) - finishWithError(AppError(TAG, 503, CODE_OTHER, response, e, json)) - } - - return - } - if (code != null - && code !is JsonNull - && (code.asString == "LuckyNumberIsNotActive" - || code.asString == "NotesIsNotActive" - || code.asString == "AccessDeny")) { - try { - callback(null) - } catch (e: NullPointerException) { - e.printStackTrace() - d(TAG, "apiRequest exception " + e.message) - finishWithError(AppError(TAG, 504, CODE_OTHER, response, e, json)) - } - - return - } - val errorText = json.get("Status").asString + " " + (if (message == null) "" else message.asString) + " " + if (code == null) "" else code.asString - if (code != null && code !is JsonNull && code.asString == "TokenIsExpired") { - finishWithError(AppError(TAG, 74, CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED, errorText, response, json)) - return - } - finishWithError(AppError(TAG, 497, CODE_OTHER, errorText, response, json)) - return - } - try { - callback(json) - } catch (e: NullPointerException) { - e.printStackTrace() - d(TAG, "apiRequest exception " + e.message) - finishWithError(AppError(TAG, 505, CODE_OTHER, response, e, json)) - } - - } - - override fun onFailure(response: Response, throwable: Throwable) { - if (response.code() == 405) { - // method not allowed - finishWithError(AppError(TAG, 511, CODE_OTHER, response, throwable)) - return - } - if (response.code() == 500) { - // TODO: 2019-09-10 dirty hotfix - if ("Classrooms" == endpoint) { - callback(null) - return - } - finishWithError(AppError(TAG, 516, CODE_MAINTENANCE, response, throwable)) - return - } - finishWithError(AppError(TAG, 520, CODE_OTHER, response, throwable)) - } - }) + .callback(callback) .build() .enqueue() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiMe.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiMe.kt index a852de2b..43a60413 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiMe.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApiMe.kt @@ -12,8 +12,12 @@ import pl.szczodrzynski.edziennik.datamodels.Profile class LibrusApiMe(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiMe" + } + init { - apiRequest("Me") { json -> + apiGet(TAG, "Me") { json -> val me = json?.getJsonObject("Me") val account = me?.getJsonObject("Account") val user = me?.getJsonObject("User") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt index e12d6713..4f28b5d8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrus.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.login +import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod @@ -31,7 +32,7 @@ class LoginLibrus(val data: DataLibrus, vararg loginMethodIds: Int, val onSucces loginMethodList = loginMethodList.toHashSet().toMutableList() loginMethodList.sort() - data.satisfyLoginMethods() + //data.satisfyLoginMethods() nextLoginMethod() } @@ -53,24 +54,28 @@ class LoginLibrus(val data: DataLibrus, vararg loginMethodIds: Int, val onSucces d(TAG, "Using login method $loginMethodId") when (loginMethodId) { LOGIN_METHOD_LIBRUS_PORTAL -> { + data.startProgress(R.string.edziennik_progress_login_librus_portal) LoginLibrusPortal(data) { data.loginMethods.add(loginMethodId) onSuccess() } } LOGIN_METHOD_LIBRUS_API -> { + data.startProgress(R.string.edziennik_progress_login_librus_api) LoginLibrusApi(data) { data.loginMethods.add(loginMethodId) onSuccess() } } LOGIN_METHOD_LIBRUS_SYNERGIA -> { + data.startProgress(R.string.edziennik_progress_login_librus_synergia) LoginLibrusSynergia(data) { data.loginMethods.add(loginMethodId) onSuccess() } } LOGIN_METHOD_LIBRUS_MESSAGES -> { + data.startProgress(R.string.edziennik_progress_login_librus_messages) LoginLibrusMessages(data) { data.loginMethods.add(loginMethodId) onSuccess() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt index d687fbdb..56624210 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusApi.kt @@ -12,6 +12,7 @@ import im.wangchao.mhttp.callback.JsonCallbackHandler import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.models.ApiError import java.net.HttpURLConnection.* class LoginLibrusApi { @@ -28,7 +29,7 @@ class LoginLibrusApi { this.onSuccess = onSuccess if (data.profile == null) { - data.error(TAG, ERROR_PROFILE_MISSING) + data.error(ApiError(TAG, ERROR_PROFILE_MISSING)) return } @@ -41,7 +42,7 @@ class LoginLibrusApi { LOGIN_MODE_LIBRUS_SYNERGIA -> loginWithSynergia() LOGIN_MODE_LIBRUS_JST -> loginWithJst() else -> { - data.error(TAG, ERROR_INVALID_LOGIN_MODE) + data.error(ApiError(TAG, ERROR_INVALID_LOGIN_MODE)) } } } @@ -49,7 +50,7 @@ class LoginLibrusApi { private fun loginWithPortal() { if (!data.loginMethods.contains(LOGIN_METHOD_LIBRUS_PORTAL)) { - data.error(TAG, ERROR_LOGIN_METHOD_NOT_SATISFIED) + data.error(ApiError(TAG, ERROR_LOGIN_METHOD_NOT_SATISFIED)) return } SynergiaTokenExtractor(data) { @@ -89,7 +90,7 @@ class LoginLibrusApi { } else { // cannot log in: token expired, no login data present - data.error(TAG, ERROR_LOGIN_DATA_MISSING) + data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING)) } } @@ -106,14 +107,15 @@ class LoginLibrusApi { } else { // cannot log in: token expired, no login data present - data.error(TAG, ERROR_LOGIN_DATA_MISSING) + data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING)) } } private val tokenCallback = object : JsonCallbackHandler() { override fun onSuccess(json: JsonObject?, response: Response?) { if (json == null) { - data.error(TAG, ERROR_RESPONSE_EMPTY, response) + data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY) + .withResponse(response)) return } if (response?.code() != 200) json.getString("error")?.let { error -> @@ -127,7 +129,9 @@ class LoginLibrusApi { "invalid_grant" -> ERROR_LOGIN_LIBRUS_API_INVALID_GRANT else -> ERROR_LOGIN_LIBRUS_API_OTHER }.let { errorCode -> - data.error(TAG, errorCode, apiResponse = json, response = response) + data.error(ApiError(TAG, errorCode) + .withApiResponse(json) + .withResponse(response)) return } } @@ -138,12 +142,17 @@ class LoginLibrusApi { data.apiTokenExpiryTime = response.getUnixDate() + json.getInt("expires_in", 86400) onSuccess() } catch (e: NullPointerException) { - data.error(TAG, EXCEPTION_LOGIN_LIBRUS_API_TOKEN, response, e, json) + data.error(ApiError(TAG, EXCEPTION_LOGIN_LIBRUS_API_TOKEN) + .withResponse(response) + .withThrowable(e) + .withApiResponse(json)) } } override fun onFailure(response: Response?, throwable: Throwable?) { - data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + data.error(ApiError(TAG, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt index 17581b03..a6921aee 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusMessages.kt @@ -12,6 +12,7 @@ import okhttp3.HttpUrl import okhttp3.internal.http.HttpDate import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.currentTimeUnix import pl.szczodrzynski.edziennik.getUnixDate @@ -22,7 +23,7 @@ class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { init { run { if (data.profile == null) { - data.error(TAG, ERROR_PROFILE_MISSING) + data.error(ApiError(TAG, ERROR_PROFILE_MISSING)) return@run } @@ -45,7 +46,7 @@ class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { loginWithCredentials() } else { - data.error(TAG, ERROR_LOGIN_DATA_MISSING) + data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING)) } } }} @@ -70,7 +71,9 @@ class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { var sessionId = data.app.cookieJar.getCookie("wiadomosci.librus.pl", "DZIENNIKSID") sessionId = sessionId?.replace("-MAINT", "") if (sessionId == null) { - data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID, response, text) + data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID) + .withResponse(response) + .withApiResponse(text)) return } data.messagesSessionId = sessionId @@ -84,7 +87,9 @@ class LoginLibrusMessages(val data: DataLibrus, val onSuccess: () -> Unit) { } override fun onFailure(response: Response?, throwable: Throwable?) { - data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + data.error(ApiError(TAG, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt index 71bec763..7a96ea5d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusPortal.kt @@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.api.AppError import pl.szczodrzynski.edziennik.api.AppError.* import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.utils.Utils.c import java.net.HttpURLConnection.HTTP_UNAUTHORIZED import java.util.ArrayList @@ -24,11 +25,11 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { init { run { if (data.loginStore.mode != LOGIN_MODE_LIBRUS_EMAIL) { - data.error(TAG, ERROR_INVALID_LOGIN_MODE) + data.error(ApiError(TAG, ERROR_INVALID_LOGIN_MODE)) return@run } if (data.portalEmail == null || data.portalPassword == null) { - data.error(TAG, ERROR_LOGIN_DATA_MISSING) + data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING)) return@run } @@ -47,7 +48,6 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { }} private fun authorize(url: String?) { - data.callback.onActionStarted(R.string.sync_action_authorizing) Request.builder() .url(url) .userAgent(LIBRUS_USER_AGENT) @@ -67,13 +67,17 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { if (csrfMatcher.find()) { login(csrfMatcher.group(1)) } else { - data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING, response, json) + data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING) + .withResponse(response) + .withApiResponse(json)) } } } override fun onFailure(response: Response, throwable: Throwable) { - data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + data.error(ApiError(TAG, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) } }) .build() @@ -81,7 +85,6 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { } private fun login(csrfToken: String) { - data.callback.onActionStarted(R.string.sync_action_logging_in) Request.builder() .url(LIBRUS_LOGIN_URL) .userAgent(LIBRUS_USER_AGENT) @@ -94,14 +97,18 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { override fun onSuccess(json: JsonObject?, response: Response) { if (json == null) { if (response.parserErrorBody?.contains("wciąż nieaktywne") == true) { - data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED, response) + data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED) + .withResponse(response)) return } - data.error(TAG, ERROR_RESPONSE_EMPTY, response) + data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY) + .withResponse(response)) return } if (json.get("errors") != null) { - data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR, response, apiResponse = json) + data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_ACTION_ERROR) + .withResponse(response) + .withApiResponse(json)) return } authorize(json.getString("redirect", LIBRUS_AUTHORIZE_URL)) @@ -109,10 +116,14 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { override fun onFailure(response: Response, throwable: Throwable) { if (response.code() == 403 || response.code() == 401) { - data.error(TAG, ERROR_LOGIN_DATA_INVALID, response, throwable) + data.error(ApiError(TAG, ERROR_LOGIN_DATA_INVALID) + .withResponse(response) + .withThrowable(throwable)) return } - data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + data.error(ApiError(TAG, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) } }) .build() @@ -150,7 +161,9 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { else -> ERROR_LOGIN_LIBRUS_PORTAL_OTHER } }.let { errorCode -> - data.error(TAG, errorCode, apiResponse = json, response = response) + data.error(ApiError(TAG, errorCode) + .withApiResponse(json) + .withResponse(response)) return } } @@ -158,12 +171,17 @@ class LoginLibrusPortal(val data: DataLibrus, val onSuccess: () -> Unit) { try { onSuccess(json, response) } catch (e: NullPointerException) { - data.error(TAG, EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN, response, e, json) + data.error(ApiError(TAG, EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN) + .withResponse(response) + .withThrowable(e) + .withApiResponse(json)) } } override fun onFailure(response: Response?, throwable: Throwable?) { - data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + data.error(ApiError(TAG, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt index 7b405304..5c59e431 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LoginLibrusSynergia.kt @@ -13,6 +13,7 @@ import okhttp3.Cookie import okhttp3.HttpUrl import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.currentTimeUnix import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.getUnixDate @@ -27,7 +28,7 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { init { run { if (data.profile == null) { - data.error(TAG, ERROR_PROFILE_MISSING) + data.error(ApiError(TAG, ERROR_PROFILE_MISSING)) return@run } @@ -50,7 +51,7 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { loginWithCredentials() } else { - data.error(TAG, ERROR_LOGIN_DATA_MISSING) + data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING)) } } }} @@ -73,7 +74,8 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { val callback = object : JsonCallbackHandler() { override fun onSuccess(json: JsonObject?, response: Response?) { if (json == null && response?.parserErrorBody == null) { - data.error(TAG, ERROR_RESPONSE_EMPTY, response) + data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY) + .withResponse(response)) return } val error = if (response?.code() == 200) null else @@ -97,26 +99,34 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { "Nieprawidłowy węzeł." -> ERROR_LIBRUS_API_INCORRECT_ENDPOINT else -> ERROR_LIBRUS_API_OTHER }.let { errorCode -> - data.error(TAG, errorCode, apiResponse = json, response = response) + data.error(ApiError(TAG, errorCode) + .withApiResponse(json) + .withResponse(response)) return } } if (json == null) { - data.error(TAG, ERROR_RESPONSE_EMPTY, response) + data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY) + .withResponse(response)) return } try { onSuccess(json) } catch (e: Exception) { - data.error(TAG, EXCEPTION_LIBRUS_API_REQUEST, response, e, json) + data.error(ApiError(TAG, EXCEPTION_LIBRUS_API_REQUEST) + .withResponse(response) + .withThrowable(e) + .withApiResponse(json)) } } override fun onFailure(response: Response?, throwable: Throwable?) { // TODO add hotfix for Classrooms 500 - data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + data.error(ApiError(TAG, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) } } @@ -135,7 +145,7 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { private fun loginWithToken(token: String?) { if (token == null) { - data.error(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_NO_TOKEN) + data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_NO_TOKEN)) return } @@ -145,7 +155,9 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { if (location?.endsWith("centrum_powiadomien") == true) { val sessionId = data.app.cookieJar.getCookie("synergia.librus.pl", "DZIENNIKSID") if (sessionId == null) { - data.error(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID, response, json) + data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID) + .withResponse(response) + .withApiResponse(json)) return } data.synergiaSessionId = sessionId @@ -153,12 +165,16 @@ class LoginLibrusSynergia(val data: DataLibrus, val onSuccess: () -> Unit) { onSuccess() } else { - data.error(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_TOKEN_INVALID, response, json) + data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_TOKEN_INVALID) + .withResponse(response) + .withApiResponse(json)) } } override fun onFailure(response: Response?, throwable: Throwable?) { - data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + data.error(ApiError(TAG, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt index 67a49ffb..8b6a4c3b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt @@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.api.AppError import pl.szczodrzynski.edziennik.api.AppError.* import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.utils.Utils.d import java.net.HttpURLConnection.* @@ -20,11 +21,11 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { init { run { if (data.loginStore.mode != LOGIN_MODE_LIBRUS_EMAIL) { - data.error(TAG, ERROR_INVALID_LOGIN_MODE) + data.error(ApiError(TAG, ERROR_INVALID_LOGIN_MODE)) return@run } if (data.profile == null) { - data.error(TAG, ERROR_PROFILE_MISSING) + data.error(ApiError(TAG, ERROR_PROFILE_MISSING)) return@run } @@ -33,7 +34,7 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { } else { if (!synergiaAccount()) { - data.error(TAG, ERROR_LOGIN_DATA_MISSING) + data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING)) } } }} @@ -51,7 +52,9 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { val accountId = json.getInt("id") val accountToken = json.getString("accessToken") if (accountId == null || accountToken == null) { - data.error(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN_MISSING, response, apiResponse = json) + data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_SYNERGIA_TOKEN_MISSING) + .withResponse(response) + .withApiResponse(json)) } else { data.apiAccessToken = accountToken @@ -69,7 +72,8 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { val callback = object : JsonCallbackHandler() { override fun onSuccess(json: JsonObject?, response: Response?) { if (json == null) { - data.error(TAG, ERROR_RESPONSE_EMPTY, response) + data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY) + .withResponse(response)) return } val error = if (response?.code() == 200) null else @@ -85,7 +89,9 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { "Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND else -> ERROR_LIBRUS_PORTAL_OTHER }.let { errorCode -> - data.error(TAG, errorCode, apiResponse = json, response = response) + data.error(ApiError(TAG, errorCode) + .withApiResponse(json) + .withResponse(response)) return } } @@ -94,16 +100,23 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { onSuccess(json, response) } catch (e: NullPointerException) { e.printStackTrace() - data.error(TAG, EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN, response, e, json) + data.error(ApiError(TAG, EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN) + .withResponse(response) + .withThrowable(e) + .withApiResponse(json)) } } else { - data.error(TAG, ERROR_REQUEST_FAILURE, response, apiResponse = json) + data.error(ApiError(TAG, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withApiResponse(json)) } } override fun onFailure(response: Response?, throwable: Throwable?) { - data.error(TAG, ERROR_REQUEST_FAILURE, response, throwable) + data.error(ApiError(TAG, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt index b83cf514..d094901e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt @@ -8,7 +8,8 @@ import com.google.gson.JsonObject import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response -class ApiError(val profileId: Int, val tag: String, val errorCode: Int) { +class ApiError(val tag: String, val errorCode: Int) { + var profileId: Int? = null private var throwable: Throwable? = null private var apiResponse: String? = null private var request: Request? = null diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 024bad40..467c4ff9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -169,7 +169,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) else -> errorCode } } - callback.onError(ApiError(profile?.id ?: -1, tag, code).withResponse(response).withThrowable(throwable).withApiResponse(apiResponse)) + callback.onError(ApiError(tag, code).apply { profileId = profile?.id ?: -1 }.withResponse(response).withThrowable(throwable).withApiResponse(apiResponse)) } fun error(tag: String, errorCode: Int, response: Response? = null, apiResponse: String? = null) { var code = when (null) { @@ -180,6 +180,15 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) else -> errorCode } } - callback.onError(ApiError(profile?.id ?: -1, tag, code).withResponse(response).withApiResponse(apiResponse)) + callback.onError(ApiError(tag, code).apply { profileId = profile?.id ?: -1 }.withResponse(response).withApiResponse(apiResponse)) + } + fun error(apiError: ApiError) { + callback.onError(apiError) + } + fun progress(step: Int) { + callback.onProgress(step) + } + fun startProgress(stringRes: Int) { + callback.onStartProgress(stringRes) } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt index 55167232..b65e4f62 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Endpoint.kt @@ -16,9 +16,12 @@ import pl.szczodrzynski.edziennik.datamodels.Profile * @param endpointIds a [List] of [Endpoint]s that satisfy this feature ID * @param requiredLoginMethod a required login method, which will have to be executed before this endpoint. */ -class Endpoint( +data class Endpoint( val loginType: Int, val featureId: Int, val endpointIds: List, - val requiredLoginMethod: Int -) \ No newline at end of file + val requiredLoginMethods: List +) { + val priority + get() = endpointIds.size +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0dd0cdc8..2559d0d9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -908,4 +908,8 @@ Serwer Discord Dołącz do naszego serwera Discord! Debugowanie + Logowanie do Portalu Librus + Logowanie do API + Logowanie do Librus Synergia + Logowanie do wiadomości Librus diff --git a/settings.gradle b/settings.gradle index 8b001cca..692dbba0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ -include ':app', ':agendacalendarview', ':MaterialDrawer', ':mhttp', ':material-about-library', ':cafebar', ':szkolny-font', ':nachos' +include ':app', ':agendacalendarview', ':mhttp', ':material-about-library', ':cafebar', ':szkolny-font', ':nachos' /* include ':Navigation' project(':Navigation').projectDir = new File(settingsDir, '../Navigation/navlib')*/ From 92880d40cf9c15c7bd2619db9c9f10eda6c79866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 1 Oct 2019 11:32:53 +0200 Subject: [PATCH 015/691] [APIv2/Librus] Update endpoint choosing algorithm --- .../edziennik/api/v2/librus/Librus.kt | 68 ++++++++++++++----- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index 523df841..3838970b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -4,13 +4,10 @@ package pl.szczodrzynski.edziennik.api.v2.librus -import android.content.Context import android.util.Log import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.api.AppError -import pl.szczodrzynski.edziennik.api.Edziennik -import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_NOT_NEEDED import pl.szczodrzynski.edziennik.api.v2.endpoints import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface @@ -20,8 +17,6 @@ import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.Endpoint import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile -import pl.szczodrzynski.edziennik.datamodels.ProfileFull -import kotlin.math.max class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { companion object { @@ -46,24 +41,65 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va possibleLoginMethods += loginMethod.loginMethodId } - var highestLoginMethod = 0 - var targetEndpointList = mutableListOf() + //var highestLoginMethod = 0 + var endpointList = mutableListOf() + val requiredLoginMethods = mutableListOf() + var targetEndpointIds = mutableListOf() + var targetLoginMethodIds = mutableListOf() + + // get all endpoints for every feature, only if possible to login for (featureId in featureIds) { - endpoints.filter { it.featureId == featureId }.forEach { endpoint -> + /*endpoints.filter { it.featureId == featureId }.forEach { endpoint -> if (possibleLoginMethods.containsAll(endpoint.requiredLoginMethods)) { - targetEndpointList.add(endpoint) - highestLoginMethod = max(highestLoginMethod, endpoint.requiredLoginMethods.max() ?: 0) + endpointList.add(endpoint) + //highestLoginMethod = max(highestLoginMethod, endpoint.requiredLoginMethods.max() ?: 0) + } + }*/ + endpoints.filter { + it.featureId == featureId && possibleLoginMethods.containsAll(it.requiredLoginMethods) + } + .let { + endpointList.addAll(it) + } + } + + endpointList = endpointList + // sort the endpoint list by feature ID and priority + .sortedWith(compareBy(Endpoint::featureId, Endpoint::priority)) + // select only the most important endpoint for each feature + .distinctBy { it.featureId } + .toMutableList() + // add all endpoint IDs and required login methods + .onEach { endpoint -> + targetEndpointIds.addAll(endpoint.endpointIds) + requiredLoginMethods.addAll(endpoint.requiredLoginMethods) + } + + // check every login method for any dependencies + for (loginMethodId in requiredLoginMethods) { + var requiredLoginMethod: Int? = loginMethodId + while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) { + librusLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod -> + if (requiredLoginMethod != null) + targetLoginMethodIds.add(requiredLoginMethod!!) + requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore) } } } - targetEndpointList = targetEndpointList - .sortedWith(compareBy(Endpoint::featureId, Endpoint::priority)) - .distinctBy { it.featureId } - .toMutableList() + // sort and distinct every login method and endpoint + targetLoginMethodIds = targetLoginMethodIds.toHashSet().toMutableList() + targetLoginMethodIds.sort() - Log.d(TAG, targetEndpointList.toString()) + targetEndpointIds = targetEndpointIds.toHashSet().toMutableList() + targetLoginMethodIds.sort() + + + + //Log.d(TAG, endpointList.toString()) + Log.d(TAG, "LoginMethod IDs: $targetLoginMethodIds") + Log.d(TAG, "Endpoint IDs: $targetEndpointIds") /* From 4c6b46784764dc0bfd138940f811ad025d0ddc9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 1 Oct 2019 21:27:09 +0200 Subject: [PATCH 016/691] [APIv2] Finalize the basic API service. Add notification. Add exported broadcast receiver. --- app/src/main/AndroidManifest.xml | 7 + .../pl/szczodrzynski/edziennik/Notifier.java | 2 +- .../edziennik/api/v2/ApiService.kt | 131 +++++++++++++--- .../edziennik/api/v2/EdziennikNotification.kt | 140 ++++++++++++++++++ .../edziennik/api/v2/Endpoints.kt | 82 +++++----- .../api/v2/events/ErrorReportTask.kt | 9 ++ .../v2/events/requests/ServiceCloseRequest.kt | 7 + .../api/v2/events/requests/SyncRequest.kt | 2 +- .../v2/events/requests/TaskCancelRequest.kt | 7 + .../api/v2/interfaces/EdziennikInterface.kt | 1 + .../edziennik/api/v2/librus/Librus.kt | 90 +++++------ .../api/v2/librus/LibrusEndpoints.kt | 52 +++++++ .../edziennik/api/v2/librus/LibrusLogin.kt | 72 +++++++++ .../edziennik/api/v2/librus/LibrusTest.kt | 10 +- .../edziennik/api/v2/models/ApiError.kt | 6 + .../edziennik/api/v2/models/Data.kt | 21 ++- .../edziennik/fragments/HomeFragment.java | 25 +++- .../edziennik/receivers/SzkolnyReceiver.kt | 23 +++ app/src/main/res/layout/fragment_home.xml | 29 +++- app/src/main/res/values/plurals.xml | 14 ++ app/src/main/res/values/strings.xml | 9 ++ 21 files changed, 615 insertions(+), 124 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EdziennikNotification.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ErrorReportTask.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/ServiceCloseRequest.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/TaskCancelRequest.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusEndpoints.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusLogin.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f5f02d8b..9031bd28 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -218,6 +218,13 @@ android:name=".sync.SyncService" android:icon="@mipmap/ic_launcher" android:label="@string/sync_service" /> + + + + + diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java b/app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java index 512affed..000fc179 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java @@ -71,7 +71,7 @@ public class Notifier { notificationColor = ContextCompat.getColor(app.getContext(), R.color.colorPrimary); notificationManager = (NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - NotificationChannel channelGetData = new NotificationChannel(GROUP_KEY_GET_DATA, CHANNEL_GET_DATA_NAME, NotificationManager.IMPORTANCE_LOW); + NotificationChannel channelGetData = new NotificationChannel(GROUP_KEY_GET_DATA, CHANNEL_GET_DATA_NAME, NotificationManager.IMPORTANCE_MIN); channelGetData.setDescription(CHANNEL_GET_DATA_DESC); notificationManager.createNotificationChannel(channelGetData); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt index 97732a36..88710709 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt @@ -14,12 +14,10 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.api.v2.events.requests.MessageGetRequest -import pl.szczodrzynski.edziennik.api.v2.events.SyncProgressEvent -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncViewRequest +import pl.szczodrzynski.edziennik.api.v2.events.* +import pl.szczodrzynski.edziennik.api.v2.events.requests.* import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback +import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.librus.Librus import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiTask @@ -37,46 +35,99 @@ class ApiService : Service() { private val taskQueue = mutableListOf() private val errorList = mutableListOf() + private var queueHasErrorReportTask = false + private var taskCancelled = false private var taskRunning = false private var taskRunningId = -1 private var taskMaximumId = 0 + private var edziennikInterface: EdziennikInterface? = null private var taskProfileId = -1 private var taskProfileName: String? = null private var taskProgress = 0 private var taskProgressRes: Int? = null + private val notification by lazy { EdziennikNotification(this) } + + /* ______ _ _ _ _ _____ _ _ _ _ + | ____| | | (_) (_) | / ____| | | | | | | + | |__ __| |_____ ___ _ __ _ __ _| | __ | | __ _| | | |__ __ _ ___| | __ + | __| / _` |_ / |/ _ \ '_ \| '_ \| | |/ / | | / _` | | | '_ \ / _` |/ __| |/ / + | |___| (_| |/ /| | __/ | | | | | | | < | |___| (_| | | | |_) | (_| | (__| < + |______\__,_/___|_|\___|_| |_|_| |_|_|_|\_\ \_____\__,_|_|_|_.__/ \__,_|\___|_|\*/ private val taskCallback = object : EdziennikCallback { override fun onCompleted() { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + edziennikInterface = null + if (!taskCancelled) { + EventBus.getDefault().post(SyncProfileFinishedEvent(taskProfileId)) + } + notification.setIdle().post() + taskRunning = false + taskRunningId = -1 + sync() } override fun onError(apiError: ApiError) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + if (!queueHasErrorReportTask) { + queueHasErrorReportTask = true + taskQueue += ErrorReportTask().apply { + taskId = ++taskMaximumId + } + } + EventBus.getDefault().post(SyncErrorEvent(apiError)) + errorList.add(apiError) + if (apiError.isCritical) { + notification.setCriticalError().post() + taskRunning = false + taskRunningId = -1 + sync() + } + else { + notification.addError().post() + } } override fun onProgress(step: Int) { taskProgress += step taskProgress = min(100, taskProgress) EventBus.getDefault().post(SyncProgressEvent(taskProfileId, taskProfileName, taskProgress, taskProgressRes)) + notification.setProgress(taskProgress).post() } override fun onStartProgress(stringRes: Int) { - + taskProgressRes = stringRes + EventBus.getDefault().post(SyncProgressEvent(taskProfileId, taskProfileName, taskProgress, taskProgressRes)) + notification.setProgressRes(taskProgressRes!!).post() } } + /* _______ _ _ _ + |__ __| | | | | (_) + | | __ _ ___| | __ _____ _____ ___ _ _| |_ _ ___ _ __ + | |/ _` / __| |/ / / _ \ \/ / _ \/ __| | | | __| |/ _ \| '_ \ + | | (_| \__ \ < | __/> < __/ (__| |_| | |_| | (_) | | | | + |_|\__,_|___/_|\_\ \___/_/\_\___|\___|\__,_|\__|_|\___/|_| |*/ private fun sync() { if (taskRunning) return - if (taskQueue.size <= 0) - return // TODO stopSelf() or sth + if (taskQueue.size <= 0) { + allCompleted() + return + } val task = taskQueue.removeAt(0) + taskCancelled = false taskRunning = true taskRunningId = task.taskId + if (task is ErrorReportTask) { + notification + .setCurrentTask(taskRunningId, null) + .setProgressRes(R.string.edziennik_notification_api_error_report_title) + return + } + // get the requested profile and login store val profile: Profile? = app.db.profileDao().getByIdNow(task.profileId) if (profile == null || !profile.syncEnabled) { @@ -92,8 +143,10 @@ class ApiService : Service() { taskProgress = 0 taskProgressRes = null + // update the notification + notification.setCurrentTask(taskRunningId, taskProfileName).post() - val edziennikInterface = when (loginStore.type) { + edziennikInterface = when (loginStore.type) { LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback) else -> null } @@ -102,17 +155,29 @@ class ApiService : Service() { } when (task) { - is SyncProfileRequest -> edziennikInterface.sync(task.featureIds ?: Features.getAllIds()) - is SyncViewRequest -> edziennikInterface.sync(Features.getIdsByView(task.targetId)) - is MessageGetRequest -> edziennikInterface.getMessage(task.messageId) + is SyncProfileRequest -> edziennikInterface?.sync(task.featureIds ?: Features.getAllIds()) + is SyncViewRequest -> edziennikInterface?.sync(Features.getIdsByView(task.targetId)) + is MessageGetRequest -> edziennikInterface?.getMessage(task.messageId) } } + private fun allCompleted() { + EventBus.getDefault().post(SyncFinishedEvent()) + stopSelf() + } + /* ______ _ ____ + | ____| | | | _ \ + | |____ _____ _ __ | |_| |_) |_ _ ___ + | __\ \ / / _ \ '_ \| __| _ <| | | / __| + | |___\ V / __/ | | | |_| |_) | |_| \__ \ + |______\_/ \___|_| |_|\__|____/ \__,_|__*/ @Subscribe(threadMode = ThreadMode.ASYNC) fun onSyncRequest(syncRequest: SyncRequest) { app.db.profileDao().idsForSyncNow.forEach { id -> - taskQueue += SyncProfileRequest(id, null) + taskQueue += SyncProfileRequest(id, null).apply { + taskId = ++taskMaximumId + } } sync() } @@ -120,31 +185,49 @@ class ApiService : Service() { @Subscribe(threadMode = ThreadMode.ASYNC) fun onSyncProfileRequest(syncProfileRequest: SyncProfileRequest) { Log.d(TAG, syncProfileRequest.toString()) - taskQueue += syncProfileRequest + taskQueue += syncProfileRequest.apply { + taskId = ++taskMaximumId + } + sync() + } + + @Subscribe(threadMode = ThreadMode.ASYNC) + fun onSyncViewRequest(syncViewRequest: SyncViewRequest) { + Log.d(TAG, syncViewRequest.toString()) + taskQueue += syncViewRequest.apply { + taskId = ++taskMaximumId + } sync() } @Subscribe(threadMode = ThreadMode.ASYNC) fun onMessageGetRequest(messageGetRequest: MessageGetRequest) { Log.d(TAG, messageGetRequest.toString()) - taskQueue += messageGetRequest + taskQueue += messageGetRequest.apply { + taskId = ++taskMaximumId + } sync() } - private val notification by lazy { - NotificationCompat.Builder(this, NOTIFICATION_API_CHANNEL_ID) - .setContentTitle("API") - .setContentText("API is running") - .setSmallIcon(R.drawable.ic_notification) - .build() + @Subscribe(threadMode = ThreadMode.ASYNC) + fun onTaskCancelRequest(taskCancelRequest: TaskCancelRequest) { + taskCancelled = true + edziennikInterface?.cancel() } + /* _____ _ _ _ + / ____| (_) (_) | | + | (___ ___ _ ____ ___ ___ ___ _____ _____ _ __ _ __ _ __| | ___ ___ + \___ \ / _ \ '__\ \ / / |/ __/ _ \ / _ \ \ / / _ \ '__| '__| |/ _` |/ _ \/ __| + ____) | __/ | \ V /| | (_| __/ | (_) \ V / __/ | | | | | (_| | __/\__ \ + |_____/ \___|_| \_/ |_|\___\___| \___/ \_/ \___|_| |_| |_|\__,_|\___||__*/ override fun onCreate() { EventBus.getDefault().register(this) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - startForeground(1, notification) + startForeground(EdziennikNotification.NOTIFICATION_ID, notification.notification) + notification.setIdle().setCloseAction().post() return START_NOT_STICKY } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EdziennikNotification.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EdziennikNotification.kt new file mode 100644 index 00000000..eeeb1968 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EdziennikNotification.kt @@ -0,0 +1,140 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-1. + */ + +package pl.szczodrzynski.edziennik.api.v2 + +import android.app.Notification +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationCompat.PRIORITY_LOW +import androidx.core.app.NotificationCompat.PRIORITY_MIN +import pl.szczodrzynski.edziennik.R + + +class EdziennikNotification(val context: Context) { + companion object { + const val NOTIFICATION_ID = 20191001 + } + + private val notificationManager by lazy { context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager } + + private val notificationBuilder: NotificationCompat.Builder by lazy { + NotificationCompat.Builder(context, ApiService.NOTIFICATION_API_CHANNEL_ID) + .setSmallIcon(R.drawable.ic_notification) + .setPriority(PRIORITY_MIN) + .setOngoing(true) + .setLocalOnly(true) + } + + val notification: Notification + get() = notificationBuilder.build() + + private var errorCount = 0 + private var criticalErrorCount = 0 + + private fun cancelPendingIntent(taskId: Int): PendingIntent { + val intent = Intent("pl.szczodrzynski.edziennik.SZKOLNY_MAIN") + intent.putExtra("task", "TaskCancelRequest") + intent.putExtra("taskId", taskId) + return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent + } + private val closePendingIntent: PendingIntent + get() { + val intent = Intent("pl.szczodrzynski.edziennik.SZKOLNY_MAIN") + intent.putExtra("task", "ServiceCloseRequest") + return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent + } + + private fun errorCountText(): String? { + var result = "" + if (criticalErrorCount > 0) { + result += context.resources.getQuantityString(R.plurals.critical_errors_format, criticalErrorCount, criticalErrorCount) + } + if (criticalErrorCount > 0 && errorCount > 0) { + result += ", " + } + if (errorCount > 0) { + result += context.resources.getQuantityString(R.plurals.normal_errors_format, errorCount, errorCount) + } + return if (result.isEmpty()) null else result + } + + fun setIdle(): EdziennikNotification { + notificationBuilder.setContentTitle(context.getString(R.string.edziennik_notification_api_title)) + notificationBuilder.setProgress(0, 0, false) + notificationBuilder.apply { + val str = context.getString(R.string.edziennik_notification_api_text) + setStyle(NotificationCompat.BigTextStyle().bigText(str)) + setContentText(str) + } + setCloseAction() + return this + } + + fun addError(): EdziennikNotification { + errorCount++ + return this + } + fun setCriticalError(): EdziennikNotification { + criticalErrorCount++ + notificationBuilder.setContentTitle(context.getString(R.string.edziennik_notification_api_error_title)) + notificationBuilder.setProgress(0, 0, false) + notificationBuilder.apply { + val str = errorCountText() + setStyle(NotificationCompat.BigTextStyle().bigText(str)) + setContentText(str) + } + setCloseAction() + return this + } + + fun setProgress(progress: Int): EdziennikNotification { + notificationBuilder.setProgress(100, progress, false) + return this + } + fun setProgressRes(progressRes: Int): EdziennikNotification { + notificationBuilder.setContentTitle(context.getString(progressRes)) + return this + } + + fun setCurrentTask(taskId: Int, profileName: String?): EdziennikNotification { + notificationBuilder.setProgress(100, 0, false) + notificationBuilder.setContentTitle(context.getString(R.string.edziennik_notification_api_sync_title_format, profileName)) + notificationBuilder.apply { + val str = errorCountText() + setStyle(NotificationCompat.BigTextStyle().bigText(str)) + setContentText(str) + } + setCancelAction(taskId) + return this + } + + fun setCloseAction(): EdziennikNotification { + notificationBuilder.mActions.clear() + notificationBuilder.addAction( + NotificationCompat.Action( + R.drawable.ic_notification, + context.getString(R.string.edziennik_notification_api_close), + closePendingIntent + )) + return this + } + private fun setCancelAction(taskId: Int) { + notificationBuilder.mActions.clear() + notificationBuilder.addAction( + NotificationCompat.Action( + R.drawable.ic_notification, + context.getString(R.string.edziennik_notification_api_cancel), + cancelPendingIntent(taskId) + )) + } + + fun post() { + notificationManager.notify(NOTIFICATION_ID, notification) + } + +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt index 4279ff02..9e65ad2e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt @@ -6,47 +6,47 @@ package pl.szczodrzynski.edziennik.api.v2 import pl.szczodrzynski.edziennik.api.v2.models.Endpoint -const val ENDPOINT_LIBRUS_API_ME = 101 -const val ENDPOINT_LIBRUS_API_SCHOOLS = 102 -const val ENDPOINT_LIBRUS_API_CLASSES = 103 -const val ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES = 104 -const val ENDPOINT_LIBRUS_API_UNITS = 105 -const val ENDPOINT_LIBRUS_API_USERS = 106 -const val ENDPOINT_LIBRUS_API_SUBJECTS = 107 -const val ENDPOINT_LIBRUS_API_CLASSROOMS = 108 -const val ENDPOINT_LIBRUS_API_TIMETABLES = 109 -const val ENDPOINT_LIBRUS_API_SUBSTITUTIONS = 110 -const val ENDPOINT_LIBRUS_API_NORMAL_GC = 111 -const val ENDPOINT_LIBRUS_API_POINT_GC = 112 -const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC = 113 -const val ENDPOINT_LIBRUS_API_TEXT_GC = 114 -const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC = 115 -const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GC = 116 -const val ENDPOINT_LIBRUS_API_NORMAL_GRADES = 117 -const val ENDPOINT_LIBRUS_API_POINT_GRADES = 118 -const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES = 119 -const val ENDPOINT_LIBRUS_API_TEXT_GRADES = 120 -const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES = 121 -const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES = 122 -const val ENDPOINT_LIBRUS_API_EVENTS = 123 -const val ENDPOINT_LIBRUS_API_EVENT_TYPES = 124 -const val ENDPOINT_LIBRUS_API_HOMEWORK = 125 -const val ENDPOINT_LIBRUS_API_LUCKY_NUMBER = 126 -const val ENDPOINT_LIBRUS_API_NOTICES = 127 -const val ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES = 128 -const val ENDPOINT_LIBRUS_API_ATTENDANCE = 129 -const val ENDPOINT_LIBRUS_API_ANNOUNCEMENTS = 130 -const val ENDPOINT_LIBRUS_API_PT_MEETINGS = 131 -const val ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS = 132 -const val ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS = 133 -const val ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS = 134 -const val ENDPOINT_LIBRUS_SYNERGIA_INFO = 201 -const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 202 -const val ENDPOINT_LIBRUS_MESSAGES_RECEIVED = 301 -const val ENDPOINT_LIBRUS_MESSAGES_SENT = 302 -const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 303 -const val ENDPOINT_LIBRUS_MESSAGES_RECEIVERS = 304 -const val ENDPOINT_LIBRUS_MESSAGES_GET = 304 +const val ENDPOINT_LIBRUS_API_ME = 1001 +const val ENDPOINT_LIBRUS_API_SCHOOLS = 1002 +const val ENDPOINT_LIBRUS_API_CLASSES = 1003 +const val ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES = 1004 +const val ENDPOINT_LIBRUS_API_UNITS = 1005 +const val ENDPOINT_LIBRUS_API_USERS = 1006 +const val ENDPOINT_LIBRUS_API_SUBJECTS = 1007 +const val ENDPOINT_LIBRUS_API_CLASSROOMS = 1008 +const val ENDPOINT_LIBRUS_API_TIMETABLES = 1015 +const val ENDPOINT_LIBRUS_API_SUBSTITUTIONS = 1016 +const val ENDPOINT_LIBRUS_API_NORMAL_GC = 1021 +const val ENDPOINT_LIBRUS_API_POINT_GC = 1022 +const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC = 1023 +const val ENDPOINT_LIBRUS_API_TEXT_GC = 1024 +const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC = 1025 +const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GC = 1026 +const val ENDPOINT_LIBRUS_API_NORMAL_GRADES = 1031 +const val ENDPOINT_LIBRUS_API_POINT_GRADES = 1032 +const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES = 1033 +const val ENDPOINT_LIBRUS_API_TEXT_GRADES = 1034 +const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES = 1035 +const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES = 1036 +const val ENDPOINT_LIBRUS_API_EVENTS = 1040 +const val ENDPOINT_LIBRUS_API_EVENT_TYPES = 1041 +const val ENDPOINT_LIBRUS_API_HOMEWORK = 1050 +const val ENDPOINT_LIBRUS_API_LUCKY_NUMBER = 1060 +const val ENDPOINT_LIBRUS_API_NOTICES = 1070 +const val ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES = 1080 +const val ENDPOINT_LIBRUS_API_ATTENDANCE = 1081 +const val ENDPOINT_LIBRUS_API_ANNOUNCEMENTS = 1090 +const val ENDPOINT_LIBRUS_API_PT_MEETINGS = 1100 +const val ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS = 1110 +const val ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS = 1120 +const val ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS = 1130 +const val ENDPOINT_LIBRUS_SYNERGIA_INFO = 2010 +const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 2020 +const val ENDPOINT_LIBRUS_MESSAGES_RECEIVED = 3010 +const val ENDPOINT_LIBRUS_MESSAGES_SENT = 3020 +const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 3030 +const val ENDPOINT_LIBRUS_MESSAGES_RECEIVERS = 3040 +const val ENDPOINT_LIBRUS_MESSAGES_GET = 3040 val endpoints = listOf( diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ErrorReportTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ErrorReportTask.kt new file mode 100644 index 00000000..444443d1 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ErrorReportTask.kt @@ -0,0 +1,9 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-1. + */ + +package pl.szczodrzynski.edziennik.api.v2.events + +import pl.szczodrzynski.edziennik.api.v2.models.ApiTask + +class ErrorReportTask : ApiTask(-1) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/ServiceCloseRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/ServiceCloseRequest.kt new file mode 100644 index 00000000..a859301c --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/ServiceCloseRequest.kt @@ -0,0 +1,7 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-1. + */ + +package pl.szczodrzynski.edziennik.api.v2.events.requests + +class ServiceCloseRequest \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncRequest.kt index 4570d3a4..ef42a557 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncRequest.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncRequest.kt @@ -4,4 +4,4 @@ package pl.szczodrzynski.edziennik.api.v2.events.requests -class SyncRequest() \ No newline at end of file +class SyncRequest \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/TaskCancelRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/TaskCancelRequest.kt new file mode 100644 index 00000000..f40a9455 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/TaskCancelRequest.kt @@ -0,0 +1,7 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-1. + */ + +package pl.szczodrzynski.edziennik.api.v2.events.requests + +class TaskCancelRequest(val taskId: Int) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt index 8265de7e..38367b8a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt @@ -7,4 +7,5 @@ package pl.szczodrzynski.edziennik.api.v2.interfaces interface EdziennikInterface { fun sync(featureIds: List) fun getMessage(messageId: Int) + fun cancel() } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index 3838970b..e1db1e50 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -6,25 +6,26 @@ package pl.szczodrzynski.edziennik.api.v2.librus import android.util.Log import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 -import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_NOT_NEEDED -import pl.szczodrzynski.edziennik.api.v2.endpoints +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus -import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods +import pl.szczodrzynski.edziennik.api.v2.librus.login.* import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.Endpoint import pl.szczodrzynski.edziennik.datamodels.LoginStore import pl.szczodrzynski.edziennik.datamodels.Profile +import pl.szczodrzynski.edziennik.utils.Utils.d class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { companion object { - const val TAG = "Librus" + private const val TAG = "Librus" } val internalErrorList = mutableListOf() val data: DataLibrus + private var cancelled = false init { data = DataLibrus(app, profile, loginStore).apply { @@ -33,6 +34,19 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va data.satisfyLoginMethods() } + private fun completed() { + data.saveData() + callback.onCompleted() + } + + /* _______ _ _ _ _ _ + |__ __| | /\ | | (_) | | | + | | | |__ ___ / \ | | __ _ ___ _ __ _| |_| |__ _ __ ___ + | | | '_ \ / _ \ / /\ \ | |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \ + | | | | | | __/ / ____ \| | (_| | (_) | | | | |_| | | | | | | | | + |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| + __/ | + |__*/ override fun sync(featureIds: List) { val possibleLoginMethods = data.loginMethods.toMutableList() @@ -45,8 +59,8 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va var endpointList = mutableListOf() val requiredLoginMethods = mutableListOf() - var targetEndpointIds = mutableListOf() - var targetLoginMethodIds = mutableListOf() + data.targetEndpointIds.clear() + data.targetLoginMethodIds.clear() // get all endpoints for every feature, only if possible to login for (featureId in featureIds) { @@ -72,7 +86,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va .toMutableList() // add all endpoint IDs and required login methods .onEach { endpoint -> - targetEndpointIds.addAll(endpoint.endpointIds) + data.targetEndpointIds.addAll(endpoint.endpointIds) requiredLoginMethods.addAll(endpoint.requiredLoginMethods) } @@ -82,64 +96,38 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) { librusLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod -> if (requiredLoginMethod != null) - targetLoginMethodIds.add(requiredLoginMethod!!) + data.targetLoginMethodIds.add(requiredLoginMethod!!) requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore) } } } // sort and distinct every login method and endpoint - targetLoginMethodIds = targetLoginMethodIds.toHashSet().toMutableList() - targetLoginMethodIds.sort() + data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList() + data.targetLoginMethodIds.sort() - targetEndpointIds = targetEndpointIds.toHashSet().toMutableList() - targetLoginMethodIds.sort() + data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList() + data.targetEndpointIds.sort() + Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") + Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") - - //Log.d(TAG, endpointList.toString()) - Log.d(TAG, "LoginMethod IDs: $targetLoginMethodIds") - Log.d(TAG, "Endpoint IDs: $targetEndpointIds") - - /* - - INPUT: [ - FEATURE_GRADES, - FEATURE_STUDENT_INFO, - FEATURE_STUDENT_NUMBER - ] - - OUTPUT: [ - Endpoint(loginType=2, - featureId=FEATURE_GRADES, endpointIds=[ - ENDPOINT_LIBRUS_API_NORMAL_GC, - ENDPOINT_LIBRUS_API_NORMAL_GRADES, - ENDPOINT_LIBRUS_SYNERGIA_GRADES - ], requiredLoginMethods=[ - LOGIN_METHOD_LIBRUS_API, - LOGIN_METHOD_LIBRUS_SYNERGIA - ]), - Endpoint(loginType=2, - featureId=FEATURE_STUDENT_INFO, endpointIds=[ - ENDPOINT_LIBRUS_API_ME - ], requiredLoginMethods=[ - LOGIN_METHOD_LIBRUS_API - ]), - Endpoint(loginType=2, - featureId=FEATURE_STUDENT_NUMBER, endpointIds=[ - ENDPOINT_LIBRUS_SYNERGIA_INFO - ], requiredLoginMethods=[ - LOGIN_METHOD_LIBRUS_SYNERGIA - ]) - ] - - */ + LibrusLogin(data) { + LibrusEndpoints(data) { + completed() + } + } } override fun getMessage(messageId: Int) { } + override fun cancel() { + d(TAG, "Cancelled") + cancelled = true + } + private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { return object : EdziennikCallback { override fun onCompleted() { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusEndpoints.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusEndpoints.kt new file mode 100644 index 00000000..7d981d48 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusEndpoints.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-1. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus + +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApiMe +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusApi +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusMessages +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusPortal +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusSynergia +import pl.szczodrzynski.edziennik.utils.Utils + +class LibrusEndpoints(val data: DataLibrus, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "LibrusEndpoints" + } + + private var cancelled = false + + init { + nextEndpoint(onSuccess) + } + + private fun nextEndpoint(onSuccess: () -> Unit) { + if (data.targetEndpointIds.isEmpty()) { + onSuccess() + return + } + useEndpoint(data.targetEndpointIds.removeAt(0)) { + if (cancelled) { + onSuccess() + return@useEndpoint + } + nextEndpoint(onSuccess) + } + } + + private fun useEndpoint(endpointId: Int, onSuccess: () -> Unit) { + Utils.d(TAG, "Using endpoint $endpointId") + when (endpointId) { + ENDPOINT_LIBRUS_API_ME -> { + data.startProgress(R.string.edziennik_progress_endpoint_student_info) + LibrusApiMe(data) { onSuccess() } + } + else -> onSuccess() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusLogin.kt new file mode 100644 index 00000000..ce9e848a --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusLogin.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-1. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus + +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_API +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_MESSAGES +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_PORTAL +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_SYNERGIA +import pl.szczodrzynski.edziennik.api.v2.librus.data.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusApi +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusMessages +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusPortal +import pl.szczodrzynski.edziennik.api.v2.librus.login.LoginLibrusSynergia +import pl.szczodrzynski.edziennik.utils.Utils + +class LibrusLogin(val data: DataLibrus, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "LibrusLogin" + } + + private var cancelled = false + + init { + nextLoginMethod(onSuccess) + } + + private fun nextLoginMethod(onSuccess: () -> Unit) { + if (data.targetLoginMethodIds.isEmpty()) { + onSuccess() + return + } + useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId -> + if (usedMethodId != -1) + data.loginMethods.add(usedMethodId) + if (cancelled) { + onSuccess() + return@useLoginMethod + } + nextLoginMethod(onSuccess) + } + } + + private fun useLoginMethod(loginMethodId: Int, onSuccess: (usedMethodId: Int) -> Unit) { + // this should never be true + if (data.loginMethods.contains(loginMethodId)) { + onSuccess(-1) + return + } + Utils.d(TAG, "Using login method $loginMethodId") + when (loginMethodId) { + LOGIN_METHOD_LIBRUS_PORTAL -> { + data.startProgress(R.string.edziennik_progress_login_librus_portal) + LoginLibrusPortal(data) { onSuccess(loginMethodId) } + } + LOGIN_METHOD_LIBRUS_API -> { + data.startProgress(R.string.edziennik_progress_login_librus_api) + LoginLibrusApi(data) { onSuccess(loginMethodId) } + } + LOGIN_METHOD_LIBRUS_SYNERGIA -> { + data.startProgress(R.string.edziennik_progress_login_librus_synergia) + LoginLibrusSynergia(data) { onSuccess(loginMethodId) } + } + LOGIN_METHOD_LIBRUS_MESSAGES -> { + data.startProgress(R.string.edziennik_progress_login_librus_messages) + LoginLibrusMessages(data) { onSuccess(loginMethodId) } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt index c6c94b80..da637044 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt @@ -49,14 +49,18 @@ class LibrusTest(val app: App) { fun go() { - Librus(app, profile, loginStore, object : EdziennikCallback { + /*Librus(app, profile, loginStore, object : EdziennikCallback { override fun onCompleted() {} override fun onError(apiError: ApiError) {} override fun onProgress(step: Int) {} override fun onStartProgress(stringRes: Int) {} - }).sync(listOf(FEATURE_GRADES, FEATURE_STUDENT_INFO, FEATURE_STUDENT_NUMBER)) + }).sync(listOf(FEATURE_GRADES, FEATURE_STUDENT_INFO, FEATURE_STUDENT_NUMBER))*/ - //app.startService(Intent(app, ApiService::class.java)) + app.startService(Intent(app, ApiService::class.java)) + + if (false) { + + } /*val data = DataLibrus(app, profile, loginStore).apply { callback = object : ProgressCallback { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt index d094901e..d7b21a90 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt @@ -14,6 +14,7 @@ class ApiError(val tag: String, val errorCode: Int) { private var apiResponse: String? = null private var request: Request? = null private var response: Response? = null + var isCritical = true fun withThrowable(throwable: Throwable?): ApiError { this.throwable = throwable @@ -36,4 +37,9 @@ class ApiError(val tag: String, val errorCode: Int) { this.request = response?.request() return this } + + fun setCritical(isCritical: Boolean): ApiError { + this.isCritical = isCritical + return this + } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 467c4ff9..cf0424e2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -38,10 +38,26 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) * A method which may be overridden in child Data* classes. * * Calling it should populate [loginMethods] with all - * already available login methods (e.g. non-expired OAuth token). + * already available login methods (e.g. a non-expired OAuth token). */ open fun satisfyLoginMethods() {} + /** + * A list of Login method IDs that are still pending + * to run. + */ + var targetLoginMethodIds = mutableListOf() + /** + * A list of endpoint IDs that are still pending + * to run. + */ + var targetEndpointIds = mutableListOf() + + /** + * A map of endpoint IDs to JSON objects, specifying their arguments bundle. + */ + var endpointArgs = mutableMapOf() + val teacherList = LongSparseArray() val subjectList = LongSparseArray() val teamList = mutableListOf() @@ -106,6 +122,9 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) if (profile == null) return + db.profileDao().add(profile) + db.loginStoreDao().add(loginStore) + if (teacherList.isNotEmpty()) { val tempList: ArrayList = ArrayList() teacherList.forEach { _, teacher -> diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/fragments/HomeFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/fragments/HomeFragment.java index b4689a40..c2f660f0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/fragments/HomeFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/fragments/HomeFragment.java @@ -35,6 +35,8 @@ import com.mikepenz.iconics.IconicsDrawable; import com.mikepenz.iconics.IconicsSize; import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial; +import org.greenrobot.eventbus.EventBus; + import java.util.ArrayList; import java.util.List; @@ -44,6 +46,8 @@ import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.api.AppError; import pl.szczodrzynski.edziennik.api.interfaces.SyncCallback; +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest; +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncViewRequest; import pl.szczodrzynski.edziennik.api.v2.librus.LibrusOld; import pl.szczodrzynski.edziennik.api.v2.librus.LibrusTest; import pl.szczodrzynski.edziennik.databinding.CardLuckyNumberBinding; @@ -67,6 +71,8 @@ import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem; import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem; import static pl.szczodrzynski.edziennik.App.UPDATES_ON_PLAY_STORE; +import static pl.szczodrzynski.edziennik.MainActivity.DRAWER_ITEM_GRADES; +import static pl.szczodrzynski.edziennik.api.v2.FeaturesKt.FEATURE_STUDENT_INFO; import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER1_FINAL; import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER1_PROPOSED; import static pl.szczodrzynski.edziennik.datamodels.Grade.TYPE_SEMESTER2_FINAL; @@ -128,6 +134,23 @@ public class HomeFragment extends Fragment { test.go(); })); + b.test2.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); + b.test2.setOnClickListener((v -> { + List list = new ArrayList<>(); + list.add(FEATURE_STUDENT_INFO); + EventBus.getDefault().post(new SyncProfileRequest(16, list)); + })); + + b.test3.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); + b.test3.setOnClickListener((v -> { + EventBus.getDefault().post(new SyncProfileRequest(16, null)); + })); + + b.test4.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); + b.test4.setOnClickListener((v -> { + EventBus.getDefault().post(new SyncViewRequest(16, DRAWER_ITEM_GRADES)); + })); + //((TextView)v.findViewById(R.id.nextSync)).setText(getString(R.string.next_sync_format,Time.fromMillis(app.appJobs.syncJobTime).getStringHMS())); @@ -559,7 +582,7 @@ public class HomeFragment extends Fragment { Button cardGradesButton = root.findViewById(R.id.cardGradesButton); buttonAddDrawable(c, cardGradesButton, CommunityMaterial.Icon.cmd_arrow_right); cardGradesButton.setOnClickListener((v1 -> new Handler().postDelayed(() -> a.runOnUiThread(() -> { - activity.loadTarget(MainActivity.DRAWER_ITEM_GRADES, null); + activity.loadTarget(DRAWER_ITEM_GRADES, null); }), 100))); //new Handler().postDelayed(() -> a.runOnUiThread(() -> updateCardGrades(c, a, root)), newRefreshInterval); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt new file mode 100644 index 00000000..540ea73d --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-1. + */ + +package pl.szczodrzynski.edziennik.receivers + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import org.greenrobot.eventbus.EventBus +import pl.szczodrzynski.edziennik.api.v2.events.requests.ServiceCloseRequest +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest +import pl.szczodrzynski.edziennik.api.v2.events.requests.TaskCancelRequest + +class SzkolnyReceiver : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + when (intent?.extras?.getString("task", null)) { + "ServiceCloseRequest" -> EventBus.getDefault().post(ServiceCloseRequest()) + "TaskCancelRequest" -> EventBus.getDefault().post(TaskCancelRequest(intent.extras?.getInt("taskId", -1) ?: return)) + "SyncRequest" -> EventBus.getDefault().post(SyncRequest()) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 768c0861..964ab259 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -55,7 +55,34 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp" - android:text="Test Librus" + android:text="Start Service" + android:visibility="gone"/> + + + + + + diff --git a/app/src/main/res/values/plurals.xml b/app/src/main/res/values/plurals.xml index b933e932..8e7275b2 100644 --- a/app/src/main/res/values/plurals.xml +++ b/app/src/main/res/values/plurals.xml @@ -67,4 +67,18 @@ %1$s - %2$d nieprzeczytane %1$s - %2$d nieprzeczytanych + + + %d błąd krytyczny + %d błędy krytyczne + %d błędów krytycznych + %d błędów krytycznych + + + + %d błąd + %d błędy + %d błędów + %d błędów + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2559d0d9..f8cf2a36 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -912,4 +912,13 @@ Logowanie do API Logowanie do Librus Synergia Logowanie do wiadomości Librus + Usługa synchronizacji + Dzięki niej, aplikacja Szkolny.eu może synchronizować dane z e-dziennikiem. Możesz ją zamknąć, ponieważ w tej chwili nic nie robi. + Zamknij + Anuluj + Trwa synchronizacja profilu %s... + Synchronizacja przerwana + Zgłaszanie błędów... + Zgłaszanie błędów + Pobieram informacje o uczniu... From 23a9f24d52c2a8866375c87e8f43e6f214e27cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Thu, 3 Oct 2019 21:10:21 +0200 Subject: [PATCH 017/691] Fix compilation issues after merge --- .../edziennik/data/api/Edziennik.java | 2 +- .../edziennik/data/api/Librus.java | 3 --- .../ui/modules/home/HomeFragment.java | 26 ++++++++++++++++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java index 99ab1a4b..328aaa95 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java @@ -676,7 +676,7 @@ public class Edziennik { } }; AsyncTask.execute(() -> { - ProfileFull profile = app.db.profileDao().getByIdNow(profileId); + ProfileFull profile = app.db.profileDao().getFullByIdNow(profileId); if (profile != null) { if (profile.getArchived()) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java index ff04e59e..cba9b250 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java @@ -51,7 +51,6 @@ import pl.szczodrzynski.edziennik.data.api.interfaces.LoginCallback; import pl.szczodrzynski.edziennik.data.api.interfaces.MessageGetCallback; import pl.szczodrzynski.edziennik.data.api.interfaces.RecipientListGetCallback; import pl.szczodrzynski.edziennik.data.api.interfaces.SyncCallback; -import pl.szczodrzynski.edziennik.data.api.v2.models.DataStore; import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement; import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance; import pl.szczodrzynski.edziennik.data.db.modules.events.Event; @@ -216,8 +215,6 @@ public class Librus implements EdziennikInterface { this.fullSync = profile == null || profile.getEmpty() || profile.shouldFullSync(activityContext); this.today = Date.getToday(); - DataStore ds = new DataStore(app.db, profileId); - this.librusEmail = loginStore.getLoginData("email", ""); this.librusPassword = loginStore.getLoginData("password", ""); if (profile == null) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java index 24df37d0..bcf3a440 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java @@ -34,6 +34,8 @@ import com.mikepenz.iconics.IconicsDrawable; import com.mikepenz.iconics.IconicsSize; import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial; +import org.greenrobot.eventbus.EventBus; + import java.util.ArrayList; import java.util.List; @@ -41,6 +43,8 @@ import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest; +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncViewRequest; import pl.szczodrzynski.edziennik.databinding.CardLuckyNumberBinding; import pl.szczodrzynski.edziennik.databinding.CardUpdateBinding; import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding; @@ -60,6 +64,8 @@ import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem; import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem; import static pl.szczodrzynski.edziennik.App.UPDATES_ON_PLAY_STORE; +import static pl.szczodrzynski.edziennik.MainActivity.DRAWER_ITEM_GRADES; +import static pl.szczodrzynski.edziennik.api.v2.FeaturesKt.FEATURE_STUDENT_INFO; import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER1_FINAL; import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER1_PROPOSED; import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER2_FINAL; @@ -113,9 +119,27 @@ public class HomeFragment extends Fragment { startActivity(new Intent(activity, MessagesComposeActivity.class)); })); + b.test2.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); + b.test2.setOnClickListener((v -> { + List list = new ArrayList<>(); + list.add(FEATURE_STUDENT_INFO); + EventBus.getDefault().post(new SyncProfileRequest(16, list)); + })); + + b.test3.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); + b.test3.setOnClickListener((v -> { + EventBus.getDefault().post(new SyncProfileRequest(16, null)); + })); + + b.test4.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); + b.test4.setOnClickListener((v -> { + EventBus.getDefault().post(new SyncViewRequest(16, DRAWER_ITEM_GRADES)); + })); + //((TextView)v.findViewById(R.id.nextSync)).setText(getString(R.string.next_sync_format,Time.fromMillis(app.appJobs.syncJobTime).getStringHMS())); + LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); ViewGroup insertPoint = b.cardInsertPoint; assert layoutInflater != null; @@ -544,7 +568,7 @@ public class HomeFragment extends Fragment { Button cardGradesButton = root.findViewById(R.id.cardGradesButton); buttonAddDrawable(c, cardGradesButton, CommunityMaterial.Icon.cmd_arrow_right); cardGradesButton.setOnClickListener((v1 -> new Handler().postDelayed(() -> a.runOnUiThread(() -> { - activity.loadTarget(MainActivity.DRAWER_ITEM_GRADES, null); + activity.loadTarget(DRAWER_ITEM_GRADES, null); }), 100))); //new Handler().postDelayed(() -> a.runOnUiThread(() -> updateCardGrades(c, a, root)), newRefreshInterval); From 870a429f3d9250cd6abe2c608dd7ee1d5d400dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Thu, 3 Oct 2019 22:16:16 +0200 Subject: [PATCH 018/691] Add back Librus test --- .../edziennik/ui/modules/home/HomeFragment.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java index bcf3a440..69fb2ac2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java @@ -45,6 +45,7 @@ import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest; import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncViewRequest; +import pl.szczodrzynski.edziennik.api.v2.librus.LibrusTest; import pl.szczodrzynski.edziennik.databinding.CardLuckyNumberBinding; import pl.szczodrzynski.edziennik.databinding.CardUpdateBinding; import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding; @@ -119,6 +120,13 @@ public class HomeFragment extends Fragment { startActivity(new Intent(activity, MessagesComposeActivity.class)); })); + LibrusTest librusTest = new LibrusTest(app); + + b.testButton.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); + b.testButton.setOnClickListener((v -> { + librusTest.go(); + })); + b.test2.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); b.test2.setOnClickListener((v -> { List list = new ArrayList<>(); From c8c933fb20ef9bed0af1ee20064e175aa6479243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 4 Oct 2019 17:14:56 +0200 Subject: [PATCH 019/691] [APIv2] Implement endpoint timers. Fix compilation issues after merge. --- .idea/misc.xml | 5 ++ .../pl/szczodrzynski/edziennik/Extensions.kt | 9 +- .../edziennik/api/v2/Endpoints.kt | 88 ++++++++++--------- .../edziennik/api/v2/librus/Librus.kt | 29 +++--- .../api/v2/librus/LibrusEndpoints.kt | 2 +- .../api/v2/librus/data/DataLibrus.kt | 1 + .../librus/data/{ => api}/LibrusApiGrades.kt | 2 +- .../v2/librus/data/{ => api}/LibrusApiMe.kt | 8 +- .../{ => synergia}/LibrusSynergiaGrades.kt | 2 +- .../edziennik/api/v2/models/Data.kt | 51 +++++++++++ .../api/v2/models/DataRemoveModel.kt | 26 ++++++ .../edziennik/api/v2/models/Endpoint.kt | 2 +- .../edziennik/data/db/AppDb.java | 22 ++++- .../data/db/modules/api/EndpointTimer.kt | 81 +++++++++++++++++ .../data/db/modules/api/EndpointTimerDao.kt | 26 ++++++ .../db/modules/teachers/TeacherAbsenceDao.kt | 5 +- .../ui/modules/agenda/AgendaFragment.java | 2 +- 17 files changed, 298 insertions(+), 63 deletions(-) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/{ => api}/LibrusApiGrades.kt (91%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/{ => api}/LibrusApiMe.kt (73%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/{ => synergia}/LibrusSynergiaGrades.kt (90%) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataRemoveModel.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/api/EndpointTimer.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/api/EndpointTimerDao.kt diff --git a/.idea/misc.xml b/.idea/misc.xml index f834a4ed..4ccf11f8 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,6 +5,11 @@ + + + + + + + + + + diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/App.java b/app/src/main/java/pl/szczodrzynski/edziennik/App.java index 5596a664..374738e1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/App.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/App.java @@ -86,6 +86,7 @@ import pl.szczodrzynski.edziennik.utils.PermissionChecker; import pl.szczodrzynski.edziennik.utils.Themes; import pl.szczodrzynski.edziennik.utils.Utils; +import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_LIBRUS; import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK; import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_VULCAN; @@ -409,20 +410,20 @@ public class App extends androidx.multidex.MultiDexApplication { FirebaseApp pushMobidziennikApp = FirebaseApp.initializeApp( this, new FirebaseOptions.Builder() - .setApplicationId("1:1029629079999:android:58bb378dab031f42") - .setGcmSenderId("1029629079999") + .setApiKey("AIzaSyCi5LmsZ5BBCQnGtrdvWnp1bWLCNP8OWQE") + .setApplicationId("1:747285019373:android:f6341bf7b158621d") .build(), - "Mobidziennik" + "Mobidziennik2" ); - /*FirebaseApp pushLibrusApp = FirebaseApp.initializeApp( + FirebaseApp pushLibrusApp = FirebaseApp.initializeApp( this, new FirebaseOptions.Builder() .setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o") .setApplicationId("1:513056078587:android:1e29083b760af544") .build(), "Librus" - );*/ + ); FirebaseApp pushVulcanApp = FirebaseApp.initializeApp( this, @@ -443,10 +444,10 @@ public class App extends androidx.multidex.MultiDexApplication { Log.d(TAG, "Token for Mobidziennik is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId()); appConfig.fcmTokens.put(LOGIN_TYPE_MOBIDZIENNIK, new Pair<>(instanceIdResult.getToken(), new ArrayList<>())); }); - /*FirebaseInstanceId.getInstance(pushLibrusApp).getInstanceId().addOnSuccessListener(instanceIdResult -> { + FirebaseInstanceId.getInstance(pushLibrusApp).getInstanceId().addOnSuccessListener(instanceIdResult -> { Log.d(TAG, "Token for Librus is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId()); appConfig.fcmTokens.put(LOGIN_TYPE_LIBRUS, new Pair<>(instanceIdResult.getToken(), new ArrayList<>())); - });*/ + }); FirebaseInstanceId.getInstance(pushVulcanApp).getInstanceId().addOnSuccessListener(instanceIdResult -> { Log.d(TAG, "Token for Vulcan is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId()); Pair> pair = appConfig.fcmTokens.get(LOGIN_TYPE_VULCAN); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index fbddf815..96b1cf26 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -218,4 +218,10 @@ fun LongSparseArray.getById(id: Long): Team? { return value } return null +} + +operator fun MatchResult.get(group: Int): String { + if (group >= groupValues.size) + return "" + return groupValues[group] } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Features.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Features.kt index 33bbe2f9..4d0c94c8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Features.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Features.kt @@ -37,6 +37,7 @@ const val FEATURE_LUCKY_NUMBER = 105 const val FEATURE_TEACHERS = 106 const val FEATURE_SUBJECTS = 107 const val FEATURE_CLASSROOMS = 108 +const val FEATURE_PUSH_CONFIG = 120 const val FEATURE_MESSAGE_GET = 201 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt index 5d08354c..a16c265e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt @@ -83,7 +83,11 @@ const val LOGIN_METHOD_MOBIDZIENNIK_API2 = 300 val mobidziennikLoginMethods = listOf( LoginMethod(LOGIN_TYPE_MOBIDZIENNIK, LOGIN_METHOD_MOBIDZIENNIK_WEB, MobidziennikLoginWeb::class.java) .withIsPossible { _, _ -> true } - .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED } + .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }/*, + + LoginMethod(LOGIN_TYPE_MOBIDZIENNIK, LOGIN_METHOD_MOBIDZIENNIK_API2, MobidziennikLoginApi2::class.java) + .withIsPossible { _, loginStore -> loginStore.hasLoginData("email") } + .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }*/ ) const val LOGIN_TYPE_VULCAN = 4 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt index 769b4787..ab0f4260 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt @@ -6,15 +6,34 @@ package pl.szczodrzynski.edziennik.api.v2 object Regexes { val MOBIDZIENNIK_GRADES_SUBJECT_NAME by lazy { - "\\n*\\s*(.+?)\\s*\\n*(?:<.*?)??".toRegex(RegexOption.DOT_MATCHES_ALL) + """\n*\s*(.+?)\s*\n*(?:<.*?)??""".toRegex(RegexOption.DOT_MATCHES_ALL) } + val MOBIDZIENNIK_GRADES_COLOR by lazy { + """background-color:([#A-Fa-f0-9]+);""".toRegex(RegexOption.DOT_MATCHES_ALL) + } + val MOBIDZIENNIK_GRADES_CATEGORY by lazy { + """> (.+?):""".toRegex(RegexOption.DOT_MATCHES_ALL) + } + val MOBIDZIENNIK_GRADES_CLASS_AVERAGE by lazy { + """Średnia ocen:.*([0-9]*\.?[0-9]*)""".toRegex(RegexOption.DOT_MATCHES_ALL) + } + val MOBIDZIENNIK_GRADES_ADDED_DATE by lazy { + """Wpisano:.*.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(RegexOption.DOT_MATCHES_ALL) + } + val MOBIDZIENNIK_GRADES_COUNT_TO_AVG by lazy { + """Liczona do średniej:.*?nie
""".toRegex(RegexOption.DOT_MATCHES_ALL) + } + val MOBIDZIENNIK_GRADES_DETAILS by lazy { + """(.+?).*?.+?.*?\((.+?)\).*?.*?Wartość oceny:.*?([0-9.]+).*?Wpisał\(a\):.*?(.+?)""".toRegex(RegexOption.DOT_MATCHES_ALL) + } + val MOBIDZIENNIK_EVENT_TYPE by lazy { - "\\(([0-9A-ząęóżźńśłć]*?)\\)$".toRegex(RegexOption.DOT_MATCHES_ALL) + """\(([0-9A-ząęóżźńśłć]*?)\)$""".toRegex(RegexOption.DOT_MATCHES_ALL) } val MOBIDZIENNIK_LUCKY_NUMBER by lazy { - "class=\"szczesliwy_numerek\".*>0*([0-9]+)(?:/0*[0-9]+)*".toRegex(RegexOption.DOT_MATCHES_ALL) + """class="szczesliwy_numerek".*>0*([0-9]+)(?:/0*[0-9]+)*""".toRegex(RegexOption.DOT_MATCHES_ALL) } val MOBIDZIENNIK_CLASS_CALENDAR by lazy { - "events: (.+),$".toRegex(RegexOption.DOT_MATCHES_ALL) + """events: (.+),$""".toRegex(RegexOption.MULTILINE) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt index 89ce94a7..497d4845 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt @@ -77,8 +77,7 @@ open class LibrusApi(open val data: DataLibrus) { data.error(ApiError(tag, EXCEPTION_LIBRUS_API_REQUEST) .withResponse(response) .withThrowable(e) - .withApiResponse(json) - .setCritical(false)) + .withApiResponse(json)) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/DataMobidziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/DataMobidziennik.kt index f4232e94..d012186f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/DataMobidziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/DataMobidziennik.kt @@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik import android.util.LongSparseArray import android.util.SparseArray import android.util.SparseIntArray +import androidx.core.util.isNotEmpty import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_MOBIDZIENNIK_WEB import pl.szczodrzynski.edziennik.api.v2.models.Data @@ -34,15 +35,20 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da val teachersMap = LongSparseArray() val subjectsMap = LongSparseArray() - val gradeAddedDates = SparseArray() - val gradeAverages = SparseArray() - val gradeColors = SparseIntArray() + val gradeAddedDates = LongSparseArray() + val gradeAverages = LongSparseArray() + val gradeColors = LongSparseArray() private var mLoginServerName: String? = null var loginServerName: String? get() { mLoginServerName = mLoginServerName ?: loginStore.getLoginData("serverName", null); return mLoginServerName } set(value) { loginStore.putLoginData("serverName", value); mLoginServerName = value } + private var mLoginEmail: String? = null + var loginEmail: String? + get() { mLoginEmail = mLoginEmail ?: loginStore.getLoginData("email", null); return mLoginEmail } + set(value) { loginStore.putLoginData("email", value); mLoginEmail = value } + private var mLoginUsername: String? = null var loginUsername: String? get() { mLoginUsername = mLoginUsername ?: loginStore.getLoginData("username", null); return mLoginUsername } @@ -85,6 +91,13 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da set(value) { loginStore.putLoginData("sessionIDTime", value); mWebSessionIdExpiryTime = value } + override fun saveData() { + super.saveData() + if (gradeAddedDates.isNotEmpty()) { + app.db.gradeDao().updateDetails(profileId, gradeAverages, gradeAddedDates, gradeColors) + } + } + val mobiLessons = mutableListOf() data class MobiLesson( diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt index a7c31d48..660f846f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt @@ -7,7 +7,7 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.models.Feature -const val ENDPOINT_MOBIDZIENNIK_API_MAIN = 1000 +const val ENDPOINT_MOBIDZIENNIK_API_MAIN = 1000 const val ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX = 2011 const val ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_SENT = 2012 const val ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL = 2019 @@ -16,7 +16,8 @@ const val ENDPOINT_MOBIDZIENNIK_WEB_GRADES = 2030 const val ENDPOINT_MOBIDZIENNIK_WEB_NOTICES = 2040 const val ENDPOINT_MOBIDZIENNIK_WEB_ATTENDANCE = 2050 const val ENDPOINT_MOBIDZIENNIK_WEB_MANUALS = 2100 -const val ENDPOINT_MOBIDZIENNIK_API2 = 3000 +const val ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL = 2200 +const val ENDPOINT_MOBIDZIENNIK_API2_MAIN = 3000 val MobidziennikFeatures = listOf( // timetable @@ -55,8 +56,13 @@ val MobidziennikFeatures = listOf( ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ALWAYS_NEEDED, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB + ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB, + ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL to LOGIN_METHOD_MOBIDZIENNIK_WEB ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), + + Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_PUSH_CONFIG, listOf( + ENDPOINT_MOBIDZIENNIK_API2_MAIN to LOGIN_METHOD_MOBIDZIENNIK_API2 + ), listOf(LOGIN_METHOD_MOBIDZIENNIK_API2)), /*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_STUDENT_INFO, listOf( ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt index b9e2ac5a..72e0ae59 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt @@ -8,6 +8,7 @@ import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.mobidziennik.* import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikApi import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebCalendar +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebGrades import pl.szczodrzynski.edziennik.utils.Utils class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) { @@ -51,11 +52,11 @@ class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) { ENDPOINT_MOBIDZIENNIK_WEB_CALENDAR -> { data.startProgress(R.string.edziennik_progress_endpoint_calendar) MobidziennikWebCalendar(data) { onSuccess() } - }/* + } ENDPOINT_MOBIDZIENNIK_WEB_GRADES -> { data.startProgress(R.string.edziennik_progress_endpoint_grades) MobidziennikWebGrades(data) { onSuccess() } - } + }/* ENDPOINT_MOBIDZIENNIK_WEB_NOTICES -> { data.startProgress(R.string.edziennik_progress_endpoint_behaviour) MobidziennikWebNotices(data) { onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikWeb.kt index 28afc3cc..67828b73 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikWeb.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikWeb.kt @@ -62,8 +62,7 @@ open class MobidziennikWeb(open val data: DataMobidziennik) { data.error(ApiError(tag, EXCEPTION_MOBIDZIENNIK_WEB_REQUEST) .withResponse(response) .withThrowable(e) - .withApiResponse(text) - .setCritical(false)) + .withApiResponse(text)) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikApi.kt index f9cbbd9b..c4eaf860 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikApi.kt @@ -4,11 +4,15 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web +import pl.szczodrzynski.edziennik.DAY +import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_ME import pl.szczodrzynski.edziennik.api.v2.ERROR_MOBIDZIENNIK_WEB_INVALID_RESPONSE import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_API_MAIN import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata.* import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS class MobidziennikApi(override val data: DataMobidziennik, val onSuccess: () -> Unit) : MobidziennikWeb(data) { @@ -45,6 +49,7 @@ class MobidziennikApi(override val data: DataMobidziennik, } } + data.setSyncNext(ENDPOINT_MOBIDZIENNIK_API_MAIN, SYNC_ALWAYS) onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebCalendar.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebCalendar.kt index d871952f..94f0a5e4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebCalendar.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebCalendar.kt @@ -5,9 +5,12 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web import com.google.gson.JsonParser +import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_ME import pl.szczodrzynski.edziennik.api.v2.Regexes import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_CALENDAR import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.events.Event import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.getString @@ -91,6 +94,8 @@ class MobidziennikWebCalendar(override val data: DataMobidziennik, )) } } + + data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_CALENDAR, SYNC_ALWAYS) onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebGrades.kt new file mode 100644 index 00000000..2a24b436 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebGrades.kt @@ -0,0 +1,154 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-10. + */ + +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web + +import android.graphics.Color +import com.google.gson.JsonParser +import org.jsoup.Jsoup +import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_ME +import pl.szczodrzynski.edziennik.api.v2.Regexes +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_GRADES +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.events.Event +import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.fixWhiteSpaces +import pl.szczodrzynski.edziennik.get +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.singleOrNull +import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.Utils.strToInt +import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Time +import java.util.* +import java.util.regex.Pattern + +class MobidziennikWebGrades(override val data: DataMobidziennik, + val onSuccess: () -> Unit) : MobidziennikWeb(data) { + companion object { + private const val TAG = "MobidziennikWebGrades" + } + + init { + webGet(TAG, "/dziennik/oceny?semestr=${profile?.currentSemester ?: 1}") { text -> + MobidziennikLuckyNumberExtractor(data, text) + + val doc = Jsoup.parse(text) + + val grades = doc.select("table.spis a, table.spis span, table.spis div") + + var gradeCategory = "" + var gradeColor = -1 + var subjectName = "" + + for (e in grades) { + when (e.tagName()) { + "div" -> { + Regexes.MOBIDZIENNIK_GRADES_SUBJECT_NAME.find(e.outerHtml())?.let { + subjectName = it[1] + } + } + "span" -> { + val css = e.attr("style") + Regexes.MOBIDZIENNIK_GRADES_COLOR.find(css)?.let { + // (#2196f3) + gradeColor = Color.parseColor(it[1]) + } + Regexes.MOBIDZIENNIK_GRADES_CATEGORY.find(e.outerHtml())?.let { + // (category) + gradeCategory = it[1] + } + } + "a" -> { + val gradeId = e.attr("rel").toLong() + var gradeAddedDateMillis: Long = -1 + var gradeSemester = 1 + + val html = e.html() + val gradeClassAverage = Regexes.MOBIDZIENNIK_GRADES_CLASS_AVERAGE.find(html)?.let { + // (4.75) + it[1].toFloatOrNull() + } ?: -1f + + Regexes.MOBIDZIENNIK_GRADES_ADDED_DATE.find(html)?.let { + // (2) (stycznia) (2019), (12:34:56) + val month = when (it[2]) { + "stycznia" -> 1 + "lutego" -> 2 + "marca" -> 3 + "kwietnia" -> 4 + "maja" -> 5 + "czerwca" -> 6 + "lipca" -> 7 + "sierpnia" -> 8 + "września" -> 9 + "października" -> 10 + "listopada" -> 11 + "grudnia" -> 12 + else -> 1 + } + val gradeAddedDate = Date( + it[3].toInt(), + month, + it[1].toInt() + ) + val time = Time.fromH_m_s(it[4]) + gradeAddedDateMillis = gradeAddedDate.combineWith(time) + gradeSemester = profile?.dateToSemester(gradeAddedDate) ?: 1 + } + + if (Regexes.MOBIDZIENNIK_GRADES_COUNT_TO_AVG.containsMatchIn(html)) { + Regexes.MOBIDZIENNIK_GRADES_DETAILS.find(html)?.let { match -> + val gradeName = match[1] + val gradeDescription = match[2] + val gradeValue = match[3].toFloatOrNull() ?: 0.0f + val teacherName = match[4].fixWhiteSpaces() + + val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == teacherName }?.id ?: -1 + val subjectId = data.subjectList.singleOrNull { it.longName == subjectName }?.id ?: -1 + + val gradeObject = Grade( + profileId, + gradeId, + gradeCategory, + gradeColor, + "NLDŚR, $gradeDescription", + gradeName, + gradeValue, + 0f, + gradeSemester, + teacherId, + subjectId + ) + + gradeObject.classAverage = gradeClassAverage + + data.gradeList.add(gradeObject) + data.metadataList.add( + Metadata( + profileId, + Metadata.TYPE_GRADE, + gradeObject.id, + profile?.empty ?: false, + profile?.empty ?: false, + gradeAddedDateMillis + )) + } + } else { + data.gradeAverages.put(gradeId, gradeClassAverage) + data.gradeAddedDates.put(gradeId, gradeAddedDateMillis) + data.gradeColors.put(gradeId, gradeColor) + } + } + } + } + + data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_GRADES, SYNC_ALWAYS) + onSuccess() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiGrades.kt index 05eb9663..056444d6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiGrades.kt @@ -23,7 +23,7 @@ class MobidziennikApiGrades(val data: DataMobidziennik, rows: List) { continue val cols = row.split("|") - val studentId = cols[2].toInt() + val studentId = cols[1].toInt() if (studentId != data.studentId) return@run @@ -35,7 +35,7 @@ class MobidziennikApiGrades(val data: DataMobidziennik, rows: List) { val semester = cols[5].toInt() val teacherId = cols[2].toLong() val subjectId = cols[3].toLong() - var type = when (cols[8]) { + val type = when (cols[8]) { "3" -> if (semester == 1) TYPE_SEMESTER1_PROPOSED else TYPE_SEMESTER2_PROPOSED "1" -> if (semester == 1) TYPE_SEMESTER1_FINAL else TYPE_SEMESTER2_FINAL "4" -> TYPE_YEAR_PROPOSED @@ -70,7 +70,7 @@ class MobidziennikApiGrades(val data: DataMobidziennik, rows: List) { weight, semester, teacherId, - subjectId) + subjectId);gradeObject.type = type data.gradeList.add(gradeObject) data.metadataList.add( diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiNotices.kt index 416d3f6d..87519aad 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiNotices.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiNotices.kt @@ -29,6 +29,7 @@ class MobidziennikApiNotices(val data: DataMobidziennik, rows: List) { val type = when (cols[3]) { "0" -> Notice.TYPE_NEGATIVE "1" -> Notice.TYPE_POSITIVE + "3" -> Notice.TYPE_NEUTRAL else -> Notice.TYPE_NEUTRAL } val teacherId = cols[5].toLong() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLogin.kt index 58f94e36..4baf451c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLogin.kt @@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.login import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_MOBIDZIENNIK_API2 import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_MOBIDZIENNIK_WEB import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.utils.Utils @@ -48,6 +49,10 @@ class MobidziennikLogin(val data: DataMobidziennik, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_login_mobidziennik_web) MobidziennikLoginWeb(data) { onSuccess(loginMethodId) } } + LOGIN_METHOD_MOBIDZIENNIK_API2 -> { + data.startProgress(R.string.edziennik_progress_login_mobidziennik_api2) + //MobidziennikLoginApi2(data) { onSuccess(loginMethodId) } + } } } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index b85fed4d..dd4e7fe9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -190,7 +190,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) messageMetadataList.clear() } - fun saveData() { + open fun saveData() { if (profile == null) return diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Mobidziennik.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Mobidziennik.java index 4a3d191f..785ab5ff 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Mobidziennik.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Mobidziennik.java @@ -5,6 +5,7 @@ import android.graphics.Color; import android.os.AsyncTask; import android.os.Handler; import android.text.Html; +import android.util.LongSparseArray; import android.util.Pair; import android.util.SparseArray; import android.util.SparseIntArray; @@ -128,9 +129,9 @@ public class Mobidziennik implements EdziennikInterface { private List subjectList; private List lessonList; private List lessonChangeList; - private SparseArray gradeAddedDates; - private SparseArray gradeAverages; - private SparseIntArray gradeColors; + private LongSparseArray gradeAddedDates; + private LongSparseArray gradeAverages; + private LongSparseArray gradeColors; private List gradeList; private List eventList; private List noticeList; @@ -179,9 +180,9 @@ public class Mobidziennik implements EdziennikInterface { subjectList = new ArrayList<>(); lessonList = new ArrayList<>(); lessonChangeList = new ArrayList<>(); - gradeAddedDates = new SparseArray<>(); - gradeAverages = new SparseArray<>(); - gradeColors = new SparseIntArray(); + gradeAddedDates = new LongSparseArray<>(); + gradeAverages = new LongSparseArray<>(); + gradeColors = new LongSparseArray<>(); gradeList = new ArrayList<>(); eventList = new ArrayList<>(); noticeList = new ArrayList<>(); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/grades/GradeDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/grades/GradeDao.java index 4487317b..5765d235 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/grades/GradeDao.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/grades/GradeDao.java @@ -9,6 +9,8 @@ import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.RawQuery; import androidx.room.Transaction; + +import android.util.LongSparseArray; import android.util.SparseArray; import android.util.SparseIntArray; @@ -88,15 +90,15 @@ public abstract class GradeDao { } @Query("UPDATE grades SET gradeClassAverage = :classAverage, gradeColor = :color WHERE profileId = :profileId AND gradeId = :gradeId") - public abstract void updateDetailsById(int profileId, int gradeId, float classAverage, int color); + public abstract void updateDetailsById(int profileId, long gradeId, float classAverage, int color); @Query("UPDATE metadata SET addedDate = :addedDate WHERE profileId = :profileId AND thingType = "+TYPE_GRADE+" AND thingId = :gradeId") - public abstract void updateAddedDateById(int profileId, int gradeId, long addedDate); + public abstract void updateAddedDateById(int profileId, long gradeId, long addedDate); @Transaction - public void updateDetails(int profileId, SparseArray gradeAverages, SparseArray gradeAddedDates, SparseIntArray gradeColors) { + public void updateDetails(int profileId, LongSparseArray gradeAverages, LongSparseArray gradeAddedDates, LongSparseArray gradeColors) { for (int i = 0; i < gradeAverages.size(); i++) { - int gradeId = gradeAverages.keyAt(i); + long gradeId = gradeAverages.keyAt(i); float classAverage = gradeAverages.valueAt(i); long addedDate = gradeAddedDates.valueAt(i); int color = gradeColors.valueAt(i); @@ -114,7 +116,7 @@ public abstract class GradeDao { @Query("SELECT addedDate FROM metadata WHERE profileId = :profileId AND thingType = "+TYPE_GRADE+" ORDER BY thingId") public abstract List getAddedDates(int profileId); @Transaction - public void getDetails(int profileId, SparseArray gradeAddedDates, SparseArray gradeAverages, SparseIntArray gradeColors) { + public void getDetails(int profileId, LongSparseArray gradeAddedDates, LongSparseArray gradeAverages, LongSparseArray gradeColors) { List ids = getIds(profileId); List classAverages = getClassAverages(profileId); List colors = getColors(profileId); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/login/LoginStore.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/login/LoginStore.java index e32dc143..f3d1b585 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/login/LoginStore.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/login/LoginStore.java @@ -63,6 +63,11 @@ public class LoginStore { } } } + public boolean hasLoginData(String key) { + if (data == null) + return false; + return data.has(key); + } @Nullable public String getLoginData(String key, @Nullable String defaultValue) { if (data == null) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/FirebaseBroadcastReceiver.kt b/app/src/main/java/pl/szczodrzynski/edziennik/sync/FirebaseBroadcastReceiver.kt new file mode 100644 index 00000000..cc437ad6 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/sync/FirebaseBroadcastReceiver.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-11. + */ + +package pl.szczodrzynski.edziennik.sync + +import android.content.Context +import android.content.Intent +import android.util.Log +import androidx.legacy.content.WakefulBroadcastReceiver +import com.google.firebase.messaging.RemoteMessage +import com.google.gson.JsonObject +import pl.szczodrzynski.edziennik.App + +class FirebaseBroadcastReceiver : WakefulBroadcastReceiver() { + + val TAG: String = FirebaseBroadcastReceiver::class.java.simpleName + + override fun onReceive(context: Context, intent: Intent) { + + val json = JsonObject() + + val dataBundle = intent.extras + if (dataBundle != null) + for (key in dataBundle.keySet()) { + dataBundle.get(key)?.let { + when (it) { + is String -> json.addProperty(key, it) + is Int -> json.addProperty(key, it) + is Long -> json.addProperty(key, it) + is Float -> json.addProperty(key, it) + is Boolean -> json.addProperty(key, it) + else -> json.addProperty(key, it.toString()) + } + } + } + + Log.d(TAG, "Firebase got push from Librus Broadcast ${json}") + + val sharedPreferences = context.getSharedPreferences("pushtest_broadcast", Context.MODE_PRIVATE) + sharedPreferences.edit().putString( + System.currentTimeMillis().toString(), + json.toString() + ).apply() + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java b/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java index d8841ebf..feae360f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java @@ -61,7 +61,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { //processAppPush processAppPush(app, remoteMessage); break; - case "1029629079999": + case "747285019373": app.debugLog("Firebase got push from Mobidziennik "+remoteMessage.getData().toString()); processMobidziennikPush(app, remoteMessage); break; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a713e520..4ffc89a7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -938,4 +938,5 @@ Pobieram uwagi ucznia... Pobieram frekwencję ucznia... Logowanie do Vulcan API... + Logowanie do API MobiDziennika... From 67fbb96cd92d8374b98567eb652befe18d85e0d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 11 Oct 2019 16:42:24 +0200 Subject: [PATCH 037/691] [APIv2/Mobidziennik] Refactor data structure --- .../v2/mobidziennik/data/MobidziennikData.kt | 2 +- .../data/{web => api}/MobidziennikApi.kt | 7 +--- .../MobidziennikApiAttendance.kt | 7 +--- .../apidata => api}/MobidziennikApiDates.kt | 6 +-- .../apidata => api}/MobidziennikApiEvents.kt | 7 +--- .../MobidziennikApiGradeCategories.kt | 2 +- .../apidata => api}/MobidziennikApiGrades.kt | 2 +- .../MobidziennikApiHomework.kt | 7 +--- .../apidata => api}/MobidziennikApiLessons.kt | 2 +- .../apidata => api}/MobidziennikApiNotices.kt | 7 +--- .../apidata => api}/MobidziennikApiStudent.kt | 2 +- .../MobidziennikApiSubjects.kt | 2 +- .../apidata => api}/MobidziennikApiTeams.kt | 6 +-- .../MobidziennikApiTimetable.kt | 8 +--- .../apidata => api}/MobidziennikApiUsers.kt | 2 +- .../data/web/MobidziennikWebNotices.kt | 38 +++++++++++++++++++ .../ui/modules/behaviour/NoticesAdapter.kt | 2 +- 17 files changed, 62 insertions(+), 47 deletions(-) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web => api}/MobidziennikApi.kt (88%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web/apidata => api}/MobidziennikApiAttendance.kt (87%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web/apidata => api}/MobidziennikApiDates.kt (75%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web/apidata => api}/MobidziennikApiEvents.kt (90%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web/apidata => api}/MobidziennikApiGradeCategories.kt (94%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web/apidata => api}/MobidziennikApiGrades.kt (97%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web/apidata => api}/MobidziennikApiHomework.kt (86%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web/apidata => api}/MobidziennikApiLessons.kt (95%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web/apidata => api}/MobidziennikApiNotices.kt (85%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web/apidata => api}/MobidziennikApiStudent.kt (94%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web/apidata => api}/MobidziennikApiSubjects.kt (90%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web/apidata => api}/MobidziennikApiTeams.kt (89%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web/apidata => api}/MobidziennikApiTimetable.kt (94%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/{web/apidata => api}/MobidziennikApiUsers.kt (91%) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebNotices.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt index 72e0ae59..dfc42b60 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt @@ -6,7 +6,7 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.mobidziennik.* -import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikApi +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api.MobidziennikApi import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebCalendar import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebGrades import pl.szczodrzynski.edziennik.utils.Utils diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApi.kt similarity index 88% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikApi.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApi.kt index c4eaf860..4a4069ed 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApi.kt @@ -1,16 +1,13 @@ /* - * Copyright (c) Kuba Szczodrzyński 2019-10-6. + * Copyright (c) Kuba Szczodrzyński 2019-10-11. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api -import pl.szczodrzynski.edziennik.DAY -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_ME import pl.szczodrzynski.edziennik.api.v2.ERROR_MOBIDZIENNIK_WEB_INVALID_RESPONSE import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_API_MAIN import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb -import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata.* import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiAttendance.kt similarity index 87% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiAttendance.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiAttendance.kt index b3d2b509..a22093e7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiAttendance.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiAttendance.kt @@ -1,15 +1,12 @@ /* - * Copyright (c) Kuba Szczodrzyński 2019-10-7. + * Copyright (c) Kuba Szczodrzyński 2019-10-11. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api -import androidx.core.util.contains import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance.* -import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher -import pl.szczodrzynski.edziennik.utils.Utils.strToInt import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata class MobidziennikApiAttendance(val data: DataMobidziennik, rows: List) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiDates.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiDates.kt similarity index 75% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiDates.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiDates.kt index 1a79b0bf..c74f79cd 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiDates.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiDates.kt @@ -1,12 +1,10 @@ /* - * Copyright (c) Kuba Szczodrzyński 2019-10-6. + * Copyright (c) Kuba Szczodrzyński 2019-10-11. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api -import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik -import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher import pl.szczodrzynski.edziennik.utils.models.Date class MobidziennikApiDates(val data: DataMobidziennik, rows: List) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiEvents.kt similarity index 90% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiEvents.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiEvents.kt index 192d9d68..446b5576 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiEvents.kt @@ -1,17 +1,14 @@ /* - * Copyright (c) Kuba Szczodrzyński 2019-10-8. + * Copyright (c) Kuba Szczodrzyński 2019-10-11. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api -import android.graphics.Color import androidx.core.util.contains import pl.szczodrzynski.edziennik.api.v2.Regexes import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.db.modules.events.Event -import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata -import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Time import java.text.ParseException diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiGradeCategories.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiGradeCategories.kt similarity index 94% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiGradeCategories.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiGradeCategories.kt index 8b9ac6dc..d13c8ecd 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiGradeCategories.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiGradeCategories.kt @@ -2,7 +2,7 @@ * Copyright (c) Kuba Szczodrzyński 2019-10-7. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api import android.graphics.Color import androidx.core.util.contains diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiGrades.kt similarity index 97% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiGrades.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiGrades.kt index 056444d6..2587245f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiGrades.kt @@ -2,7 +2,7 @@ * Copyright (c) Kuba Szczodrzyński 2019-10-8. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiHomework.kt similarity index 86% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiHomework.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiHomework.kt index af597202..4e074f26 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiHomework.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiHomework.kt @@ -1,16 +1,13 @@ /* - * Copyright (c) Kuba Szczodrzyński 2019-10-8. + * Copyright (c) Kuba Szczodrzyński 2019-10-11. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api -import android.graphics.Color import androidx.core.util.contains import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.db.modules.events.Event -import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata -import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Time diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiLessons.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiLessons.kt similarity index 95% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiLessons.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiLessons.kt index 2fa318f9..ee0579c4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiLessons.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiLessons.kt @@ -2,7 +2,7 @@ * Copyright (c) Kuba Szczodrzyński 2019-10-7. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.utils.models.Date diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiNotices.kt similarity index 85% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiNotices.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiNotices.kt index 87519aad..5e9a4c23 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiNotices.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiNotices.kt @@ -1,15 +1,12 @@ /* - * Copyright (c) Kuba Szczodrzyński 2019-10-8. + * Copyright (c) Kuba Szczodrzyński 2019-10-11. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api -import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik -import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice -import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher import pl.szczodrzynski.edziennik.utils.models.Date class MobidziennikApiNotices(val data: DataMobidziennik, rows: List) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiStudent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiStudent.kt similarity index 94% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiStudent.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiStudent.kt index cc20bf1d..01f7ecc4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiStudent.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiStudent.kt @@ -2,7 +2,7 @@ * Copyright (c) Kuba Szczodrzyński 2019-10-6. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiSubjects.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiSubjects.kt similarity index 90% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiSubjects.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiSubjects.kt index 08b02f1e..541be25b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiSubjects.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiSubjects.kt @@ -2,7 +2,7 @@ * Copyright (c) Kuba Szczodrzyński 2019-10-6. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiTeams.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTeams.kt similarity index 89% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiTeams.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTeams.kt index d3823f1e..2d1d3c73 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiTeams.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTeams.kt @@ -1,15 +1,13 @@ /* - * Copyright (c) Kuba Szczodrzyński 2019-10-6. + * Copyright (c) Kuba Szczodrzyński 2019-10-11. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api import pl.szczodrzynski.edziennik.App.profileId import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik -import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher import pl.szczodrzynski.edziennik.data.db.modules.teams.Team import pl.szczodrzynski.edziennik.getById -import pl.szczodrzynski.edziennik.utils.Utils.strToInt import pl.szczodrzynski.edziennik.values class MobidziennikApiTeams(val data: DataMobidziennik, tableTeams: List?, tableRelations: List?) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTimetable.kt similarity index 94% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiTimetable.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTimetable.kt index 38f69990..62f760ab 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiTimetable.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTimetable.kt @@ -1,19 +1,15 @@ /* - * Copyright (c) Kuba Szczodrzyński 2019-10-8. + * Copyright (c) Kuba Szczodrzyński 2019-10-11. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api -import android.graphics.Color -import androidx.core.util.contains import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik -import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory import pl.szczodrzynski.edziennik.data.db.modules.lessons.Lesson import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.fixWhiteSpaces import pl.szczodrzynski.edziennik.singleOrNull -import pl.szczodrzynski.edziennik.utils.Utils.strToInt class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List) { init { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiUsers.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiUsers.kt similarity index 91% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiUsers.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiUsers.kt index 4d3fb7cf..7b3fed51 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/apidata/MobidziennikApiUsers.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiUsers.kt @@ -2,7 +2,7 @@ * Copyright (c) Kuba Szczodrzyński 2019-10-6. */ -package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.apidata +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebNotices.kt new file mode 100644 index 00000000..ab7d8760 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebNotices.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-11. + */ + +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web + +import com.google.gson.JsonParser +import pl.szczodrzynski.edziennik.api.v2.Regexes +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_CALENDAR +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_NOTICES +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.events.Event +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.models.Date +import java.util.* + +class MobidziennikWebNotices(override val data: DataMobidziennik, + val onSuccess: () -> Unit) : MobidziennikWeb(data) { + companion object { + private const val TAG = "MobidziennikWebNotices" + } + + init { + // TODO this does no longer work: Mobidziennik changed their mobile page in 2019.09 + data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_NOTICES, SYNC_ALWAYS) + onSuccess() + /*webGet(TAG, "/mobile/zachowanie") { text -> + MobidziennikLuckyNumberExtractor(data, text) + + data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_NOTICES, SYNC_ALWAYS) + onSuccess() + }*/ + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/NoticesAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/NoticesAdapter.kt index c9693842..2f5bfe36 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/NoticesAdapter.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/NoticesAdapter.kt @@ -40,7 +40,7 @@ class NoticesAdapter//getting the context and product list with constructor val notice = noticeList[position] - if (app.profile.loginStoreType == LOGIN_TYPE_MOBIDZIENNIK) { + if (app.profile.loginStoreType == LOGIN_TYPE_MOBIDZIENNIK && false) { holder.noticesItemReason.text = bs(null, notice.category, "\n") + notice.text holder.noticesItemTeacherName.text = app.getString(R.string.notices_points_format, notice.teacherFullName, if (notice.points > 0) "+" + notice.points else notice.points) } else { From cf0aa2788d7866251ce0a3fc92fecb9937073c48 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Fri, 11 Oct 2019 20:03:03 +0200 Subject: [PATCH 038/691] [API/Debug] Make debugging active profile in the home fragment --- .../edziennik/ui/modules/home/HomeFragment.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java index be045510..8e8e1652 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java @@ -134,21 +134,21 @@ public class HomeFragment extends Fragment { b.test2.setOnClickListener((v -> { List> list = new ArrayList<>(); list.add(new Pair<>(DRAWER_ITEM_HOME, 0)); - EventBus.getDefault().post(new SyncProfileRequest(10, list)); + EventBus.getDefault().post(new SyncProfileRequest(app.profile.getId(), list)); })); b.test3.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); b.test3.setOnClickListener((v -> { List> list = new ArrayList<>(); list.add(new Pair<>(DRAWER_ITEM_MESSAGES, TYPE_SENT)); - EventBus.getDefault().post(new SyncProfileRequest(10, list)); + EventBus.getDefault().post(new SyncProfileRequest(app.profile.getId(), list)); })); b.test4.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); b.test4.setOnClickListener((v -> { List> list = new ArrayList<>(); list.add(new Pair<>(DRAWER_ITEM_GRADES, 0)); - EventBus.getDefault().post(new SyncProfileRequest(10, list)); + EventBus.getDefault().post(new SyncProfileRequest(app.profile.getId(), list)); })); //((TextView)v.findViewById(R.id.nextSync)).setText(getString(R.string.next_sync_format,Time.fromMillis(app.appJobs.syncJobTime).getStringHMS())); From f6b50fbb5838893df6e5d6f0b591492a68cd7c1c Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Fri, 11 Oct 2019 20:27:24 +0200 Subject: [PATCH 039/691] [API/Librus] Fix grade syncing --- .../edziennik/api/v2/librus/data/api/LibrusApiGrades.kt | 6 +++--- .../edziennik/api/v2/librus/data/api/LibrusApiMe.kt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt index 3b93fa81..f95a6647 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt @@ -93,10 +93,10 @@ class LibrusApiGrades(override val data: DataLibrus, profile?.empty ?: false, addedDate )) - - data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GRADES, SYNC_ALWAYS, MainActivity.DRAWER_ITEM_GRADES) - onSuccess() } + + data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GRADES, SYNC_ALWAYS, MainActivity.DRAWER_ITEM_GRADES) + onSuccess() } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiMe.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiMe.kt index 2e408b0b..586dbbf7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiMe.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiMe.kt @@ -17,7 +17,7 @@ class LibrusApiMe(override val data: DataLibrus, init { apiGet(TAG, "Me") { json -> - val me = json?.getJsonObject("Me") + val me = json.getJsonObject("Me") val account = me?.getJsonObject("Account") val user = me?.getJsonObject("User") @@ -36,4 +36,4 @@ class LibrusApiMe(override val data: DataLibrus, onSuccess() } } -} \ No newline at end of file +} From 93d55969423aa9281e145b7c2382dca15fb73344 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Fri, 11 Oct 2019 22:07:50 +0200 Subject: [PATCH 040/691] [APIv2/Librus] Add events syncing and fix NPE in grades --- .../api/v2/librus/data/LibrusData.kt | 6 ++ .../api/v2/librus/data/api/LibrusApiEvents.kt | 61 +++++++++++++++++-- .../api/v2/librus/data/api/LibrusApiGrades.kt | 27 ++++---- 3 files changed, 74 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index 5e355bae..08a32a06 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -5,10 +5,12 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_EVENTS import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_ME import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_NORMAL_GRADES import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_SCHOOLS import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.data.api.LibrusApiEvents import pl.szczodrzynski.edziennik.api.v2.librus.data.api.LibrusApiGrades import pl.szczodrzynski.edziennik.api.v2.librus.data.api.LibrusApiMe import pl.szczodrzynski.edziennik.api.v2.librus.data.api.LibrusApiSchools @@ -52,6 +54,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_grades) LibrusApiGrades(data) { onSuccess() } } + ENDPOINT_LIBRUS_API_EVENTS -> { + data.startProgress(R.string.sync_action_syncing_events) + LibrusApiEvents(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt index 7beeaace..2d9211de 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt @@ -4,8 +4,15 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_EVENTS import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.events.Event +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Time class LibrusApiEvents(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusApi(data) { @@ -14,13 +21,55 @@ class LibrusApiEvents(override val data: DataLibrus, } init { - /*apiGet(LibrusApiMe.TAG, "") { json -> + apiGet(TAG, "HomeWorks") { json -> + val events = json.getJsonArray("HomeWorks") - // on error - data.error(TAG, ERROR_LIBRUS_API_, response, json) + events?.forEach { eventEl -> + val event = eventEl.asJsonObject - data.setSyncNext(ENDPOINT_LIBRUS_API_, 2 * DAY) + val id = event.getLong("Id") ?: return@forEach + val eventDate = Date.fromY_m_d(event.getString("Date") ?: return@forEach) + val topic = event.getString("Content") ?: return@forEach + val type = event.getJsonObject("Category")?.getInt("Id") ?: -1 + val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1 + val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1 + val teamId = event.getJsonObject("Class")?.getLong("Id") ?: -1 + + val lessonNo = event.getInt("LessonNo") + val lessonRange = data.lessonRanges.singleOrNull { it.lessonNumber == lessonNo } + val startTime = lessonRange?.startTime + ?: Time.fromH_m(event.getString("TimeFrom") ?: return@forEach) + + val eventObject = Event( + profileId, + id, + eventDate, + startTime, + topic, + -1, + type, + false, + teacherId, + subjectId, + teamId + ) + + val addedDate = Date.fromIso(event.get("AddDate").asString) + + data.eventList.add(eventObject) + data.metadataList.add( + Metadata( + profileId, + Metadata.TYPE_EVENT, + id, + profile?.empty ?: false, + profile?.empty ?: false, + addedDate + )) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_EVENTS, SYNC_ALWAYS) onSuccess() - }*/ + } } -} \ No newline at end of file +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt index f95a6647..65ba5628 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt @@ -21,16 +21,15 @@ class LibrusApiGrades(override val data: DataLibrus, apiGet(TAG, "Grades") { json -> val grades = json.getJsonArray("Grades") - grades?.forEach { gradeEl -> val grade = gradeEl.asJsonObject - val id = grade.get("Id").asLong - val categoryId = grade.get("Category").asJsonObject.get("Id").asLong - val name = grade.get("Grade").asString - val semester = grade.get("Semester").asInt - val teacherId = grade.get("AddedBy").asJsonObject.get("Id").asLong - val subjectId = grade.get("Subject").asJsonObject.get("Id").asLong + val id = grade.getLong("Id") ?: return@forEach + val categoryId = grade.getJsonObject("Category")?.getLong("Id") ?: return@forEach + val name = grade.getString("Grade") ?: return@forEach + val semester = grade.getInt("Semester") ?: return@forEach + val teacherId = grade.getJsonObject("AddedBy")?.getLong("Id") ?: -1 + val subjectId = grade.getJsonObject("Subject")?.getLong("Id") ?: -1 val category = data.gradeCategories.singleOrNull { it.categoryId == categoryId } val categoryName = category?.text ?: "" @@ -60,20 +59,20 @@ class LibrusApiGrades(override val data: DataLibrus, ) when { - grade.get("IsConstituent").asBoolean -> + grade.getBoolean("IsConstituent") ?: false -> gradeObject.type = Grade.TYPE_NORMAL - grade.get("IsSemester").asBoolean -> // semester final + grade.getBoolean("IsSemester") ?: false -> // semester final gradeObject.type = if (gradeObject.semester == 1) Grade.TYPE_SEMESTER1_FINAL else Grade.TYPE_SEMESTER2_FINAL - grade.get("IsSemesterProposition").asBoolean -> // semester proposed + grade.getBoolean("IsSemesterProposition") ?: false -> // semester proposed gradeObject.type = if (gradeObject.semester == 1) Grade.TYPE_SEMESTER1_PROPOSED else Grade.TYPE_SEMESTER2_PROPOSED - grade.get("IsFinal").asBoolean -> // year final + grade.getBoolean("IsFinal") ?: false -> // year final gradeObject.type = Grade.TYPE_YEAR_FINAL - grade.get("IsFinalProposition").asBoolean -> // year final + grade.getBoolean("IsFinalProposition") ?: false -> // year final gradeObject.type = Grade.TYPE_YEAR_PROPOSED } grade.getJsonObject("Improvement")?.also { - val historicalId = it.get("Id").asLong + val historicalId = it.getLong("Id") data.gradeList.firstOrNull { grade -> grade.id == historicalId }?.also { grade -> grade.parentId = gradeObject.id if (grade.name == "nb") grade.weight = 0f @@ -95,7 +94,7 @@ class LibrusApiGrades(override val data: DataLibrus, )) } - data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GRADES, SYNC_ALWAYS, MainActivity.DRAWER_ITEM_GRADES) + data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GRADES, SYNC_ALWAYS) onSuccess() } } From 7594fdd57856ad48f1025a8c0c3c865446b88df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 11 Oct 2019 23:00:19 +0200 Subject: [PATCH 041/691] [APIv2/Mobidziennik] Add Messages All+Inbox --- .../v2/mobidziennik/MobidziennikFeatures.kt | 5 +- .../v2/mobidziennik/data/MobidziennikData.kt | 6 +- .../data/web/MobidziennikWebMessagesAll.kt | 89 +++++++++++++++++++ .../data/web/MobidziennikWebMessagesInbox.kt | 86 ++++++++++++++++++ 4 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebMessagesAll.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebMessagesInbox.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt index 660f846f..0e92fe6d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt @@ -47,11 +47,14 @@ val MobidziennikFeatures = listOf( Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ATTENDANCE, listOf( ENDPOINT_MOBIDZIENNIK_WEB_ATTENDANCE to LOGIN_METHOD_MOBIDZIENNIK_WEB ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), + // messages Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_MESSAGES_INBOX, listOf( - ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX to LOGIN_METHOD_MOBIDZIENNIK_WEB + ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX to LOGIN_METHOD_MOBIDZIENNIK_WEB, + ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL to LOGIN_METHOD_MOBIDZIENNIK_WEB ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_MESSAGES_SENT, listOf( + ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_SENT to LOGIN_METHOD_MOBIDZIENNIK_WEB, ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL to LOGIN_METHOD_MOBIDZIENNIK_WEB ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt index dfc42b60..304f036e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt @@ -9,6 +9,8 @@ import pl.szczodrzynski.edziennik.api.v2.mobidziennik.* import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api.MobidziennikApi import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebCalendar import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebGrades +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebMessagesAll +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebMessagesInbox import pl.szczodrzynski.edziennik.utils.Utils class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) { @@ -40,7 +42,7 @@ class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) { ENDPOINT_MOBIDZIENNIK_API_MAIN -> { data.startProgress(R.string.edziennik_progress_endpoint_data) MobidziennikApi(data) { onSuccess() } - }/* + } ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX -> { data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) MobidziennikWebMessagesInbox(data) { onSuccess() } @@ -48,7 +50,7 @@ class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) { ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL -> { data.startProgress(R.string.edziennik_progress_endpoint_messages) MobidziennikWebMessagesAll(data) { onSuccess() } - }*/ + } ENDPOINT_MOBIDZIENNIK_WEB_CALENDAR -> { data.startProgress(R.string.edziennik_progress_endpoint_calendar) MobidziennikWebCalendar(data) { onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebMessagesAll.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebMessagesAll.kt new file mode 100644 index 00000000..2c246620 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebMessagesAll.kt @@ -0,0 +1,89 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-11. + */ + +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web + +import org.jsoup.Jsoup +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT +import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.singleOrNull +import pl.szczodrzynski.edziennik.utils.models.Date + +class MobidziennikWebMessagesAll(override val data: DataMobidziennik, + val onSuccess: () -> Unit) : MobidziennikWeb(data) { + companion object { + private const val TAG = "MobidziennikWebMessagesAll" + } + + init { + webGet(TAG, "/dziennik/wyszukiwarkawiadomosci?q=+") { text -> + MobidziennikLuckyNumberExtractor(data, text) + + val doc = Jsoup.parse(text) + + val listElement = doc.getElementsByClass("spis").first() + if (listElement == null) { + data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL, SYNC_ALWAYS) + onSuccess() + return@webGet + } + val list = listElement.getElementsByClass("podswietl") + for (item in list) { + val id = item.attr("rel").replace("[^\\d]".toRegex(), "").toLongOrNull() ?: continue + + val subjectEl = item.select("td:eq(0) div").first() + val subject = subjectEl.text() + + val addedDateEl = item.select("td:eq(1)").first() + val addedDate = Date.fromIsoHm(addedDateEl.text()) + + val typeEl = item.select("td:eq(2) img").first() + var type = TYPE_RECEIVED + if (typeEl.outerHtml().contains("mail_send.png")) + type = TYPE_SENT + + val senderEl = item.select("td:eq(3) div").first() + var senderId: Long = -1 + + if (type == TYPE_RECEIVED) { + // search sender teacher + val senderName = senderEl.text() + senderId = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }?.id ?: -1 + data.messageRecipientList.add(MessageRecipient(profileId, -1, id)) + } else { + // TYPE_SENT, so multiple recipients possible + val recipientNames = senderEl.text().split(", ") + for (recipientName in recipientNames) { + val recipientId = data.teacherList.singleOrNull { it.fullNameLastFirst == recipientName }?.id ?: -1 + data.messageRecipientIgnoreList.add(MessageRecipient(profileId, recipientId, id)) + } + } + + val message = Message( + profileId, + id, + subject, + null, + type, + senderId, + -1 + ) + + data.messageList.add(message) + data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, addedDate)) + } + + data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL, SYNC_ALWAYS) + onSuccess() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebMessagesInbox.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebMessagesInbox.kt new file mode 100644 index 00000000..7f66b213 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebMessagesInbox.kt @@ -0,0 +1,86 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-11. + */ + +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web + +import org.jsoup.Jsoup +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message +import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.singleOrNull +import pl.szczodrzynski.edziennik.utils.models.Date + +class MobidziennikWebMessagesInbox(override val data: DataMobidziennik, + val onSuccess: () -> Unit) : MobidziennikWeb(data) { + companion object { + private const val TAG = "MobidziennikWebMessagesInbox" + } + + init { + webGet(TAG, "/dziennik/wiadomosci") { text -> + MobidziennikLuckyNumberExtractor(data, text) + + if (text.contains("Brak wiadomości odebranych.")) { + data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX, SYNC_ALWAYS) + onSuccess() + return@webGet + } + + val doc = Jsoup.parse(text) + + val list = doc.getElementsByClass("spis").first().getElementsByClass("podswietl") + for (item in list) { + val id = item.attr("rel").toLongOrNull() ?: continue + + val subjectEl = item.select("td:eq(0)").first() + var hasAttachments = false + if (subjectEl.getElementsByTag("a").size != 0) { + hasAttachments = true + } + val subject = subjectEl.ownText() + + val addedDateEl = item.select("td:eq(1) small").first() + val addedDate = Date.fromIsoHm(addedDateEl.text()) + + val senderEl = item.select("td:eq(2)").first() + val senderName = senderEl.ownText() + val senderId = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }?.id ?: -1 + data.messageRecipientIgnoreList.add(MessageRecipient(profileId, -1, id)) + + val isRead = item.select("td:eq(3) span").first().hasClass("wiadomosc_przeczytana") + + val message = Message( + profileId, + id, + subject, + null, + Message.TYPE_RECEIVED, + senderId, + -1 + ) + + if (hasAttachments) + message.setHasAttachments() + + data.messageList.add(message) + data.messageMetadataList.add( + Metadata( + profileId, + Metadata.TYPE_MESSAGE, + message.id, + isRead, + isRead || profile?.empty ?: false, + addedDate + )) + } + + data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX, SYNC_ALWAYS) + onSuccess() + } + } +} \ No newline at end of file From 0875d13737ce66fdfd1b9d61dfc9e9122ef5b6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 11 Oct 2019 23:09:23 +0200 Subject: [PATCH 042/691] [APIv2/Librus] Update endpoint list structure --- .../edziennik/api/v2/librus/Librus.kt | 4 +--- .../LibrusFeatures.kt} | 23 ++++-------------- .../api/v2/librus/data/LibrusData.kt | 8 +++---- .../api/v2/librus/data/api/LibrusApiEvents.kt | 2 +- .../api/v2/librus/data/api/LibrusApiGrades.kt | 2 +- .../api/v2/librus/data/api/LibrusApiMe.kt | 2 +- .../v2/librus/data/api/LibrusApiSchools.kt | 2 +- .../data/web/MobidziennikWebCalendar.kt | 1 - .../data/web/MobidziennikWebGrades.kt | 8 ------- .../edziennik/api/v2/template/Template.kt | 3 +-- .../api/v2/template/TemplateFeatures.kt | 24 +++++++++++++++++++ .../api/v2/template/data/TemplateData.kt | 6 ++--- .../v2/template/data/api/TemplateApiSample.kt | 2 +- .../v2/template/data/web/TemplateWebSample.kt | 2 +- .../template/data/web/TemplateWebSample2.kt | 2 +- 15 files changed, 44 insertions(+), 47 deletions(-) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/{Endpoints.kt => librus/LibrusFeatures.kt} (90%) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/TemplateFeatures.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index d8b9e596..667683c1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -8,7 +8,6 @@ import android.util.Log import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_NOT_NEEDED -import pl.szczodrzynski.edziennik.api.v2.endpoints import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData @@ -19,7 +18,6 @@ import pl.szczodrzynski.edziennik.api.v2.models.Feature import pl.szczodrzynski.edziennik.data.db.modules.api.* import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile -import pl.szczodrzynski.edziennik.utils.Utils.d class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { companion object { @@ -67,7 +65,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va // get all endpoints for every feature, only if possible to login for (featureId in featureIds) { - endpoints.filter { + LibrusFeatures.filter { it.featureId == featureId && possibleLoginMethods.containsAll(it.requiredLoginMethods) } .let { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt similarity index 90% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt index c04e1343..46ba4706 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Endpoints.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt @@ -1,9 +1,10 @@ /* - * Copyright (c) Kuba Szczodrzyński 2019-9-20. + * Copyright (c) Kuba Szczodrzyński 2019-10-11. */ -package pl.szczodrzynski.edziennik.api.v2 +package pl.szczodrzynski.edziennik.api.v2.librus +import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.models.Feature const val ENDPOINT_LIBRUS_API_ME = 1001 @@ -48,11 +49,7 @@ const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 3030 const val ENDPOINT_LIBRUS_MESSAGES_RECEIVERS = 3040 const val ENDPOINT_LIBRUS_MESSAGES_GET = 3040 -const val ENDPOINT_TEMPLATE_WEB_SAMPLE = 9991 -const val ENDPOINT_TEMPLATE_WEB_SAMPLE_2 = 9992 -const val ENDPOINT_TEMPLATE_API_SAMPLE = 9993 - -val endpoints = listOf( +val LibrusFeatures = listOf( // LIBRUS: API Feature(LOGIN_TYPE_LIBRUS, FEATURE_TIMETABLE, listOf( @@ -143,16 +140,4 @@ val endpoints = listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_SENT, listOf( ENDPOINT_LIBRUS_MESSAGES_SENT to LOGIN_METHOD_LIBRUS_MESSAGES ), listOf(LOGIN_METHOD_LIBRUS_MESSAGES)) -) - -val templateEndpoints = listOf( - Feature(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf( - ENDPOINT_TEMPLATE_WEB_SAMPLE to LOGIN_METHOD_TEMPLATE_WEB - ), listOf(LOGIN_METHOD_TEMPLATE_WEB)), - Feature(LOGIN_TYPE_LIBRUS, FEATURE_SCHOOL_INFO, listOf( - ENDPOINT_TEMPLATE_WEB_SAMPLE_2 to LOGIN_METHOD_TEMPLATE_WEB - ), listOf(LOGIN_METHOD_TEMPLATE_WEB)), - Feature(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( - ENDPOINT_TEMPLATE_API_SAMPLE to LOGIN_METHOD_TEMPLATE_API - ), listOf(LOGIN_METHOD_TEMPLATE_API)) ) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index 08a32a06..f55dec05 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -5,10 +5,10 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_EVENTS -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_ME -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_NORMAL_GRADES -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_SCHOOLS +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_EVENTS +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ME +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADES +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_SCHOOLS import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.data.api.LibrusApiEvents import pl.szczodrzynski.edziennik.api.v2.librus.data.api.LibrusApiGrades diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt index 2d9211de..fc57f248 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt @@ -5,7 +5,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_EVENTS +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_EVENTS import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt index 65ba5628..226ea6d4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt @@ -1,7 +1,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_NORMAL_GRADES +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADES import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiMe.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiMe.kt index 586dbbf7..ad1a8efb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiMe.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiMe.kt @@ -5,7 +5,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_ME +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ME import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiSchools.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiSchools.kt index e48d8068..5b1b1086 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiSchools.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiSchools.kt @@ -5,7 +5,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_SCHOOLS +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_SCHOOLS import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonRange diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebCalendar.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebCalendar.kt index 94f0a5e4..2fa16345 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebCalendar.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebCalendar.kt @@ -5,7 +5,6 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web import com.google.gson.JsonParser -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_ME import pl.szczodrzynski.edziennik.api.v2.Regexes import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_CALENDAR diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebGrades.kt index 2a24b436..cad45ab8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebGrades.kt @@ -5,27 +5,19 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web import android.graphics.Color -import com.google.gson.JsonParser import org.jsoup.Jsoup -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_LIBRUS_API_ME import pl.szczodrzynski.edziennik.api.v2.Regexes import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_GRADES import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS -import pl.szczodrzynski.edziennik.data.db.modules.events.Event import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.fixWhiteSpaces import pl.szczodrzynski.edziennik.get -import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.singleOrNull -import pl.szczodrzynski.edziennik.utils.Utils -import pl.szczodrzynski.edziennik.utils.Utils.strToInt import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Time -import java.util.* -import java.util.regex.Pattern class MobidziennikWebGrades(override val data: DataMobidziennik, val onSuccess: () -> Unit) : MobidziennikWeb(data) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt index 6e39087c..a703f73a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt @@ -14,7 +14,6 @@ import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.Feature import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateData import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLogin -import pl.szczodrzynski.edziennik.api.v2.templateEndpoints import pl.szczodrzynski.edziennik.api.v2.templateLoginMethods import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS @@ -69,7 +68,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, // get all endpoints for every feature, only if possible to login for (featureId in featureIds) { - templateEndpoints.filter { + TemplateFeatures.filter { it.featureId == featureId && possibleLoginMethods.containsAll(it.requiredLoginMethods) } .let { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/TemplateFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/TemplateFeatures.kt new file mode 100644 index 00000000..b643f74d --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/TemplateFeatures.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-11. + */ + +package pl.szczodrzynski.edziennik.api.v2.template + +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.models.Feature + +const val ENDPOINT_TEMPLATE_WEB_SAMPLE = 9991 +const val ENDPOINT_TEMPLATE_WEB_SAMPLE_2 = 9992 +const val ENDPOINT_TEMPLATE_API_SAMPLE = 9993 + +val TemplateFeatures = listOf( + Feature(LOGIN_TYPE_TEMPLATE, FEATURE_STUDENT_INFO, listOf( + ENDPOINT_TEMPLATE_WEB_SAMPLE to LOGIN_METHOD_TEMPLATE_WEB + ), listOf(LOGIN_METHOD_TEMPLATE_WEB)), + Feature(LOGIN_TYPE_TEMPLATE, FEATURE_SCHOOL_INFO, listOf( + ENDPOINT_TEMPLATE_WEB_SAMPLE_2 to LOGIN_METHOD_TEMPLATE_WEB + ), listOf(LOGIN_METHOD_TEMPLATE_WEB)), + Feature(LOGIN_TYPE_TEMPLATE, FEATURE_GRADES, listOf( + ENDPOINT_TEMPLATE_API_SAMPLE to LOGIN_METHOD_TEMPLATE_API + ), listOf(LOGIN_METHOD_TEMPLATE_API)) +) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/TemplateData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/TemplateData.kt index dfdc7813..35b2f004 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/TemplateData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/TemplateData.kt @@ -5,10 +5,10 @@ package pl.szczodrzynski.edziennik.api.v2.template.data import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_API_SAMPLE -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_WEB_SAMPLE -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_WEB_SAMPLE_2 import pl.szczodrzynski.edziennik.api.v2.template.DataTemplate +import pl.szczodrzynski.edziennik.api.v2.template.ENDPOINT_TEMPLATE_API_SAMPLE +import pl.szczodrzynski.edziennik.api.v2.template.ENDPOINT_TEMPLATE_WEB_SAMPLE +import pl.szczodrzynski.edziennik.api.v2.template.ENDPOINT_TEMPLATE_WEB_SAMPLE_2 import pl.szczodrzynski.edziennik.api.v2.template.data.api.TemplateApiSample import pl.szczodrzynski.edziennik.api.v2.template.data.web.TemplateWebSample import pl.szczodrzynski.edziennik.api.v2.template.data.web.TemplateWebSample2 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/api/TemplateApiSample.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/api/TemplateApiSample.kt index ef0ac8e6..470b81ef 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/api/TemplateApiSample.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/api/TemplateApiSample.kt @@ -6,8 +6,8 @@ package pl.szczodrzynski.edziennik.api.v2.template.data.api import pl.szczodrzynski.edziennik.DAY import pl.szczodrzynski.edziennik.MainActivity -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_API_SAMPLE import pl.szczodrzynski.edziennik.api.v2.template.DataTemplate +import pl.szczodrzynski.edziennik.api.v2.template.ENDPOINT_TEMPLATE_API_SAMPLE import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateApi import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/web/TemplateWebSample.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/web/TemplateWebSample.kt index 6e5340e1..f015debd 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/web/TemplateWebSample.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/web/TemplateWebSample.kt @@ -7,8 +7,8 @@ package pl.szczodrzynski.edziennik.api.v2.template.data.web import pl.szczodrzynski.edziennik.DAY import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_WEB_SAMPLE import pl.szczodrzynski.edziennik.api.v2.template.DataTemplate +import pl.szczodrzynski.edziennik.api.v2.template.ENDPOINT_TEMPLATE_WEB_SAMPLE import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateWeb import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/web/TemplateWebSample2.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/web/TemplateWebSample2.kt index 94e2235d..7a3cdb33 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/web/TemplateWebSample2.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/web/TemplateWebSample2.kt @@ -6,8 +6,8 @@ package pl.szczodrzynski.edziennik.api.v2.template.data.web import pl.szczodrzynski.edziennik.DAY import pl.szczodrzynski.edziennik.MainActivity -import pl.szczodrzynski.edziennik.api.v2.ENDPOINT_TEMPLATE_WEB_SAMPLE_2 import pl.szczodrzynski.edziennik.api.v2.template.DataTemplate +import pl.szczodrzynski.edziennik.api.v2.template.ENDPOINT_TEMPLATE_WEB_SAMPLE_2 import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateWeb import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS From b0fb87acdb7f8f9d8d02740c08997debbf2300c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sat, 12 Oct 2019 17:01:21 +0200 Subject: [PATCH 043/691] [APIv2] Add Feature priority setter and shouldSync method. --- .../edziennik/api/v2/librus/Librus.kt | 14 ++++++++------ .../edziennik/api/v2/mobidziennik/Mobidziennik.kt | 12 +++++++----- .../edziennik/api/v2/models/Feature.kt | 13 +++++++++++++ .../edziennik/api/v2/template/Template.kt | 15 +++++++++------ .../edziennik/api/v2/vulcan/Vulcan.kt | 12 +++++++----- 5 files changed, 44 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index 667683c1..d9cf3662 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.MobidziennikFeatures import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.Feature import pl.szczodrzynski.edziennik.data.db.modules.api.* @@ -63,14 +64,15 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va data.targetEndpointIds.clear() data.targetLoginMethodIds.clear() - // get all endpoints for every feature, only if possible to login + // get all endpoints for every feature, only if possible to login and possible/necessary to sync for (featureId in featureIds) { LibrusFeatures.filter { - it.featureId == featureId && possibleLoginMethods.containsAll(it.requiredLoginMethods) - } - .let { - endpointList.addAll(it) - } + it.featureId == featureId // feature ID matches + && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login + && it.shouldSync?.invoke(profile, loginStore) ?: true // is necessary/possible to sync + }.let { + endpointList.addAll(it) + } } val timestamp = System.currentTimeMillis() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt index 123eba43..c8687faa 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt @@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface +import pl.szczodrzynski.edziennik.api.v2.librus.LibrusFeatures import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikData import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLogin import pl.szczodrzynski.edziennik.api.v2.models.ApiError @@ -64,14 +65,15 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto data.targetEndpointIds.clear() data.targetLoginMethodIds.clear() - // get all endpoints for every feature, only if possible to login + // get all endpoints for every feature, only if possible to login and possible/necessary to sync for (featureId in featureIds) { MobidziennikFeatures.filter { - it.featureId == featureId && possibleLoginMethods.containsAll(it.requiredLoginMethods) + it.featureId == featureId // feature ID matches + && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login + && it.shouldSync?.invoke(profile, loginStore) ?: true // is necessary/possible to sync + }.let { + endpointList.addAll(it) } - .let { - endpointList.addAll(it) - } } val timestamp = System.currentTimeMillis() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt index b8d9b8a7..91ac6563 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt @@ -1,5 +1,8 @@ package pl.szczodrzynski.edziennik.api.v2.models +import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile + /** * A Endpoint descriptor class. * @@ -20,4 +23,14 @@ data class Feature( val requiredLoginMethods: List ) { var priority = endpointIds.size + fun withPriority(priority: Int): Feature { + this.priority = priority + return this + } + + var shouldSync: ((Profile?, LoginStore) -> Boolean)? = null + fun withShouldSync(shouldSync: ((Profile?, LoginStore) -> Boolean)?): Feature { + this.shouldSync = shouldSync + return this + } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt index a703f73a..a639e65e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt @@ -10,11 +10,13 @@ import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_NOT_NEEDED import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface +import pl.szczodrzynski.edziennik.api.v2.librus.LibrusFeatures import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.Feature import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateData import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLogin import pl.szczodrzynski.edziennik.api.v2.templateLoginMethods +import pl.szczodrzynski.edziennik.api.v2.vulcan.VulcanFeatures import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_NEVER @@ -66,14 +68,15 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, data.targetEndpointIds.clear() data.targetLoginMethodIds.clear() - // get all endpoints for every feature, only if possible to login + // get all endpoints for every feature, only if possible to login and possible/necessary to sync for (featureId in featureIds) { - TemplateFeatures.filter { - it.featureId == featureId && possibleLoginMethods.containsAll(it.requiredLoginMethods) + VulcanFeatures.filter { + it.featureId == featureId // feature ID matches + && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login + && it.shouldSync?.invoke(profile, loginStore) ?: true // is necessary/possible to sync + }.let { + endpointList.addAll(it) } - .let { - endpointList.addAll(it) - } } val timestamp = System.currentTimeMillis() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt index c88023e5..d5af284f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt @@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_NOT_NEEDED import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface +import pl.szczodrzynski.edziennik.api.v2.librus.LibrusFeatures import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.Feature import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanData @@ -66,14 +67,15 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va data.targetEndpointIds.clear() data.targetLoginMethodIds.clear() - // get all endpoints for every feature, only if possible to login + // get all endpoints for every feature, only if possible to login and possible/necessary to sync for (featureId in featureIds) { VulcanFeatures.filter { - it.featureId == featureId && possibleLoginMethods.containsAll(it.requiredLoginMethods) + it.featureId == featureId // feature ID matches + && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login + && it.shouldSync?.invoke(profile, loginStore) ?: true // is necessary/possible to sync + }.let { + endpointList.addAll(it) } - .let { - endpointList.addAll(it) - } } val timestamp = System.currentTimeMillis() From 3b273440cc5eb4d04aba84240e1eb041cb98fc73 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sat, 12 Oct 2019 23:25:53 +0200 Subject: [PATCH 044/691] [APIv2/Librus] Add getting homework --- .../api/v2/librus/data/LibrusData.kt | 21 +++--- .../api/v2/librus/data/api/LibrusApiEvents.kt | 2 +- .../v2/librus/data/api/LibrusApiHomework.kt | 69 +++++++++++++++++++ .../v2/librus/data/api/LibrusApiTemplate.kt | 10 ++- 4 files changed, 83 insertions(+), 19 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiHomework.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index f55dec05..734f71a9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -5,15 +5,8 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_EVENTS -import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ME -import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADES -import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_SCHOOLS -import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus -import pl.szczodrzynski.edziennik.api.v2.librus.data.api.LibrusApiEvents -import pl.szczodrzynski.edziennik.api.v2.librus.data.api.LibrusApiGrades -import pl.szczodrzynski.edziennik.api.v2.librus.data.api.LibrusApiMe -import pl.szczodrzynski.edziennik.api.v2.librus.data.api.LibrusApiSchools +import pl.szczodrzynski.edziennik.api.v2.librus.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.api.* import pl.szczodrzynski.edziennik.utils.Utils class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { @@ -43,21 +36,25 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { Utils.d(TAG, "Using endpoint $endpointId") when (endpointId) { ENDPOINT_LIBRUS_API_ME -> { - data.startProgress(R.string.edziennik_progress_endpoint_student_info) + data.startProgress(R.string.sync_action_getting_account) LibrusApiMe(data) { onSuccess() } } ENDPOINT_LIBRUS_API_SCHOOLS -> { - data.startProgress(R.string.edziennik_progress_endpoint_school_info) + data.startProgress(R.string.sync_action_syncing_school_info) LibrusApiSchools(data) { onSuccess() } } ENDPOINT_LIBRUS_API_NORMAL_GRADES -> { - data.startProgress(R.string.edziennik_progress_endpoint_grades) + data.startProgress(R.string.sync_action_syncing_grades) LibrusApiGrades(data) { onSuccess() } } ENDPOINT_LIBRUS_API_EVENTS -> { data.startProgress(R.string.sync_action_syncing_events) LibrusApiEvents(data) { onSuccess() } } + ENDPOINT_LIBRUS_API_HOMEWORK -> { + data.startProgress(R.string.sync_action_syncing_homework) + LibrusApiHomework(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt index fc57f248..f495c94c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt @@ -54,7 +54,7 @@ class LibrusApiEvents(override val data: DataLibrus, teamId ) - val addedDate = Date.fromIso(event.get("AddDate").asString) + val addedDate = Date.fromIso(event.getString("AddDate") ?: return@forEach) data.eventList.add(eventObject) data.metadataList.add( diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiHomework.kt new file mode 100644 index 00000000..093acac4 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiHomework.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-12. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_HOMEWORK +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.events.Event +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.getJsonArray +import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.getLong +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.utils.models.Date + +class LibrusApiHomework(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiHomework" + } + + init { + apiGet(TAG, "HomeWorkAssignments") { json -> + val homeworkList = json.getJsonArray("HomeWorkAssignments") + + homeworkList?.forEach { homeworkEl -> + val homework = homeworkEl.asJsonObject + + val id = homework.getLong("Id") ?: return@forEach + val eventDate = Date.fromY_m_d(homework.getString("DueDate") ?: return@forEach) + val topic = (homework.getString("Topic") ?: "") + "\n" + + (homework.getString("Text") ?: "") + val teacherId = homework.getJsonObject("Teacher")?.getLong("Id") ?: -1 + + val eventObject = Event( + profileId, + id, + eventDate, + null, + topic, + -1, + -1, + false, + teacherId, + -1, + -1 + ) + + val addedDate = Date.fromY_m_d(homework.getString("Date") ?: return@forEach) + + data.eventList.add(eventObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_HOMEWORK, + id, + profile?.empty ?: false, + profile?.empty ?: false, + addedDate.inMillis + )) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_HOMEWORK, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTemplate.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTemplate.kt index e09456f2..efa7baf6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTemplate.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTemplate.kt @@ -6,6 +6,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS class LibrusApiTemplate(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusApi(data) { @@ -14,13 +15,10 @@ class LibrusApiTemplate(override val data: DataLibrus, } init { - /*apiGet(LibrusApiMe.TAG, "") { json -> + /*apiGet(TAG, "") { json -> - // on error - data.error(TAG, ERROR_LIBRUS_API_, response, json) - - data.setSyncNext(ENDPOINT_LIBRUS_API_, 2 * DAY) + data.setSyncNext(ENDPOINT_LIBRUS_API_, SYNC_ALWAYS) onSuccess() }*/ } -} \ No newline at end of file +} From 4f9b9c5f7b68e59a867b583dcbeac029684b34bf Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sat, 12 Oct 2019 23:29:21 +0200 Subject: [PATCH 045/691] [Copyright] Add profile_settings.xml to .gitignore --- .gitignore | 1 + .idea/copyright/profiles_settings.xml | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 .idea/copyright/profiles_settings.xml diff --git a/.gitignore b/.gitignore index 047ad745..13af317f 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ captures/ .idea/modules.xml # Comment next line if keeping position of elements in Navigation Editor is relevant for you .idea/navEditor.xml +.idea/copyright/profiles_settings.xml # Keystore files # Uncomment the following lines if you do not want to check your keystore files in. diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml deleted file mode 100644 index 61f16974..00000000 --- a/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file From fb945470c0ede3436fa39d04841b2b476f81ebe0 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sun, 13 Oct 2019 15:44:52 +0200 Subject: [PATCH 046/691] [APIv2/Librus] Add getting attendances and their types --- .../edziennik/api/v2/librus/LibrusFeatures.kt | 8 +- .../api/v2/librus/data/LibrusData.kt | 8 ++ .../data/api/LibrusApiAttendanceTypes.kt | 42 ++++++++++ .../librus/data/api/LibrusApiAttendances.kt | 83 +++++++++++++++++++ .../edziennik/api/v2/models/Data.kt | 5 +- 5 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendanceTypes.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt index 46ba4706..a71b74d7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt @@ -35,7 +35,7 @@ const val ENDPOINT_LIBRUS_API_HOMEWORK = 1050 const val ENDPOINT_LIBRUS_API_LUCKY_NUMBER = 1060 const val ENDPOINT_LIBRUS_API_NOTICES = 1070 const val ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES = 1080 -const val ENDPOINT_LIBRUS_API_ATTENDANCE = 1081 +const val ENDPOINT_LIBRUS_API_ATTENDANCES = 1081 const val ENDPOINT_LIBRUS_API_ANNOUNCEMENTS = 1090 const val ENDPOINT_LIBRUS_API_PT_MEETINGS = 1100 const val ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS = 1110 @@ -85,8 +85,8 @@ val LibrusFeatures = listOf( ENDPOINT_LIBRUS_API_NOTICES to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), Feature(LOGIN_TYPE_LIBRUS, FEATURE_ATTENDANCE, listOf( - ENDPOINT_LIBRUS_API_ATTENDANCE to LOGIN_METHOD_LIBRUS_API, - ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES to LOGIN_METHOD_LIBRUS_API + ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES to LOGIN_METHOD_LIBRUS_API, + ENDPOINT_LIBRUS_API_ATTENDANCES to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), Feature(LOGIN_TYPE_LIBRUS, FEATURE_ANNOUNCEMENTS, listOf( ENDPOINT_LIBRUS_API_ANNOUNCEMENTS to LOGIN_METHOD_LIBRUS_API @@ -140,4 +140,4 @@ val LibrusFeatures = listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_SENT, listOf( ENDPOINT_LIBRUS_MESSAGES_SENT to LOGIN_METHOD_LIBRUS_MESSAGES ), listOf(LOGIN_METHOD_LIBRUS_MESSAGES)) -) \ No newline at end of file +) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index 734f71a9..c4e867f9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -55,6 +55,14 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.sync_action_syncing_homework) LibrusApiHomework(data) { onSuccess() } } + ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES -> { + data.startProgress(R.string.sync_action_syncing_attendance_types) + LibrusApiAttendanceTypes(data) { onSuccess() } + } + ENDPOINT_LIBRUS_API_ATTENDANCES -> { + data.startProgress(R.string.sync_action_syncing_attendance) + LibrusApiAttendances(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendanceTypes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendanceTypes.kt new file mode 100644 index 00000000..aa268a80 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendanceTypes.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-13 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS + +class LibrusApiAttendanceTypes(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiAttendanceTypes" + } + + init { + apiGet(TAG, "Attendances/Types") { json -> + val attendanceTypes = json.getJsonArray("Types") + + attendanceTypes?.forEach { attendanceTypeEl -> + val attendanceType = attendanceTypeEl.asJsonObject + + val id = attendanceType.getInt("Id") ?: return@forEach + val standardId = when (attendanceType.getBoolean("Standard") ?: false) { + true -> id + false -> attendanceType.getJsonObject("StandardType")?.getInt("Id") + ?: return@forEach + } + + val name = attendanceType.getString("Name") ?: "" + + data.attendanceTypes.put(id, Pair(standardId, name)) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt new file mode 100644 index 00000000..28635ded --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt @@ -0,0 +1,83 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-13 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ATTENDANCES +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.models.Date + +class LibrusApiAttendances(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiAttendances" + } + + init { + apiGet(TAG, "Attendances") { json -> + val attendances = json.getJsonArray("Attendances") + + attendances?.forEach { attendanceEl -> + val attendance = attendanceEl.asJsonObject + + val teacherId = attendance.getJsonObject("AddedBy")?.getLong("Id") ?: -1 + val lessonNo = attendance.getInt("LessonNo") ?: return@forEach + val startTime = data.lessonRanges.get(lessonNo).startTime + val lessonDate = Date.fromY_m_d(attendance.getString("Date") ?: return@forEach) + val semester = attendance.getInt("Semester") ?: return@forEach + var type = attendance.getJsonObject("Type")?.getInt("Id") ?: return@forEach + val attendanceType = data.attendanceTypes.get(type) + val topic = attendanceType.second + + val id = Utils.strToInt((attendance.getString("Id") ?: return@forEach) + .replace("[^\\d.]".toRegex(), "")).toLong() + + val subjectId = data.lessonList.singleOrNull { + it.weekDay == lessonDate.weekDay && it.startTime.value == startTime.value + }?.subjectId ?: -1 + + type = when(type) { + 1 -> Attendance.TYPE_ABSENT + 2 -> Attendance.TYPE_BELATED + 3 -> Attendance.TYPE_ABSENT_EXCUSED + 4 -> Attendance.TYPE_RELEASED + else -> Attendance.TYPE_PRESENT + } + + val attendanceObject = Attendance( + profileId, + id, + teacherId, + subjectId, + semester, + topic, + lessonDate, + startTime, + type + ) + + val addedDate = Date.fromIso(attendance.getString("AddDate") ?: return@forEach) + + data.attendanceList.add(attendanceObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_ATTENDANCE, + id, + profile?.empty ?: false, + profile?.empty ?: false, + addedDate + )) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCES, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index dd4e7fe9..bbff319c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -103,6 +103,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) val teamList = LongSparseArray() val lessonRanges = SparseArray() val gradeCategories = LongSparseArray() + val attendanceTypes = SparseArray>() private var mTeamClass: Team? = null var teamClass: Team? @@ -129,7 +130,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) var noticesToRemove: DataRemoveModel? = null val noticeList = mutableListOf() - var attendanceToRemove: DataRemoveModel? = null + var attendancesToRemove: DataRemoveModel? = null val attendanceList = mutableListOf() var announcementsToRemove: DataRemoveModel? = null @@ -304,4 +305,4 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) fun startProgress(stringRes: Int) { callback.onStartProgress(stringRes) } -} \ No newline at end of file +} From 42b56fa4a2b04f988defc313252c81a86e8598ab Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sun, 13 Oct 2019 16:17:39 +0200 Subject: [PATCH 047/691] [APIv2/Librus] Add getting announcements --- .../api/v2/librus/data/LibrusData.kt | 4 ++ .../librus/data/api/LibrusApiAnnouncements.kt | 68 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAnnouncements.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index c4e867f9..3420730b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -63,6 +63,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.sync_action_syncing_attendance) LibrusApiAttendances(data) { onSuccess() } } + ENDPOINT_LIBRUS_API_ANNOUNCEMENTS -> { + data.startProgress(R.string.sync_action_syncing_announcements) + LibrusApiAnnouncements(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAnnouncements.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAnnouncements.kt new file mode 100644 index 00000000..cb0c3014 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAnnouncements.kt @@ -0,0 +1,68 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-13 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ANNOUNCEMENTS +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.models.Date + +class LibrusApiAnnouncements(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiAnnouncements" + } + + init { + apiGet(TAG, "SchoolNotices") { json -> + val announcements = json.getJsonArray("SchoolNotices") + + announcements?.forEach { announcementEl -> + val announcement = announcementEl.asJsonObject + + val id = Utils.crc16(announcement.getString("Id")?.toByteArray() + ?: return@forEach).toLong() + val subject = announcement.getString("Subject") ?: "" + val text = announcement.getString("Content") ?: "" + val startDate = Date.fromY_m_d(announcement.getString("StartDate") + ?: return@forEach) + val endDate = Date.fromY_m_d(announcement.getString("EndDate") ?: return@forEach) + val teacherId = announcement.getJsonObject("AddedBy")?.getLong("Id") ?: -1 + + val announcementObject = Announcement( + profileId, + id, + subject, + text, + startDate, + endDate, + teacherId + ) + + val addedDate = Date.fromIso(announcement.getString("CreationDate") + ?: return@forEach) + val read = announcement.getBoolean("WasRead") ?: false + + data.announcementList.add(announcementObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_ANNOUNCEMENT, + id, + read, + read, + addedDate + )) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_ANNOUNCEMENTS, SYNC_ALWAYS) + onSuccess() + } + } +} From 70d35e12e5a934cec982e96ab9ea1ca7c94d20f6 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sun, 13 Oct 2019 16:34:44 +0200 Subject: [PATCH 048/691] [APIv2/Librus] Fix attendance metadata --- .../v2/librus/data/api/LibrusApiAttendances.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt index 28635ded..8a508ebb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt @@ -66,14 +66,16 @@ class LibrusApiAttendances(override val data: DataLibrus, val addedDate = Date.fromIso(attendance.getString("AddDate") ?: return@forEach) data.attendanceList.add(attendanceObject) - data.metadataList.add(Metadata( - profileId, - Metadata.TYPE_ATTENDANCE, - id, - profile?.empty ?: false, - profile?.empty ?: false, - addedDate - )) + if(type != Attendance.TYPE_PRESENT) { + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_ATTENDANCE, + id, + profile?.empty ?: false, + profile?.empty ?: false, + addedDate + )) + } } data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCES, SYNC_ALWAYS) From 6ff439b20d39308543b21c8cb7a44b632abdbf4c Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sun, 13 Oct 2019 17:00:54 +0200 Subject: [PATCH 049/691] [Database/Librus] Fix duplicate homework --- .../pl/szczodrzynski/edziennik/data/api/Librus.java | 2 +- .../pl/szczodrzynski/edziennik/data/db/AppDb.java | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java index cba9b250..32468093 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java @@ -2821,7 +2821,7 @@ public class Librus implements EdziennikInterface { ); eventList.add(eventObject); - metadataList.add(new Metadata(profileId, Metadata.TYPE_EVENT, eventObject.id, profile.getEmpty(), profile.getEmpty(), addedDate.getInMillis())); + metadataList.add(new Metadata(profileId, Metadata.TYPE_HOMEWORK, eventObject.id, profile.getEmpty(), profile.getEmpty(), addedDate.getInMillis())); } r("finish", "Homework"); } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java index 9f23d38b..b06a2135 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java @@ -87,7 +87,7 @@ import android.content.Context; DebugLog.class, EndpointTimer.class, LessonRange.class, - Metadata.class}, version = 57) + Metadata.class}, version = 58) @TypeConverters({ ConverterTime.class, ConverterDate.class, @@ -597,6 +597,12 @@ public abstract class AppDb extends RoomDatabase { database.execSQL("ALTER TABLE gradeCategories ADD type INTEGER NOT NULL DEFAULT 0"); } }; + private static final Migration MIGRATION_57_58 = new Migration(57, 58) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("UPDATE metadata SET thingType = 5 WHERE thingType = 4 AND thingId IN (SELECT eventId FROM events WHERE eventType = -1)"); + } + }; public static AppDb getDatabase(final Context context) { @@ -651,7 +657,8 @@ public abstract class AppDb extends RoomDatabase { MIGRATION_53_54, MIGRATION_54_55, MIGRATION_55_56, - MIGRATION_56_57 + MIGRATION_56_57, + MIGRATION_57_58 ) .allowMainThreadQueries() //.fallbackToDestructiveMigration() From 6b80d7cbd08512dbc9a392a3c5fe3e45c3b10d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 13 Oct 2019 19:40:56 +0200 Subject: [PATCH 050/691] [APIv2] Update Feature shouldSync method. --- .../pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt | 2 +- .../edziennik/api/v2/mobidziennik/Mobidziennik.kt | 2 +- .../pl/szczodrzynski/edziennik/api/v2/models/Feature.kt | 7 ++----- .../pl/szczodrzynski/edziennik/api/v2/template/Template.kt | 2 +- .../pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt | 2 +- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index d9cf3662..d782f721 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -69,7 +69,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va LibrusFeatures.filter { it.featureId == featureId // feature ID matches && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login - && it.shouldSync?.invoke(profile, loginStore) ?: true // is necessary/possible to sync + && it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync }.let { endpointList.addAll(it) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt index c8687faa..c0df3eb5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt @@ -70,7 +70,7 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto MobidziennikFeatures.filter { it.featureId == featureId // feature ID matches && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login - && it.shouldSync?.invoke(profile, loginStore) ?: true // is necessary/possible to sync + && it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync }.let { endpointList.addAll(it) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt index 91ac6563..380c34ad 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Feature.kt @@ -1,8 +1,5 @@ package pl.szczodrzynski.edziennik.api.v2.models -import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore -import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile - /** * A Endpoint descriptor class. * @@ -28,8 +25,8 @@ data class Feature( return this } - var shouldSync: ((Profile?, LoginStore) -> Boolean)? = null - fun withShouldSync(shouldSync: ((Profile?, LoginStore) -> Boolean)?): Feature { + var shouldSync: ((Data) -> Boolean)? = null + fun withShouldSync(shouldSync: ((Data) -> Boolean)?): Feature { this.shouldSync = shouldSync return this } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt index a639e65e..0c1da62d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt @@ -73,7 +73,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, VulcanFeatures.filter { it.featureId == featureId // feature ID matches && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login - && it.shouldSync?.invoke(profile, loginStore) ?: true // is necessary/possible to sync + && it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync }.let { endpointList.addAll(it) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt index d5af284f..ae8362bc 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt @@ -72,7 +72,7 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va VulcanFeatures.filter { it.featureId == featureId // feature ID matches && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login - && it.shouldSync?.invoke(profile, loginStore) ?: true // is necessary/possible to sync + && it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync }.let { endpointList.addAll(it) } From da9ccf6d29e749d4eb67271eb9917dd598dd5278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 13 Oct 2019 20:06:45 +0200 Subject: [PATCH 051/691] [APIv2] Update Feature lists and progress strings. --- .../edziennik/api/v2/librus/LibrusFeatures.kt | 99 ++++++++++++++- .../api/v2/librus/data/LibrusData.kt | 16 +-- .../v2/mobidziennik/MobidziennikFeatures.kt | 120 ++++++++---------- app/src/main/res/values/strings.xml | 4 + 4 files changed, 160 insertions(+), 79 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt index a71b74d7..a565cb38 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt @@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData import pl.szczodrzynski.edziennik.api.v2.models.Feature const val ENDPOINT_LIBRUS_API_ME = 1001 @@ -15,6 +16,7 @@ const val ENDPOINT_LIBRUS_API_UNITS = 1005 const val ENDPOINT_LIBRUS_API_USERS = 1006 const val ENDPOINT_LIBRUS_API_SUBJECTS = 1007 const val ENDPOINT_LIBRUS_API_CLASSROOMS = 1008 +const val ENDPOINT_LIBRUS_API_PUSH_CONFIG = 1010 const val ENDPOINT_LIBRUS_API_TIMETABLES = 1015 const val ENDPOINT_LIBRUS_API_SUBSTITUTIONS = 1016 const val ENDPOINT_LIBRUS_API_NORMAL_GC = 1021 @@ -51,11 +53,28 @@ const val ENDPOINT_LIBRUS_MESSAGES_GET = 3040 val LibrusFeatures = listOf( - // LIBRUS: API + // push config + Feature(LOGIN_TYPE_LIBRUS, FEATURE_PUSH_CONFIG, listOf( + ENDPOINT_LIBRUS_API_PUSH_CONFIG to LOGIN_METHOD_LIBRUS_API + ), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data -> + data.app.appConfig.fcmTokens[LOGIN_TYPE_LIBRUS]?.second?.contains(data.profileId) == false + }, + + + + + + /** + * Timetable - using API. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_TIMETABLE, listOf( ENDPOINT_LIBRUS_API_TIMETABLES to LOGIN_METHOD_LIBRUS_API, ENDPOINT_LIBRUS_API_SUBSTITUTIONS to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + /** + * Agenda - using API. + * Events, Parent-teacher meetings, free days (teacher/school/class). + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_AGENDA, listOf( ENDPOINT_LIBRUS_API_EVENTS to LOGIN_METHOD_LIBRUS_API, ENDPOINT_LIBRUS_API_EVENT_TYPES to LOGIN_METHOD_LIBRUS_API, @@ -64,6 +83,10 @@ val LibrusFeatures = listOf( ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS to LOGIN_METHOD_LIBRUS_API, ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + /** + * Grades - using API. + * All grades + categories. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( ENDPOINT_LIBRUS_API_NORMAL_GC to LOGIN_METHOD_LIBRUS_API, ENDPOINT_LIBRUS_API_POINT_GC to LOGIN_METHOD_LIBRUS_API, @@ -78,65 +101,127 @@ val LibrusFeatures = listOf( ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES to LOGIN_METHOD_LIBRUS_API, ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + /** + * Homework - using API. + * Sync only if account has premium access. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf( ENDPOINT_LIBRUS_API_HOMEWORK to LOGIN_METHOD_LIBRUS_API - ), listOf(LOGIN_METHOD_LIBRUS_API)), + ), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data -> + (data as DataLibrus).isPremium + }, + /** + * Behaviour - using API. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_BEHAVIOUR, listOf( ENDPOINT_LIBRUS_API_NOTICES to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + /** + * Attendance - using API. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_ATTENDANCE, listOf( ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES to LOGIN_METHOD_LIBRUS_API, ENDPOINT_LIBRUS_API_ATTENDANCES to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + /** + * Announcements - using API. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_ANNOUNCEMENTS, listOf( ENDPOINT_LIBRUS_API_ANNOUNCEMENTS to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + + + + + + /** + * Student info - using API. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf( ENDPOINT_LIBRUS_API_ME to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + /** + * School info - using API. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_SCHOOL_INFO, listOf( ENDPOINT_LIBRUS_API_SCHOOLS to LOGIN_METHOD_LIBRUS_API, ENDPOINT_LIBRUS_API_UNITS to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + /** + * Class info - using API. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_CLASS_INFO, listOf( ENDPOINT_LIBRUS_API_CLASSES to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + /** + * Team info - using API. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_TEAM_INFO, listOf( ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + /** + * Lucky number - using API. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_LUCKY_NUMBER, listOf( ENDPOINT_LIBRUS_API_LUCKY_NUMBER to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + /** + * Teacher list - using API. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_TEACHERS, listOf( ENDPOINT_LIBRUS_API_USERS to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + /** + * Subject list - using API. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_SUBJECTS, listOf( ENDPOINT_LIBRUS_API_SUBJECTS to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + /** + * Classroom list - using API. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_CLASSROOMS, listOf( ENDPOINT_LIBRUS_API_CLASSROOMS to LOGIN_METHOD_LIBRUS_API ), listOf(LOGIN_METHOD_LIBRUS_API)), + /** + * Student info - using synergia scrapper. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf( ENDPOINT_LIBRUS_SYNERGIA_INFO to LOGIN_METHOD_LIBRUS_SYNERGIA ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)), + /** + * Student number - using synergia scrapper. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_NUMBER, listOf( ENDPOINT_LIBRUS_SYNERGIA_INFO to LOGIN_METHOD_LIBRUS_SYNERGIA ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)), + + + /** + * Grades - using API + synergia scrapper. + */ + /*Feature(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( + ENDPOINT_LIBRUS_API_NORMAL_GC to LOGIN_METHOD_LIBRUS_API, + ENDPOINT_LIBRUS_API_NORMAL_GRADES to LOGIN_METHOD_LIBRUS_API, + ENDPOINT_LIBRUS_SYNERGIA_GRADES to LOGIN_METHOD_LIBRUS_SYNERGIA + ), listOf(LOGIN_METHOD_LIBRUS_API, LOGIN_METHOD_LIBRUS_SYNERGIA)),*/ /*Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( ENDPOINT_LIBRUS_SYNERGIA_GRADES to LOGIN_METHOD_LIBRUS_SYNERGIA ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)),*/ - Feature(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf( - ENDPOINT_LIBRUS_API_NORMAL_GC to LOGIN_METHOD_LIBRUS_API, - ENDPOINT_LIBRUS_API_NORMAL_GRADES to LOGIN_METHOD_LIBRUS_API, - ENDPOINT_LIBRUS_SYNERGIA_GRADES to LOGIN_METHOD_LIBRUS_SYNERGIA - ), listOf(LOGIN_METHOD_LIBRUS_API, LOGIN_METHOD_LIBRUS_SYNERGIA)), + + /** + * Messages inbox - using messages website. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_INBOX, listOf( ENDPOINT_LIBRUS_MESSAGES_RECEIVED to LOGIN_METHOD_LIBRUS_MESSAGES ), listOf(LOGIN_METHOD_LIBRUS_MESSAGES)), + /** + * Messages sent - using messages website. + */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_SENT, listOf( ENDPOINT_LIBRUS_MESSAGES_SENT to LOGIN_METHOD_LIBRUS_MESSAGES ), listOf(LOGIN_METHOD_LIBRUS_MESSAGES)) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index 3420730b..16c944fe 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -36,35 +36,35 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { Utils.d(TAG, "Using endpoint $endpointId") when (endpointId) { ENDPOINT_LIBRUS_API_ME -> { - data.startProgress(R.string.sync_action_getting_account) + data.startProgress(R.string.edziennik_progress_endpoint_student_info) LibrusApiMe(data) { onSuccess() } } ENDPOINT_LIBRUS_API_SCHOOLS -> { - data.startProgress(R.string.sync_action_syncing_school_info) + data.startProgress(R.string.edziennik_progress_endpoint_school_info) LibrusApiSchools(data) { onSuccess() } } ENDPOINT_LIBRUS_API_NORMAL_GRADES -> { - data.startProgress(R.string.sync_action_syncing_grades) + data.startProgress(R.string.edziennik_progress_endpoint_grades) LibrusApiGrades(data) { onSuccess() } } ENDPOINT_LIBRUS_API_EVENTS -> { - data.startProgress(R.string.sync_action_syncing_events) + data.startProgress(R.string.edziennik_progress_endpoint_events) LibrusApiEvents(data) { onSuccess() } } ENDPOINT_LIBRUS_API_HOMEWORK -> { - data.startProgress(R.string.sync_action_syncing_homework) + data.startProgress(R.string.edziennik_progress_endpoint_homework) LibrusApiHomework(data) { onSuccess() } } ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES -> { - data.startProgress(R.string.sync_action_syncing_attendance_types) + data.startProgress(R.string.edziennik_progress_endpoint_attendance_types) LibrusApiAttendanceTypes(data) { onSuccess() } } ENDPOINT_LIBRUS_API_ATTENDANCES -> { - data.startProgress(R.string.sync_action_syncing_attendance) + data.startProgress(R.string.edziennik_progress_endpoint_attendance) LibrusApiAttendances(data) { onSuccess() } } ENDPOINT_LIBRUS_API_ANNOUNCEMENTS -> { - data.startProgress(R.string.sync_action_syncing_announcements) + data.startProgress(R.string.edziennik_progress_endpoint_announcements) LibrusApiAnnouncements(data) { onSuccess() } } else -> onSuccess() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt index 0e92fe6d..788ad4af 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt @@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_PUSH_CONFIG import pl.szczodrzynski.edziennik.api.v2.models.Feature const val ENDPOINT_MOBIDZIENNIK_API_MAIN = 1000 @@ -20,80 +21,71 @@ const val ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL = 2200 const val ENDPOINT_MOBIDZIENNIK_API2_MAIN = 3000 val MobidziennikFeatures = listOf( - // timetable - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_TIMETABLE, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), - // agenda - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_AGENDA, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB, - ENDPOINT_MOBIDZIENNIK_WEB_CALENDAR to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB, LOGIN_METHOD_MOBIDZIENNIK_WEB)), - // grades - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_GRADES, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB, - ENDPOINT_MOBIDZIENNIK_WEB_GRADES to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB, LOGIN_METHOD_MOBIDZIENNIK_WEB)), - // homework - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_HOMEWORK, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), - // behaviour - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_BEHAVIOUR, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB, - ENDPOINT_MOBIDZIENNIK_WEB_NOTICES to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB, LOGIN_METHOD_MOBIDZIENNIK_WEB)), - // attendance - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ATTENDANCE, listOf( - ENDPOINT_MOBIDZIENNIK_WEB_ATTENDANCE to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), - - // messages - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_MESSAGES_INBOX, listOf( - ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX to LOGIN_METHOD_MOBIDZIENNIK_WEB, - ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_MESSAGES_SENT, listOf( - ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_SENT to LOGIN_METHOD_MOBIDZIENNIK_WEB, - ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), - + // always synced Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ALWAYS_NEEDED, listOf( ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB, ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL to LOGIN_METHOD_MOBIDZIENNIK_WEB ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), + // push config Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_PUSH_CONFIG, listOf( ENDPOINT_MOBIDZIENNIK_API2_MAIN to LOGIN_METHOD_MOBIDZIENNIK_API2 - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_API2)), - /*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_STUDENT_INFO, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_STUDENT_NUMBER, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_SCHOOL_INFO, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_CLASS_INFO, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_TEAM_INFO, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_TEACHERS, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_SUBJECTS, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_CLASSROOMS, listOf( - ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB + ), listOf(LOGIN_METHOD_MOBIDZIENNIK_API2)).withShouldSync { data -> + data.app.appConfig.fcmTokens[LOGIN_TYPE_MOBIDZIENNIK]?.second?.contains(data.profileId) == false + }, + + + + + + /** + * Agenda - "API" + web scraping. + */ + Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_AGENDA, listOf( + ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB, + ENDPOINT_MOBIDZIENNIK_WEB_CALENDAR to LOGIN_METHOD_MOBIDZIENNIK_WEB + ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB, LOGIN_METHOD_MOBIDZIENNIK_WEB)), + /** + * Grades - "API" + web scraping. + */ + Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_GRADES, listOf( + ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB, + ENDPOINT_MOBIDZIENNIK_WEB_GRADES to LOGIN_METHOD_MOBIDZIENNIK_WEB + ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB, LOGIN_METHOD_MOBIDZIENNIK_WEB)), + /** + * Behaviour - "API" + web scraping. + */ + Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_BEHAVIOUR, listOf( + ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB, + ENDPOINT_MOBIDZIENNIK_WEB_NOTICES to LOGIN_METHOD_MOBIDZIENNIK_WEB + ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB, LOGIN_METHOD_MOBIDZIENNIK_WEB)), + // attendance TODO implement website attendance scraping + /*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ATTENDANCE, listOf( + ENDPOINT_MOBIDZIENNIK_WEB_ATTENDANCE to LOGIN_METHOD_MOBIDZIENNIK_WEB ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),*/ + + + + + /** + * Messages inbox - using web scraper. + */ + Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_MESSAGES_INBOX, listOf( + ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX to LOGIN_METHOD_MOBIDZIENNIK_WEB, + ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL to LOGIN_METHOD_MOBIDZIENNIK_WEB + ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), + /** + * Messages sent - using web scraper. + */ + Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_MESSAGES_SENT, listOf( + ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_SENT to LOGIN_METHOD_MOBIDZIENNIK_WEB, + ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL to LOGIN_METHOD_MOBIDZIENNIK_WEB + ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)) + // lucky number possibilities // all endpoints that may supply the lucky number - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_LUCKY_NUMBER, listOf( + /*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_LUCKY_NUMBER, listOf( ENDPOINT_MOBIDZIENNIK_WEB_MANUALS to LOGIN_METHOD_MOBIDZIENNIK_WEB ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)).apply { priority = 10 }, @@ -111,6 +103,6 @@ val MobidziennikFeatures = listOf( Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_LUCKY_NUMBER, listOf( ENDPOINT_MOBIDZIENNIK_WEB_ATTENDANCE to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)).apply { priority = 4 } + ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)).apply { priority = 4 }*/ ) \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4ffc89a7..bba42794 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -939,4 +939,8 @@ Pobieram frekwencję ucznia... Logowanie do Vulcan API... Logowanie do API MobiDziennika... + Pobieranie wydarzeń kalendarza... + Pobieranie zadań domowych... + Pobieranie kategorii obecności... + Pobieranie ogłoszeń szkolnych... From b59887d4e0966de4960ab83de4bba0789b0cc0b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 14 Oct 2019 10:02:24 +0200 Subject: [PATCH 052/691] [APIv2/Mobidziennik] Change all messages list sync frequency. --- .../v2/mobidziennik/data/web/MobidziennikWebMessagesAll.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebMessagesAll.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebMessagesAll.kt index 2c246620..a9581acb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebMessagesAll.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikWebMessagesAll.kt @@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web import org.jsoup.Jsoup +import pl.szczodrzynski.edziennik.DAY import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL import pl.szczodrzynski.edziennik.api.v2.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX @@ -32,7 +33,7 @@ class MobidziennikWebMessagesAll(override val data: DataMobidziennik, val listElement = doc.getElementsByClass("spis").first() if (listElement == null) { - data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL, SYNC_ALWAYS) + data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL, 7*DAY) onSuccess() return@webGet } @@ -82,7 +83,9 @@ class MobidziennikWebMessagesAll(override val data: DataMobidziennik, data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, addedDate)) } - data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL, SYNC_ALWAYS) + // sync every 7 days as we probably don't except more than + // 30 received messages during a week, without any normal sync + data.setSyncNext(ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_ALL, 7*DAY) onSuccess() } } From b35df5ef112199071a761cf6dfb99bd30f1af452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 14 Oct 2019 10:25:44 +0200 Subject: [PATCH 053/691] [APIv2] Add API first login method. Better cancellation handling. --- .../edziennik/api/v2/ApiService.kt | 34 +++++++++++++------ .../v2/events/requests/FirstLoginRequest.kt | 10 ++++++ .../api/v2/interfaces/EdziennikInterface.kt | 1 + .../edziennik/api/v2/librus/Librus.kt | 4 +++ .../api/v2/librus/data/LibrusData.kt | 8 ++--- .../api/v2/librus/login/LibrusLogin.kt | 8 ++--- .../api/v2/mobidziennik/Mobidziennik.kt | 4 +++ .../v2/mobidziennik/data/MobidziennikData.kt | 8 ++--- .../mobidziennik/login/MobidziennikLogin.kt | 8 ++--- .../edziennik/api/v2/template/Template.kt | 4 +++ .../api/v2/template/data/TemplateData.kt | 8 ++--- .../api/v2/template/login/TemplateLogin.kt | 8 ++--- .../edziennik/api/v2/vulcan/Vulcan.kt | 4 +++ .../api/v2/vulcan/data/VulcanData.kt | 8 ++--- .../api/v2/vulcan/login/VulcanLogin.kt | 8 ++--- app/src/main/res/values/strings.xml | 1 + 16 files changed, 84 insertions(+), 42 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/FirstLoginRequest.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt index f76a792b..4ffc122c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt @@ -87,6 +87,7 @@ class ApiService : Service() { apiError.throwable?.printStackTrace() if (apiError.isCritical) { notification.setCriticalError().post() + taskRunningObject = null taskRunning = false taskRunningId = -1 sync() @@ -148,18 +149,30 @@ class ApiService : Service() { return } - // get the requested profile and login store - val profile: Profile? = app.db.profileDao().getByIdNow(task.profileId) - if (profile == null || !profile.syncEnabled) { - return + val profile: Profile? + val loginStore: LoginStore + if (task is FirstLoginRequest) { + // get the requested profile and login store + profile = null + loginStore = task.loginStore + // save the profile ID and name as the current task's + taskProfileId = -1 + taskProfileName = getString(R.string.edziennik_notification_api_first_login_title) } - val loginStore: LoginStore? = app.db.loginStoreDao().getByIdNow(profile.loginStoreId) - if (loginStore == null) { - return + else { + // get the requested profile and login store + profile = app.db.profileDao().getByIdNow(task.profileId) + if (profile == null || !profile.syncEnabled) { + return + } + loginStore = app.db.loginStoreDao().getByIdNow(profile.loginStoreId) + if (loginStore == null) { + return + } + // save the profile ID and name as the current task's + taskProfileId = profile.id + taskProfileName = profile.name } - // save the profile ID and name as the current task's - taskProfileId = profile.id - taskProfileName = profile.name taskProgress = 0 taskProgressRes = null @@ -182,6 +195,7 @@ class ApiService : Service() { featureIds = task.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) } ?: Features.getAllIds(), viewId = task.viewIds?.get(0)?.first) is MessageGetRequest -> edziennikInterface?.getMessage(task.messageId) + is FirstLoginRequest -> edziennikInterface?.firstLogin() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/FirstLoginRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/FirstLoginRequest.kt new file mode 100644 index 00000000..75e92793 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/FirstLoginRequest.kt @@ -0,0 +1,10 @@ +package pl.szczodrzynski.edziennik.api.v2.events.requests + +import pl.szczodrzynski.edziennik.api.v2.models.ApiTask +import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore + +data class FirstLoginRequest(val loginStore: LoginStore) : ApiTask(-1) { + override fun toString(): String { + return "FirstLoginRequest(loginStore=$loginStore)" + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt index 8a2b6076..20535a0e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt @@ -7,5 +7,6 @@ package pl.szczodrzynski.edziennik.api.v2.interfaces interface EdziennikInterface { fun sync(featureIds: List, viewId: Int? = null) fun getMessage(messageId: Int) + fun firstLogin() fun cancel() } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index d782f721..c1d479bf 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -132,6 +132,10 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va } + override fun firstLogin() { + // TODO + } + override fun cancel() { data.cancel() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index 16c944fe..cab131f4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -23,11 +23,11 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { onSuccess() return } + if (data.cancelled) { + onSuccess() + return + } useEndpoint(data.targetEndpointIds.removeAt(0)) { - if (data.cancelled) { - onSuccess() - return@useEndpoint - } nextEndpoint(onSuccess) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLogin.kt index b0dea3d4..6cc54567 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLogin.kt @@ -28,13 +28,13 @@ class LibrusLogin(val data: DataLibrus, val onSuccess: () -> Unit) { onSuccess() return } + if (cancelled) { + onSuccess() + return + } useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId -> if (usedMethodId != -1) data.loginMethods.add(usedMethodId) - if (cancelled) { - onSuccess() - return@useLoginMethod - } nextLoginMethod(onSuccess) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt index c0df3eb5..e62dd984 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt @@ -133,6 +133,10 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto } + override fun firstLogin() { + // TODO + } + override fun cancel() { Utils.d(TAG, "Cancelled") cancelled = true diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt index 304f036e..553c1e0d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt @@ -27,11 +27,11 @@ class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) { onSuccess() return } + if (data.cancelled) { + onSuccess() + return + } useEndpoint(data.targetEndpointIds.removeAt(0)) { - if (data.cancelled) { - onSuccess() - return@useEndpoint - } nextEndpoint(onSuccess) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLogin.kt index 4baf451c..e6b17e1a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLogin.kt @@ -26,13 +26,13 @@ class MobidziennikLogin(val data: DataMobidziennik, val onSuccess: () -> Unit) { onSuccess() return } + if (cancelled) { + onSuccess() + return + } useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId -> if (usedMethodId != -1) data.loginMethods.add(usedMethodId) - if (cancelled) { - onSuccess() - return@useLoginMethod - } nextLoginMethod(onSuccess) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt index 0c1da62d..bdf8e0dd 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt @@ -136,6 +136,10 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, } + override fun firstLogin() { + // TODO + } + override fun cancel() { Utils.d(TAG, "Cancelled") cancelled = true diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/TemplateData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/TemplateData.kt index 35b2f004..9aef7cbe 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/TemplateData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/TemplateData.kt @@ -30,11 +30,11 @@ class TemplateData(val data: DataTemplate, val onSuccess: () -> Unit) { onSuccess() return } + if (cancelled) { + onSuccess() + return + } useEndpoint(data.targetEndpointIds.removeAt(0)) { - if (cancelled) { - onSuccess() - return@useEndpoint - } nextEndpoint(onSuccess) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/login/TemplateLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/login/TemplateLogin.kt index 111f0fd1..1f08ef44 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/login/TemplateLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/login/TemplateLogin.kt @@ -25,13 +25,13 @@ class TemplateLogin(val data: DataTemplate, val onSuccess: () -> Unit) { onSuccess() return } + if (cancelled) { + onSuccess() + return + } useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId -> if (usedMethodId != -1) data.loginMethods.add(usedMethodId) - if (cancelled) { - onSuccess() - return@useLoginMethod - } nextLoginMethod(onSuccess) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt index ae8362bc..70d852f9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt @@ -135,6 +135,10 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va } + override fun firstLogin() { + // TODO + } + override fun cancel() { Utils.d(TAG, "Cancelled") cancelled = true diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt index 554c060a..1023cb4f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt @@ -23,11 +23,11 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { onSuccess() return } + if (cancelled) { + onSuccess() + return + } useEndpoint(data.targetEndpointIds.removeAt(0)) { - if (cancelled) { - onSuccess() - return@useEndpoint - } nextEndpoint(onSuccess) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLogin.kt index 6ecfeb19..8cfdb960 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLogin.kt @@ -25,13 +25,13 @@ class VulcanLogin(val data: DataVulcan, val onSuccess: () -> Unit) { onSuccess() return } + if (cancelled) { + onSuccess() + return + } useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId -> if (usedMethodId != -1) data.loginMethods.add(usedMethodId) - if (cancelled) { - onSuccess() - return@useLoginMethod - } nextLoginMethod(onSuccess) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bba42794..b8767b62 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -943,4 +943,5 @@ Pobieranie zadań domowych... Pobieranie kategorii obecności... Pobieranie ogłoszeń szkolnych... + Pierwsze logowanie From bdc0ceb11d8c378ca91d44a3cc265cada4c85a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 14 Oct 2019 10:59:11 +0200 Subject: [PATCH 054/691] [APIv2] Simplify endpoint choosing. Optimize imports. --- .../edziennik/api/v2/EndpointChooser.kt | 80 ++++++++++++++++++ .../edziennik/api/v2/librus/Librus.kt | 80 ++---------------- .../api/v2/mobidziennik/Mobidziennik.kt | 84 ++----------------- .../edziennik/api/v2/models/Data.kt | 20 ----- .../edziennik/api/v2/template/Template.kt | 83 +----------------- .../edziennik/api/v2/vulcan/Vulcan.kt | 82 +----------------- 6 files changed, 99 insertions(+), 330 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EndpointChooser.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EndpointChooser.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EndpointChooser.kt new file mode 100644 index 00000000..9a8a5bed --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EndpointChooser.kt @@ -0,0 +1,80 @@ +package pl.szczodrzynski.edziennik.api.v2 + +import pl.szczodrzynski.edziennik.api.v2.models.Data +import pl.szczodrzynski.edziennik.api.v2.models.Feature +import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod +import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_NEVER + +fun Data.prepare(loginMethods: List, features: List, featureIds: List, viewId: Int?) { + val data = this + + val possibleLoginMethods = data.loginMethods.toMutableList() + + for (loginMethod in loginMethods) { + if (loginMethod.isPossible(profile, loginStore)) + possibleLoginMethods += loginMethod.loginMethodId + } + + //var highestLoginMethod = 0 + var endpointList = mutableListOf() + val requiredLoginMethods = mutableListOf() + + data.targetEndpointIds.clear() + data.targetLoginMethodIds.clear() + + // get all endpoints for every feature, only if possible to login and possible/necessary to sync + for (featureId in featureIds) { + features.filter { + it.featureId == featureId // feature ID matches + && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login + && it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync + }.let { + endpointList.addAll(it) + } + } + + val timestamp = System.currentTimeMillis() + + endpointList = endpointList + // sort the endpoint list by feature ID and priority + .sortedWith(compareBy(Feature::featureId, Feature::priority)) + // select only the most important endpoint for each feature + .distinctBy { it.featureId } + .toMutableList() + // add all endpoint IDs and required login methods, filtering using timers + .onEach { feature -> + feature.endpointIds.forEach { endpoint -> + (data.endpointTimers + .singleOrNull { it.endpointId == endpoint.first } ?: EndpointTimer(data.profile?.id ?: -1, endpoint.first)) + .let { timer -> + if (timer.nextSync == SYNC_ALWAYS || + (viewId != null && timer.viewId == viewId) || + (timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp)) { + data.targetEndpointIds.add(endpoint.first) + requiredLoginMethods.add(endpoint.second) + } + } + } + } + + // check every login method for any dependencies + for (loginMethodId in requiredLoginMethods) { + var requiredLoginMethod: Int? = loginMethodId + while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) { + loginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod -> + if (requiredLoginMethod != null) + data.targetLoginMethodIds.add(requiredLoginMethod!!) + requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore) + } + } + } + + // sort and distinct every login method and endpoint + data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList() + data.targetLoginMethodIds.sort() + + data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList() + data.targetEndpointIds.sort() +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index c1d479bf..22fcef35 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -7,18 +7,16 @@ package pl.szczodrzynski.edziennik.api.v2.librus import android.util.Log import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 -import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_NOT_NEEDED import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods -import pl.szczodrzynski.edziennik.api.v2.mobidziennik.MobidziennikFeatures import pl.szczodrzynski.edziennik.api.v2.models.ApiError -import pl.szczodrzynski.edziennik.api.v2.models.Feature -import pl.szczodrzynski.edziennik.data.db.modules.api.* +import pl.szczodrzynski.edziennik.api.v2.prepare import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile +import pl.szczodrzynski.edziennik.utils.Utils class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { companion object { @@ -27,13 +25,12 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va val internalErrorList = mutableListOf() val data: DataLibrus - private var cancelled = false init { data = DataLibrus(app, profile, loginStore).apply { callback = wrapCallback(this@Librus.callback) + satisfyLoginMethods() } - data.satisfyLoginMethods() } private fun completed() { @@ -50,77 +47,9 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va __/ | |__*/ override fun sync(featureIds: List, viewId: Int?) { - val possibleLoginMethods = data.loginMethods.toMutableList() - - for (loginMethod in librusLoginMethods) { - if (loginMethod.isPossible(profile, loginStore)) - possibleLoginMethods += loginMethod.loginMethodId - } - - //var highestLoginMethod = 0 - var endpointList = mutableListOf() - val requiredLoginMethods = mutableListOf() - - data.targetEndpointIds.clear() - data.targetLoginMethodIds.clear() - - // get all endpoints for every feature, only if possible to login and possible/necessary to sync - for (featureId in featureIds) { - LibrusFeatures.filter { - it.featureId == featureId // feature ID matches - && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login - && it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync - }.let { - endpointList.addAll(it) - } - } - - val timestamp = System.currentTimeMillis() - - endpointList = endpointList - // sort the endpoint list by feature ID and priority - .sortedWith(compareBy(Feature::featureId, Feature::priority)) - // select only the most important endpoint for each feature - .distinctBy { it.featureId } - .toMutableList() - // add all endpoint IDs and required login methods, filtering using timers - .onEach { feature -> - feature.endpointIds.forEach { endpoint -> - (data.endpointTimers - .singleOrNull { it.endpointId == endpoint.first } ?: EndpointTimer(data.profile?.id ?: -1, endpoint.first)) - .let { timer -> - if (timer.nextSync == SYNC_ALWAYS || - (viewId != null && timer.viewId == viewId) || - (timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp)) { - data.targetEndpointIds.add(endpoint.first) - requiredLoginMethods.add(endpoint.second) - } - } - } - } - - // check every login method for any dependencies - for (loginMethodId in requiredLoginMethods) { - var requiredLoginMethod: Int? = loginMethodId - while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) { - librusLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod -> - if (requiredLoginMethod != null) - data.targetLoginMethodIds.add(requiredLoginMethod!!) - requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore) - } - } - } - - // sort and distinct every login method and endpoint - data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList() - data.targetLoginMethodIds.sort() - - data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList() - data.targetEndpointIds.sort() - + data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId) Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") - LibrusLogin(data) { LibrusData(data) { completed() @@ -137,6 +66,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va } override fun cancel() { + Utils.d(TAG, "Cancelled") data.cancel() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt index e62dd984..a0f0e0b5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt @@ -6,17 +6,14 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik import android.util.Log import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface -import pl.szczodrzynski.edziennik.api.v2.librus.LibrusFeatures import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikData import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLogin +import pl.szczodrzynski.edziennik.api.v2.mobidziennikLoginMethods import pl.szczodrzynski.edziennik.api.v2.models.ApiError -import pl.szczodrzynski.edziennik.api.v2.models.Feature -import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer -import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS -import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_NEVER +import pl.szczodrzynski.edziennik.api.v2.prepare import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.utils.Utils @@ -28,13 +25,12 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto val internalErrorList = mutableListOf() val data: DataMobidziennik - private var cancelled = false init { data = DataMobidziennik(app, profile, loginStore).apply { callback = wrapCallback(this@Mobidziennik.callback) + satisfyLoginMethods() } - data.satisfyLoginMethods() } private fun completed() { @@ -51,77 +47,9 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto __/ | |__*/ override fun sync(featureIds: List, viewId: Int?) { - val possibleLoginMethods = data.loginMethods.toMutableList() - - for (loginMethod in mobidziennikLoginMethods) { - if (loginMethod.isPossible(profile, loginStore)) - possibleLoginMethods += loginMethod.loginMethodId - } - - //var highestLoginMethod = 0 - var endpointList = mutableListOf() - val requiredLoginMethods = mutableListOf() - - data.targetEndpointIds.clear() - data.targetLoginMethodIds.clear() - - // get all endpoints for every feature, only if possible to login and possible/necessary to sync - for (featureId in featureIds) { - MobidziennikFeatures.filter { - it.featureId == featureId // feature ID matches - && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login - && it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync - }.let { - endpointList.addAll(it) - } - } - - val timestamp = System.currentTimeMillis() - - endpointList = endpointList - // sort the endpoint list by feature ID and priority - .sortedWith(compareBy(Feature::featureId, Feature::priority)) - // select only the most important endpoint for each feature - .distinctBy { it.featureId } - .toMutableList() - // add all endpoint IDs and required login methods, filtering using timers - .onEach { feature -> - feature.endpointIds.forEach { endpoint -> - (data.endpointTimers - .singleOrNull { it.endpointId == endpoint.first } ?: EndpointTimer(data.profile?.id ?: -1, endpoint.first)) - .let { timer -> - if (timer.nextSync == SYNC_ALWAYS || - (viewId != null && timer.viewId == viewId) || - (timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp)) { - data.targetEndpointIds.add(endpoint.first) - requiredLoginMethods.add(endpoint.second) - } - } - } - } - - // check every login method for any dependencies - for (loginMethodId in requiredLoginMethods) { - var requiredLoginMethod: Int? = loginMethodId - while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) { - mobidziennikLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod -> - if (requiredLoginMethod != null) - data.targetLoginMethodIds.add(requiredLoginMethod!!) - requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore) - } - } - } - - // sort and distinct every login method and endpoint - data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList() - data.targetLoginMethodIds.sort() - - data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList() - data.targetEndpointIds.sort() - + data.prepare(mobidziennikLoginMethods, MobidziennikFeatures, featureIds, viewId) Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") - MobidziennikLogin(data) { MobidziennikData(data) { completed() @@ -139,7 +67,7 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto override fun cancel() { Utils.d(TAG, "Cancelled") - cancelled = true + data.cancel() } private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index bbff319c..0f997738 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -2,7 +2,6 @@ package pl.szczodrzynski.edziennik.api.v2.models import android.util.LongSparseArray import android.util.SparseArray -import androidx.core.util.isNotEmpty import com.google.gson.JsonObject import im.wangchao.mhttp.Response import pl.szczodrzynski.edziennik.App @@ -84,18 +83,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) */ var endpointArgs = mutableMapOf() - /** - * A list of per-endpoint next sync time descriptors. - * - * [EndpointTimer.nextSync] may be: - * - [pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_NEVER] to never sync the endpoint (pretty useless) - * - [pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS] to sync the endpoint during every sync - * - [pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_IF_EXPLICIT] to sync the endpoint only if the matching - * feature ID is in the input set - * - [pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_IF_EXPLICIT_OR_ALL] to sync if the matching feature ID - * is in the input set OR the sync covers all feature IDs - * - a Unix-epoch timestamp (in millis) to sync the endpoint if [System.currentTimeMillis] is greater or equal to this value - */ var endpointTimers = mutableListOf() val teacherList = LongSparseArray() @@ -148,9 +135,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) val db by lazy { app.db } init { - clear() - if (profile != null) { endpointTimers = db.endpointTimerDao().getAllNow(profile.id).toMutableList() db.teacherDao().getAllNow(profileId).toSparseArray(teacherList) { it.id } @@ -159,11 +144,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) db.lessonRangeDao().getAllNow(profileId).toSparseArray(lessonRanges) { it.lessonNumber } db.gradeCategoryDao().getAllNow(profileId).toSparseArray(gradeCategories) { it.categoryId } } - - /*val teacher = teachers.byNameFirstLast("Jan Kowalski") ?: Teacher(1, 1, "", "").let { - teachers.add(it) - }*/ - } fun clear() { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt index bdf8e0dd..4f2f6580 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt @@ -7,19 +7,13 @@ package pl.szczodrzynski.edziennik.api.v2.template import android.util.Log import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 -import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_NOT_NEEDED import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface -import pl.szczodrzynski.edziennik.api.v2.librus.LibrusFeatures import pl.szczodrzynski.edziennik.api.v2.models.ApiError -import pl.szczodrzynski.edziennik.api.v2.models.Feature +import pl.szczodrzynski.edziennik.api.v2.prepare import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateData import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLogin import pl.szczodrzynski.edziennik.api.v2.templateLoginMethods -import pl.szczodrzynski.edziennik.api.v2.vulcan.VulcanFeatures -import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer -import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS -import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_NEVER import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.utils.Utils @@ -31,13 +25,12 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, val internalErrorList = mutableListOf() val data: DataTemplate - private var cancelled = false init { data = DataTemplate(app, profile, loginStore).apply { callback = wrapCallback(this@Template.callback) + satisfyLoginMethods() } - data.satisfyLoginMethods() } private fun completed() { @@ -54,77 +47,9 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, __/ | |__*/ override fun sync(featureIds: List, viewId: Int?) { - val possibleLoginMethods = data.loginMethods.toMutableList() - - for (loginMethod in templateLoginMethods) { - if (loginMethod.isPossible(profile, loginStore)) - possibleLoginMethods += loginMethod.loginMethodId - } - - //var highestLoginMethod = 0 - var endpointList = mutableListOf() - val requiredLoginMethods = mutableListOf() - - data.targetEndpointIds.clear() - data.targetLoginMethodIds.clear() - - // get all endpoints for every feature, only if possible to login and possible/necessary to sync - for (featureId in featureIds) { - VulcanFeatures.filter { - it.featureId == featureId // feature ID matches - && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login - && it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync - }.let { - endpointList.addAll(it) - } - } - - val timestamp = System.currentTimeMillis() - - endpointList = endpointList - // sort the endpoint list by feature ID and priority - .sortedWith(compareBy(Feature::featureId, Feature::priority)) - // select only the most important endpoint for each feature - .distinctBy { it.featureId } - .toMutableList() - // add all endpoint IDs and required login methods, filtering using timers - .onEach { feature -> - feature.endpointIds.forEach { endpoint -> - (data.endpointTimers - .singleOrNull { it.endpointId == endpoint.first } ?: EndpointTimer(data.profile?.id ?: -1, endpoint.first)) - .let { timer -> - if (timer.nextSync == SYNC_ALWAYS || - (viewId != null && timer.viewId == viewId) || - (timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp)) { - data.targetEndpointIds.add(endpoint.first) - requiredLoginMethods.add(endpoint.second) - } - } - } - } - - // check every login method for any dependencies - for (loginMethodId in requiredLoginMethods) { - var requiredLoginMethod: Int? = loginMethodId - while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) { - templateLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod -> - if (requiredLoginMethod != null) - data.targetLoginMethodIds.add(requiredLoginMethod!!) - requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore) - } - } - } - - // sort and distinct every login method and endpoint - data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList() - data.targetLoginMethodIds.sort() - - data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList() - data.targetEndpointIds.sort() - + data.prepare(templateLoginMethods, TemplateFeatures, featureIds, viewId) Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") - TemplateLogin(data) { TemplateData(data) { completed() @@ -142,7 +67,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, override fun cancel() { Utils.d(TAG, "Cancelled") - cancelled = true + data.cancel() } private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt index 70d852f9..5b2b6e79 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt @@ -7,18 +7,13 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan import android.util.Log import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 -import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_NOT_NEEDED import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface -import pl.szczodrzynski.edziennik.api.v2.librus.LibrusFeatures import pl.szczodrzynski.edziennik.api.v2.models.ApiError -import pl.szczodrzynski.edziennik.api.v2.models.Feature +import pl.szczodrzynski.edziennik.api.v2.prepare import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanData import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLogin import pl.szczodrzynski.edziennik.api.v2.vulcanLoginMethods -import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer -import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS -import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_NEVER import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.utils.Utils @@ -30,13 +25,12 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va val internalErrorList = mutableListOf() val data: DataVulcan - private var cancelled = false init { data = DataVulcan(app, profile, loginStore).apply { callback = wrapCallback(this@Vulcan.callback) + satisfyLoginMethods() } - data.satisfyLoginMethods() } private fun completed() { @@ -53,77 +47,9 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va __/ | |__*/ override fun sync(featureIds: List, viewId: Int?) { - val possibleLoginMethods = data.loginMethods.toMutableList() - - for (loginMethod in vulcanLoginMethods) { - if (loginMethod.isPossible(profile, loginStore)) - possibleLoginMethods += loginMethod.loginMethodId - } - - //var highestLoginMethod = 0 - var endpointList = mutableListOf() - val requiredLoginMethods = mutableListOf() - - data.targetEndpointIds.clear() - data.targetLoginMethodIds.clear() - - // get all endpoints for every feature, only if possible to login and possible/necessary to sync - for (featureId in featureIds) { - VulcanFeatures.filter { - it.featureId == featureId // feature ID matches - && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login - && it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync - }.let { - endpointList.addAll(it) - } - } - - val timestamp = System.currentTimeMillis() - - endpointList = endpointList - // sort the endpoint list by feature ID and priority - .sortedWith(compareBy(Feature::featureId, Feature::priority)) - // select only the most important endpoint for each feature - .distinctBy { it.featureId } - .toMutableList() - // add all endpoint IDs and required login methods, filtering using timers - .onEach { feature -> - feature.endpointIds.forEach { endpoint -> - (data.endpointTimers - .singleOrNull { it.endpointId == endpoint.first } ?: EndpointTimer(data.profile?.id ?: -1, endpoint.first)) - .let { timer -> - if (timer.nextSync == SYNC_ALWAYS || - (viewId != null && timer.viewId == viewId) || - (timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp)) { - data.targetEndpointIds.add(endpoint.first) - requiredLoginMethods.add(endpoint.second) - } - } - } - } - - // check every login method for any dependencies - for (loginMethodId in requiredLoginMethods) { - var requiredLoginMethod: Int? = loginMethodId - while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) { - vulcanLoginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod -> - if (requiredLoginMethod != null) - data.targetLoginMethodIds.add(requiredLoginMethod!!) - requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore) - } - } - } - - // sort and distinct every login method and endpoint - data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList() - data.targetLoginMethodIds.sort() - - data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList() - data.targetEndpointIds.sort() - + data.prepare(vulcanLoginMethods, VulcanFeatures, featureIds, viewId) Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") - VulcanLogin(data) { VulcanData(data) { completed() @@ -141,7 +67,7 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va override fun cancel() { Utils.d(TAG, "Cancelled") - cancelled = true + data.cancel() } private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { From fe62c93602f0bd4a3c51d766f2397c7ae6dd5738 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Mon, 14 Oct 2019 12:46:36 +0200 Subject: [PATCH 055/691] [Gradle] Update gradle to 3.5.1 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ca180a12..6ed7da8c 100644 --- a/build.gradle +++ b/build.gradle @@ -58,7 +58,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.5.1' classpath 'me.tatarka:gradle-retrolambda:3.7.0' classpath 'com.google.gms:google-services:4.3.1' classpath 'io.fabric.tools:gradle:1.28.1' From 33cfaef4542dc20e80bcef67164829788265816a Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Mon, 14 Oct 2019 13:32:58 +0200 Subject: [PATCH 056/691] [APIv2/Librus] Add getting the lucky number --- .../api/v2/librus/data/LibrusData.kt | 4 ++ .../librus/data/api/LibrusApiLuckyNumber.kt | 49 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index cab131f4..b5e9c245 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -67,6 +67,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_announcements) LibrusApiAnnouncements(data) { onSuccess() } } + ENDPOINT_LIBRUS_API_LUCKY_NUMBER -> { + data.startProgress(R.string.edziennik_progress_endpoint_lucky_number) + LibrusApiLuckyNumber(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt new file mode 100644 index 00000000..596fe4a7 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-14 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_LUCKY_NUMBER +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.luckynumber.LuckyNumber +import pl.szczodrzynski.edziennik.getInt +import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.utils.models.Date + +class LibrusApiLuckyNumber(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiLuckyNumber" + } + + init { + apiGet(TAG, "LuckyNumbers") { json -> + if (json.isJsonNull) { + profile?.luckyNumberEnabled = false + } else { + profile?.also { profile -> + profile.luckyNumber = -1 + profile.luckyNumberDate = Date.getToday() + + json.getJsonObject("LuckyNumber")?.also { luckyNumber -> + profile.luckyNumber = luckyNumber.getInt("LuckyNumber") ?: -1 + profile.luckyNumberDate = Date.fromY_m_d(luckyNumber.getString("LuckyNumberDay")) + } + + data.luckyNumberList.add(LuckyNumber( + profileId, + profile.luckyNumberDate ?: Date.getToday(), + profile.luckyNumber + )) + } + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_LUCKY_NUMBER, SYNC_ALWAYS) + onSuccess() + } + } +} From 7b5269a1fe2ca76618102bae6574a72c99124f2b Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Mon, 14 Oct 2019 14:04:02 +0200 Subject: [PATCH 057/691] [APIv2/Librus] Add getting classes --- .../api/v2/librus/data/LibrusData.kt | 4 ++ .../v2/librus/data/api/LibrusApiClasses.kt | 57 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 62 insertions(+) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClasses.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index b5e9c245..fb67e8f9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -71,6 +71,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_lucky_number) LibrusApiLuckyNumber(data) { onSuccess() } } + ENDPOINT_LIBRUS_API_CLASSES -> { + data.startProgress(R.string.edziennik_progress_endpoint_classes) + LibrusApiClasses(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClasses.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClasses.kt new file mode 100644 index 00000000..31d3490b --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClasses.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-14 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_CLASSES +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.teams.Team +import pl.szczodrzynski.edziennik.utils.models.Date + +class LibrusApiClasses(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiClasses" + } + + init { + apiGet(TAG, "Classes") { json -> + json.getJsonObject("Class")?.also { studentClass -> + val id = studentClass.getLong("Id") ?: return@also + val name = studentClass.getString("Number") + + studentClass.getString("Symbol") + val code = data.schoolName + ":" + name + val teacherId = studentClass.getJsonObject("ClassTutor")?.getLong("Id") ?: -1 + + val teamObject = Team( + profileId, + id, + name, + 1, + code, + teacherId + ) + + data.teamList.put(id, teamObject) + + val unitId = studentClass.getJsonObject("Unit").getLong("Id") + + profile?.apply { + dateSemester1Start = Date.fromY_m_d(studentClass.getString("BeginSchoolYear") + ?: return@apply) + dateSemester2Start = Date.fromY_m_d(studentClass.getString("EndFirstSemester") + ?: return@apply) + dateYearEnd = Date.fromY_m_d(studentClass.getString("EndSchoolYear") + ?: return@apply) + if (unitId != null) putStudentData("unitId", unitId) + } + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_CLASSES, 4 * DAY) + onSuccess() + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b8767b62..c73422ab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -944,4 +944,5 @@ Pobieranie kategorii obecności... Pobieranie ogłoszeń szkolnych... Pierwsze logowanie + Pobieranie informacji o klasie... From c433a615dbda9d7aa0ae273434b469eeeaae9944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 14 Oct 2019 15:20:21 +0200 Subject: [PATCH 058/691] [Database] Fix homework/event types migration. --- .../edziennik/data/db/AppDb.java | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java index b06a2135..37ef3a78 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java @@ -1,24 +1,26 @@ package pl.szczodrzynski.edziennik.data.db; +import android.content.Context; + import androidx.annotation.NonNull; -import androidx.sqlite.db.SupportSQLiteDatabase; import androidx.room.Database; import androidx.room.Room; import androidx.room.RoomDatabase; import androidx.room.TypeConverters; import androidx.room.migration.Migration; +import androidx.sqlite.db.SupportSQLiteDatabase; +import pl.szczodrzynski.edziennik.data.db.converters.ConverterDate; +import pl.szczodrzynski.edziennik.data.db.converters.ConverterJsonObject; +import pl.szczodrzynski.edziennik.data.db.converters.ConverterListLong; +import pl.szczodrzynski.edziennik.data.db.converters.ConverterListString; +import pl.szczodrzynski.edziennik.data.db.converters.ConverterTime; import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement; import pl.szczodrzynski.edziennik.data.db.modules.announcements.AnnouncementDao; import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer; import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimerDao; import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance; import pl.szczodrzynski.edziennik.data.db.modules.attendance.AttendanceDao; -import pl.szczodrzynski.edziennik.data.db.converters.ConverterDate; -import pl.szczodrzynski.edziennik.data.db.converters.ConverterJsonObject; -import pl.szczodrzynski.edziennik.data.db.converters.ConverterListLong; -import pl.szczodrzynski.edziennik.data.db.converters.ConverterListString; -import pl.szczodrzynski.edziennik.data.db.converters.ConverterTime; import pl.szczodrzynski.edziennik.data.db.modules.debuglog.DebugLog; import pl.szczodrzynski.edziennik.data.db.modules.debuglog.DebugLogDao; import pl.szczodrzynski.edziennik.data.db.modules.events.Event; @@ -61,8 +63,6 @@ import pl.szczodrzynski.edziennik.data.db.modules.teams.Team; import pl.szczodrzynski.edziennik.data.db.modules.teams.TeamDao; import pl.szczodrzynski.edziennik.utils.models.Date; -import android.content.Context; - @Database(entities = { Grade.class, //GradeCategory.class, @@ -600,7 +600,20 @@ public abstract class AppDb extends RoomDatabase { private static final Migration MIGRATION_57_58 = new Migration(57, 58) { @Override public void migrate(@NonNull SupportSQLiteDatabase database) { - database.execSQL("UPDATE metadata SET thingType = 5 WHERE thingType = 4 AND thingId IN (SELECT eventId FROM events WHERE eventType = -1)"); + database.execSQL("ALTER TABLE metadata RENAME TO _metadata_old;"); + database.execSQL("DROP INDEX index_metadata_profileId_thingType_thingId;"); + database.execSQL("UPDATE _metadata_old SET thingType = "+Metadata.TYPE_HOMEWORK+" WHERE thingType = "+Metadata.TYPE_EVENT+" AND thingId IN (SELECT eventId FROM events WHERE eventType = -1);"); + database.execSQL("CREATE TABLE metadata (\n"+ + "profileId INTEGER NOT NULL,\n"+ + "metadataId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"+ + "thingType INTEGER NOT NULL,\n"+ + "thingId INTEGER NOT NULL,\n"+ + "seen INTEGER NOT NULL,\n"+ + "notified INTEGER NOT NULL,\n"+ + "addedDate INTEGER NOT NULL);"); + database.execSQL("INSERT INTO metadata SELECT * FROM (SELECT * FROM _metadata_old ORDER BY addedDate DESC) GROUP BY thingId;"); + database.execSQL("DROP TABLE _metadata_old;"); + database.execSQL("CREATE UNIQUE INDEX index_metadata_profileId_thingType_thingId ON metadata (profileId, thingType, thingId);"); } }; From 440b76d3029be79ef2761d5b1955640bcec3e10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 14 Oct 2019 15:30:34 +0200 Subject: [PATCH 059/691] [APIv2/Login] EventBus: sticky events in Service. Add first login request in fragment. --- .../edziennik/api/v2/ApiService.kt | 53 +++-- .../api/v2/events/FirstLoginFinishedEvent.kt | 6 + .../edziennik/api/v2/librus/LibrusTest.kt | 81 -------- .../edziennik/api/v2/models/ApiError.kt | 15 +- .../edziennik/api/v2/models/Data.kt | 2 +- .../ui/modules/home/HomeFragment.java | 29 ++- .../modules/login/LoginProgressFragment.java | 185 +++++------------- 7 files changed, 116 insertions(+), 255 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/FirstLoginFinishedEvent.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt index 4ffc122c..d505afc5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt @@ -133,6 +133,7 @@ class ApiService : Service() { taskRunningObject = task if (task is ErrorReportTask) { + queueHasErrorReportTask = false notification .setCurrentTask(taskRunningId, null) .setProgressRes(R.string.edziennik_notification_api_error_report_title) @@ -210,8 +211,11 @@ class ApiService : Service() { | __\ \ / / _ \ '_ \| __| _ <| | | / __| | |___\ V / __/ | | | |_| |_) | |_| \__ \ |______\_/ \___|_| |_|\__|____/ \__,_|__*/ - @Subscribe(threadMode = ThreadMode.ASYNC) - fun onSyncRequest(syncRequest: SyncRequest) { + @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) + fun onSyncRequest(request: SyncRequest) { + EventBus.getDefault().removeStickyEvent(request) + Log.d(TAG, request.toString()) + app.db.profileDao().idsForSyncNow.forEach { id -> taskQueue += SyncProfileRequest(id, null).apply { taskId = ++taskMaximumId @@ -220,31 +224,52 @@ class ApiService : Service() { sync() } - @Subscribe(threadMode = ThreadMode.ASYNC) - fun onSyncProfileRequest(syncProfileRequest: SyncProfileRequest) { - Log.d(TAG, syncProfileRequest.toString()) - taskQueue += syncProfileRequest.apply { + @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) + fun onSyncProfileRequest(request: SyncProfileRequest) { + EventBus.getDefault().removeStickyEvent(request) + Log.d(TAG, request.toString()) + + taskQueue += request.apply { taskId = ++taskMaximumId } sync() } - @Subscribe(threadMode = ThreadMode.ASYNC) - fun onMessageGetRequest(messageGetRequest: MessageGetRequest) { - Log.d(TAG, messageGetRequest.toString()) - taskQueue += messageGetRequest.apply { + @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) + fun onMessageGetRequest(request: MessageGetRequest) { + EventBus.getDefault().removeStickyEvent(request) + Log.d(TAG, request.toString()) + + taskQueue += request.apply { taskId = ++taskMaximumId } sync() } - @Subscribe(threadMode = ThreadMode.ASYNC) - fun onTaskCancelRequest(taskCancelRequest: TaskCancelRequest) { + @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) + fun onFirstLoginRequest(request: FirstLoginRequest) { + EventBus.getDefault().removeStickyEvent(request) + Log.d(TAG, request.toString()) + + taskQueue += request.apply { + taskId = ++taskMaximumId + } + sync() + } + + @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) + fun onTaskCancelRequest(request: TaskCancelRequest) { + EventBus.getDefault().removeStickyEvent(request) + Log.d(TAG, request.toString()) + taskCancelled = true edziennikInterface?.cancel() } - @Subscribe(threadMode = ThreadMode.ASYNC) - fun onServiceCloseRequest(serviceCloseRequest: ServiceCloseRequest) { + @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) + fun onServiceCloseRequest(request: ServiceCloseRequest) { + EventBus.getDefault().removeStickyEvent(request) + Log.d(TAG, request.toString()) + serviceClosed = true taskCancelled = true edziennikInterface?.cancel() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/FirstLoginFinishedEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/FirstLoginFinishedEvent.kt new file mode 100644 index 00000000..31fba7c9 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/FirstLoginFinishedEvent.kt @@ -0,0 +1,6 @@ +package pl.szczodrzynski.edziennik.api.v2.events + +import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile + +data class FirstLoginFinishedEvent(val profileList: List, val loginStore: LoginStore) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt deleted file mode 100644 index 99647557..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusTest.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2019-9-21. - */ - -package pl.szczodrzynski.edziennik.api.v2.librus - -import android.content.Intent -import com.google.gson.JsonObject -import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.api.v2.ApiService -import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_EMAIL -import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_LIBRUS -import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore -import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile - -class LibrusTest(val app: App) { - companion object { - private const val TAG = "LibrusTest" - } - - val profile = Profile(1, "Profil", "xd", 1).apply { - //putStudentData("accountLogin", "1234567") - - //putStudentData("accountCode", LIBRUS_JST_DEMO_CODE) - //putStudentData("accountPin", LIBRUS_JST_DEMO_PIN) - - putStudentData("accountLogin", "1234567") - - //putStudentData("accountToken", "token") - //putStudentData("accountTokenTime", 1569458277) - } - val loginStore = LoginStore(1, LOGIN_TYPE_LIBRUS, JsonObject().apply { - addProperty("email", "test@example.com") - addProperty("password", "zaq1@WSX") - - //addProperty("accessToken", "token") - //addProperty("refreshToken", "refresh") - //addProperty("tokenExpiryTime", 1569523077) - }).also { - it.mode = LOGIN_MODE_LIBRUS_EMAIL - } - - fun go() { - - /*Librus(app, profile, loginStore, object : EdziennikCallback { - override fun onCompleted() {} - override fun onError(apiError: ApiError) {} - override fun onProgress(step: Int) {} - override fun onStartProgress(stringRes: Int) {} - }).sync(listOf(FEATURE_GRADES, FEATURE_STUDENT_INFO, FEATURE_STUDENT_NUMBER))*/ - - app.startService(Intent(app, ApiService::class.java)) - - if (false) { - - } - - /*val data = DataLibrus(app, profile, loginStore).apply { - callback = object : ProgressCallback { - override fun onProgress(progressStep: Int) { - - } - - override fun onActionStarted(stringResId: Int) { - d(TAG, app.getString(stringResId)) - } - - override fun onError(activityContext: Context?, error: AppError) { - error.changeIfCodeOther() - d(TAG, "Error "+error.getDetails(app)) - } - } - }*/ - - /*LoginLibrus(data, LOGIN_METHOD_LIBRUS_MESSAGES) { - d(TAG, "Login succeeded.") - d(TAG, "Profile data: ${data.profile?.studentData?.toString()}") - d(TAG, "LoginStore data: ${data.loginStore.data}") - }*/ - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt index 825809f9..0f5a484e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt @@ -7,13 +7,14 @@ package pl.szczodrzynski.edziennik.api.v2.models import com.google.gson.JsonObject import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response +import pl.szczodrzynski.edziennik.data.api.AppError class ApiError(val tag: String, val errorCode: Int) { var profileId: Int? = null var throwable: Throwable? = null - private var apiResponse: String? = null - private var request: Request? = null - private var response: Response? = null + var apiResponse: String? = null + var request: Request? = null + var response: Response? = null var isCritical = true fun withThrowable(throwable: Throwable?): ApiError { @@ -42,4 +43,12 @@ class ApiError(val tag: String, val errorCode: Int) { this.isCritical = isCritical return this } + + fun toAppError(): AppError { + return AppError( + tag, + -1, + errorCode, response, throwable, apiResponse + ) + } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 0f997738..ac2f1344 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -173,7 +173,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) open fun saveData() { if (profile == null) - return + return // return on first login db.profileDao().add(profile) db.loginStoreDao().add(loginStore) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java index 8e8e1652..be65d045 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java @@ -42,25 +42,25 @@ import java.util.List; import kotlin.Pair; import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; -import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; +import pl.szczodrzynski.edziennik.R; +import pl.szczodrzynski.edziennik.api.v2.ApiService; import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest; -import pl.szczodrzynski.edziennik.api.v2.librus.LibrusTest; -import pl.szczodrzynski.edziennik.databinding.CardLuckyNumberBinding; -import pl.szczodrzynski.edziennik.databinding.CardUpdateBinding; -import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding; import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeFull; import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull; import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject; -import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeActivity; -import pl.szczodrzynski.edziennik.utils.models.Date; -import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel; -import pl.szczodrzynski.edziennik.utils.models.Time; +import pl.szczodrzynski.edziennik.databinding.CardLuckyNumberBinding; +import pl.szczodrzynski.edziennik.databinding.CardUpdateBinding; +import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding; import pl.szczodrzynski.edziennik.receivers.BootReceiver; +import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeActivity; import pl.szczodrzynski.edziennik.utils.Colors; import pl.szczodrzynski.edziennik.utils.Themes; import pl.szczodrzynski.edziennik.utils.Utils; +import pl.szczodrzynski.edziennik.utils.models.Date; +import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel; +import pl.szczodrzynski.edziennik.utils.models.Time; import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem; import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem; @@ -68,7 +68,6 @@ import static pl.szczodrzynski.edziennik.App.UPDATES_ON_PLAY_STORE; import static pl.szczodrzynski.edziennik.MainActivity.DRAWER_ITEM_GRADES; import static pl.szczodrzynski.edziennik.MainActivity.DRAWER_ITEM_HOME; import static pl.szczodrzynski.edziennik.MainActivity.DRAWER_ITEM_MESSAGES; -import static pl.szczodrzynski.edziennik.api.v2.FeaturesKt.FEATURE_STUDENT_INFO; import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER1_FINAL; import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER1_PROPOSED; import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER2_FINAL; @@ -123,32 +122,30 @@ public class HomeFragment extends Fragment { startActivity(new Intent(activity, MessagesComposeActivity.class)); })); - LibrusTest librusTest = new LibrusTest(app); - b.testButton.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); b.testButton.setOnClickListener((v -> { - librusTest.go(); + app.startService(new Intent(app, ApiService.class)); })); b.test2.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); b.test2.setOnClickListener((v -> { List> list = new ArrayList<>(); list.add(new Pair<>(DRAWER_ITEM_HOME, 0)); - EventBus.getDefault().post(new SyncProfileRequest(app.profile.getId(), list)); + EventBus.getDefault().postSticky(new SyncProfileRequest(app.profile.getId(), list)); })); b.test3.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); b.test3.setOnClickListener((v -> { List> list = new ArrayList<>(); list.add(new Pair<>(DRAWER_ITEM_MESSAGES, TYPE_SENT)); - EventBus.getDefault().post(new SyncProfileRequest(app.profile.getId(), list)); + EventBus.getDefault().postSticky(new SyncProfileRequest(app.profile.getId(), list)); })); b.test4.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); b.test4.setOnClickListener((v -> { List> list = new ArrayList<>(); list.add(new Pair<>(DRAWER_ITEM_GRADES, 0)); - EventBus.getDefault().post(new SyncProfileRequest(app.profile.getId(), list)); + EventBus.getDefault().postSticky(new SyncProfileRequest(app.profile.getId(), list)); })); //((TextView)v.findViewById(R.id.nextSync)).setText(getString(R.string.next_sync_format,Time.fromMillis(app.appJobs.syncJobTime).getStringHMS())); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java index 93f00fa3..943a14e5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java @@ -1,32 +1,33 @@ package pl.szczodrzynski.edziennik.ui.modules.login; -import androidx.databinding.DataBindingUtil; - -import android.content.Context; +import android.content.Intent; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; - import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.google.gson.JsonObject; - -import java.util.List; - +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.databinding.DataBindingUtil; +import androidx.fragment.app.Fragment; import androidx.navigation.NavController; import androidx.navigation.Navigation; + +import com.google.gson.JsonObject; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.data.api.Edziennik; +import pl.szczodrzynski.edziennik.api.v2.ApiService; +import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent; +import pl.szczodrzynski.edziennik.api.v2.events.SyncErrorEvent; +import pl.szczodrzynski.edziennik.api.v2.events.requests.FirstLoginRequest; import pl.szczodrzynski.edziennik.data.api.AppError; -import pl.szczodrzynski.edziennik.data.api.interfaces.SyncCallback; -import pl.szczodrzynski.edziennik.databinding.FragmentLoginProgressBinding; import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore; -import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; -import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; +import pl.szczodrzynski.edziennik.databinding.FragmentLoginProgressBinding; import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_OTHER; @@ -53,6 +54,22 @@ public class LoginProgressFragment extends Fragment { return b.getRoot(); } + @Subscribe(threadMode = ThreadMode.MAIN) + public void onFirstLoginFinishedEvent(FirstLoginFinishedEvent event) { + LoginActivity.profileObjects.add(new LoginProfileObject( + event.getLoginStore(), + event.getProfileList())); + nav.navigate(R.id.loginSummaryFragment, null, LoginActivity.navOptions); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onSyncErrorEvent(SyncErrorEvent event) { + LoginActivity.error = event.getError().toAppError(); + if (getActivity() == null) + return; + nav.navigateUp(); + } + @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { assert getContext() != null; @@ -72,131 +89,19 @@ public class LoginProgressFragment extends Fragment { LoginStore loginStore = new LoginStore(-1, loginType, new JsonObject()); loginStore.copyFrom(args); - Edziennik.getApi(app, loginType).sync(getActivity(), new SyncCallback() { - @Override - public void onLoginFirst(List profileList, LoginStore loginStore) { - // because these callbacks are always on a worker thread - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - LoginActivity.profileObjects.add(new LoginProfileObject( - loginStore, - profileList)); - nav.navigate(R.id.loginSummaryFragment, null, LoginActivity.navOptions); - }); - } - } + getContext().startService(new Intent(app, ApiService.class)); + EventBus.getDefault().postSticky(new FirstLoginRequest(loginStore)); + } - @Override - public void onSuccess(Context activityContext, ProfileFull profileFull) { + @Override + public void onStart() { + EventBus.getDefault().register(this); + super.onStart(); + } - } - - @Override - public void onError(Context activityContext, AppError error) { - LoginActivity.error = error; - // because these callbacks are always on a worker thread - if (getActivity() == null) - return; - getActivity().runOnUiThread(() -> { - nav.navigateUp(); - }); - } - - @Override - public void onProgress(int progressStep) { - - } - - @Override - public void onActionStarted(int stringResId) { - - } - }, -1, null, loginStore); - - /*if (true) - return; - JsonObject loginData = new JsonObject(); - loginData.addProperty("serverName", b.loginServerAddress.getText().toString()); - loginData.addProperty("username", b.loginUsername.getText().toString()); - loginData.addProperty("password", b.loginPassword.getText().toString()); - getApi(app, LOGIN_TYPE_MOBIDZIENNIK).sync(getActivity(), new Edziennik.DataCallback() { - @Override - public void onLoginFirst(List profileList, LoginStore loginStore) { - int profileId = app.profileLastId()+1; - if (profileList.size() == 1) { - Profile profile = profileList.get(0); - saveProfile(profile, loginStore, profileId, profileId); - finishSaving(); - return; - } - List profileNames = new ArrayList<>(); - for (Profile profile: profileList) { - profileNames.add(profile.name); - } - new MaterialDialog.Builder(getActivity()) - .title(R.string.sync_multiaccount_select_students) - .items(profileNames) - .positiveText(R.string.ok) - .negativeText(R.string.cancel) - .neutralText(R.string.help) - .autoDismiss(false) - .canceledOnTouchOutside(false) - .onNeutral((dialog, which) -> - new MaterialDialog.Builder(getActivity()) - .title(R.string.help) - .content(R.string.sync_multiaccount_select_students_text) - .positiveText(R.string.ok) - .show() - ) - .onNegative(((dialog, which) -> dialog.dismiss())) - .itemsCallbackMultiChoice(null, (dialog, which, text) -> { - // create new profiles, then restart the application or sth - if (text.length < 1 || which.length < 1) { - Toast.makeText(app, R.string.sync_multiaccount_select_students_error, Toast.LENGTH_SHORT).show(); - return false; - } - dialog.dismiss(); - int pos = 0; - for (int index: which) { - Profile profile = profileList.get(index); - saveProfile(profile, loginStore, profileId+(pos++), profileId); - } - finishSaving(); - - - String list = ""; - for (ProfileFull profileFull: app.db.profileDao().getAllFullNow()) { - d(TAG, profileFull.toString()); - list += profileFull.studentNameLong+" student ID "+profileFull.getStudentData("studentId", -1)+"\n"; - } - d(TAG, loginStore.toString()); - list += loginStore.getLoginData("username", "(NO USERNAME)")+"\n"; - new MaterialDialog.Builder(getActivity()) - .title("Znaleziono profile") - .content(list) - .positiveText("OK") - .show(); - - - return false; - }) - .show(); - } - - @Override - public void onSuccess(Context activityContext, ProfileFull profile) { - Toast.makeText(activityContext, "Zakończono", Toast.LENGTH_SHORT).show(); - } - - @Override - public void onError(Context activityContext, int errorCode, String errorText, Throwable throwable, String apiResponse) { - - } - - @Override - public void onProgress(int progressStep) { - - } - }, -1, null, new LoginStore(-1, LOGIN_TYPE_MOBIDZIENNIK, loginData));*/ + @Override + public void onStop() { + super.onStop(); + EventBus.getDefault().unregister(this); } } From 0b7f9a08ef47cf2d532aa6122a1ac9657ebf676a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 14 Oct 2019 15:31:34 +0200 Subject: [PATCH 060/691] [Profile] Remove all Profile.loggedIn usages. --- .../edziennik/data/api/Iuczniowie.java | 1 - .../edziennik/data/api/Librus.java | 4 +- .../edziennik/data/api/Mobidziennik.java | 2 - .../edziennik/data/api/Vulcan.java | 3 +- .../data/db/modules/profiles/Profile.kt | 10 ++--- .../modules/login/LoginMigrationFragment.java | 37 +++++++++---------- 6 files changed, 23 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Iuczniowie.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Iuczniowie.java index 33c69671..6d1beed8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Iuczniowie.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Iuczniowie.java @@ -611,7 +611,6 @@ public class Iuczniowie implements EdziennikInterface { newProfile.setName(newProfile.getStudentNameLong()); newProfile.setSubname(loginUsername); newProfile.setEmpty(true); - newProfile.setLoggedIn(true); newProfile.putStudentData("studentId", studentIds.get(index)); newProfile.putStudentData("registerId", registerIds.get(index)); newProfile.putStudentData("schoolYearId", loginSchoolYearId); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java index 32468093..a554b25c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java @@ -74,11 +74,11 @@ import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher; import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsence; import pl.szczodrzynski.edziennik.data.db.modules.teams.Team; import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeInfo; +import pl.szczodrzynski.edziennik.utils.Utils; import pl.szczodrzynski.edziennik.utils.models.Date; import pl.szczodrzynski.edziennik.utils.models.Endpoint; import pl.szczodrzynski.edziennik.utils.models.Time; import pl.szczodrzynski.edziennik.utils.models.Week; -import pl.szczodrzynski.edziennik.utils.Utils; import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; import static java.net.HttpURLConnection.HTTP_FORBIDDEN; @@ -99,7 +99,6 @@ import static pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance.T import static pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance.TYPE_PRESENT; import static pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance.TYPE_RELEASED; import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_PT_MEETING; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_TEACHER_ABSENCE; import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_NORMAL; import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER1_FINAL; import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER1_PROPOSED; @@ -741,7 +740,6 @@ public class Librus implements EdziennikInterface { newProfile.setName(newProfile.getStudentNameLong()); newProfile.setSubname(librusEmail); newProfile.setEmpty(true); - newProfile.setLoggedIn(true); newProfile.putStudentData("accountId", accountIds.get(index)); newProfile.putStudentData("accountLogin", accountLogins.get(index)); newProfile.putStudentData("accountToken", accountTokens.get(index)); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Mobidziennik.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Mobidziennik.java index 785ab5ff..23fed516 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Mobidziennik.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Mobidziennik.java @@ -8,7 +8,6 @@ import android.text.Html; import android.util.LongSparseArray; import android.util.Pair; import android.util.SparseArray; -import android.util.SparseIntArray; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -488,7 +487,6 @@ public class Mobidziennik implements EdziennikInterface { profile.setName(profile.getStudentNameLong()); profile.setSubname(loginUsername); profile.setEmpty(true); - profile.setLoggedIn(true); profile.putStudentData("studentId", studentIds.get(index)); profileList.add(profile); } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Vulcan.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Vulcan.java index 090c9393..c8dc1a7a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Vulcan.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Vulcan.java @@ -60,11 +60,11 @@ import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject; import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher; import pl.szczodrzynski.edziennik.data.db.modules.teams.Team; import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeInfo; +import pl.szczodrzynski.edziennik.utils.Utils; import pl.szczodrzynski.edziennik.utils.models.Date; import pl.szczodrzynski.edziennik.utils.models.Endpoint; import pl.szczodrzynski.edziennik.utils.models.Time; import pl.szczodrzynski.edziennik.utils.models.Week; -import pl.szczodrzynski.edziennik.utils.Utils; import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_OTHER; import static pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance.TYPE_ABSENT; @@ -516,7 +516,6 @@ public class Vulcan implements EdziennikInterface { Profile newProfile = new Profile(); newProfile.setEmpty(true); - newProfile.setLoggedIn(true); saveStudentData(newProfile, account); profileList.add(newProfile); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/profiles/Profile.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/profiles/Profile.kt index 0c737b43..24029bcb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/profiles/Profile.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/profiles/Profile.kt @@ -1,7 +1,5 @@ package pl.szczodrzynski.edziennik.data.db.modules.profiles -import androidx.room.ColumnInfo -import androidx.room.Entity import android.content.Context import android.graphics.PorterDuff import android.graphics.PorterDuffColorFilter @@ -9,13 +7,12 @@ import android.graphics.drawable.Drawable import android.net.ConnectivityManager import android.widget.ImageView import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory - -import com.google.gson.JsonObject - +import androidx.room.ColumnInfo +import androidx.room.Entity import androidx.room.Ignore +import com.google.gson.JsonObject import pl.droidsonroids.gif.GifDrawable import pl.szczodrzynski.edziennik.colorFromName - import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.navlib.ImageHolder import pl.szczodrzynski.navlib.R @@ -294,7 +291,6 @@ open class Profile : IDrawerProfile { ", syncEnabled=" + syncEnabled + ", syncNotifications=" + syncNotifications + ", enableSharedEvents=" + enableSharedEvents + - ", loggedIn=" + loggedIn + ", empty=" + empty + ", studentNameLong='" + studentNameLong + '\''.toString() + ", studentNameShort='" + studentNameShort + '\''.toString() + diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMigrationFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMigrationFragment.java index cd881665..846d45b5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMigrationFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMigrationFragment.java @@ -6,6 +6,11 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.AsyncTask; import android.os.Bundle; +import android.util.LongSparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -14,10 +19,21 @@ import androidx.databinding.DataBindingUtil; import androidx.fragment.app.Fragment; import androidx.navigation.NavController; import androidx.navigation.Navigation; + +import com.afollestad.materialdialogs.MaterialDialog; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.databinding.FragmentLoginMigrationBinding; import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance; import pl.szczodrzynski.edziennik.data.db.modules.events.Event; import pl.szczodrzynski.edziennik.data.db.modules.events.EventType; @@ -32,26 +48,10 @@ import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject; import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher; import pl.szczodrzynski.edziennik.data.db.modules.teams.Team; +import pl.szczodrzynski.edziennik.databinding.FragmentLoginMigrationBinding; import pl.szczodrzynski.edziennik.utils.models.Date; import pl.szczodrzynski.edziennik.utils.models.Time; -import android.util.LongSparseArray; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Toast; - -import com.afollestad.materialdialogs.MaterialDialog; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.reflect.TypeToken; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_CLASS_EVENT; import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_DEFAULT; import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_ESSAY; @@ -183,7 +183,6 @@ public class LoginMigrationFragment extends Fragment { profile.setSyncNotifications(Boolean.parseBoolean(p.getString("app.register.syncNotificationsEnabled", Boolean.toString(profile.getSyncNotifications())))); profile.setEnableSharedEvents(Boolean.parseBoolean(p.getString("app.register.eventsShared", Boolean.toString(profile.getEnableSharedEvents())))); app.appConfig.countInSeconds = Boolean.parseBoolean(p.getString("app.register.countInSeconds", Boolean.toString(app.appConfig.countInSeconds))); - profile.setLoggedIn(true); // so in some APIs we force a full, clean sync profile.setEmpty(true);//Boolean.parseBoolean(p.getString("app.register.empty", Boolean.toString(profile.empty))); profile.setArchived(false); From e91b4fcf8b49cd5d9dfc3846a01bc6992d782c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 14 Oct 2019 15:44:02 +0200 Subject: [PATCH 061/691] [APIv2/Mobidziennik] Implement First login. --- .../api/v2/mobidziennik/Mobidziennik.kt | 5 +- .../firstlogin/MobidziennikFirstLogin.kt | 55 +++++++++++++++++++ .../login/MobidziennikLoginWeb.kt | 6 -- 3 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/firstlogin/MobidziennikFirstLogin.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt index a0f0e0b5..dc1aa662 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt @@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikData +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.firstlogin.MobidziennikFirstLogin import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLogin import pl.szczodrzynski.edziennik.api.v2.mobidziennikLoginMethods import pl.szczodrzynski.edziennik.api.v2.models.ApiError @@ -62,7 +63,9 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto } override fun firstLogin() { - // TODO + MobidziennikFirstLogin(data) { + completed() + } } override fun cancel() { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/firstlogin/MobidziennikFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/firstlogin/MobidziennikFirstLogin.kt new file mode 100644 index 00000000..1dfdfb84 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/firstlogin/MobidziennikFirstLogin.kt @@ -0,0 +1,55 @@ +package pl.szczodrzynski.edziennik.api.v2.mobidziennik.firstlogin + +import org.greenrobot.eventbus.EventBus +import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLoginWeb +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile + +class MobidziennikFirstLogin(val data: DataMobidziennik, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "MobidziennikFirstLogin" + } + + private val web = MobidziennikWeb(data) + private val profileList = mutableListOf() + + init { + MobidziennikLoginWeb(data) { + web.webGet(TAG, "/api/zrzutbazy") { text -> + val tables = text.split("T@B#LA") + + val studentIds = mutableListOf() + val studentNamesLong = mutableListOf() + val studentNamesShort = mutableListOf() + val student = tables[8].split("\n") + + for (aStudent in student) { + if (aStudent.isEmpty()) + continue + val student1 = aStudent.split("|") + if (student1.size == 2) + continue + studentIds += student1[0].toInt() + studentNamesLong.add(student1[2] + " " + student1[4]) + studentNamesShort.add(student1[2] + " " + student1[4][0] + ".") + } + + for (index in studentIds.indices) { + val profile = Profile() + profile.studentNameLong = studentNamesLong[index] + profile.studentNameShort = studentNamesShort[index] + profile.name = profile.studentNameLong + " (APIv2)" + profile.subname = data.loginUsername + profile.empty = true + profile.putStudentData("studentId", studentIds[index]) + profileList.add(profile) + } + + EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore)) + onSuccess() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLoginWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLoginWeb.kt index 1a40d786..f40b4750 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLoginWeb.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLoginWeb.kt @@ -12,7 +12,6 @@ import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.getUnixDate import pl.szczodrzynski.edziennik.isNotNullNorEmpty -import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils.d class MobidziennikLoginWeb(val data: DataMobidziennik, val onSuccess: () -> Unit) { @@ -21,11 +20,6 @@ class MobidziennikLoginWeb(val data: DataMobidziennik, val onSuccess: () -> Unit } init { run { - if (data.profile == null) { - data.error(ApiError(TAG, ERROR_PROFILE_MISSING)) - return@run - } - if (data.isWebLoginValid()) { onSuccess() } From f79263e628f0e314a72ce9d95e0e2df152c38d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 15 Oct 2019 06:49:19 +0200 Subject: [PATCH 062/691] [APIv2] Implement Grades remove model. Implement Librus first login. --- .../edziennik/api/v2/Constants.kt | 6 +- .../szczodrzynski/edziennik/api/v2/Errors.kt | 2 + .../edziennik/api/v2/librus/Librus.kt | 5 +- .../edziennik/api/v2/librus/data/LibrusApi.kt | 2 +- .../api/v2/librus/data/LibrusPortal.kt | 101 +++++++++++++++++ .../v2/librus/firstlogin/LibrusFirstLogin.kt | 102 ++++++++++++++++++ .../api/v2/librus/login/LibrusLoginApi.kt | 11 +- .../v2/librus/login/SynergiaTokenExtractor.kt | 76 +------------ .../firstlogin/MobidziennikFirstLogin.kt | 2 +- .../edziennik/api/v2/models/Data.kt | 6 +- .../api/v2/models/DataRemoveModel.kt | 5 + 11 files changed, 236 insertions(+), 82 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index fecfecbd..465ea051 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -19,11 +19,13 @@ const val LIBRUS_AUTHORIZE_URL = "https://portal.librus.pl/oauth2/authorize?clie const val LIBRUS_LOGIN_URL = "https://portal.librus.pl/rodzina/login/action" const val LIBRUS_TOKEN_URL = "https://portal.librus.pl/oauth2/access_token" -const val LIBRUS_ACCOUNT_URL = "https://portal.librus.pl/api/v2/SynergiaAccounts/fresh/" // + login -const val LIBRUS_ACCOUNTS_URL = "https://portal.librus.pl/api/v2/SynergiaAccounts" +const val LIBRUS_ACCOUNT_URL = "/v2/SynergiaAccounts/fresh/" // + login +const val LIBRUS_ACCOUNTS_URL = "/v2/SynergiaAccounts" /** https://api.librus.pl/2.0 */ const val LIBRUS_API_URL = "https://api.librus.pl/2.0" +/** https://portal.librus.pl/api */ +const val LIBRUS_PORTAL_URL = "https://portal.librus.pl/api" /** https://api.librus.pl/OAuth/Token */ const val LIBRUS_API_TOKEN_URL = "https://api.librus.pl/OAuth/Token" /** https://api.librus.pl/OAuth/TokenJST */ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index 676ccdb9..6b0df35a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -46,6 +46,8 @@ const val ERROR_PROFILE_MISSING = 105 const val ERROR_INVALID_LOGIN_MODE = 110 const val ERROR_LOGIN_METHOD_NOT_SATISFIED = 111 +const val ERROR_NO_STUDENTS_IN_ACCOUNT = 115 + const val CODE_INTERNAL_LIBRUS_ACCOUNT_410 = 120 const val CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED = 121 const val ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED = 124 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index 22fcef35..d8961e74 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData +import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.LibrusFirstLogin import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods import pl.szczodrzynski.edziennik.api.v2.models.ApiError @@ -62,7 +63,9 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va } override fun firstLogin() { - // TODO + LibrusFirstLogin(data) { + completed() + } } override fun cancel() { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt index 497d4845..d913e303 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt @@ -17,7 +17,7 @@ import java.net.HttpURLConnection.* open class LibrusApi(open val data: DataLibrus) { companion object { - const val TAG = "LibrusApi" + private const val TAG = "LibrusApi" } val profileId diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt new file mode 100644 index 00000000..c5cc886c --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt @@ -0,0 +1,101 @@ +package pl.szczodrzynski.edziennik.api.v2.librus.data + +import com.google.gson.JsonObject +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.callback.JsonCallbackHandler +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.utils.Utils.d +import java.net.HttpURLConnection + +open class LibrusPortal(open val data: DataLibrus) { + companion object { + private const val TAG = "LibrusPortal" + } + + val profileId + get() = data.profile?.id ?: -1 + + val profile + get() = data.profile + + fun portalGet(tag: String, endpoint: String, method: Int = GET, payload: JsonObject? = null, onSuccess: (json: JsonObject, response: Response?) -> Unit) { + + d(tag, "Request: Librus/Portal - $LIBRUS_PORTAL_URL$endpoint") + + val callback = object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response?) { + if (json == null) { + data.error(ApiError(tag, ERROR_RESPONSE_EMPTY) + .withResponse(response)) + return + } + val error = if (response?.code() == 200) null else + json.getString("reason") ?: + json.getString("message") ?: + json.getString("hint") ?: + json.getString("Code") + error?.let { code -> + when (code) { + "requires_an_action" -> ERROR_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED + "Access token is invalid" -> ERROR_LIBRUS_PORTAL_TOKEN_EXPIRED + "ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED + "Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND + else -> ERROR_LIBRUS_PORTAL_OTHER + }.let { errorCode -> + data.error(ApiError(tag, errorCode) + .withApiResponse(json) + .withResponse(response)) + return + } + } + if (response?.code() == HttpURLConnection.HTTP_OK) { + try { + onSuccess(json, response) + } catch (e: NullPointerException) { + e.printStackTrace() + data.error(ApiError(tag, EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN) + .withResponse(response) + .withThrowable(e) + .withApiResponse(json)) + } + + } else { + data.error(ApiError(tag, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withApiResponse(json)) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(ApiError(tag, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) + } + } + + Request.builder() + .url(LIBRUS_PORTAL_URL + endpoint) + .userAgent(LIBRUS_USER_AGENT) + .addHeader("Authorization", "Bearer ${data.portalAccessToken}") + .apply { + when (method) { + GET -> get() + POST -> post() + } + if (payload != null) + setJsonBody(payload) + } + .allowErrorCode(HttpURLConnection.HTTP_NOT_FOUND) + .allowErrorCode(HttpURLConnection.HTTP_FORBIDDEN) + .allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED) + .allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST) + .allowErrorCode(HttpURLConnection.HTTP_GONE) + .callback(callback) + .build() + .enqueue() + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt new file mode 100644 index 00000000..5a92178c --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt @@ -0,0 +1,102 @@ +package pl.szczodrzynski.edziennik.api.v2.librus.firstlogin + +import org.greenrobot.eventbus.EventBus +import pl.szczodrzynski.edziennik.api.v2.ERROR_NO_STUDENTS_IN_ACCOUNT +import pl.szczodrzynski.edziennik.api.v2.LIBRUS_ACCOUNTS_URL +import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_EMAIL +import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusPortal +import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi +import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginPortal +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.data.api.AppError.CODE_LIBRUS_DISCONNECTED +import pl.szczodrzynski.edziennik.data.api.AppError.CODE_SYNERGIA_NOT_ACTIVATED +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile +import pl.szczodrzynski.edziennik.getInt +import pl.szczodrzynski.edziennik.getJsonArray +import pl.szczodrzynski.edziennik.getLong +import pl.szczodrzynski.edziennik.getString + +class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "LibrusFirstLogin" + } + + private val portal = LibrusPortal(data) + private val api = LibrusApi(data) + private val profileList = mutableListOf() + + init { + if (data.loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) { + // email login: use Portal for account list + LibrusLoginPortal(data) { + portal.portalGet(TAG, LIBRUS_ACCOUNTS_URL) { json, response -> + val accounts = json.getJsonArray("accounts") + + if (accounts == null || accounts.size() < 1) { + data.error(ApiError(TAG, ERROR_NO_STUDENTS_IN_ACCOUNT) + .withResponse(response) + .withApiResponse(json)) + return@portalGet + } + val accountDataTime = json.getLong("lastModification") + val accountIds = mutableListOf() + val accountLogins = mutableListOf() + val accountTokens = mutableListOf() + val accountNamesLong = mutableListOf() + val accountNamesShort = mutableListOf() + + for (accountEl in accounts) { + val account = accountEl.asJsonObject + + val state = account.getString("state") + when (state) { + "requiring_an_action" -> CODE_LIBRUS_DISCONNECTED + "need-activation" -> CODE_SYNERGIA_NOT_ACTIVATED + else -> null + }?.let { errorCode -> + data.error(ApiError(TAG, errorCode) + .withApiResponse(json) + .withResponse(response)) + return@portalGet + } + + accountIds.add(account.getInt("id") ?: continue) + accountLogins.add(account.getString("login") ?: continue) + accountTokens.add(account.getString("accessToken") ?: continue) + accountNamesLong.add(account.getString("studentName") ?: continue) + val nameParts = account.getString("studentName")?.split(" ") ?: continue + accountNamesShort.add(nameParts[0] + " " + nameParts[1][0] + ".") + } + + for (index in accountIds.indices) { + val newProfile = Profile() + newProfile.studentNameLong = accountNamesLong[index] + newProfile.studentNameShort = accountNamesShort[index] + newProfile.name = newProfile.studentNameLong + newProfile.subname = data.portalEmail + newProfile.empty = true + newProfile.putStudentData("accountId", accountIds[index]) + newProfile.putStudentData("accountLogin", accountLogins[index]) + newProfile.putStudentData("accountToken", accountTokens[index]) + newProfile.putStudentData("accountTokenTime", accountDataTime ?: 0) + profileList.add(newProfile) + } + + EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore)) + onSuccess() + } + } + } + else { + // synergia or JST login: use Api for account info + LibrusLoginApi(data) { + api.apiGet(TAG, "Me") { json -> + + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLoginApi.kt index 22309ac6..fe8bd5cd 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLoginApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLoginApi.kt @@ -9,14 +9,15 @@ import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response import im.wangchao.mhttp.body.MediaTypeUtils import im.wangchao.mhttp.callback.JsonCallbackHandler -import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus -import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLoginWeb import pl.szczodrzynski.edziennik.api.v2.models.ApiError -import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.getInt +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.getUnixDate import pl.szczodrzynski.edziennik.utils.Utils.d -import java.net.HttpURLConnection.* +import java.net.HttpURLConnection.HTTP_BAD_REQUEST +import java.net.HttpURLConnection.HTTP_UNAUTHORIZED class LibrusLoginApi { companion object { @@ -31,7 +32,7 @@ class LibrusLoginApi { this.data = data this.onSuccess = onSuccess - if (data.profile == null) { + if (data.loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL && data.profile == null) { data.error(ApiError(TAG, ERROR_PROFILE_MISSING)) return } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt index be0fd126..27fdbe6a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt @@ -1,18 +1,15 @@ package pl.szczodrzynski.edziennik.api.v2.librus.login import com.google.gson.JsonObject -import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response -import im.wangchao.mhttp.callback.JsonCallbackHandler import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusPortal import pl.szczodrzynski.edziennik.api.v2.models.ApiError -import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils.d -import java.net.HttpURLConnection.* -class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { +class SynergiaTokenExtractor(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusPortal(data) { companion object { private const val TAG = "SynergiaTokenExtractor" } @@ -44,7 +41,7 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { private fun synergiaAccount(): Boolean { val accountLogin = data.apiLogin ?: return false - val accessToken = data.portalAccessToken ?: return false + data.portalAccessToken ?: return false d(TAG, "Request: Librus/SynergiaTokenExtractor - $LIBRUS_ACCOUNT_URL$accountLogin") @@ -63,77 +60,14 @@ class SynergiaTokenExtractor(val data: DataLibrus, val onSuccess: () -> Unit) { // TODO remove this data.profile?.studentNameLong = json.getString("studentName") - val nameParts = json.getString("studentName")?.split(" ")?.toTypedArray() + val nameParts = json.getString("studentName")?.split(" ") data.profile?.studentNameShort = nameParts?.get(0) + " " + nameParts?.get(1)?.get(0) onSuccess() } } - val callback = object : JsonCallbackHandler() { - override fun onSuccess(json: JsonObject?, response: Response?) { - if (json == null) { - data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY) - .withResponse(response)) - return - } - val error = if (response?.code() == 200) null else - json.getString("reason") ?: - json.getString("message") ?: - json.getString("hint") ?: - json.getString("Code") - error?.let { code -> - when (code) { - "requires_an_action" -> ERROR_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED - "Access token is invalid" -> ERROR_LIBRUS_PORTAL_TOKEN_EXPIRED - "ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED - "Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND - else -> ERROR_LIBRUS_PORTAL_OTHER - }.let { errorCode -> - data.error(ApiError(TAG, errorCode) - .withApiResponse(json) - .withResponse(response)) - return - } - } - if (response?.code() == HTTP_OK) { - try { - onSuccess(json, response) - } catch (e: NullPointerException) { - e.printStackTrace() - data.error(ApiError(TAG, EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN) - .withResponse(response) - .withThrowable(e) - .withApiResponse(json)) - } - - } else { - data.error(ApiError(TAG, ERROR_REQUEST_FAILURE) - .withResponse(response) - .withApiResponse(json)) - } - } - - override fun onFailure(response: Response?, throwable: Throwable?) { - data.error(ApiError(TAG, ERROR_REQUEST_FAILURE) - .withResponse(response) - .withThrowable(throwable)) - } - } - - Request.builder() - .url(LIBRUS_ACCOUNT_URL + accountLogin) - .userAgent(LIBRUS_USER_AGENT) - .addHeader("Authorization", "Bearer $accessToken") - .get() - .allowErrorCode(HTTP_NOT_FOUND) - .allowErrorCode(HTTP_FORBIDDEN) - .allowErrorCode(HTTP_UNAUTHORIZED) - .allowErrorCode(HTTP_BAD_REQUEST) - .allowErrorCode(HTTP_GONE) - .callback(callback) - .build() - .enqueue() + portalGet(TAG, LIBRUS_ACCOUNT_URL+accountLogin, onSuccess = onSuccess) return true } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/firstlogin/MobidziennikFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/firstlogin/MobidziennikFirstLogin.kt index 1dfdfb84..aa98c478 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/firstlogin/MobidziennikFirstLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/firstlogin/MobidziennikFirstLogin.kt @@ -40,7 +40,7 @@ class MobidziennikFirstLogin(val data: DataMobidziennik, val onSuccess: () -> Un val profile = Profile() profile.studentNameLong = studentNamesLong[index] profile.studentNameShort = studentNamesShort[index] - profile.name = profile.studentNameLong + " (APIv2)" + profile.name = profile.studentNameLong profile.subname = data.loginUsername profile.empty = true profile.putStudentData("studentId", studentIds[index]) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index ac2f1344..c32703d2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -190,6 +190,11 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) db.gradeCategoryDao().clear(profileId) db.gradeCategoryDao().addAll(gradeCategories.values()) + gradesToRemove?.let { it -> + it.removeAll?.let { _ -> db.gradeDao().clear(profileId) } + it.removeSemester?.let { semester -> db.gradeDao().clearForSemester(profileId, semester) } + } + if (lessonList.isNotEmpty()) { db.lessonDao().clear(profile.id) db.lessonDao().addAll(lessonList) @@ -197,7 +202,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) if (lessonChangeList.isNotEmpty()) db.lessonChangeDao().addAll(lessonChangeList) if (gradeList.isNotEmpty()) { - db.gradeDao().clear(profile.id) db.gradeDao().addAll(gradeList) } if (eventList.isNotEmpty()) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataRemoveModel.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataRemoveModel.kt index 19d24371..1e97d733 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataRemoveModel.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataRemoveModel.kt @@ -7,10 +7,15 @@ package pl.szczodrzynski.edziennik.api.v2.models import pl.szczodrzynski.edziennik.utils.models.Date class DataRemoveModel { + var removeAll: Boolean? = null var removeSemester: Int? = null var removeDateFrom: Date? = null var removeDateTo: Date? = null + constructor() { + this.removeAll = true + } + constructor(semester: Int) { this.removeSemester = semester } From c03eca380440bc205708d86f106479884a094c37 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Thu, 17 Oct 2019 14:35:10 +0200 Subject: [PATCH 063/691] [APIv2/Librus] Use apply in getting lucky numbers --- .../v2/librus/data/api/LibrusApiLuckyNumber.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt index 596fe4a7..14d0cc84 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt @@ -25,19 +25,19 @@ class LibrusApiLuckyNumber(override val data: DataLibrus, if (json.isJsonNull) { profile?.luckyNumberEnabled = false } else { - profile?.also { profile -> - profile.luckyNumber = -1 - profile.luckyNumberDate = Date.getToday() + profile?.apply { + luckyNumber = -1 + luckyNumberDate = Date.getToday() - json.getJsonObject("LuckyNumber")?.also { luckyNumber -> - profile.luckyNumber = luckyNumber.getInt("LuckyNumber") ?: -1 - profile.luckyNumberDate = Date.fromY_m_d(luckyNumber.getString("LuckyNumberDay")) + json.getJsonObject("LuckyNumber")?.also { luckyNumberEl -> + luckyNumber = luckyNumberEl.getInt("LuckyNumber") ?: -1 + luckyNumberDate = Date.fromY_m_d(luckyNumberEl.getString("LuckyNumberDay")) } data.luckyNumberList.add(LuckyNumber( profileId, - profile.luckyNumberDate ?: Date.getToday(), - profile.luckyNumber + luckyNumberDate ?: Date.getToday(), + luckyNumber )) } } From 24ab2e7795f3b7f9428a8bd4648e6be019075177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 18 Oct 2019 08:46:55 +0200 Subject: [PATCH 064/691] [LuckyNumber] Add lucky number metadata. Migrate Date field to int. --- .../librus/data/api/LibrusApiLuckyNumber.kt | 34 ++++++----- .../web/MobidziennikLuckyNumberExtractor.kt | 26 ++++++--- .../edziennik/data/db/AppDb.java | 35 +++++++++++- .../data/db/converters/ConverterDateInt.java | 22 +++++++ .../db/modules/luckynumber/LuckyNumber.java | 9 ++- .../modules/luckynumber/LuckyNumberDao.java | 57 +++++++++++++++---- .../modules/luckynumber/LuckyNumberFull.java | 13 +++++ .../data/db/modules/metadata/Metadata.java | 1 + 8 files changed, 161 insertions(+), 36 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/converters/ConverterDateInt.java create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumberFull.java diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt index 14d0cc84..c7289b88 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt @@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_LUCKY_NUMBER import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.luckynumber.LuckyNumber +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.getInt import pl.szczodrzynski.edziennik.getJsonObject import pl.szczodrzynski.edziennik.getString @@ -21,24 +22,31 @@ class LibrusApiLuckyNumber(override val data: DataLibrus, } init { + data.profile?.luckyNumber = -1 + data.profile?.luckyNumberDate = null + apiGet(TAG, "LuckyNumbers") { json -> if (json.isJsonNull) { - profile?.luckyNumberEnabled = false + //profile?.luckyNumberEnabled = false } else { - profile?.apply { - luckyNumber = -1 - luckyNumberDate = Date.getToday() + json.getJsonObject("LuckyNumber")?.also { luckyNumberEl -> - json.getJsonObject("LuckyNumber")?.also { luckyNumberEl -> - luckyNumber = luckyNumberEl.getInt("LuckyNumber") ?: -1 - luckyNumberDate = Date.fromY_m_d(luckyNumberEl.getString("LuckyNumberDay")) - } - - data.luckyNumberList.add(LuckyNumber( + val luckyNumberObject = LuckyNumber( profileId, - luckyNumberDate ?: Date.getToday(), - luckyNumber - )) + Date.fromY_m_d(luckyNumberEl.getString("LuckyNumberDay")) ?: Date.getToday(), + luckyNumberEl.getInt("LuckyNumber") ?: -1 + ) + + data.luckyNumberList.add(luckyNumberObject) + data.metadataList.add( + Metadata( + profileId, + Metadata.TYPE_LUCKY_NUMBER, + luckyNumberObject.date.value.toLong(), + profile?.empty ?: false, + profile?.empty ?: false, + System.currentTimeMillis() + )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt index 810d1131..9e9c1557 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt @@ -4,27 +4,37 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web +import pl.szczodrzynski.edziennik.App.profileId import pl.szczodrzynski.edziennik.api.v2.Regexes import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.db.modules.luckynumber.LuckyNumber +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.utils.models.Date class MobidziennikLuckyNumberExtractor(val data: DataMobidziennik, text: String) { init { data.profile?.luckyNumber = -1 - data.profile?.luckyNumberDate = Date.getToday() + data.profile?.luckyNumberDate = null Regexes.MOBIDZIENNIK_LUCKY_NUMBER.find(text)?.let { try { val luckyNumber = it.groupValues[1].toInt() - data.profile?.luckyNumber = luckyNumber - data.profile?.luckyNumberDate = Date.getToday() - data.luckyNumberList.add( - LuckyNumber( - data.profileId, - Date.getToday(), - luckyNumber + val luckyNumberObject = LuckyNumber( + data.profileId, + Date.getToday(), + luckyNumber + ) + + data.luckyNumberList.add(luckyNumberObject) + data.metadataList.add( + Metadata( + profileId, + Metadata.TYPE_LUCKY_NUMBER, + luckyNumberObject.date.value.toLong(), + data.profile?.empty ?: false, + data.profile?.empty ?: false, + System.currentTimeMillis() )) } catch (_: Exception){} } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java index 37ef3a78..5922b5bf 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java @@ -11,6 +11,7 @@ import androidx.room.migration.Migration; import androidx.sqlite.db.SupportSQLiteDatabase; import pl.szczodrzynski.edziennik.data.db.converters.ConverterDate; +import pl.szczodrzynski.edziennik.data.db.converters.ConverterDateInt; import pl.szczodrzynski.edziennik.data.db.converters.ConverterJsonObject; import pl.szczodrzynski.edziennik.data.db.converters.ConverterListLong; import pl.szczodrzynski.edziennik.data.db.converters.ConverterListString; @@ -87,13 +88,14 @@ import pl.szczodrzynski.edziennik.utils.models.Date; DebugLog.class, EndpointTimer.class, LessonRange.class, - Metadata.class}, version = 58) + Metadata.class}, version = 59) @TypeConverters({ ConverterTime.class, ConverterDate.class, ConverterJsonObject.class, ConverterListLong.class, - ConverterListString.class}) + ConverterListString.class, + ConverterDateInt.class}) public abstract class AppDb extends RoomDatabase { public abstract GradeDao gradeDao(); @@ -616,6 +618,32 @@ public abstract class AppDb extends RoomDatabase { database.execSQL("CREATE UNIQUE INDEX index_metadata_profileId_thingType_thingId ON metadata (profileId, thingType, thingId);"); } }; + private static final Migration MIGRATION_58_59 = new Migration(58, 59) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("UPDATE metadata SET addedDate = addedDate*1000 WHERE addedDate < 10000000000;"); + database.execSQL("INSERT INTO metadata (profileId, thingType, thingId, seen, notified, addedDate)\n" + + "SELECT profileId,\n" + + "10 AS thingType,\n" + + "luckyNumberDate*10000+substr(luckyNumberDate, 6)*100+substr(luckyNumberDate, 9) AS thingId,\n" + + "1 AS seen,\n" + + "1 AS notified,\n" + + "CAST(strftime('%s', luckyNumberDate) AS INT)*1000 AS addedDate\n" + + "FROM luckyNumbers;"); + database.execSQL("ALTER TABLE luckyNumbers RENAME TO _old_luckyNumbers;"); + database.execSQL("CREATE TABLE luckyNumbers (\n" + + "profileId INTEGER NOT NULL,\n" + + "luckyNumberDate INTEGER NOT NULL, \n" + + "luckyNumber INTEGER NOT NULL, \n" + + "PRIMARY KEY(profileId, luckyNumberDate))"); + database.execSQL("INSERT INTO luckyNumbers (profileId, luckyNumberDate, luckyNumber)\n" + + "SELECT profileId,\n" + + "luckyNumberDate*10000+substr(luckyNumberDate, 6)*100+substr(luckyNumberDate, 9) AS luckyNumberDate,\n" + + "luckyNumber\n" + + "FROM _old_luckyNumbers;"); + database.execSQL("DROP TABLE _old_luckyNumbers;"); + } + }; public static AppDb getDatabase(final Context context) { @@ -671,7 +699,8 @@ public abstract class AppDb extends RoomDatabase { MIGRATION_54_55, MIGRATION_55_56, MIGRATION_56_57, - MIGRATION_57_58 + MIGRATION_57_58, + MIGRATION_58_59 ) .allowMainThreadQueries() //.fallbackToDestructiveMigration() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/converters/ConverterDateInt.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/converters/ConverterDateInt.java new file mode 100644 index 00000000..05e72774 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/converters/ConverterDateInt.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-17. + */ + +package pl.szczodrzynski.edziennik.data.db.converters; + +import androidx.room.TypeConverter; + +import pl.szczodrzynski.edziennik.utils.models.Date; + +public class ConverterDateInt { + + @TypeConverter + public static Date toDate(int value) { + return Date.fromYmd(Integer.toString(value)); + } + + @TypeConverter + public static int toInt(Date value) { + return value == null ? 0 : value.getValue(); + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumber.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumber.java index c2ccd4bb..cad09b62 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumber.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumber.java @@ -1,8 +1,9 @@ package pl.szczodrzynski.edziennik.data.db.modules.luckynumber; +import androidx.annotation.NonNull; import androidx.room.ColumnInfo; import androidx.room.Entity; -import androidx.annotation.NonNull; +import androidx.room.Ignore; import pl.szczodrzynski.edziennik.utils.models.Date; @@ -12,7 +13,7 @@ public class LuckyNumber { public int profileId; @NonNull - @ColumnInfo(name = "luckyNumberDate") + @ColumnInfo(name = "luckyNumberDate", typeAffinity = 3) public Date date; @ColumnInfo(name = "luckyNumber") public int number; @@ -23,4 +24,8 @@ public class LuckyNumber { this.number = number; } + @Ignore + public LuckyNumber() { + this.date = Date.getToday(); + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumberDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumberDao.java index 6418e054..ced9b474 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumberDao.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumberDao.java @@ -1,35 +1,72 @@ package pl.szczodrzynski.edziennik.data.db.modules.luckynumber; +import androidx.annotation.Nullable; import androidx.lifecycle.LiveData; import androidx.room.Dao; import androidx.room.Insert; import androidx.room.OnConflictStrategy; import androidx.room.Query; +import androidx.room.RawQuery; +import androidx.sqlite.db.SimpleSQLiteQuery; +import androidx.sqlite.db.SupportSQLiteQuery; import java.util.List; +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata; +import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice; import pl.szczodrzynski.edziennik.utils.models.Date; +import static pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.TYPE_LUCKY_NUMBER; + @Dao -public interface LuckyNumberDao { +public abstract class LuckyNumberDao { @Insert(onConflict = OnConflictStrategy.REPLACE) - void add(LuckyNumber luckyNumber); + public abstract void add(LuckyNumber luckyNumber); @Insert(onConflict = OnConflictStrategy.REPLACE) - void addAll(List luckyNumberList); + public abstract void addAll(List luckyNumberList); @Query("DELETE FROM luckyNumbers WHERE profileId = :profileId") - void clear(int profileId); + public abstract void clear(int profileId); @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate = :date") - LiveData getByDate(int profileId, Date date); + public abstract LiveData getByDate(int profileId, Date date); @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate = :date") - LuckyNumber getByDateNow(int profileId, Date date); + public abstract LuckyNumber getByDateNow(int profileId, Date date); - @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId ORDER BY luckyNumberDate DESC") - LiveData> getAll(int profileId); + @Nullable + @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate > :date ORDER BY luckyNumberDate ASC LIMIT 1") + public abstract LuckyNumber getNearestFutureNow(int profileId, Date date); - @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId ORDER BY luckyNumberDate DESC") - List getAllNow(int profileId); + @RawQuery(observedEntities = {LuckyNumber.class}) + abstract LiveData> getAll(SupportSQLiteQuery query); + public LiveData> getAll(int profileId, String filter) { + return getAll(new SimpleSQLiteQuery("SELECT\n" + + "*\n" + + "FROM luckyNumbers\n" + + "LEFT JOIN metadata ON luckyNumberDate = thingId AND thingType = "+TYPE_LUCKY_NUMBER+" AND metadata.profileId = "+profileId+"\n" + + "WHERE luckyNumbers.profileId = "+profileId+" AND "+filter+"\n" + + "ORDER BY addedDate DESC")); + } + public LiveData> getAll(int profileId) { + return getAll(profileId, "1"); + } + public LiveData> getAllWhere(int profileId, String filter) { + return getAll(profileId, filter); + } + + @RawQuery(observedEntities = {Notice.class, Metadata.class}) + abstract List getAllNow(SupportSQLiteQuery query); + public List getAllNow(int profileId, String filter) { + return getAllNow(new SimpleSQLiteQuery("SELECT\n" + + "*\n" + + "FROM luckyNumbers\n" + + "LEFT JOIN metadata ON luckyNumberDate = thingId AND thingType = "+TYPE_LUCKY_NUMBER+" AND metadata.profileId = "+profileId+"\n" + + "WHERE luckyNumbers.profileId = "+profileId+" AND "+filter+"\n" + + "ORDER BY addedDate DESC")); + } + public List getNotNotifiedNow(int profileId) { + return getAllNow(profileId, "notified = 0"); + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumberFull.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumberFull.java new file mode 100644 index 00000000..f08b7d0d --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumberFull.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-18. + */ + +package pl.szczodrzynski.edziennik.data.db.modules.luckynumber; + +public class LuckyNumberFull extends LuckyNumber { + // metadata + public boolean seen; + public boolean notified; + public long addedDate; +} + diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/metadata/Metadata.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/metadata/Metadata.java index a621fe26..78dac639 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/metadata/Metadata.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/metadata/Metadata.java @@ -19,6 +19,7 @@ public class Metadata { public static final int TYPE_ANNOUNCEMENT = 7; public static final int TYPE_MESSAGE = 8; public static final int TYPE_TEACHER_ABSENCE = 9; + public static final int TYPE_LUCKY_NUMBER = 10; public int profileId; From 8dc358b0754c54afe168b894306da46c9e1fa3da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 18 Oct 2019 22:12:40 +0200 Subject: [PATCH 065/691] [APIv2] Notifications - DB entity, move to APIv2. Add Server event sync and web push. --- .../pl/szczodrzynski/edziennik/Notifier.java | 29 +-- .../edziennik/api/v2/ApiService.kt | 57 +++-- .../edziennik/api/v2/DataNotifications.kt | 210 ++++++++++++++++++ .../szczodrzynski/edziennik/api/v2/Errors.kt | 3 +- .../edziennik/api/v2/ServerSync.kt | 179 +++++++++++++++ .../api/v2/events/ErrorReportTask.kt | 9 - .../api/v2/events/task/ErrorReportTask.kt | 26 +++ .../api/v2/events/task/NotifyTask.kt | 79 +++++++ .../edziennik/api/v2/librus/Librus.kt | 4 +- .../api/v2/mobidziennik/Mobidziennik.kt | 4 +- .../edziennik/api/v2/models/Data.kt | 32 ++- .../edziennik/api/v2/template/Template.kt | 4 +- .../edziennik/api/v2/vulcan/Vulcan.kt | 4 +- .../edziennik/data/api/Edziennik.java | 45 ++-- .../edziennik/data/db/AppDb.java | 28 ++- .../db/modules/notification/Notification.kt | 126 +++++++++++ .../modules/notification/NotificationDao.kt | 35 +++ .../data/db/modules/profiles/Profile.kt | 2 + .../edziennik/network/ServerRequest.java | 17 +- .../sync/MyFirebaseMessagingService.java | 18 +- .../edziennik/utils/models/Notification.java | 37 ++- app/src/main/res/values-en/strings.xml | 7 +- app/src/main/res/values/strings.xml | 9 +- 23 files changed, 865 insertions(+), 99 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/DataNotifications.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ServerSync.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ErrorReportTask.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/ErrorReportTask.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/NotifyTask.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notification/Notification.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notification/NotificationDao.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java b/app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java index fa13e360..7b316ed8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java @@ -8,20 +8,21 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Build; +import android.util.Log; + import androidx.core.app.NotificationCompat; import androidx.core.content.ContextCompat; -import android.util.Log; import java.util.ArrayList; import java.util.Collections; import java.util.List; import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; -import pl.szczodrzynski.edziennik.utils.models.Date; -import pl.szczodrzynski.edziennik.utils.models.Time; import pl.szczodrzynski.edziennik.receivers.BootReceiver; import pl.szczodrzynski.edziennik.sync.SyncJob; import pl.szczodrzynski.edziennik.sync.SyncService; +import pl.szczodrzynski.edziennik.utils.models.Date; +import pl.szczodrzynski.edziennik.utils.models.Time; import static androidx.core.app.NotificationCompat.PRIORITY_DEFAULT; import static androidx.core.app.NotificationCompat.PRIORITY_MAX; @@ -36,14 +37,14 @@ public class Notifier { private static String CHANNEL_GET_DATA_DESC; private static final String GROUP_KEY_GET_DATA = "pl.szczodrzynski.edziennik.GET_DATA"; - private static final int ID_NOTIFICATIONS = 1337002; - private static String CHANNEL_NOTIFICATIONS_NAME; - private static String CHANNEL_NOTIFICATIONS_DESC; + public static final int ID_NOTIFICATIONS = 1337002; + public static String CHANNEL_NOTIFICATIONS_NAME; + public static String CHANNEL_NOTIFICATIONS_DESC; public static final String GROUP_KEY_NOTIFICATIONS = "pl.szczodrzynski.edziennik.NOTIFICATIONS"; - private static final int ID_NOTIFICATIONS_QUIET = 1337002; - private static String CHANNEL_NOTIFICATIONS_QUIET_NAME; - private static String CHANNEL_NOTIFICATIONS_QUIET_DESC; + public static final int ID_NOTIFICATIONS_QUIET = 1337002; + public static String CHANNEL_NOTIFICATIONS_QUIET_NAME; + public static String CHANNEL_NOTIFICATIONS_QUIET_DESC; public static final String GROUP_KEY_NOTIFICATIONS_QUIET = "pl.szczodrzynski.edziennik.NOTIFICATIONS_QUIET"; private static final int ID_UPDATES = 1337003; @@ -52,9 +53,9 @@ public class Notifier { private static final String GROUP_KEY_UPDATES = "pl.szczodrzynski.edziennik.UPDATES"; private App app; - private NotificationManager notificationManager; + public NotificationManager notificationManager; private NotificationCompat.Builder getDataNotificationBuilder; - private int notificationColor; + public int notificationColor; Notifier(App _app) { this.app = _app; @@ -109,13 +110,13 @@ public class Notifier { return app.appConfig.quietHoursStart > 0 && now >= start && now <= end; } - private int getNotificationDefaults() { + public int getNotificationDefaults() { return (shouldBeQuiet() ? 0 : Notification.DEFAULT_ALL); } - private String getNotificationGroup() { + public String getNotificationGroup() { return shouldBeQuiet() ? GROUP_KEY_NOTIFICATIONS_QUIET : GROUP_KEY_NOTIFICATIONS; } - private int getNotificationPriority() { + public int getNotificationPriority() { return shouldBeQuiet() ? PRIORITY_DEFAULT : PRIORITY_MAX; } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt index d505afc5..6ec5b605 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt @@ -13,8 +13,13 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.api.v2.events.* +import pl.szczodrzynski.edziennik.api.v2.events.SyncErrorEvent +import pl.szczodrzynski.edziennik.api.v2.events.SyncFinishedEvent +import pl.szczodrzynski.edziennik.api.v2.events.SyncProfileFinishedEvent +import pl.szczodrzynski.edziennik.api.v2.events.SyncProgressEvent import pl.szczodrzynski.edziennik.api.v2.events.requests.* +import pl.szczodrzynski.edziennik.api.v2.events.task.ErrorReportTask +import pl.szczodrzynski.edziennik.api.v2.events.task.NotifyTask import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.librus.Librus @@ -25,7 +30,7 @@ import pl.szczodrzynski.edziennik.api.v2.template.Template import pl.szczodrzynski.edziennik.api.v2.vulcan.Vulcan import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile -import pl.szczodrzynski.edziennik.utils.Utils.d +import kotlin.math.max import kotlin.math.min class ApiService : Service() { @@ -39,6 +44,7 @@ class ApiService : Service() { private val taskQueue = mutableListOf() private val errorList = mutableListOf() private var queueHasErrorReportTask = false + private var queueHasNotifyTask = false private var serviceClosed = false private var taskCancelled = false @@ -64,8 +70,13 @@ class ApiService : Service() { private val taskCallback = object : EdziennikCallback { override fun onCompleted() { edziennikInterface = null - if (!taskCancelled) { - EventBus.getDefault().post(SyncProfileFinishedEvent(taskProfileId)) + if (taskRunningObject is SyncProfileRequest) { + // post an event if this task is a sync, not e.g. first login or message getting + if (!taskCancelled) { + EventBus.getDefault().post(SyncProfileFinishedEvent(taskProfileId)) + } + // add a notifying task to create data notifications of this profile + addNotifyTask() } notification.setIdle().post() taskRunningObject = null @@ -86,6 +97,12 @@ class ApiService : Service() { errorList.add(apiError) apiError.throwable?.printStackTrace() if (apiError.isCritical) { + // if this error ends the sync, post an error notification + // if this is a sync task, create a notifying task + if (taskRunningObject is SyncProfileRequest) { + // add a notifying task to create data notifications of this profile + addNotifyTask() + } notification.setCriticalError().post() taskRunningObject = null taskRunning = false @@ -109,6 +126,18 @@ class ApiService : Service() { EventBus.getDefault().post(SyncProgressEvent(taskProfileId, taskProfileName, taskProgress, taskProgressRes)) notification.setProgressRes(taskProgressRes!!).post() } + + fun addNotifyTask() { + if (!queueHasNotifyTask) { + queueHasNotifyTask = true + taskQueue.add( + if (queueHasErrorReportTask) max(taskQueue.size-1, 0) else taskQueue.size, + NotifyTask().apply { + taskId = ++taskMaximumId + } + ) + } + } } /* _______ _ _ _ @@ -134,15 +163,17 @@ class ApiService : Service() { if (task is ErrorReportTask) { queueHasErrorReportTask = false - notification - .setCurrentTask(taskRunningId, null) - .setProgressRes(R.string.edziennik_notification_api_error_report_title) - .post() - errorList.forEach { error -> - d(TAG, "Error ${error.tag} profile ${error.profileId}: code ${error.errorCode}") - } - errorList.clear() - notification.setIdle().post() + task.run(notification, errorList) + taskRunningObject = null + taskRunning = false + taskRunningId = -1 + sync() + return + } + + if (task is NotifyTask) { + queueHasNotifyTask = false + task.run(app) taskRunningObject = null taskRunning = false taskRunningId = -1 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/DataNotifications.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/DataNotifications.kt new file mode 100644 index 00000000..73f6de4f --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/DataNotifications.kt @@ -0,0 +1,210 @@ +package pl.szczodrzynski.edziennik.api.v2 + +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS +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_GRADES +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.models.Data +import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance +import pl.szczodrzynski.edziennik.data.db.modules.events.Event +import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.* +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message +import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_LUCKY_NUMBER +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_ANNOUNCEMENT +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_ATTENDANCE +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_EVENT +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_GRADE +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_HOMEWORK +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_MESSAGE +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_NOTICE +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_TIMETABLE_LESSON_CHANGE +import pl.szczodrzynski.edziennik.data.db.modules.notification.getNotificationTitle +import pl.szczodrzynski.edziennik.utils.models.Date + +class DataNotifications(val data: Data) { + companion object { + private const val TAG = "DataNotifications" + } + + val app = data.app + val profileId = data.profile?.id ?: -1 + val profileName = data.profile?.name ?: "" + val profile = data.profile + val loginStore = data.loginStore + + init { run { + if (profile == null) { + return@run + } + + for (change in app.db.lessonChangeDao().getNotNotifiedNow(profileId)) { + val text = app.getString(R.string.notification_lesson_change_format, change.changeTypeStr(app), if (change.lessonDate == null) "" else change.lessonDate!!.formattedString, change.subjectLongName) + data.notifications += Notification( + title = app.getNotificationTitle(TYPE_TIMETABLE_LESSON_CHANGE), + text = text, + type = TYPE_TIMETABLE_LESSON_CHANGE, + profileId = profileId, + profileName = profileName, + viewId = DRAWER_ITEM_TIMETABLE, + addedDate = change.addedDate + ).addExtra("timetableDate", change.lessonDate?.value?.toLong()) + } + + for (event in app.db.eventDao().getNotNotifiedNow(profileId)) { + val text = if (event.type == Event.TYPE_HOMEWORK) + app.getString( + if (event.subjectLongName.isNullOrEmpty()) + R.string.notification_homework_no_subject_format + else + R.string.notification_homework_format, + event.subjectLongName, + event.eventDate.formattedString + ) + else + app.getString( + if (event.subjectLongName.isNullOrEmpty()) + R.string.notification_event_no_subject_format + else + R.string.notification_event_format, + event.typeName, + event.eventDate.formattedString, + event.subjectLongName + ) + val type = if (event.type == Event.TYPE_HOMEWORK) TYPE_NEW_HOMEWORK else TYPE_NEW_EVENT + data.notifications += Notification( + title = app.getNotificationTitle(type), + text = text, + type = type, + profileId = profileId, + profileName = profileName, + viewId = if (event.type == Event.TYPE_HOMEWORK) DRAWER_ITEM_HOMEWORK else DRAWER_ITEM_AGENDA, + addedDate = event.addedDate + ).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong()) + } + + val today = Date.getToday() + val todayValue = today.value + profile.currentSemester = profile.dateToSemester(today) + + for (grade in app.db.gradeDao().getNotNotifiedNow(profileId)) { + val gradeName = when (grade.type) { + TYPE_SEMESTER1_PROPOSED, TYPE_SEMESTER2_PROPOSED -> app.getString(R.string.grade_semester_proposed_format_2, grade.name) + TYPE_SEMESTER1_FINAL, TYPE_SEMESTER2_FINAL -> app.getString(R.string.grade_semester_final_format_2, grade.name) + TYPE_YEAR_PROPOSED -> app.getString(R.string.grade_year_proposed_format_2, grade.name) + TYPE_YEAR_FINAL -> app.getString(R.string.grade_year_final_format_2, grade.name) + else -> grade.name + } + val text = app.getString(R.string.notification_grade_format, gradeName, grade.subjectLongName) + data.notifications += Notification( + title = app.getNotificationTitle(TYPE_NEW_GRADE), + text = text, + type = TYPE_NEW_GRADE, + profileId = profileId, + profileName = profileName, + viewId = DRAWER_ITEM_GRADES, + addedDate = grade.addedDate + ).addExtra("gradeId", grade.id).addExtra("gradesSubjectId", grade.subjectId) + } + + for (notice in app.db.noticeDao().getNotNotifiedNow(profileId)) { + val noticeTypeStr = if (notice.type == Notice.TYPE_POSITIVE) app.getString(R.string.notification_notice_praise) else if (notice.type == Notice.TYPE_NEGATIVE) app.getString(R.string.notification_notice_warning) else app.getString(R.string.notification_notice_new) + val text = app.getString(R.string.notification_notice_format, noticeTypeStr, notice.teacherFullName, Date.fromMillis(notice.addedDate).formattedString) + data.notifications += Notification( + title = app.getNotificationTitle(TYPE_NEW_NOTICE), + text = text, + type = TYPE_NEW_NOTICE, + profileId = profileId, + profileName = profileName, + viewId = DRAWER_ITEM_BEHAVIOUR, + addedDate = notice.addedDate + ).addExtra("noticeId", notice.id) + } + + for (attendance in app.db.attendanceDao().getNotNotifiedNow(profileId)) { + var attendanceTypeStr = app.getString(R.string.notification_type_attendance) + when (attendance.type) { + Attendance.TYPE_ABSENT -> attendanceTypeStr = app.getString(R.string.notification_absence) + Attendance.TYPE_ABSENT_EXCUSED -> attendanceTypeStr = app.getString(R.string.notification_absence_excused) + Attendance.TYPE_BELATED -> attendanceTypeStr = app.getString(R.string.notification_belated) + Attendance.TYPE_BELATED_EXCUSED -> attendanceTypeStr = app.getString(R.string.notification_belated_excused) + Attendance.TYPE_RELEASED -> attendanceTypeStr = app.getString(R.string.notification_release) + } + val text = app.getString( + if (attendance.subjectLongName.isNullOrEmpty()) + R.string.notification_attendance_no_lesson_format + else + R.string.notification_attendance_format, + attendanceTypeStr, + attendance.subjectLongName, + attendance.lessonDate.formattedString + ) + data.notifications += Notification( + title = app.getNotificationTitle(TYPE_NEW_ATTENDANCE), + text = text, + type = TYPE_NEW_ATTENDANCE, + profileId = profileId, + profileName = profileName, + viewId = DRAWER_ITEM_ATTENDANCE, + addedDate = attendance.addedDate + ).addExtra("attendanceId", attendance.id).addExtra("attendanceSubjectId", attendance.subjectId) + } + + for (announcement in app.db.announcementDao().getNotNotifiedNow(profileId)) { + val text = app.context.getString(R.string.notification_announcement_format, announcement.subject) + data.notifications += Notification( + title = app.getNotificationTitle(TYPE_NEW_ANNOUNCEMENT), + text = text, + type = TYPE_NEW_ANNOUNCEMENT, + profileId = profileId, + profileName = profileName, + viewId = DRAWER_ITEM_ANNOUNCEMENTS, + addedDate = announcement.addedDate + ).addExtra("announcementId", announcement.id) + } + + for (message in app.db.messageDao().getReceivedNotNotifiedNow(profileId)) { + val text = app.context.getString(R.string.notification_message_format, message.senderFullName, message.subject) + data.notifications += Notification( + title = app.getNotificationTitle(TYPE_NEW_MESSAGE), + text = text, + type = TYPE_NEW_MESSAGE, + profileId = profileId, + profileName = profileName, + viewId = DRAWER_ITEM_MESSAGES, + addedDate = message.addedDate + ).addExtra("messageType", Message.TYPE_RECEIVED.toLong()).addExtra("messageId", message.id) + } + + val luckyNumbers = app.db.luckyNumberDao().getNotNotifiedNow(profileId) + luckyNumbers?.removeAll { it.date < today } + luckyNumbers?.forEach { luckyNumber -> + val text = when { + luckyNumber.date.value == todayValue -> // LN for today + app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_format else R.string.notification_lucky_number_format, luckyNumber.number) + luckyNumber.date.value == todayValue + 1 -> // LN for tomorrow + app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_tomorrow_format else R.string.notification_lucky_number_tomorrow_format, luckyNumber.number) + else -> // LN for later + app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_later_format else R.string.notification_lucky_number_later_format, luckyNumber.date.formattedString, luckyNumber.number) + } + data.notifications += Notification( + title = app.getNotificationTitle(TYPE_LUCKY_NUMBER), + text = text, + type = TYPE_LUCKY_NUMBER, + profileId = profileId, + profileName = profileName, + viewId = DRAWER_ITEM_HOME, + addedDate = luckyNumber.addedDate + ) + } + + data.db.metadataDao().setAllNotified(profileId, true) + }} +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index 6b0df35a..229ff405 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -125,4 +125,5 @@ const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901 const val EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN = 902 const val EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN = 903 const val EXCEPTION_LIBRUS_API_REQUEST = 904 -const val EXCEPTION_MOBIDZIENNIK_WEB_REQUEST = 905 \ No newline at end of file +const val EXCEPTION_MOBIDZIENNIK_WEB_REQUEST = 905 +const val EXCEPTION_NOTIFY_AND_SYNC = 910 \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ServerSync.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ServerSync.kt new file mode 100644 index 00000000..7114501a --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ServerSync.kt @@ -0,0 +1,179 @@ +package pl.szczodrzynski.edziennik.api.v2 + +import pl.szczodrzynski.edziennik.App.APP_URL +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.api.v2.models.Data +import pl.szczodrzynski.edziennik.data.api.AppError.CODE_APP_SERVER_ERROR +import pl.szczodrzynski.edziennik.data.db.modules.events.Event +import pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK +import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_SHARED_EVENT +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_SHARED_HOMEWORK +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_SERVER_MESSAGE +import pl.szczodrzynski.edziennik.data.db.modules.notification.getNotificationTitle +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile +import pl.szczodrzynski.edziennik.getJsonArray +import pl.szczodrzynski.edziennik.getLong +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.network.ServerRequest + +class ServerSync(val data: Data, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "ServerSync" + } + + val app = data.app + val profileId = data.profile?.id ?: -1 + val profileName = data.profile?.name ?: "" + val profile = data.profile + val loginStore = data.loginStore + + private fun getUsernameId(): String { + if (loginStore.data == null) { + return "NO_LOGIN_STORE" + } + if (profile?.studentData == null) { + return "NO_STUDENT_STORE" + } + return when (data.loginStore.type) { + LoginStore.LOGIN_TYPE_MOBIDZIENNIK -> loginStore.getLoginData("serverName", "MOBI_UN") + ":" + loginStore.getLoginData("username", "MOBI_UN") + ":" + profile.getStudentData("studentId", -1) + LoginStore.LOGIN_TYPE_LIBRUS -> profile.getStudentData("schoolName", "LIBRUS_UN") + ":" + profile.getStudentData("accountLogin", "LIBRUS_LOGIN_UN") + LoginStore.LOGIN_TYPE_IUCZNIOWIE -> loginStore.getLoginData("schoolName", "IUCZNIOWIE_UN") + ":" + loginStore.getLoginData("username", "IUCZNIOWIE_UN") + ":" + profile.getStudentData("registerId", -1) + LoginStore.LOGIN_TYPE_VULCAN -> profile.getStudentData("schoolName", "VULCAN_UN") + ":" + profile.getStudentData("studentId", -1) + LoginStore.LOGIN_TYPE_DEMO -> loginStore.getLoginData("serverName", "DEMO_UN") + ":" + loginStore.getLoginData("username", "DEMO_UN") + ":" + profile.getStudentData("studentId", -1) + else -> "TYPE_UNKNOWN" + } + } + + init { run { + if (profile?.registration != Profile.REGISTRATION_ENABLED) { + onSuccess() + return@run + } + + val request = ServerRequest( + app, + app.requestScheme+APP_URL+"main.php?sync", + "Edziennik2/REG", + profile, + data.loginStore.type, + getUsernameId() + ) + + if (profile.empty) { + request.setBodyParameter("first_run", "true") + } + + var hasNotifications = true + if (app.appConfig.webPushEnabled) { + data.notifications + .filterNot { it.posted } + .let { + if (it.isEmpty()) { + hasNotifications = false + null + } + else + it + }?.forEachIndexed { index, notification -> + if (notification.type != TYPE_NEW_SHARED_EVENT + && notification.type != TYPE_SERVER_MESSAGE + && notification.type != TYPE_NEW_SHARED_HOMEWORK) { + request.setBodyParameter("notify[$index][type]", notification.type.toString()) + request.setBodyParameter("notify[$index][title]", notification.title) + request.setBodyParameter("notify[$index][text]", notification.text) + } + } + } + + if ((!app.appConfig.webPushEnabled || !hasNotifications) && !profile.enableSharedEvents) { + onSuccess() + return@run + } + + val result = request.runSync() + + if (result == null) { + data.error(ApiError(TAG, CODE_APP_SERVER_ERROR) + .setCritical(false)) + onSuccess() + return@run + } + var apiResponse = result.toString() + if (result.getString("success") != "true") { + data.error(ApiError(TAG, CODE_APP_SERVER_ERROR) + .setCritical(false)) + onSuccess() + return@run + } + // HERE PROCESS ALL THE RECEIVED EVENTS + // add them to the profile and create appropriate notifications + result.getJsonArray("events")?.forEach { jEventEl -> + val event = jEventEl.asJsonObject + val teamCode = event.getString("team") + + // get the target Team from teamCode + val team = app.db.teamDao().getByCodeNow(profile.id, teamCode) + if (team != null) { + + // create the event from Json. Add the missing teamId and !!profileId!! + val eventObject = app.gson.fromJson(event.toString(), Event::class.java) + // proguard. disable for Event.class + if (eventObject.eventDate == null) { + apiResponse += "\n\nEventDate == null\n$event" + } + eventObject.profileId = profileId + eventObject.teamId = team.id + eventObject.addedManually = true + + if (eventObject.sharedBy == getUsernameId()) { + eventObject.sharedBy = "self" + eventObject.sharedByName = profile.studentNameLong + } + + val typeObject = app.db.eventTypeDao().getByIdNow(profileId, eventObject.type) + + app.db.eventDao().add(eventObject) + + val metadata = Metadata( + profileId, + if (eventObject.type == TYPE_HOMEWORK) Metadata.TYPE_HOMEWORK else Metadata.TYPE_EVENT, + eventObject.id, + profile.empty, + true, + event.getLong("addedDate") ?: 0 + ) + + val metadataId = app.db.metadataDao().add(metadata) + + // notify if the event is new and not first sync + if (metadataId != -1L && !profile.empty) { + val text = app.getString( + R.string.notification_shared_event_format, + eventObject.sharedByName, + if (typeObject != null) typeObject.name else "wydarzenie", + if (eventObject.eventDate == null) "???" else eventObject.eventDate.formattedString, + eventObject.topic + ) + val type = if (eventObject.type == TYPE_HOMEWORK) TYPE_NEW_SHARED_HOMEWORK else TYPE_NEW_SHARED_EVENT + data.notifications += Notification( + title = app.getNotificationTitle(type), + text = text, + type = type, + profileId = profileId, + profileName = profileName, + viewId = if (eventObject.type == TYPE_HOMEWORK) DRAWER_ITEM_HOMEWORK else DRAWER_ITEM_AGENDA, + addedDate = metadata.addedDate + ).addExtra("eventId", eventObject.id).addExtra("eventDate", eventObject.eventDate.value.toLong()) + } + } + } + + onSuccess() + }} +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ErrorReportTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ErrorReportTask.kt deleted file mode 100644 index 444443d1..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ErrorReportTask.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2019-10-1. - */ - -package pl.szczodrzynski.edziennik.api.v2.events - -import pl.szczodrzynski.edziennik.api.v2.models.ApiTask - -class ErrorReportTask : ApiTask(-1) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/ErrorReportTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/ErrorReportTask.kt new file mode 100644 index 00000000..aa1a52cf --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/ErrorReportTask.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-1. + */ + +package pl.szczodrzynski.edziennik.api.v2.events.task + +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.ApiService +import pl.szczodrzynski.edziennik.api.v2.EdziennikNotification +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.api.v2.models.ApiTask +import pl.szczodrzynski.edziennik.utils.Utils + +class ErrorReportTask : ApiTask(-1) { + fun run(notification: EdziennikNotification, errorList: MutableList) { + notification + .setCurrentTask(taskId, null) + .setProgressRes(R.string.edziennik_notification_api_error_report_title) + .post() + errorList.forEach { error -> + Utils.d(ApiService.TAG, "Error ${error.tag} profile ${error.profileId}: code ${error.errorCode}") + } + errorList.clear() + notification.setIdle().post() + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/NotifyTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/NotifyTask.kt new file mode 100644 index 00000000..753040a0 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/NotifyTask.kt @@ -0,0 +1,79 @@ +package pl.szczodrzynski.edziennik.api.v2.events.task + +import android.app.PendingIntent +import android.content.Intent +import android.os.Build +import androidx.core.app.NotificationCompat +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.MainActivity +import pl.szczodrzynski.edziennik.Notifier.ID_NOTIFICATIONS +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.models.ApiTask +import pl.szczodrzynski.edziennik.utils.models.Notification +import kotlin.math.min + +class NotifyTask : ApiTask(-1) { + fun run(app: App) { + val list = app.db.notificationDao().getNotPostedNow() + val notificationList = list.subList(0, min(8, list.size)) + + var unreadCount = list.size + + for (notification in notificationList) { + val intent = Intent(app, MainActivity::class.java) + notification.fillIntent(intent) + val pendingIntent = PendingIntent.getActivity(app, notification.id, intent, 0) + val notificationBuilder = NotificationCompat.Builder(app, app.notifier.notificationGroup) + // title, text, type, date + .setContentTitle(notification.title) + .setContentText(notification.text) + .setSubText(Notification.stringType(app, notification.type)) + .setWhen(notification.addedDate) + .setTicker(app.getString(R.string.notification_ticker_format, Notification.stringType(app, notification.type))) + // icon, color, lights, priority + .setSmallIcon(R.drawable.ic_notification) + .setColor(app.notifier.notificationColor) + .setLights(-0xff0001, 2000, 2000) + .setPriority(app.notifier.notificationPriority) + // channel, group, style + .setChannelId(app.notifier.notificationGroup) + .setGroup(app.notifier.notificationGroup) + .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY) + .setStyle(NotificationCompat.BigTextStyle().bigText(notification.text)) + // intent, auto cancel + .setContentIntent(pendingIntent) + .setAutoCancel(true) + if (!app.notifier.shouldBeQuiet()) { + notificationBuilder.setDefaults(app.notifier.notificationDefaults) + } + app.notifier.notificationManager.notify(notification.id, notificationBuilder.build()) + } + + if (notificationList.isNotEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + val intent = Intent(app, MainActivity::class.java) + intent.action = "android.intent.action.MAIN" + intent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_NOTIFICATIONS) + val pendingIntent = PendingIntent.getActivity(app, ID_NOTIFICATIONS, + intent, 0) + + val groupBuilder = NotificationCompat.Builder(app, app.notifier.notificationGroup) + .setSmallIcon(R.drawable.ic_notification) + .setColor(app.notifier.notificationColor) + .setContentTitle(app.getString(R.string.notification_new_notification_title_format, unreadCount)) + .setGroupSummary(true) + .setAutoCancel(true) + .setChannelId(app.notifier.notificationGroup) + .setGroup(app.notifier.notificationGroup) + .setLights(-0xff0001, 2000, 2000) + .setPriority(app.notifier.notificationPriority) + .setContentIntent(pendingIntent) + .setStyle(NotificationCompat.BigTextStyle()) + if (!app.notifier.shouldBeQuiet()) { + groupBuilder.setDefaults(app.notifier.notificationDefaults) + } + app.notifier.notificationManager.notify(ID_NOTIFICATIONS, groupBuilder.build()) + } + + app.db.notificationDao().setAllPosted() + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index d8961e74..0e3d093e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -36,7 +36,9 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va private fun completed() { data.saveData() - callback.onCompleted() + data.notifyAndSyncEvents { + callback.onCompleted() + } } /* _______ _ _ _ _ _ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt index dc1aa662..f94d7ec9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt @@ -36,7 +36,9 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto private fun completed() { data.saveData() - callback.onCompleted() + data.notifyAndSyncEvents { + callback.onCompleted() + } } /* _______ _ _ _ _ _ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index c32703d2..5b1eeed7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -5,8 +5,12 @@ import android.util.SparseArray import com.google.gson.JsonObject import im.wangchao.mhttp.Response import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.v2.DataNotifications +import pl.szczodrzynski.edziennik.api.v2.EXCEPTION_NOTIFY_AND_SYNC +import pl.szczodrzynski.edziennik.api.v2.ServerSync import pl.szczodrzynski.edziennik.api.v2.interfaces.EndpointCallback import pl.szczodrzynski.edziennik.data.api.AppError.* +import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance @@ -23,6 +27,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.messages.Message import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher @@ -38,6 +43,9 @@ import java.net.UnknownHostException import javax.net.ssl.SSLException open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) { + companion object { + private const val TAG = "Data" + } var fakeLogin = false @@ -85,6 +93,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) var endpointTimers = mutableListOf() + val notifications = mutableListOf() + val teacherList = LongSparseArray() val subjectList = LongSparseArray() val teamList = LongSparseArray() @@ -132,7 +142,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) val metadataList = mutableListOf() val messageMetadataList = mutableListOf() - val db by lazy { app.db } + val db: AppDb by lazy { app.db } init { clear() @@ -175,6 +185,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) if (profile == null) return // return on first login + profile.empty = false + db.profileDao().add(profile) db.loginStoreDao().add(loginStore) @@ -233,6 +245,20 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) db.metadataDao().setSeen(messageMetadataList) } + fun notifyAndSyncEvents(onSuccess: () -> Unit) { + try { + DataNotifications(this) + ServerSync(this) { + db.notificationDao().addAll(notifications) + onSuccess() + } + } + catch (e: Exception) { + error(ApiError(TAG, EXCEPTION_NOTIFY_AND_SYNC) + .withThrowable(e)) + } + } + fun setSyncNext(endpointId: Int, syncIn: Long? = null, viewId: Int? = null) { EndpointTimer(profile?.id ?: -1, endpointId).apply { syncedNow() @@ -257,7 +283,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) } fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) { - var code = when (throwable) { + val code = when (throwable) { is UnknownHostException, is SSLException, is InterruptedIOException -> CODE_NO_INTERNET is SocketTimeoutException -> CODE_TIMEOUT else -> when (response?.code()) { @@ -268,7 +294,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) error(ApiError(tag, code).apply { profileId = profile?.id ?: -1 }.withResponse(response).withThrowable(throwable).withApiResponse(apiResponse)) } fun error(tag: String, errorCode: Int, response: Response? = null, apiResponse: String? = null) { - var code = when (null) { + val code = when (null) { is UnknownHostException, is SSLException, is InterruptedIOException -> CODE_NO_INTERNET is SocketTimeoutException -> CODE_TIMEOUT else -> when (response?.code()) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt index 4f2f6580..b9949410 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt @@ -35,7 +35,9 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, private fun completed() { data.saveData() - callback.onCompleted() + data.notifyAndSyncEvents { + callback.onCompleted() + } } /* _______ _ _ _ _ _ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt index 5b2b6e79..60504a98 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt @@ -35,7 +35,9 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va private fun completed() { data.saveData() - callback.onCompleted() + data.notifyAndSyncEvents { + callback.onCompleted() + } } /* _______ _ _ _ _ _ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java index 328aaa95..e21a12dc 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java @@ -40,8 +40,8 @@ import java.util.List; import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; -import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; +import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.WidgetTimetable; import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface; import pl.szczodrzynski.edziennik.data.api.interfaces.SyncCallback; @@ -62,11 +62,11 @@ import pl.szczodrzynski.edziennik.data.db.modules.notices.NoticeFull; import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; import pl.szczodrzynski.edziennik.data.db.modules.teams.Team; -import pl.szczodrzynski.edziennik.utils.models.Date; -import pl.szczodrzynski.edziennik.utils.models.Notification; import pl.szczodrzynski.edziennik.network.ServerRequest; import pl.szczodrzynski.edziennik.sync.SyncJob; import pl.szczodrzynski.edziennik.utils.Themes; +import pl.szczodrzynski.edziennik.utils.models.Date; +import pl.szczodrzynski.edziennik.utils.models.Notification; import pl.szczodrzynski.edziennik.widgets.luckynumber.WidgetLuckyNumber; import pl.szczodrzynski.edziennik.widgets.notifications.WidgetNotifications; @@ -102,6 +102,19 @@ import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_ import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_LIBRUS; import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK; import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_VULCAN; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_AUTO_ARCHIVING; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_LUCKY_NUMBER; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_ANNOUNCEMENT; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_ATTENDANCE; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_EVENT; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_GRADE; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_HOMEWORK; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_MESSAGE; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_NOTICE; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_SHARED_EVENT; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_SHARED_HOMEWORK; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_SERVER_MESSAGE; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_TIMETABLE_LESSON_CHANGE; import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.REGISTRATION_ENABLED; import static pl.szczodrzynski.edziennik.sync.SyncService.PROFILE_MAX_PROGRESS; import static pl.szczodrzynski.edziennik.utils.Utils.d; @@ -291,7 +304,7 @@ public class Edziennik { String text = app.getContext().getString(R.string.notification_lesson_change_format, change.changeTypeStr(app.getContext()), change.lessonDate == null ? "" : change.lessonDate.getFormattedString(), change.subjectLongName); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) - .withType(Notification.TYPE_TIMETABLE_LESSON_CHANGE) + .withType(TYPE_TIMETABLE_LESSON_CHANGE) .withFragmentRedirect(MainActivity.DRAWER_ITEM_TIMETABLE) .withLongExtra("timetableDate", change.lessonDate.getValue()) .withAddedDate(change.addedDate) @@ -305,7 +318,7 @@ public class Edziennik { text = app.getContext().getString(R.string.notification_event_format, event.typeName, event.eventDate.getFormattedString(), ns(app.getString(R.string.notification_event_no_subject), event.subjectLongName)); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) - .withType(event.type == TYPE_HOMEWORK ? Notification.TYPE_NEW_HOMEWORK : Notification.TYPE_NEW_EVENT) + .withType(event.type == TYPE_HOMEWORK ? TYPE_NEW_HOMEWORK : TYPE_NEW_EVENT) .withFragmentRedirect(event.type == TYPE_HOMEWORK ? MainActivity.DRAWER_ITEM_HOMEWORK : MainActivity.DRAWER_ITEM_AGENDA) .withLongExtra("eventId", event.id) .withLongExtra("eventDate", event.eventDate.getValue()) @@ -342,7 +355,7 @@ public class Edziennik { String text = app.getContext().getString(R.string.notification_grade_format, gradeName, grade.subjectLongName); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) - .withType(Notification.TYPE_NEW_GRADE) + .withType(TYPE_NEW_GRADE) .withFragmentRedirect(MainActivity.DRAWER_ITEM_GRADES) .withLongExtra("gradesSubjectId", grade.subjectId) .withAddedDate(grade.addedDate) @@ -353,7 +366,7 @@ public class Edziennik { String text = app.getContext().getString(R.string.notification_notice_format, noticeTypeStr, notice.teacherFullName, Date.fromMillis(notice.addedDate).getFormattedString()); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) - .withType(Notification.TYPE_NEW_NOTICE) + .withType(TYPE_NEW_NOTICE) .withFragmentRedirect(MainActivity.DRAWER_ITEM_BEHAVIOUR) .withLongExtra("noticeId", notice.id) .withAddedDate(notice.addedDate) @@ -381,7 +394,7 @@ public class Edziennik { String text = app.getContext().getString(R.string.notification_attendance_format, attendanceTypeStr, attendance.subjectLongName, attendance.lessonDate.getFormattedString()); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) - .withType(Notification.TYPE_NEW_ATTENDANCE) + .withType(TYPE_NEW_ATTENDANCE) .withFragmentRedirect(MainActivity.DRAWER_ITEM_ATTENDANCE) .withLongExtra("attendanceId", attendance.id) .withAddedDate(attendance.addedDate) @@ -391,7 +404,7 @@ public class Edziennik { String text = app.getContext().getString(R.string.notification_announcement_format, announcement.subject); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) - .withType(Notification.TYPE_NEW_ANNOUNCEMENT) + .withType(TYPE_NEW_ANNOUNCEMENT) .withFragmentRedirect(MainActivity.DRAWER_ITEM_ANNOUNCEMENTS) .withLongExtra("announcementId", announcement.id) .withAddedDate(announcement.addedDate) @@ -401,7 +414,7 @@ public class Edziennik { String text = app.getContext().getString(R.string.notification_message_format, message.senderFullName, message.subject); app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) - .withType(Notification.TYPE_NEW_MESSAGE) + .withType(TYPE_NEW_MESSAGE) .withFragmentRedirect(MainActivity.DRAWER_ITEM_MESSAGES) .withLongExtra("messageType", Message.TYPE_RECEIVED) .withLongExtra("messageId", message.id) @@ -423,7 +436,7 @@ public class Edziennik { } app.notifier.add(new Notification(app.getContext(), text) .withProfileData(profile.getId(), profile.getName()) - .withType(Notification.TYPE_LUCKY_NUMBER) + .withType(TYPE_LUCKY_NUMBER) .withFragmentRedirect(MainActivity.DRAWER_ITEM_HOME) ); oldLuckyNumber = profile.getLuckyNumber(); @@ -456,9 +469,9 @@ public class Edziennik { for (Notification notification : app.appConfig.notifications) { //Log.d(TAG, notification.text); if (!notification.notified) { - if (notification.type != Notification.TYPE_NEW_SHARED_EVENT - && notification.type != Notification.TYPE_SERVER_MESSAGE - && notification.type != Notification.TYPE_NEW_SHARED_HOMEWORK) // these are automatically sent to the browser by the server + if (notification.type != TYPE_NEW_SHARED_EVENT + && notification.type != TYPE_SERVER_MESSAGE + && notification.type != TYPE_NEW_SHARED_HOMEWORK) // these are automatically sent to the browser by the server { //Log.d(TAG, "Adding notify[" + position + "]"); syncRequest.setBodyParameter("notify[" + position + "][type]", Integer.toString(notification.type)); @@ -520,7 +533,7 @@ public class Edziennik { if (metadataId != -1 && !registerEmpty) { app.notifier.add(new Notification(app.getContext(), app.getString(R.string.notification_shared_event_format, event.sharedByName, type != null ? type.name : "wydarzenie", event.eventDate == null ? "nieznana data" : event.eventDate.getFormattedString(), event.topic)) .withProfileData(profile.getId(), profile.getName()) - .withType(event.type == TYPE_HOMEWORK ? Notification.TYPE_NEW_SHARED_HOMEWORK : Notification.TYPE_NEW_SHARED_EVENT) + .withType(event.type == TYPE_HOMEWORK ? TYPE_NEW_SHARED_HOMEWORK : TYPE_NEW_SHARED_EVENT) .withFragmentRedirect(event.type == TYPE_HOMEWORK ? MainActivity.DRAWER_ITEM_HOMEWORK : MainActivity.DRAWER_ITEM_AGENDA) .withLongExtra("eventDate", event.eventDate.getValue()) ); @@ -687,7 +700,7 @@ public class Edziennik { profile.setArchived(true); app.notifier.add(new Notification(app.getContext(), app.getString(R.string.profile_auto_archiving_format, profile.getName(), profile.getDateYearEnd().getFormattedString())) .withProfileData(profile.getId(), profile.getName()) - .withType(Notification.TYPE_AUTO_ARCHIVING) + .withType(TYPE_AUTO_ARCHIVING) .withFragmentRedirect(DRAWER_ITEM_HOME) .withLongExtra("autoArchiving", 1L) ); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java index 5922b5bf..1ba10997 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java @@ -52,6 +52,8 @@ import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata; import pl.szczodrzynski.edziennik.data.db.modules.metadata.MetadataDao; import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice; import pl.szczodrzynski.edziennik.data.db.modules.notices.NoticeDao; +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification; +import pl.szczodrzynski.edziennik.data.db.modules.notification.NotificationDao; import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileDao; import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject; @@ -88,7 +90,8 @@ import pl.szczodrzynski.edziennik.utils.models.Date; DebugLog.class, EndpointTimer.class, LessonRange.class, - Metadata.class}, version = 59) + Notification.class, + Metadata.class}, version = 60) @TypeConverters({ ConverterTime.class, ConverterDate.class, @@ -121,6 +124,7 @@ public abstract class AppDb extends RoomDatabase { public abstract DebugLogDao debugLogDao(); public abstract EndpointTimerDao endpointTimerDao(); public abstract LessonRangeDao lessonRangeDao(); + public abstract NotificationDao notificationDao(); public abstract MetadataDao metadataDao(); private static volatile AppDb INSTANCE; @@ -644,6 +648,25 @@ public abstract class AppDb extends RoomDatabase { database.execSQL("DROP TABLE _old_luckyNumbers;"); } }; + private static final Migration MIGRATION_59_60 = new Migration(59, 60) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("CREATE TABLE notifications (\n" + + " id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" + + " title TEXT NOT NULL,\n" + + " `text` TEXT NOT NULL,\n" + + " `type` INTEGER NOT NULL,\n" + + " profileId INTEGER DEFAULT NULL,\n" + + " profileName TEXT DEFAULT NULL,\n" + + " posted INTEGER NOT NULL DEFAULT 0,\n" + + " viewId INTEGER DEFAULT NULL,\n" + + " extras TEXT DEFAULT NULL,\n" + + " addedDate INTEGER NOT NULL\n" + + ");"); + + database.execSQL("ALTER TABLE profiles ADD COLUMN disabledNotifications TEXT DEFAULT NULL"); + } + }; public static AppDb getDatabase(final Context context) { @@ -700,7 +723,8 @@ public abstract class AppDb extends RoomDatabase { MIGRATION_55_56, MIGRATION_56_57, MIGRATION_57_58, - MIGRATION_58_59 + MIGRATION_58_59, + MIGRATION_59_60 ) .allowMainThreadQueries() //.fallbackToDestructiveMigration() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notification/Notification.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notification/Notification.kt new file mode 100644 index 00000000..39f3d37f --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notification/Notification.kt @@ -0,0 +1,126 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-18. + */ + +package pl.szczodrzynski.edziennik.data.db.modules.notification + +import android.content.Context +import android.content.Intent +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.google.gson.JsonObject +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_AUTO_ARCHIVING +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_ERROR +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_FEEDBACK_MESSAGE +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_GENERAL +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_LUCKY_NUMBER +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_ANNOUNCEMENT +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_ATTENDANCE +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_EVENT +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_GRADE +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_HOMEWORK +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_MESSAGE +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_NOTICE +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_SHARED_EVENT +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_SERVER_MESSAGE +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_TIMETABLE_CHANGED +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_TIMETABLE_LESSON_CHANGE +import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_UPDATE + +@Entity(tableName = "notifications") +data class Notification( + @PrimaryKey(autoGenerate = true) + val id: Int = 0, + + val title: String, + val text: String, + + val type: Int, + + val profileId: Int?, + val profileName: String?, + + var posted: Boolean = false, + + var viewId: Int? = null, + var extras: JsonObject? = null, + + val addedDate: Long = System.currentTimeMillis() +) { + companion object { + const val TYPE_GENERAL = 0 + const val TYPE_UPDATE = 1 + const val TYPE_ERROR = 2 + const val TYPE_TIMETABLE_CHANGED = 3 + const val TYPE_TIMETABLE_LESSON_CHANGE = 4 + const val TYPE_NEW_GRADE = 5 + const val TYPE_NEW_EVENT = 6 + const val TYPE_NEW_HOMEWORK = 10 + const val TYPE_NEW_SHARED_EVENT = 7 + const val TYPE_NEW_SHARED_HOMEWORK = 12 + const val TYPE_NEW_MESSAGE = 8 + const val TYPE_NEW_NOTICE = 9 + const val TYPE_NEW_ATTENDANCE = 13 + const val TYPE_SERVER_MESSAGE = 11 + const val TYPE_LUCKY_NUMBER = 14 + const val TYPE_NEW_ANNOUNCEMENT = 15 + const val TYPE_FEEDBACK_MESSAGE = 16 + const val TYPE_AUTO_ARCHIVING = 17 + } + + fun addExtra(key: String, value: Long?): Notification { + extras = extras ?: JsonObject() + extras?.addProperty(key, value) + return this + } + fun addExtra(key: String, value: String?): Notification { + extras = extras ?: JsonObject() + extras?.addProperty(key, value) + return this + } + + fun fillIntent(intent: Intent) { + if (profileId != -1) + intent.putExtra("profileId", profileId) + if (viewId != -1) + intent.putExtra("fragmentId", viewId) + try { + extras?.entrySet()?.forEach { (key, value) -> + if (!value.isJsonPrimitive) + return@forEach + val primitive = value.asJsonPrimitive + if (primitive.isNumber) { + intent.putExtra(key, primitive.asLong) + } else if (primitive.isString) { + intent.putExtra(key, primitive.asString) + } + } + } catch (e: NullPointerException) { + e.printStackTrace() + } + } +} + +fun Context.getNotificationTitle(type: Int): String { + return getString(when (type) { + TYPE_UPDATE -> R.string.notification_type_update + TYPE_ERROR -> R.string.notification_type_error + TYPE_TIMETABLE_CHANGED -> R.string.notification_type_timetable_change + TYPE_TIMETABLE_LESSON_CHANGE -> R.string.notification_type_timetable_lesson_change + TYPE_NEW_GRADE -> R.string.notification_type_new_grade + TYPE_NEW_EVENT -> R.string.notification_type_new_event + TYPE_NEW_HOMEWORK -> R.string.notification_type_new_homework + TYPE_NEW_SHARED_EVENT -> R.string.notification_type_new_shared_event + TYPE_NEW_MESSAGE -> R.string.notification_type_new_message + TYPE_NEW_NOTICE -> R.string.notification_type_notice + TYPE_NEW_ATTENDANCE -> R.string.notification_type_attendance + TYPE_SERVER_MESSAGE -> R.string.notification_type_server_message + TYPE_LUCKY_NUMBER -> R.string.notification_type_lucky_number + TYPE_FEEDBACK_MESSAGE -> R.string.notification_type_feedback_message + TYPE_NEW_ANNOUNCEMENT -> R.string.notification_type_new_announcement + TYPE_AUTO_ARCHIVING -> R.string.notification_type_auto_archiving + TYPE_GENERAL -> R.string.notification_type_general + else -> R.string.notification_type_general + }) +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notification/NotificationDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notification/NotificationDao.kt new file mode 100644 index 00000000..c08e7e6d --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notification/NotificationDao.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-18. + */ + +package pl.szczodrzynski.edziennik.data.db.modules.notification + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query + +@Dao +interface NotificationDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun add(notification: Notification) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun addAll(notificationList: List) + + @Query("DELETE FROM notifications WHERE profileId = :profileId") + fun clear(profileId: Int) + + @Query("SELECT * FROM notifications") + fun getAll(): LiveData> + + @Query("SELECT * FROM notifications") + fun getAllNow(): List + + @Query("SELECT * FROM notifications WHERE posted = 0 ORDER BY addedDate DESC") + fun getNotPostedNow(): List + + @Query("UPDATE notifications SET posted = 1 WHERE posted = 0") + fun setAllPosted() +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/profiles/Profile.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/profiles/Profile.kt index 24029bcb..82d07f86 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/profiles/Profile.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/profiles/Profile.kt @@ -84,6 +84,8 @@ open class Profile : IDrawerProfile { var changedEndpoints: List? = null + var disabledNotifications: List? = null + var lastFullSync: Long = 0 var lastReceiversSync: Long = 0 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/network/ServerRequest.java b/app/src/main/java/pl/szczodrzynski/edziennik/network/ServerRequest.java index 4d8f9a80..53be419d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/network/ServerRequest.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/network/ServerRequest.java @@ -16,6 +16,7 @@ import im.wangchao.mhttp.ThreadMode; import im.wangchao.mhttp.callback.JsonCallbackHandler; import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; import pl.szczodrzynski.edziennik.utils.Utils; @@ -33,21 +34,25 @@ public class ServerRequest { } public ServerRequest(App app, String url, String source, ProfileFull profileFull) { + this(app, url, source, profileFull, profileFull == null ? -1 : profileFull.getLoginStoreType(), profileFull == null ? "" : profileFull.getUsernameId()); + } + + public ServerRequest(App app, String url, String source, Profile profile, int loginStoreType, String usernameId) { this.app = app; this.url = url; this.params = new ArrayList<>(); - this.username = (profileFull != null && profileFull.getRegistration() == REGISTRATION_ENABLED ? profileFull.getUsernameId() : app.deviceId); + this.username = (profile != null && profile.getRegistration() == REGISTRATION_ENABLED ? usernameId : app.deviceId); this.source = source; - if (profileFull != null && profileFull.getRegistration() == REGISTRATION_ENABLED) { - this.setBodyParameter("login_type", Integer.toString(profileFull.getLoginStoreType())); - this.setBodyParameter("name_long", profileFull.getStudentNameLong()); - this.setBodyParameter("name_short", profileFull.getStudentNameShort()); + if (profile != null && profile.getRegistration() == REGISTRATION_ENABLED) { + this.setBodyParameter("login_type", Integer.toString(loginStoreType)); + this.setBodyParameter("name_long", profile.getStudentNameLong()); + this.setBodyParameter("name_short", profile.getStudentNameShort()); //if (Looper.myLooper() == Looper.getMainLooper()) { if (Looper.getMainLooper().getThread() == Thread.currentThread()) { this.setBodyParameter("team_ids", "UI_THREAD"); } else { - this.setBodyParameter("team_ids", app.gson.toJson(app.db.teamDao().getAllCodesNow(profileFull.getId()))); + this.setBodyParameter("team_ids", app.gson.toJson(app.db.teamDao().getAllCodesNow(profile.getId()))); } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java b/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java index feae360f..312724b7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java @@ -15,21 +15,25 @@ import java.util.List; import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; -import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; +import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.data.db.modules.events.Event; import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull; import pl.szczodrzynski.edziennik.data.db.modules.events.EventType; import pl.szczodrzynski.edziennik.data.db.modules.feedback.FeedbackMessage; import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; import pl.szczodrzynski.edziennik.data.db.modules.teams.Team; +import pl.szczodrzynski.edziennik.network.ServerRequest; import pl.szczodrzynski.edziennik.ui.modules.base.DebugFragment; import pl.szczodrzynski.edziennik.utils.models.Notification; -import pl.szczodrzynski.edziennik.network.ServerRequest; import static pl.szczodrzynski.edziennik.App.APP_URL; import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK; import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_FEEDBACK_MESSAGE; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_SHARED_EVENT; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_SHARED_HOMEWORK; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_SERVER_MESSAGE; import static pl.szczodrzynski.edziennik.utils.Utils.d; import static pl.szczodrzynski.edziennik.utils.Utils.strToInt; @@ -175,7 +179,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { case "message": app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message")) .withTitle(remoteMessage.getData().get("title")) - .withType(Notification.TYPE_SERVER_MESSAGE) + .withType(TYPE_SERVER_MESSAGE) .withFragmentRedirect(MainActivity.DRAWER_ITEM_NOTIFICATIONS) ); app.notifier.postAll(null); @@ -203,7 +207,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { app.notifier.add(new Notification(app.getContext(), feedbackMessage.text) .withTitle(remoteMessage.getData().get("title")) - .withType(Notification.TYPE_FEEDBACK_MESSAGE) + .withType(TYPE_FEEDBACK_MESSAGE) .withFragmentRedirect(MainActivity.TARGET_FEEDBACK) ); app.notifier.postAll(null); @@ -225,7 +229,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { }); app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message")) .withTitle(remoteMessage.getData().get("title")) - .withType(Notification.TYPE_FEEDBACK_MESSAGE) + .withType(TYPE_FEEDBACK_MESSAGE) .withFragmentRedirect(MainActivity.TARGET_FEEDBACK) ); app.notifier.postAll(null); @@ -279,7 +283,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { EventType eventType = app.db.eventTypeDao().getByIdNow(profile.getId(), event.type); app.notifier.add(new Notification(app.getContext(), app.getString((oldEvent == null ? R.string.notification_shared_event_format : R.string.notification_shared_event_modified_format), event.sharedByName, eventType == null ? "wydarzenie" : eventType.name, event.eventDate.getFormattedString(), event.topic)) .withProfileData(profile.getId(), profile.getName()) - .withType(event.type == TYPE_HOMEWORK ? Notification.TYPE_NEW_SHARED_HOMEWORK : Notification.TYPE_NEW_SHARED_EVENT) + .withType(event.type == TYPE_HOMEWORK ? TYPE_NEW_SHARED_HOMEWORK : TYPE_NEW_SHARED_EVENT) .withFragmentRedirect(event.type == TYPE_HOMEWORK ? MainActivity.DRAWER_ITEM_HOMEWORK : MainActivity.DRAWER_ITEM_AGENDA) .withLongExtra("eventDate", event.eventDate.getValue()) ); @@ -298,7 +302,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { if (oldEvent != null) { app.notifier.add(new Notification(app.getContext(), app.getString(R.string.notification_shared_event_removed_format, oldEvent.sharedByName, oldEvent.typeName, oldEvent.eventDate.getFormattedString(), oldEvent.topic)) .withProfileData(profile.getId(), profile.getName()) - .withType(oldEvent.type == TYPE_HOMEWORK ? Notification.TYPE_NEW_SHARED_HOMEWORK : Notification.TYPE_NEW_SHARED_EVENT) + .withType(oldEvent.type == TYPE_HOMEWORK ? TYPE_NEW_SHARED_HOMEWORK : TYPE_NEW_SHARED_EVENT) .withFragmentRedirect(oldEvent.type == TYPE_HOMEWORK ? MainActivity.DRAWER_ITEM_HOMEWORK : MainActivity.DRAWER_ITEM_AGENDA) .withLongExtra("eventDate", oldEvent.eventDate.getValue()) ); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Notification.java b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Notification.java index 047cd16f..9a3dc5eb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Notification.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Notification.java @@ -12,6 +12,24 @@ import java.util.Random; import pl.szczodrzynski.edziennik.R; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_AUTO_ARCHIVING; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_ERROR; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_FEEDBACK_MESSAGE; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_GENERAL; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_LUCKY_NUMBER; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_ANNOUNCEMENT; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_ATTENDANCE; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_EVENT; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_GRADE; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_HOMEWORK; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_MESSAGE; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_NOTICE; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_NEW_SHARED_EVENT; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_SERVER_MESSAGE; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_TIMETABLE_CHANGED; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_TIMETABLE_LESSON_CHANGE; +import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_UPDATE; + public class Notification { public int profileId; public String title; @@ -78,25 +96,6 @@ public class Notification { return this; } - public static final int TYPE_GENERAL = 0; - public static final int TYPE_UPDATE = 1; - public static final int TYPE_ERROR = 2; - public static final int TYPE_TIMETABLE_CHANGED = 3; - public static final int TYPE_TIMETABLE_LESSON_CHANGE = 4; - public static final int TYPE_NEW_GRADE = 5; - public static final int TYPE_NEW_EVENT = 6; - public static final int TYPE_NEW_HOMEWORK = 10; - public static final int TYPE_NEW_SHARED_EVENT = 7; - public static final int TYPE_NEW_SHARED_HOMEWORK = 12; - public static final int TYPE_NEW_MESSAGE = 8; - public static final int TYPE_NEW_NOTICE = 9; - public static final int TYPE_NEW_ATTENDANCE = 13; - public static final int TYPE_SERVER_MESSAGE = 11; - public static final int TYPE_LUCKY_NUMBER = 14; - public static final int TYPE_NEW_ANNOUNCEMENT = 15; - public static final int TYPE_FEEDBACK_MESSAGE = 16; - public static final int TYPE_AUTO_ARCHIVING = 17; - public static String stringType(Context context, int errorCode) { switch (errorCode) { diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 82f32af5..bbd86648 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -446,7 +446,8 @@ Absence Excused absence School announcement: %s - %s on lesson %s z %s + %1$s on lesson %2$s on day %3$s + %1$s on day %3$s Late Excused late Notification about data downloading @@ -458,7 +459,8 @@ Notifications about new versions of the app App updates Downloading update… - %s %s from %s + %1$s on %2$s from %3$s + %1$s on %2$s unknown subject Cancel Szkolny.eu: error @@ -469,6 +471,7 @@ Update New grade (%s) from %s Homework from %s for %s + Homework for %s Today %d is the lucky number. The lucky number for %s is %d. The lucky number for tomorrow is %d. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c73422ab..7f43e31a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -487,7 +487,8 @@ Nieobecność Nieobecność usprawiedliwiona Ogłoszenie szkolne: %s - %s na lekcji %s z %s + %1$s na lekcji %2$s z dnia %3$s + %1$s z dnia %3$s Spóźnienie Spóźnienie usprawiedliwione Powiadomienie o pobieraniu danych dla e-dziennika @@ -499,7 +500,8 @@ Powiadomienia o nowych wersjach aplikacji Aktualizacje Pobieranie aktualizacji… - %s %s z %s + %1$s dnia %2$s z %3$s + %1$s dnia %2$s nieznanego przedmiotu Przerwij Szkolny.eu: błąd @@ -509,7 +511,8 @@ Pobieranie danych Synchronizacja Nowa ocena (%s) z %s - Zadanie domowe z %s na %s + Zadanie domowe z %1$s na %2$s + Zadanie domowe na %2$s %s %s - %s Dzisiaj %d to szczęśliwy numerek. Szczęsliwy numerek na %s to %d. From cf8afc03bc72d5bce271adee5b58a56e3b866229 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Fri, 18 Oct 2019 23:56:03 +0200 Subject: [PATCH 066/691] [APIv2/Librus] Add getting teacher absences --- .../api/v2/librus/data/LibrusData.kt | 4 ++ .../data/api/LibrusApiTeacherFreeDays.kt | 56 +++++++++++++++++++ .../edziennik/api/v2/models/Data.kt | 6 ++ app/src/main/res/values/strings.xml | 1 + 4 files changed, 67 insertions(+) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index fb67e8f9..94d440ca 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -75,6 +75,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_classes) LibrusApiClasses(data) { onSuccess() } } + ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS -> { + data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_days) + LibrusApiTeacherFreeDays(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt new file mode 100644 index 00000000..cb5efb6a --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-4. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsence +import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Time + +class LibrusApiTeacherFreeDays(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiTeacherFreeDays" + } + + init { + apiGet(TAG, "TeacherFreeDays") { json -> + val teacherAbsences = json.getJsonArray("TeacherFreeDays") + + teacherAbsences?.forEach { teacherAbsenceEl -> + val teacherAbsence = teacherAbsenceEl.asJsonObject + + val id = teacherAbsence.getLong("Id") ?: return@forEach + val teacherId = teacherAbsence.getJsonObject("Teacher")?.getLong("Id") + ?: return@forEach + val type = teacherAbsence.getJsonObject("Type").getLong("Id") ?: return@forEach + val dateFrom = Date.fromY_m_d(teacherAbsence.getString("DateFrom")) + val dateTo = Date.fromY_m_d(teacherAbsence.getString("DateTo")) + val timeFrom = teacherAbsence.getString("TimeFrom")?.let { Time.fromH_m_s(it) } + val timeTo = teacherAbsence.getString("TimeTo")?.let { Time.fromH_m_s(it) } + + val teacherAbsenceObject = TeacherAbsence( + profileId, + id, + teacherId, + type, + dateFrom, + dateTo, + timeFrom, + timeTo + ) + + data.teacherAbsenceList.add(teacherAbsenceObject) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 5b1eeed7..fd008d31 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -31,6 +31,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher +import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsence import pl.szczodrzynski.edziennik.data.db.modules.teams.Team import pl.szczodrzynski.edziennik.singleOrNull import pl.szczodrzynski.edziennik.toSparseArray @@ -135,6 +136,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) val luckyNumberList = mutableListOf() + val teacherAbsenceList = mutableListOf() + val messageList = mutableListOf() val messageRecipientList = mutableListOf() val messageRecipientIgnoreList = mutableListOf() @@ -174,6 +177,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) attendanceList.clear() announcementList.clear() luckyNumberList.clear() + teacherAbsenceList.clear() messageList.clear() messageRecipientList.clear() messageRecipientIgnoreList.clear() @@ -232,6 +236,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) db.announcementDao().addAll(announcementList) if (luckyNumberList.isNotEmpty()) db.luckyNumberDao().addAll(luckyNumberList) + if (teacherAbsenceList.isNotEmpty()) + db.teacherAbsenceDao().addAll(teacherAbsenceList) if (messageList.isNotEmpty()) db.messageDao().addAllIgnore(messageList) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7f43e31a..d6e6a75e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -948,4 +948,5 @@ Pobieranie ogłoszeń szkolnych... Pierwsze logowanie Pobieranie informacji o klasie... + Pobieranie nieobecności nauczycieli... From e138ca6eab3b5ed33fd90c245ed897b16877fe4e Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sat, 19 Oct 2019 13:33:50 +0200 Subject: [PATCH 067/691] [APIv2/Librus] Add getting and showing teacher absence reason --- .../edziennik/api/v2/librus/LibrusFeatures.kt | 2 + .../api/v2/librus/data/LibrusData.kt | 4 ++ .../data/api/LibrusApiTeacherFreeDayTypes.kt | 46 +++++++++++++++++++ .../data/api/LibrusApiTeacherFreeDays.kt | 2 + .../edziennik/api/v2/models/Data.kt | 17 ++++++- .../edziennik/data/api/Librus.java | 1 + .../edziennik/data/db/AppDb.java | 20 +++++++- .../db/modules/teachers/TeacherAbsence.kt | 3 ++ .../db/modules/teachers/TeacherAbsenceFull.kt | 4 +- .../db/modules/teachers/TeacherAbsenceType.kt | 20 ++++++++ .../modules/teachers/TeacherAbsenceTypeDao.kt | 25 ++++++++++ .../teacherabsence/TeacherAbsenceAdapter.kt | 10 ++++ .../row_dialog_teacher_absence_item.xml | 9 ++++ app/src/main/res/values/strings.xml | 1 + 14 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDayTypes.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsenceType.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsenceTypeDao.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt index a565cb38..48218bc0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt @@ -40,6 +40,7 @@ const val ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES = 1080 const val ENDPOINT_LIBRUS_API_ATTENDANCES = 1081 const val ENDPOINT_LIBRUS_API_ANNOUNCEMENTS = 1090 const val ENDPOINT_LIBRUS_API_PT_MEETINGS = 1100 +const val ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES = 1109 const val ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS = 1110 const val ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS = 1120 const val ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS = 1130 @@ -79,6 +80,7 @@ val LibrusFeatures = listOf( ENDPOINT_LIBRUS_API_EVENTS to LOGIN_METHOD_LIBRUS_API, ENDPOINT_LIBRUS_API_EVENT_TYPES to LOGIN_METHOD_LIBRUS_API, ENDPOINT_LIBRUS_API_PT_MEETINGS to LOGIN_METHOD_LIBRUS_API, + ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES to LOGIN_METHOD_LIBRUS_API, ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS to LOGIN_METHOD_LIBRUS_API, ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS to LOGIN_METHOD_LIBRUS_API, ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS to LOGIN_METHOD_LIBRUS_API diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index 94d440ca..98a6b653 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -75,6 +75,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_classes) LibrusApiClasses(data) { onSuccess() } } + ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES -> { + data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_day_types) + LibrusApiTeacherFreeDayTypes(data) { onSuccess() } + } ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS -> { data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_days) LibrusApiTeacherFreeDays(data) { onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDayTypes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDayTypes.kt new file mode 100644 index 00000000..dcfdcbeb --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDayTypes.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-19 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.DAY +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceType +import pl.szczodrzynski.edziennik.getJsonArray +import pl.szczodrzynski.edziennik.getLong +import pl.szczodrzynski.edziennik.getString + +class LibrusApiTeacherFreeDayTypes(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiTeacherFreeDayTypes" + } + + init { + apiGet(TAG, "TeacherFreeDays/Types") { json -> + val teacherAbsenceTypes = json.getJsonArray("Types") + + teacherAbsenceTypes?.forEach { teacherAbsenceTypeEl -> + val teacherAbsenceType = teacherAbsenceTypeEl.asJsonObject + + val id = teacherAbsenceType.getLong("Id") ?: return@forEach + val name = teacherAbsenceType.getString("Name") ?: return@forEach + + val teacherAbsenceTypeObject = TeacherAbsenceType( + profileId, + id, + name + ) + + data.teacherAbsenceTypes.put(id, teacherAbsenceTypeObject) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES, 4 * DAY) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt index cb5efb6a..756d64e1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt @@ -30,6 +30,7 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus, val teacherId = teacherAbsence.getJsonObject("Teacher")?.getLong("Id") ?: return@forEach val type = teacherAbsence.getJsonObject("Type").getLong("Id") ?: return@forEach + val name = data.teacherAbsenceTypes.singleOrNull { it.id == type }?.name val dateFrom = Date.fromY_m_d(teacherAbsence.getString("DateFrom")) val dateTo = Date.fromY_m_d(teacherAbsence.getString("DateTo")) val timeFrom = teacherAbsence.getString("TimeFrom")?.let { Time.fromH_m_s(it) } @@ -40,6 +41,7 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus, id, teacherId, type, + name, dateFrom, dateTo, timeFrom, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index fd008d31..d573068c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -32,6 +32,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsence +import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceType import pl.szczodrzynski.edziennik.data.db.modules.teams.Team import pl.szczodrzynski.edziennik.singleOrNull import pl.szczodrzynski.edziennik.toSparseArray @@ -102,6 +103,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) val lessonRanges = SparseArray() val gradeCategories = LongSparseArray() val attendanceTypes = SparseArray>() + val teacherAbsenceTypes = LongSparseArray() private var mTeamClass: Team? = null var teamClass: Team? @@ -156,6 +158,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) db.teamDao().getAllNow(profileId).toSparseArray(teamList) { it.id } db.lessonRangeDao().getAllNow(profileId).toSparseArray(lessonRanges) { it.lessonNumber } db.gradeCategoryDao().getAllNow(profileId).toSparseArray(gradeCategories) { it.categoryId } + db.teacherAbsenceTypeDao().getAllNow(profileId).toSparseArray(teacherAbsenceTypes) { it.id } } } @@ -205,6 +208,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) db.lessonRangeDao().addAll(lessonRanges.values()) db.gradeCategoryDao().clear(profileId) db.gradeCategoryDao().addAll(gradeCategories.values()) + db.teacherAbsenceTypeDao().clear(profileId) + db.teacherAbsenceTypeDao().addAll(teacherAbsenceTypes.values()) gradesToRemove?.let { it -> it.removeAll?.let { _ -> db.gradeDao().clear(profileId) } @@ -297,8 +302,11 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) else -> errorCode } } - error(ApiError(tag, code).apply { profileId = profile?.id ?: -1 }.withResponse(response).withThrowable(throwable).withApiResponse(apiResponse)) + error(ApiError(tag, code).apply { + profileId = profile?.id ?: -1 + }.withResponse(response).withThrowable(throwable).withApiResponse(apiResponse)) } + fun error(tag: String, errorCode: Int, response: Response? = null, apiResponse: String? = null) { val code = when (null) { is UnknownHostException, is SSLException, is InterruptedIOException -> CODE_NO_INTERNET @@ -308,16 +316,21 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) else -> errorCode } } - error(ApiError(tag, code).apply { profileId = profile?.id ?: -1 }.withResponse(response).withApiResponse(apiResponse)) + error(ApiError(tag, code).apply { + profileId = profile?.id ?: -1 + }.withResponse(response).withApiResponse(apiResponse)) } + fun error(apiError: ApiError) { if (apiError.isCritical) cancel() callback.onError(apiError) } + fun progress(step: Int) { callback.onProgress(step) } + fun startProgress(stringRes: Int) { callback.onStartProgress(stringRes) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java index a554b25c..9baf3c4c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java @@ -3201,6 +3201,7 @@ public class Librus implements EdziennikInterface { id, teacherId, type, + null, dateFrom, dateTo, timeFrom, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java index 1ba10997..4cbcb3a7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java @@ -61,6 +61,8 @@ import pl.szczodrzynski.edziennik.data.db.modules.subjects.SubjectDao; import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher; import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsence; import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceDao; +import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceType; +import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceTypeDao; import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherDao; import pl.szczodrzynski.edziennik.data.db.modules.teams.Team; import pl.szczodrzynski.edziennik.data.db.modules.teams.TeamDao; @@ -71,6 +73,7 @@ import pl.szczodrzynski.edziennik.utils.models.Date; //GradeCategory.class, Teacher.class, TeacherAbsence.class, + TeacherAbsenceType.class, Subject.class, Notice.class, Lesson.class, @@ -91,7 +94,7 @@ import pl.szczodrzynski.edziennik.utils.models.Date; EndpointTimer.class, LessonRange.class, Notification.class, - Metadata.class}, version = 60) + Metadata.class}, version = 61) @TypeConverters({ ConverterTime.class, ConverterDate.class, @@ -105,6 +108,7 @@ public abstract class AppDb extends RoomDatabase { //public abstract GradeCategoryDao gradeCategoryDao(); public abstract TeacherDao teacherDao(); public abstract TeacherAbsenceDao teacherAbsenceDao(); + public abstract TeacherAbsenceTypeDao teacherAbsenceTypeDao(); public abstract SubjectDao subjectDao(); public abstract NoticeDao noticeDao(); public abstract LessonDao lessonDao(); @@ -667,6 +671,17 @@ public abstract class AppDb extends RoomDatabase { database.execSQL("ALTER TABLE profiles ADD COLUMN disabledNotifications TEXT DEFAULT NULL"); } }; + private static final Migration MIGRATION_60_61 = new Migration(60, 61) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("CREATE TABLE IF NOT EXISTS teacherAbsenceTypes (" + + "profileId INTEGER NOT NULL," + + "teacherAbsenceTypeId INTEGER NOT NULL," + + "teacherAbsenceTypeName TEXT NOT NULL," + + "PRIMARY KEY(profileId, teacherAbsenceTypeId))"); + database.execSQL("ALTER TABLE teacherAbsence ADD COLUMN teacherAbsenceName TEXT DEFAULT NULL"); + } + }; public static AppDb getDatabase(final Context context) { @@ -724,7 +739,8 @@ public abstract class AppDb extends RoomDatabase { MIGRATION_56_57, MIGRATION_57_58, MIGRATION_58_59, - MIGRATION_59_60 + MIGRATION_59_60, + MIGRATION_60_61 ) .allowMainThreadQueries() //.fallbackToDestructiveMigration() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsence.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsence.kt index 76ed7450..c73dc490 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsence.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsence.kt @@ -19,6 +19,9 @@ open class TeacherAbsence ( @ColumnInfo(name = "teacherAbsenceType") val type: Long, + @ColumnInfo(name = "teacherAbsenceName") + val name: String?, + @ColumnInfo(name = "teacherAbsenceDateFrom") val dateFrom: Date, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsenceFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsenceFull.kt index 1c36c19c..800f2d00 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsenceFull.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsenceFull.kt @@ -3,9 +3,9 @@ package pl.szczodrzynski.edziennik.data.db.modules.teachers import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Time -class TeacherAbsenceFull(profileId: Int, id: Long, teacherId: Long, type: Long, +class TeacherAbsenceFull(profileId: Int, id: Long, teacherId: Long, type: Long, name: String?, dateFrom: Date, dateTo: Date, timeFrom: Time?, timeTo: Time?) - : TeacherAbsence(profileId, id, teacherId, type, dateFrom, dateTo, timeFrom, timeTo) { + : TeacherAbsence(profileId, id, teacherId, type, name, dateFrom, dateTo, timeFrom, timeTo) { var teacherFullName = "" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsenceType.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsenceType.kt new file mode 100644 index 00000000..ca9db678 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsenceType.kt @@ -0,0 +1,20 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-18 + */ + +package pl.szczodrzynski.edziennik.data.db.modules.teachers + +import androidx.room.ColumnInfo +import androidx.room.Entity + +@Entity(tableName = "teacherAbsenceTypes", + primaryKeys = ["profileId", "teacherAbsenceTypeId"]) +data class TeacherAbsenceType ( + val profileId: Int, + + @ColumnInfo(name = "teacherAbsenceTypeId") + val id: Long, + + @ColumnInfo(name = "teacherAbsenceTypeName") + val name: String +) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsenceTypeDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsenceTypeDao.kt new file mode 100644 index 00000000..12dbf591 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/teachers/TeacherAbsenceTypeDao.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-18 + */ + +package pl.szczodrzynski.edziennik.data.db.modules.teachers + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query + +@Dao +interface TeacherAbsenceTypeDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun add(teacherAbsence: TeacherAbsenceType) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun addAll(teacherAbsenceList: List) + + @Query("DELETE FROM teacherAbsenceTypes WHERE profileId = :profileId") + fun clear(profileId: Int) + + @Query("SELECT * FROM teacherAbsenceTypes WHERE profileId = :profileId") + fun getAllNow(profileId: Int): List +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceAdapter.kt index 2d8eb2c6..b8e165d1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceAdapter.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceAdapter.kt @@ -1,6 +1,7 @@ package pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence import android.content.Context +import android.opengl.Visibility import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -47,10 +48,19 @@ class TeacherAbsenceAdapter( } holder.teacherAbsenceTime.text = time + + if (teacherAbsence.name != null) { + holder.teacherAbsenceName.visibility = View.VISIBLE + holder.teacherAbsenceName.text = teacherAbsence.name + } else { + holder.teacherAbsenceName.visibility = View.GONE + } + } class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { var teacherAbsenceTeacher: TextView = itemView.findViewById(R.id.teacherAbsenceTeacher) var teacherAbsenceTime: TextView = itemView.findViewById(R.id.teacherAbsenceTime) + var teacherAbsenceName: TextView = itemView.findViewById(R.id.teacherAbsenceName) } } diff --git a/app/src/main/res/layout/row_dialog_teacher_absence_item.xml b/app/src/main/res/layout/row_dialog_teacher_absence_item.xml index 6aacce23..8d303464 100644 --- a/app/src/main/res/layout/row_dialog_teacher_absence_item.xml +++ b/app/src/main/res/layout/row_dialog_teacher_absence_item.xml @@ -36,6 +36,15 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" tools:text="Jan Kowalski"/> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d6e6a75e..e8158af5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -949,4 +949,5 @@ Pierwsze logowanie Pobieranie informacji o klasie... Pobieranie nieobecności nauczycieli... + Pobieranie rodzajów nieobecności nauczycieli... From b32ebe4479f14a42377eeb23bdc1a20c36a41aea Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sat, 19 Oct 2019 13:51:01 +0200 Subject: [PATCH 068/691] [APIv2/Librus] Move some code --- .../v2/librus/data/api/LibrusApiAnnouncements.kt | 11 ++++------- .../api/v2/librus/data/api/LibrusApiAttendances.kt | 14 ++++++-------- .../api/v2/librus/data/api/LibrusApiClasses.kt | 2 +- .../api/v2/librus/data/api/LibrusApiEvents.kt | 10 ++++------ .../api/v2/librus/data/api/LibrusApiGrades.kt | 7 +++---- .../api/v2/librus/data/api/LibrusApiHomework.kt | 8 +++----- .../v2/librus/data/api/LibrusApiTeacherFreeDays.kt | 9 +++++++++ 7 files changed, 30 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAnnouncements.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAnnouncements.kt index cb0c3014..4e93e290 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAnnouncements.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAnnouncements.kt @@ -31,10 +31,11 @@ class LibrusApiAnnouncements(override val data: DataLibrus, ?: return@forEach).toLong() val subject = announcement.getString("Subject") ?: "" val text = announcement.getString("Content") ?: "" - val startDate = Date.fromY_m_d(announcement.getString("StartDate") - ?: return@forEach) - val endDate = Date.fromY_m_d(announcement.getString("EndDate") ?: return@forEach) + val startDate = Date.fromY_m_d(announcement.getString("StartDate")) + val endDate = Date.fromY_m_d(announcement.getString("EndDate")) val teacherId = announcement.getJsonObject("AddedBy")?.getLong("Id") ?: -1 + val addedDate = Date.fromIso(announcement.getString("CreationDate")) + val read = announcement.getBoolean("WasRead") ?: false val announcementObject = Announcement( profileId, @@ -46,10 +47,6 @@ class LibrusApiAnnouncements(override val data: DataLibrus, teacherId ) - val addedDate = Date.fromIso(announcement.getString("CreationDate") - ?: return@forEach) - val read = announcement.getBoolean("WasRead") ?: false - data.announcementList.add(announcementObject) data.metadataList.add(Metadata( profileId, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt index 8a508ebb..8bd7f243 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt @@ -27,22 +27,20 @@ class LibrusApiAttendances(override val data: DataLibrus, attendances?.forEach { attendanceEl -> val attendance = attendanceEl.asJsonObject + val id = Utils.strToInt((attendance.getString("Id") ?: return@forEach) + .replace("[^\\d.]".toRegex(), "")).toLong() val teacherId = attendance.getJsonObject("AddedBy")?.getLong("Id") ?: -1 val lessonNo = attendance.getInt("LessonNo") ?: return@forEach val startTime = data.lessonRanges.get(lessonNo).startTime - val lessonDate = Date.fromY_m_d(attendance.getString("Date") ?: return@forEach) + val lessonDate = Date.fromY_m_d(attendance.getString("Date")) + val subjectId = data.lessonList.singleOrNull { + it.weekDay == lessonDate.weekDay && it.startTime.value == startTime.value + }?.subjectId ?: -1 val semester = attendance.getInt("Semester") ?: return@forEach var type = attendance.getJsonObject("Type")?.getInt("Id") ?: return@forEach val attendanceType = data.attendanceTypes.get(type) val topic = attendanceType.second - val id = Utils.strToInt((attendance.getString("Id") ?: return@forEach) - .replace("[^\\d.]".toRegex(), "")).toLong() - - val subjectId = data.lessonList.singleOrNull { - it.weekDay == lessonDate.weekDay && it.startTime.value == startTime.value - }?.subjectId ?: -1 - type = when(type) { 1 -> Attendance.TYPE_ABSENT 2 -> Attendance.TYPE_BELATED diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClasses.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClasses.kt index 31d3490b..3f58300d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClasses.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClasses.kt @@ -46,7 +46,7 @@ class LibrusApiClasses(override val data: DataLibrus, ?: return@apply) dateYearEnd = Date.fromY_m_d(studentClass.getString("EndSchoolYear") ?: return@apply) - if (unitId != null) putStudentData("unitId", unitId) + unitId?.let { putStudentData("unitId", it) } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt index f495c94c..ed6ece5c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt @@ -28,8 +28,8 @@ class LibrusApiEvents(override val data: DataLibrus, val event = eventEl.asJsonObject val id = event.getLong("Id") ?: return@forEach - val eventDate = Date.fromY_m_d(event.getString("Date") ?: return@forEach) - val topic = event.getString("Content") ?: return@forEach + val eventDate = Date.fromY_m_d(event.getString("Date")) + val topic = event.getString("Content") ?: "" val type = event.getJsonObject("Category")?.getInt("Id") ?: -1 val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1 val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1 @@ -37,8 +37,8 @@ class LibrusApiEvents(override val data: DataLibrus, val lessonNo = event.getInt("LessonNo") val lessonRange = data.lessonRanges.singleOrNull { it.lessonNumber == lessonNo } - val startTime = lessonRange?.startTime - ?: Time.fromH_m(event.getString("TimeFrom") ?: return@forEach) + val startTime = lessonRange?.startTime ?: Time.fromH_m(event.getString("TimeFrom")) + val addedDate = Date.fromIso(event.getString("AddDate")) val eventObject = Event( profileId, @@ -54,8 +54,6 @@ class LibrusApiEvents(override val data: DataLibrus, teamId ) - val addedDate = Date.fromIso(event.getString("AddDate") ?: return@forEach) - data.eventList.add(eventObject) data.metadataList.add( Metadata( diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt index 226ea6d4..8f4699c9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt @@ -25,11 +25,12 @@ class LibrusApiGrades(override val data: DataLibrus, val grade = gradeEl.asJsonObject val id = grade.getLong("Id") ?: return@forEach - val categoryId = grade.getJsonObject("Category")?.getLong("Id") ?: return@forEach - val name = grade.getString("Grade") ?: return@forEach + val categoryId = grade.getJsonObject("Category")?.getLong("Id") ?: -1 + val name = grade.getString("Grade") ?: "" val semester = grade.getInt("Semester") ?: return@forEach val teacherId = grade.getJsonObject("AddedBy")?.getLong("Id") ?: -1 val subjectId = grade.getJsonObject("Subject")?.getLong("Id") ?: -1 + val addedDate = Date.fromIso(grade.getString("AddDate")) val category = data.gradeCategories.singleOrNull { it.categoryId == categoryId } val categoryName = category?.text ?: "" @@ -80,8 +81,6 @@ class LibrusApiGrades(override val data: DataLibrus, gradeObject.isImprovement = true } - val addedDate = Date.fromIso(grade.get("AddDate").asString) - data.gradeList.add(gradeObject) data.metadataList.add( Metadata( diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiHomework.kt index 093acac4..c3f67c8d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiHomework.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiHomework.kt @@ -30,10 +30,10 @@ class LibrusApiHomework(override val data: DataLibrus, val homework = homeworkEl.asJsonObject val id = homework.getLong("Id") ?: return@forEach - val eventDate = Date.fromY_m_d(homework.getString("DueDate") ?: return@forEach) - val topic = (homework.getString("Topic") ?: "") + "\n" + - (homework.getString("Text") ?: "") + val eventDate = Date.fromY_m_d(homework.getString("DueDate")) + val topic = homework.getString("Topic") + "\n" + homework.getString("Text") val teacherId = homework.getJsonObject("Teacher")?.getLong("Id") ?: -1 + val addedDate = Date.fromY_m_d(homework.getString("Date")) val eventObject = Event( profileId, @@ -49,8 +49,6 @@ class LibrusApiHomework(override val data: DataLibrus, -1 ) - val addedDate = Date.fromY_m_d(homework.getString("Date") ?: return@forEach) - data.eventList.add(eventObject) data.metadataList.add(Metadata( profileId, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt index 756d64e1..d4791b39 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt @@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsence import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Time @@ -49,6 +50,14 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus, ) data.teacherAbsenceList.add(teacherAbsenceObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_TEACHER_ABSENCE, + id, + profile?.empty ?: false, + profile?.empty ?: false, + System.currentTimeMillis() + )) } data.setSyncNext(ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS, SYNC_ALWAYS) From 92fb83ccf9943a0f38653888702a0a00ee4ca799 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sat, 19 Oct 2019 20:38:30 +0200 Subject: [PATCH 069/691] [APIv2/Vulcan] Add Vulcan Api --- app/build.gradle | 2 + .../edziennik/api/v2/Constants.kt | 29 +++--- .../szczodrzynski/edziennik/api/v2/Errors.kt | 3 +- .../edziennik/api/v2/vulcan/DataVulcan.kt | 5 + .../edziennik/api/v2/vulcan/data/VulcanApi.kt | 96 +++++++++++++++++++ .../api/v2/vulcan/data/VulcanData.kt | 13 ++- .../api/v2/vulcan/data/api/VulcanApiGrades.kt | 27 ++++++ .../api/v2/vulcan/login/VulcanLoginApi.kt | 3 +- 8 files changed, 156 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt diff --git a/app/build.gradle b/app/build.gradle index b9d3d155..df5dde57 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -155,6 +155,8 @@ dependencies { debugImplementation "com.github.ChuckerTeam.Chucker:library:3.0.1" releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:3.0.1" + + implementation 'com.github.wulkanowy:uonet-request-signer:master-SNAPSHOT' } repositories { mavenCentral() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index 465ea051..178b3bc9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -51,19 +51,20 @@ val MOBIDZIENNIK_USER_AGENT = SYSTEM_USER_AGENT const val VULCAN_API_USER_AGENT = "MobileUserAgent" const val VULCAN_API_APP_NAME = "VULCAN-Android-ModulUcznia" const val VULCAN_API_APP_VERSION = "19.4.1.436" +const val VULCAN_API_PASSWORD = "CE75EA598C7743AD9B0B7328DED85B06" val VULCAN_API_DEVICE_NAME = "Szkolny.eu ${Build.MODEL}" -const val VULCAN_API_ENDPOINT_CERTIFICATE = "mobile-api/Uczen.v3.UczenStart/Certyfikat"; -const val VULCAN_API_ENDPOINT_STUDENT_LIST = "mobile-api/Uczen.v3.UczenStart/ListaUczniow"; -const val VULCAN_API_ENDPOINT_DICTIONARIES = "mobile-api/Uczen.v3.Uczen/Slowniki"; -const val VULCAN_API_ENDPOINT_TIMETABLE = "mobile-api/Uczen.v3.Uczen/PlanLekcjiZeZmianami"; -const val VULCAN_API_ENDPOINT_GRADES = "mobile-api/Uczen.v3.Uczen/Oceny"; -const val VULCAN_API_ENDPOINT_GRADES_PROPOSITIONS = "mobile-api/Uczen.v3.Uczen/OcenyPodsumowanie"; -const val VULCAN_API_ENDPOINT_EVENTS = "mobile-api/Uczen.v3.Uczen/Sprawdziany"; -const val VULCAN_API_ENDPOINT_HOMEWORK = "mobile-api/Uczen.v3.Uczen/ZadaniaDomowe"; -const val VULCAN_API_ENDPOINT_NOTICES = "mobile-api/Uczen.v3.Uczen/UwagiUcznia"; -const val VULCAN_API_ENDPOINT_ATTENDANCE = "mobile-api/Uczen.v3.Uczen/Frekwencje"; -const val VULCAN_API_ENDPOINT_MESSAGES_RECEIVED = "mobile-api/Uczen.v3.Uczen/WiadomosciOdebrane"; -const val VULCAN_API_ENDPOINT_MESSAGES_SENT = "mobile-api/Uczen.v3.Uczen/WiadomosciWyslane"; -const val VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS = "mobile-api/Uczen.v3.Uczen/ZmienStatusWiadomosci"; -const val VULCAN_API_ENDPOINT_PUSH = "mobile-api/Uczen.v3.Uczen/UstawPushToken"; \ No newline at end of file +const val VULCAN_API_ENDPOINT_CERTIFICATE = "mobile-api/Uczen.v3.UczenStart/Certyfikat" +const val VULCAN_API_ENDPOINT_STUDENT_LIST = "mobile-api/Uczen.v3.UczenStart/ListaUczniow" +const val VULCAN_API_ENDPOINT_DICTIONARIES = "mobile-api/Uczen.v3.Uczen/Slowniki" +const val VULCAN_API_ENDPOINT_TIMETABLE = "mobile-api/Uczen.v3.Uczen/PlanLekcjiZeZmianami" +const val VULCAN_API_ENDPOINT_GRADES = "mobile-api/Uczen.v3.Uczen/Oceny" +const val VULCAN_API_ENDPOINT_GRADES_PROPOSITIONS = "mobile-api/Uczen.v3.Uczen/OcenyPodsumowanie" +const val VULCAN_API_ENDPOINT_EVENTS = "mobile-api/Uczen.v3.Uczen/Sprawdziany" +const val VULCAN_API_ENDPOINT_HOMEWORK = "mobile-api/Uczen.v3.Uczen/ZadaniaDomowe" +const val VULCAN_API_ENDPOINT_NOTICES = "mobile-api/Uczen.v3.Uczen/UwagiUcznia" +const val VULCAN_API_ENDPOINT_ATTENDANCE = "mobile-api/Uczen.v3.Uczen/Frekwencje" +const val VULCAN_API_ENDPOINT_MESSAGES_RECEIVED = "mobile-api/Uczen.v3.Uczen/WiadomosciOdebrane" +const val VULCAN_API_ENDPOINT_MESSAGES_SENT = "mobile-api/Uczen.v3.Uczen/WiadomosciWyslane" +const val VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS = "mobile-api/Uczen.v3.Uczen/ZmienStatusWiadomosci" +const val VULCAN_API_ENDPOINT_PUSH = "mobile-api/Uczen.v3.Uczen/UstawPushToken" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index 229ff405..bad56ff6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -126,4 +126,5 @@ const val EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN = 902 const val EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN = 903 const val EXCEPTION_LIBRUS_API_REQUEST = 904 const val EXCEPTION_MOBIDZIENNIK_WEB_REQUEST = 905 -const val EXCEPTION_NOTIFY_AND_SYNC = 910 \ No newline at end of file +const val EXCEPTION_VULCAN_API_REQUEST = 906 +const val EXCEPTION_NOTIFY_AND_SYNC = 910 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt index d19a95af..fd3b1d22 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt @@ -169,4 +169,9 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app else -> null } } + + val fullApiUrl: String? + get() { + return "${apiUrl}${schoolSymbol}/" + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt new file mode 100644 index 00000000..5d818426 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt @@ -0,0 +1,96 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-19 + */ + +package pl.szczodrzynski.edziennik.api.v2.vulcan.data + +import com.google.gson.JsonObject +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.callback.JsonCallbackHandler +import io.github.wulkanowy.signer.signContent +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.utils.Utils.d +import java.net.HttpURLConnection +import java.util.* + +open class VulcanApi(open val data: DataVulcan) { + companion object { + const val TAG = "VulcanApi" + } + + val profileId + get() = data.profile?.id ?: -1 + + val profile + get() = data.profile + + fun apiGet(tag: String, endpoint: String, method: Int = POST, payload: JsonObject? = null, onSuccess: (json: JsonObject) -> Unit) { + d(tag, "Request: Librus/Api - ${data.fullApiUrl}/$endpoint") + + val finalPayload = JsonObject() + finalPayload.addProperty("IdUczen", data.studentId) + finalPayload.addProperty("IdOkresKlasyfikacyjny", data.studentSemesterId) + finalPayload.addProperty("IdOddzial", data.studentClassId) + finalPayload.addProperty("RemoteMobileTimeKey", System.currentTimeMillis() / 1000) + finalPayload.addProperty("TimeKey", System.currentTimeMillis() / 1000 - 1) + finalPayload.addProperty("RequestId", UUID.randomUUID().toString()) + finalPayload.addProperty("RemoteMobileAppVersion", VULCAN_API_APP_VERSION) + finalPayload.addProperty("RemoteMobileAppName", VULCAN_API_APP_NAME) + + val callback = object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response?) { + if (json == null && response?.parserErrorBody == null) { + data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY) + .withResponse(response)) + return + } + + // TODO: Vulcan error handling + + if (json == null) { + data.error(ApiError(tag, ERROR_RESPONSE_EMPTY) + .withResponse(response)) + return + } + + try { + onSuccess(json) + } catch (e: Exception) { + data.error(ApiError(tag, EXCEPTION_VULCAN_API_REQUEST) + .withResponse(response) + .withThrowable(e) + .withApiResponse(json)) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(ApiError(tag, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) + } + } + + Request.builder() + .url("${data.fullApiUrl}$endpoint") + .userAgent(VULCAN_API_USER_AGENT) + .addHeader("RequestCertificateKey", data.apiCertificateKey) + .addHeader("RequestSignatureValue", + signContent(VULCAN_API_PASSWORD, data.apiCertificatePfx, finalPayload.toString())) + .apply { + when (method) { + GET -> get() + POST -> post() + } + } + .setJsonBody(finalPayload) + .allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST) + .allowErrorCode(HttpURLConnection.HTTP_FORBIDDEN) + .allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED) + .callback(callback) + .build() + .enqueue() + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt index 1023cb4f..fb2dc15d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt @@ -4,7 +4,10 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data +import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_GRADES +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiGrades import pl.szczodrzynski.edziennik.utils.Utils class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { @@ -35,11 +38,11 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { private fun useEndpoint(endpointId: Int, onSuccess: () -> Unit) { Utils.d(TAG, "Using endpoint $endpointId") when (endpointId) { - /*ENDPOINT_VULCAN_API -> { - data.startProgress(R.string.edziennik_progress_endpoint_data) - VulcanApi(data) { onSuccess() } - }*/ + ENDPOINT_VULCAN_API_GRADES -> { + data.startProgress(R.string.edziennik_progress_endpoint_grades) + VulcanApiGrades(data) { onSuccess() } + } else -> onSuccess() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt new file mode 100644 index 00000000..7818ed00 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-19 + */ + +package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api + +import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_GRADES +import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_GRADES +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.getJsonArray + +class VulcanApiGrades(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { + companion object { + const val TAG = "VulcanApiGrades" + } + + init { + apiGet(TAG, VULCAN_API_ENDPOINT_GRADES) { json -> + val grades = json.getJsonArray("Data") + + data.setSyncNext(ENDPOINT_VULCAN_API_GRADES, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt index be8e9aa9..abfa5c19 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt @@ -13,7 +13,6 @@ import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan -import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils.d import java.net.HttpURLConnection.HTTP_BAD_REQUEST import java.util.* @@ -131,4 +130,4 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) { .build() .enqueue() } -} \ No newline at end of file +} From dd34e7d00838f858d06dcc23891285fca4884304 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sun, 20 Oct 2019 00:00:50 +0200 Subject: [PATCH 070/691] [APIv2/Vulcan] Add Vulcan first login --- .../edziennik/api/v2/vulcan/DataVulcan.kt | 29 ++--- .../edziennik/api/v2/vulcan/Vulcan.kt | 7 +- .../edziennik/api/v2/vulcan/data/VulcanApi.kt | 11 +- .../api/v2/vulcan/data/api/VulcanApiGrades.kt | 2 +- .../v2/vulcan/firstlogin/VulcanFirstLogin.kt | 104 ++++++++++++++++++ .../api/v2/vulcan/login/VulcanLoginApi.kt | 7 +- 6 files changed, 133 insertions(+), 27 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt index fd3b1d22..7d3f8367 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt @@ -152,22 +152,23 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app val apiUrl: String? get() { - return when (apiToken?.substring(0, 3)) { - "3S1" -> "https://lekcjaplus.vulcan.net.pl/$symbol/" - "TA1" -> "https://uonetplus-komunikacja.umt.tarnow.pl/$symbol/" - "OP1" -> "https://uonetplus-komunikacja.eszkola.opolskie.pl/$symbol/" - "RZ1" -> "https://uonetplus-komunikacja.resman.pl/$symbol/" - "GD1" -> "https://uonetplus-komunikacja.edu.gdansk.pl/$symbol/" - "KA1" -> "https://uonetplus-komunikacja.mcuw.katowice.eu/$symbol/" - "KA2" -> "https://uonetplus-komunikacja-test.mcuw.katowice.eu/$symbol/" - "P03" -> "https://efeb-komunikacja-pro-efebmobile.pro.vulcan.pl/$symbol/" - "P01" -> "http://efeb-komunikacja.pro-hudson.win.vulcan.pl/$symbol/" - "P02" -> "http://efeb-komunikacja.pro-hudsonrc.win.vulcan.pl/$symbol/" - "P90" -> "http://efeb-komunikacja-pro-mwujakowska.neo.win.vulcan.pl/$symbol/" - "FK1", "FS1" -> "http://api.fakelog.cf/$symbol/" - "SZ9" -> "http://vulcan.szkolny.eu/$symbol/" + val url = when (apiToken?.substring(0, 3)) { + "3S1" -> "https://lekcjaplus.vulcan.net.pl/" + "TA1" -> "https://uonetplus-komunikacja.umt.tarnow.pl/" + "OP1" -> "https://uonetplus-komunikacja.eszkola.opolskie.pl/" + "RZ1" -> "https://uonetplus-komunikacja.resman.pl/" + "GD1" -> "https://uonetplus-komunikacja.edu.gdansk.pl/" + "KA1" -> "https://uonetplus-komunikacja.mcuw.katowice.eu/" + "KA2" -> "https://uonetplus-komunikacja-test.mcuw.katowice.eu/" + "P03" -> "https://efeb-komunikacja-pro-efebmobile.pro.vulcan.pl/" + "P01" -> "http://efeb-komunikacja.pro-hudson.win.vulcan.pl/" + "P02" -> "http://efeb-komunikacja.pro-hudsonrc.win.vulcan.pl/" + "P90" -> "http://efeb-komunikacja-pro-mwujakowska.neo.win.vulcan.pl/" + "FK1", "FS1" -> "http://api.fakelog.cf/" + "SZ9" -> "http://vulcan.szkolny.eu/" else -> null } + return if (url != null) "$url$symbol/" else null } val fullApiUrl: String? diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt index 60504a98..5997b72d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt @@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.prepare import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanData +import pl.szczodrzynski.edziennik.api.v2.vulcan.firstlogin.VulcanFirstLogin import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLogin import pl.szczodrzynski.edziennik.api.v2.vulcanLoginMethods import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore @@ -64,7 +65,9 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va } override fun firstLogin() { - // TODO + VulcanFirstLogin(data) { + completed() + } } override fun cancel() { @@ -102,4 +105,4 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt index 5d818426..546692d1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt @@ -27,8 +27,11 @@ open class VulcanApi(open val data: DataVulcan) { val profile get() = data.profile - fun apiGet(tag: String, endpoint: String, method: Int = POST, payload: JsonObject? = null, onSuccess: (json: JsonObject) -> Unit) { - d(tag, "Request: Librus/Api - ${data.fullApiUrl}/$endpoint") + fun apiGet(tag: String, endpoint: String, method: Int = POST, payload: JsonObject? = null, + baseUrl: Boolean = false, onSuccess: (json: JsonObject, response: Response?) -> Unit) { + val url = "${if (baseUrl) data.apiUrl else data.fullApiUrl}$endpoint" + + d(tag, "Request: Librus/Api - $url") val finalPayload = JsonObject() finalPayload.addProperty("IdUczen", data.studentId) @@ -57,7 +60,7 @@ open class VulcanApi(open val data: DataVulcan) { } try { - onSuccess(json) + onSuccess(json, response) } catch (e: Exception) { data.error(ApiError(tag, EXCEPTION_VULCAN_API_REQUEST) .withResponse(response) @@ -74,7 +77,7 @@ open class VulcanApi(open val data: DataVulcan) { } Request.builder() - .url("${data.fullApiUrl}$endpoint") + .url(url) .userAgent(VULCAN_API_USER_AGENT) .addHeader("RequestCertificateKey", data.apiCertificateKey) .addHeader("RequestSignatureValue", diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt index 7818ed00..63a45186 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt @@ -17,7 +17,7 @@ class VulcanApiGrades(override val data: DataVulcan, val onSuccess: () -> Unit) } init { - apiGet(TAG, VULCAN_API_ENDPOINT_GRADES) { json -> + apiGet(TAG, VULCAN_API_ENDPOINT_GRADES) { json, _ -> val grades = json.getJsonArray("Data") data.setSyncNext(ENDPOINT_VULCAN_API_GRADES, SYNC_ALWAYS) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt new file mode 100644 index 00000000..adae6249 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt @@ -0,0 +1,104 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-19 + */ + +package pl.szczodrzynski.edziennik.api.v2.vulcan.firstlogin + +import org.greenrobot.eventbus.EventBus +import pl.szczodrzynski.edziennik.api.v2.ERROR_NO_STUDENTS_IN_ACCOUNT +import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_STUDENT_LIST +import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi +import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLoginApi +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile +import pl.szczodrzynski.edziennik.getInt +import pl.szczodrzynski.edziennik.getJsonArray +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.utils.models.Date + +class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) { + companion object { + const val TAG = "VulcanFirstLogin" + } + + private val api = VulcanApi(data) + private val profileList = mutableListOf() + + init { + VulcanLoginApi(data) { + api.apiGet(TAG, VULCAN_API_ENDPOINT_STUDENT_LIST, baseUrl = true) { json, response -> + val students = json.getJsonArray("Data") + + if (students == null || students.size() < 1) { + data.error(ApiError(TAG, ERROR_NO_STUDENTS_IN_ACCOUNT) + .withResponse(response) + .withApiResponse(json)) + return@apiGet + } + + students.forEach { studentEl -> + val student = studentEl.asJsonObject + + val studentId = student.getInt("Id") ?: return@forEach + val studentLoginId = student.getInt("UzytkownikLoginId") ?: return@forEach + val studentClassId = student.getInt("IdOddzial") ?: return@forEach + val studentClassNumber = student.getString("OkresPoziom") + val studentClassSymbol = student.getString("OddzialSymbol") + val studentClassName = "$studentClassNumber$studentClassSymbol" + val studentSemesterId = student.getInt("IdOkresKlasyfikacyjny") + ?: return@forEach + val studentFirstName = student.getString("Imie") + val studentLastName = student.getString("Nazwisko") + val studentNameLong = "$studentFirstName $studentLastName" + val studentNameShort = "$studentFirstName ${studentLastName?.get(0)}." + val userName = student.getString("UzytkownikNazwa") ?: "" + val userLogin = student.getString("UzytkownikLogin") ?: "" + val schoolSymbol = student.getString("JednostkaSprawozdawczaSymbol") + ?: return@forEach + val schoolName = "${data.symbol}_$schoolSymbol" + val currentSemesterStartDate = student.getInt("OkresDataOd") ?: return@forEach + val currentSemesterEndDate = (student.getInt("OkresDataDo") + ?: return@forEach) + 86400 + val studentSemesterNumber = student.getInt("OkresNumer") ?: return@forEach + + val newProfile = Profile() + newProfile.empty = true + + newProfile.putStudentData("studentId", studentId) + newProfile.putStudentData("studentLoginId", studentLoginId) + newProfile.putStudentData("studentClassId", studentClassId) + newProfile.putStudentData("studentClassName", studentClassName) + newProfile.putStudentData("studentSemesterId", studentSemesterId) + newProfile.putStudentData("userName", userName) + newProfile.putStudentData("schoolSymbol", schoolSymbol) + newProfile.putStudentData("schoolName", schoolName) + newProfile.putStudentData("currentSemesterEndDate", currentSemesterEndDate) + newProfile.putStudentData("studentSemesterNumber", studentSemesterNumber) + + when (studentSemesterNumber) { + 1 -> { + newProfile.dateSemester1Start = Date.fromMillis((currentSemesterStartDate * 1000).toLong()) + newProfile.dateSemester2Start = Date.fromMillis((currentSemesterEndDate * 1000).toLong()) + } + 2 -> { + newProfile.dateSemester2Start = Date.fromMillis((currentSemesterStartDate * 1000).toLong()) + newProfile.dateYearEnd = Date.fromMillis((currentSemesterEndDate * 1000).toLong()) + } + } + + newProfile.studentNameLong = studentNameLong + newProfile.studentNameShort = studentNameShort + newProfile.name = studentNameLong + newProfile.subname = userLogin + + profileList.add(newProfile) + } + + EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore)) + onSuccess() + } + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt index abfa5c19..7de629ac 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt @@ -24,12 +24,7 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) { } init { run { - if (data.profile == null) { - data.error(ApiError(TAG, ERROR_PROFILE_MISSING)) - return@run - } - - if (data.isApiLoginValid()) { + if (data.profile != null && data.isApiLoginValid()) { onSuccess() } else { From a0fe24ada00622fe4b00de3c50d9dc2ebfc26b5a Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sun, 20 Oct 2019 14:07:34 +0200 Subject: [PATCH 071/691] [APIv2/Vulcan] Add getting grades --- .../pl/szczodrzynski/edziennik/Extensions.kt | 4 +- .../api/v2/vulcan/data/api/VulcanApiGrades.kt | 87 ++++++++++++++++++- .../v2/vulcan/data/api/VulcanApiTemplate.kt | 26 ++++++ 3 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiTemplate.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index 96b1cf26..2ab17634 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -36,6 +36,7 @@ fun JsonObject?.getBoolean(key: String): Boolean? = get(key)?.let { if (it.isJso fun JsonObject?.getString(key: String): String? = get(key)?.let { if (it.isJsonNull) null else it.asString } fun JsonObject?.getInt(key: String): Int? = get(key)?.let { if (it.isJsonNull) null else it.asInt } 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?.getJsonObject(key: String): JsonObject? = get(key)?.let { if (it.isJsonNull) null else it.asJsonObject } fun JsonObject?.getJsonArray(key: String): JsonArray? = get(key)?.let { if (it.isJsonNull) null else it.asJsonArray } @@ -43,6 +44,7 @@ fun JsonObject?.getBoolean(key: String, defaultValue: Boolean): Boolean = get(ke fun JsonObject?.getString(key: String, defaultValue: String): String = get(key)?.let { if (it.isJsonNull) defaultValue else it.asString } ?: defaultValue fun JsonObject?.getInt(key: String, defaultValue: Int): Int = get(key)?.let { if (it.isJsonNull) defaultValue else it.asInt } ?: 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?.getJsonObject(key: String, defaultValue: JsonObject): JsonObject = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonObject } ?: defaultValue fun JsonObject?.getJsonArray(key: String, defaultValue: JsonArray): JsonArray = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonArray } ?: defaultValue @@ -224,4 +226,4 @@ operator fun MatchResult.get(group: Int): String { if (group >= groupValues.size) return "" return groupValues[group] -} \ No newline at end of file +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt index 63a45186..acf13fd8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt @@ -4,12 +4,16 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api +import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_GRADES import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_GRADES import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS -import pl.szczodrzynski.edziennik.getJsonArray +import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import java.text.DecimalFormat +import kotlin.math.roundToInt class VulcanApiGrades(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { companion object { @@ -20,6 +24,87 @@ class VulcanApiGrades(override val data: DataVulcan, val onSuccess: () -> Unit) apiGet(TAG, VULCAN_API_ENDPOINT_GRADES) { json, _ -> val grades = json.getJsonArray("Data") + grades?.forEach { gradeEl -> + val grade = gradeEl.asJsonObject + + val id = grade.getLong("Id") ?: return@forEach + val categoryId = grade.getLong("IdKategoria") ?: -1 + val category = data.gradeCategories.singleOrNull{ it.categoryId == categoryId }?.text + ?: "" + val teacherId = grade.getLong("IdPracownikD") ?: -1 + val subjectId = grade.getLong("IdPrzedmiot") ?: -1 + val description = grade.getString("Opis") + val comment = grade.getString("Komentarz") + var value = grade.getFloat("Wartosc") + var weight = grade.getFloat("WagaOceny") ?: 0.0f + val modificatorValue = grade.getFloat("WagaModyfikatora") + val numerator = grade.getFloat("Licznik") + val denominator = grade.getFloat("Mianownik") + val addedDate = (grade.getLong("DataModyfikacji") ?: return@forEach) * 1000 + + var finalDescription = "" + + var name = when (numerator != null && denominator != null) { + true -> { + value = numerator / denominator + finalDescription += DecimalFormat("#.##").format(numerator) + + "/" + DecimalFormat("#.##").format(denominator) + weight = 0.0f + (value * 100).roundToInt().toString() + "%" + } + else -> { + if (value != null) modificatorValue?.also { value += it } + else weight = 0.0f + + grade.getString("Wpis") ?: "" + } + } + + comment?.also { + if (name == "") name = it + else finalDescription = (if (finalDescription == "") "" else " ") + it + } + + description?.also { + finalDescription = (if (finalDescription == "") "" else " - ") + it + } + + val color = when (name) { + "1-", "1", "1+" -> 0xffd65757 + "2-", "2", "2+" -> 0xff9071b3 + "3-", "3", "3+" -> 0xffd2ab24 + "4-", "4", "4+" -> 0xff50b6d6 + "5-", "5", "5+" -> 0xff2cbd92 + "6-", "6", "6+" -> 0xff91b43c + else -> 0xff3D5F9C + }.toInt() + + val gradeObject = Grade( + profileId, + id, + category, + color, + finalDescription, + name, + value ?: 0.0f, + weight, + data.studentSemesterNumber, + teacherId, + subjectId + ) + + data.gradeList.add(gradeObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_GRADE, + id, + profile?.empty ?: false, + profile?.empty ?: false, + addedDate + + )) + } + data.setSyncNext(ENDPOINT_VULCAN_API_GRADES, SYNC_ALWAYS) onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiTemplate.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiTemplate.kt new file mode 100644 index 00000000..db380c59 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiTemplate.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-20 + */ + +package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api + +import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_GRADES +import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_GRADES +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.getJsonArray + +class VulcanApiTemplate(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { + companion object { + const val TAG = "VulcanApi" + } + + init { + /* apiGet(TAG, VULCAN_API_ENDPOINT_) { json, _ -> + + data.setSyncNext(ENDPOINT_VULCAN_API_, SYNC_ALWAYS) + onSuccess() + } */ + } +} From 929287a553dfd0e17a25ea4e830bfe60fae2c6ab Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sun, 20 Oct 2019 14:46:00 +0200 Subject: [PATCH 072/691] [APIv2/Vulcan] Add getting basic dictionaries (teachers and subjects) --- .../api/v2/vulcan/data/VulcanData.kt | 6 ++ .../vulcan/data/api/VulcanApiDictionaries.kt | 68 +++++++++++++++++++ .../v2/vulcan/data/api/VulcanApiTemplate.kt | 2 - app/src/main/res/values/strings.xml | 1 + 4 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiDictionaries.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt index fb2dc15d..ff860a7f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt @@ -6,7 +6,9 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_DICTIONARIES import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_GRADES +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiDictionaries import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiGrades import pl.szczodrzynski.edziennik.utils.Utils @@ -38,6 +40,10 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { private fun useEndpoint(endpointId: Int, onSuccess: () -> Unit) { Utils.d(TAG, "Using endpoint $endpointId") when (endpointId) { + ENDPOINT_VULCAN_API_DICTIONARIES -> { + data.startProgress(R.string.edziennik_progress_endpoint_dictionaries) + VulcanApiDictionaries(data) { onSuccess() } + } ENDPOINT_VULCAN_API_GRADES -> { data.startProgress(R.string.edziennik_progress_endpoint_grades) VulcanApiGrades(data) { onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiDictionaries.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiDictionaries.kt new file mode 100644 index 00000000..3fb89406 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiDictionaries.kt @@ -0,0 +1,68 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-20 + */ + +package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api + +import com.google.gson.JsonObject +import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_DICTIONARIES +import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_DICTIONARIES +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject +import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher +import pl.szczodrzynski.edziennik.getJsonArray +import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.getLong +import pl.szczodrzynski.edziennik.getString + +class VulcanApiDictionaries(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { + companion object { + const val TAG = "VulcanApiDictionaries" + } + + init { + apiGet(TAG, VULCAN_API_ENDPOINT_DICTIONARIES) { json, _ -> + val elements = json.getJsonObject("Data") + + elements?.getJsonArray("Pracownicy")?.forEach { saveTeacher(it.asJsonObject) } + elements?.getJsonArray("Przedmioty")?.forEach { saveSubject(it.asJsonObject) } + + data.setSyncNext(ENDPOINT_VULCAN_API_DICTIONARIES, SYNC_ALWAYS) + onSuccess() + } + } + + private fun saveTeacher(teacher: JsonObject) { + val id = teacher.getLong("Id") ?: return + val name = teacher.getString("Imie") ?: "" + val surname = teacher.getString("Nazwisko") ?: "" + val loginId = teacher.getString("LoginId") ?: "-1" + + val teacherObject = Teacher( + profileId, + id, + name, + surname, + loginId + ) + + data.teacherList.put(id, teacherObject) + } + + private fun saveSubject(subject: JsonObject) { + val id = subject.getLong("Id") ?: return + val longName = subject.getString("Nazwa") ?: "" + val shortName = subject.getString("Kod") ?: "" + + val subjectObject = Subject( + profileId, + id, + longName, + shortName + ) + + data.subjectList.put(id, subjectObject) + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiTemplate.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiTemplate.kt index db380c59..2c2bb824 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiTemplate.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiTemplate.kt @@ -4,9 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api -import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_GRADES import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan -import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_GRADES import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.getJsonArray diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e8158af5..af102476 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -950,4 +950,5 @@ Pobieranie informacji o klasie... Pobieranie nieobecności nauczycieli... Pobieranie rodzajów nieobecności nauczycieli... + Pobieranie słowników... From 0e5a32b25309c0f1763376b2a70a422f30d6c8b7 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sun, 20 Oct 2019 22:35:13 +0200 Subject: [PATCH 073/691] [APIv2/Vulcan] Fix setting semester dates --- .../api/v2/vulcan/firstlogin/VulcanFirstLogin.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt index adae6249..2424adc2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt @@ -15,6 +15,7 @@ import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLoginApi import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.getInt import pl.szczodrzynski.edziennik.getJsonArray +import pl.szczodrzynski.edziennik.getLong import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.utils.models.Date @@ -58,8 +59,8 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) { val schoolSymbol = student.getString("JednostkaSprawozdawczaSymbol") ?: return@forEach val schoolName = "${data.symbol}_$schoolSymbol" - val currentSemesterStartDate = student.getInt("OkresDataOd") ?: return@forEach - val currentSemesterEndDate = (student.getInt("OkresDataDo") + val currentSemesterStartDate = student.getLong("OkresDataOd") ?: return@forEach + val currentSemesterEndDate = (student.getLong("OkresDataDo") ?: return@forEach) + 86400 val studentSemesterNumber = student.getInt("OkresNumer") ?: return@forEach @@ -79,12 +80,12 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) { when (studentSemesterNumber) { 1 -> { - newProfile.dateSemester1Start = Date.fromMillis((currentSemesterStartDate * 1000).toLong()) - newProfile.dateSemester2Start = Date.fromMillis((currentSemesterEndDate * 1000).toLong()) + newProfile.dateSemester1Start = Date.fromMillis(currentSemesterStartDate * 1000) + newProfile.dateSemester2Start = Date.fromMillis(currentSemesterEndDate * 1000) } 2 -> { - newProfile.dateSemester2Start = Date.fromMillis((currentSemesterStartDate * 1000).toLong()) - newProfile.dateYearEnd = Date.fromMillis((currentSemesterEndDate * 1000).toLong()) + newProfile.dateSemester2Start = Date.fromMillis(currentSemesterStartDate * 1000) + newProfile.dateYearEnd = Date.fromMillis(currentSemesterEndDate * 1000) } } From 9e6741d5425e73872dfeea51d7b60ebb2463694c Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sun, 20 Oct 2019 22:45:44 +0200 Subject: [PATCH 074/691] [APIv2/Vulcan] Add getting events --- .../edziennik/api/v2/vulcan/VulcanFeatures.kt | 6 +- .../edziennik/api/v2/vulcan/data/VulcanApi.kt | 15 +++- .../api/v2/vulcan/data/VulcanData.kt | 6 ++ .../api/v2/vulcan/data/api/VulcanApiEvents.kt | 75 +++++++++++++++++++ .../data/db/modules/events/Event.java | 4 +- 5 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/VulcanFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/VulcanFeatures.kt index ab65467e..e039609d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/VulcanFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/VulcanFeatures.kt @@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.api.v2.models.Feature const val ENDPOINT_VULCAN_API_STUDENT_LIST = 1000 const val ENDPOINT_VULCAN_API_DICTIONARIES = 1010 const val ENDPOINT_VULCAN_API_TIMETABLE = 1020 -const val ENDPOINT_VULCAN_API_EXAMS = 1030 +const val ENDPOINT_VULCAN_API_EVENTS = 1030 const val ENDPOINT_VULCAN_API_GRADES = 1040 const val ENDPOINT_VULCAN_API_GRADES_SUMMARY = 1050 const val ENDPOINT_VULCAN_API_HOMEWORK = 1060 @@ -26,7 +26,7 @@ val VulcanFeatures = listOf( ), listOf(LOGIN_METHOD_VULCAN_API)), // agenda Feature(LOGIN_TYPE_VULCAN, FEATURE_AGENDA, listOf( - ENDPOINT_VULCAN_API_EXAMS to LOGIN_METHOD_VULCAN_API + ENDPOINT_VULCAN_API_EVENTS to LOGIN_METHOD_VULCAN_API ), listOf(LOGIN_METHOD_VULCAN_API)), // grades Feature(LOGIN_TYPE_VULCAN, FEATURE_GRADES, listOf( @@ -82,4 +82,4 @@ val VulcanFeatures = listOf( ENDPOINT_VULCAN_API to LOGIN_METHOD_VULCAN_WEB ), listOf(LOGIN_METHOD_VULCAN_WEB)),*/ -) \ No newline at end of file +) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt index 546692d1..57602f45 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt @@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan import pl.szczodrzynski.edziennik.utils.Utils.d +import pl.szczodrzynski.edziennik.utils.models.Date import java.net.HttpURLConnection import java.util.* @@ -31,18 +32,30 @@ open class VulcanApi(open val data: DataVulcan) { baseUrl: Boolean = false, onSuccess: (json: JsonObject, response: Response?) -> Unit) { val url = "${if (baseUrl) data.apiUrl else data.fullApiUrl}$endpoint" - d(tag, "Request: Librus/Api - $url") + d(tag, "Request: Vulcan/Api - $url") + + val startDate = when(profile?.empty) { + true -> profile?.getSemesterStart(profile?.currentSemester ?: 1)?.stringY_m_d + else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d + } + val endDate = profile?.getSemesterEnd(profile?.currentSemester ?: 1)?.stringY_m_d val finalPayload = JsonObject() finalPayload.addProperty("IdUczen", data.studentId) finalPayload.addProperty("IdOkresKlasyfikacyjny", data.studentSemesterId) finalPayload.addProperty("IdOddzial", data.studentClassId) + finalPayload.addProperty("DataPoczatkowa", startDate) + finalPayload.addProperty("DataKoncowa", endDate) finalPayload.addProperty("RemoteMobileTimeKey", System.currentTimeMillis() / 1000) finalPayload.addProperty("TimeKey", System.currentTimeMillis() / 1000 - 1) finalPayload.addProperty("RequestId", UUID.randomUUID().toString()) finalPayload.addProperty("RemoteMobileAppVersion", VULCAN_API_APP_VERSION) finalPayload.addProperty("RemoteMobileAppName", VULCAN_API_APP_NAME) + payload?.keySet()?.forEach { + finalPayload.add(it, payload.get(it)) + } + val callback = object : JsonCallbackHandler() { override fun onSuccess(json: JsonObject?, response: Response?) { if (json == null && response?.parserErrorBody == null) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt index ff860a7f..b10f2d11 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt @@ -7,8 +7,10 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_DICTIONARIES +import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_EVENTS import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_GRADES import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiDictionaries +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiEvents import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiGrades import pl.szczodrzynski.edziennik.utils.Utils @@ -48,6 +50,10 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_grades) VulcanApiGrades(data) { onSuccess() } } + ENDPOINT_VULCAN_API_EVENTS -> { + data.startProgress(R.string.edziennik_progress_endpoint_events) + VulcanApiEvents(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt new file mode 100644 index 00000000..599c2ddb --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt @@ -0,0 +1,75 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-20 + */ + +package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api + +import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_EVENTS +import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_EVENTS +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.events.Event +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.getBoolean +import pl.szczodrzynski.edziennik.getJsonArray +import pl.szczodrzynski.edziennik.getLong +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.utils.models.Date + +class VulcanApiEvents(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { + companion object { + const val TAG = "VulcanApi" + } + + init { + apiGet(TAG, VULCAN_API_ENDPOINT_EVENTS) { json, _ -> + val events = json.getJsonArray("Data") + + events?.forEach { eventEl -> + val event = eventEl.asJsonObject + + val id = event?.getLong("Id") ?: return@forEach + val eventDate = Date.fromY_m_d(event.getString("DataTekst") ?: return@forEach) + val subjectId = event.getLong("IdPrzedmiot") ?: -1 + val teacherId = event.getLong("IdPracownik") ?: -1 + val startTime = data.lessonList.singleOrNull { + it.weekDay == eventDate.weekDay && it.subjectId == subjectId + }?.startTime + val topic = event.getString("Opis") ?: "" + val type = when (event.getBoolean("Rodzaj")) { + true -> Event.TYPE_EXAM + else -> Event.TYPE_SHORT_QUIZ + } + val teamId = event.getLong("IdOddzial") ?: data.teamClass?.id ?: return@forEach + + val eventObject = Event( + profileId, + id, + eventDate, + startTime, + topic, + -1, + type, + false, + teacherId, + subjectId, + teamId + ) + + data.eventList.add(eventObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_EVENT, + id, + profile?.empty ?: false, + profile?.empty ?: false, + System.currentTimeMillis() + )) + } + + data.setSyncNext(ENDPOINT_VULCAN_API_EVENTS, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/events/Event.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/events/Event.java index e55e2a47..b572ab48 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/events/Event.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/events/Event.java @@ -1,5 +1,6 @@ package pl.szczodrzynski.edziennik.data.db.modules.events; +import androidx.annotation.Nullable; import androidx.room.ColumnInfo; import androidx.room.Entity; import androidx.room.Ignore; @@ -19,6 +20,7 @@ public class Event { @ColumnInfo(name = "eventDate") public Date eventDate; @ColumnInfo(name = "eventStartTime") + @Nullable public Time startTime; // null for allDay @ColumnInfo(name = "eventTopic") public String topic; @@ -68,7 +70,7 @@ public class Event { @Ignore public Event() {} - public Event(int profileId, long id, Date eventDate, Time startTime, String topic, int color, int type, boolean addedManually, long teacherId, long subjectId, long teamId) + public Event(int profileId, long id, Date eventDate, @Nullable Time startTime, String topic, int color, int type, boolean addedManually, long teacherId, long subjectId, long teamId) { this.profileId = profileId; this.id = id; From 2a7535920efbd144fffdd3ea7876210468dc50ff Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Mon, 21 Oct 2019 00:27:24 +0200 Subject: [PATCH 075/691] [APIv2/Vulcan] Add getting team if it doesn't exist --- .../edziennik/api/v2/vulcan/data/VulcanApi.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt index 57602f45..de9574c4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt @@ -12,6 +12,8 @@ import io.github.wulkanowy.signer.signContent import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.data.db.modules.teams.Team +import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.models.Date import java.net.HttpURLConnection @@ -34,6 +36,22 @@ open class VulcanApi(open val data: DataVulcan) { d(tag, "Request: Vulcan/Api - $url") + if (data.teamList.size() == 0) { + profile?.getStudentData("studentClassName", null)?.also { name -> + val id = Utils.crc16(name.toByteArray()).toLong() + + val teamObject = Team( + profileId, + id, + name, + Team.TYPE_CLASS, + "${data.schoolName}:$name", + -1 + ) + data.teamList.put(id, teamObject) + } + } + val startDate = when(profile?.empty) { true -> profile?.getSemesterStart(profile?.currentSemester ?: 1)?.stringY_m_d else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d From f44b64fcc50727c849d46c50c03faf5a12c61ff5 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Mon, 21 Oct 2019 00:28:27 +0200 Subject: [PATCH 076/691] [APIv2/Vulcan] Add getting homework --- .../api/v2/vulcan/data/VulcanData.kt | 11 ++++---- .../api/v2/vulcan/data/api/VulcanApiEvents.kt | 28 +++++++++++++------ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt index b10f2d11..fec47ca2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt @@ -5,10 +5,7 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan -import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_DICTIONARIES -import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_EVENTS -import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_GRADES +import pl.szczodrzynski.edziennik.api.v2.vulcan.* import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiDictionaries import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiEvents import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiGrades @@ -52,7 +49,11 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { } ENDPOINT_VULCAN_API_EVENTS -> { data.startProgress(R.string.edziennik_progress_endpoint_events) - VulcanApiEvents(data) { onSuccess() } + VulcanApiEvents(data, isHomework = false) { onSuccess() } + } + ENDPOINT_VULCAN_API_HOMEWORK -> { + data.startProgress(R.string.edziennik_progress_endpoint_homework) + VulcanApiEvents(data, isHomework = true) { onSuccess() } } else -> onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt index 599c2ddb..71bf916c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt @@ -5,8 +5,10 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_EVENTS +import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_HOMEWORK import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_EVENTS +import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_HOMEWORK import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.events.Event @@ -17,13 +19,17 @@ import pl.szczodrzynski.edziennik.getLong import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.utils.models.Date -class VulcanApiEvents(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { +class VulcanApiEvents(override val data: DataVulcan, private val isHomework: Boolean, val onSuccess: () -> Unit) : VulcanApi(data) { companion object { - const val TAG = "VulcanApi" + const val TAG = "VulcanApiEvents" } init { - apiGet(TAG, VULCAN_API_ENDPOINT_EVENTS) { json, _ -> + val endpoint = when (isHomework) { + true -> VULCAN_API_ENDPOINT_HOMEWORK + else -> VULCAN_API_ENDPOINT_EVENTS + } + apiGet(TAG, endpoint) { json, _ -> val events = json.getJsonArray("Data") events?.forEach { eventEl -> @@ -37,9 +43,12 @@ class VulcanApiEvents(override val data: DataVulcan, val onSuccess: () -> Unit) it.weekDay == eventDate.weekDay && it.subjectId == subjectId }?.startTime val topic = event.getString("Opis") ?: "" - val type = when (event.getBoolean("Rodzaj")) { - true -> Event.TYPE_EXAM - else -> Event.TYPE_SHORT_QUIZ + val type = when (isHomework) { + true -> Event.TYPE_HOMEWORK + else -> when (event.getBoolean("Rodzaj")) { + false -> Event.TYPE_SHORT_QUIZ + else -> Event.TYPE_EXAM + } } val teamId = event.getLong("IdOddzial") ?: data.teamClass?.id ?: return@forEach @@ -60,7 +69,7 @@ class VulcanApiEvents(override val data: DataVulcan, val onSuccess: () -> Unit) data.eventList.add(eventObject) data.metadataList.add(Metadata( profileId, - Metadata.TYPE_EVENT, + if (isHomework) Metadata.TYPE_HOMEWORK else Metadata.TYPE_EVENT, id, profile?.empty ?: false, profile?.empty ?: false, @@ -68,7 +77,10 @@ class VulcanApiEvents(override val data: DataVulcan, val onSuccess: () -> Unit) )) } - data.setSyncNext(ENDPOINT_VULCAN_API_EVENTS, SYNC_ALWAYS) + when (isHomework) { + true -> data.setSyncNext(ENDPOINT_VULCAN_API_HOMEWORK, SYNC_ALWAYS) + false -> data.setSyncNext(ENDPOINT_VULCAN_API_EVENTS, SYNC_ALWAYS) + } onSuccess() } } From 25744037f5dc183480da7281d769fea4776aa10e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 21 Oct 2019 17:25:46 +0200 Subject: [PATCH 077/691] [DB] Fix migration compatibility for different app versions --- .../pl/szczodrzynski/edziennik/data/db/AppDb.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java index 4cbcb3a7..6d850b7b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java @@ -577,6 +577,13 @@ public abstract class AppDb extends RoomDatabase { } }; private static final Migration MIGRATION_54_55 = new Migration(54, 55) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + // 2019-10-21 for merge compatibility between 3.1.1 and api-v2 + // moved to Migration 55->56 + } + }; + private static final Migration MIGRATION_55_56 = new Migration(55, 56) { @Override public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL("CREATE TABLE IF NOT EXISTS endpointTimers (" + @@ -587,11 +594,6 @@ public abstract class AppDb extends RoomDatabase { "endpointViewId INTEGER DEFAULT NULL," + "PRIMARY KEY(profileId, endpointId)" + ")"); - } - }; - private static final Migration MIGRATION_55_56 = new Migration(55, 56) { - @Override - public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL("CREATE TABLE IF NOT EXISTS lessonRanges (" + "profileId INTEGER NOT NULL," + "lessonRangeNumber INTEGER NOT NULL," + From 3540b09623df0453704d0353bc5222e69ef0bf78 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Tue, 22 Oct 2019 10:30:19 +0200 Subject: [PATCH 078/691] [APIv2/Vulcan] Temporary fix for signing requests --- .../edziennik/api/v2/vulcan/data/VulcanApi.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt index de9574c4..b0c66a66 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data +import android.util.Base64 import com.google.gson.JsonObject import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response @@ -16,6 +17,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.teams.Team import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.models.Date +import java.io.ByteArrayInputStream import java.net.HttpURLConnection import java.util.* @@ -52,7 +54,7 @@ open class VulcanApi(open val data: DataVulcan) { } } - val startDate = when(profile?.empty) { + val startDate = when (profile?.empty) { true -> profile?.getSemesterStart(profile?.currentSemester ?: 1)?.stringY_m_d else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d } @@ -112,7 +114,9 @@ open class VulcanApi(open val data: DataVulcan) { .userAgent(VULCAN_API_USER_AGENT) .addHeader("RequestCertificateKey", data.apiCertificateKey) .addHeader("RequestSignatureValue", - signContent(VULCAN_API_PASSWORD, data.apiCertificatePfx, finalPayload.toString())) + Utils.VulcanRequestEncryptionUtils.signContent( + finalPayload.toString().toByteArray(), + ByteArrayInputStream(Base64.decode(data.apiCertificatePfx, Base64.DEFAULT)))) .apply { when (method) { GET -> get() From b8f58328cbdb95a4d63a092b7f8bdb8ff6aca0be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 22 Oct 2019 22:34:13 +0200 Subject: [PATCH 079/691] [APIv2/Vulcan] Add faster request signing. --- app/build.gradle | 3 +- .../edziennik/api/v2/Constants.kt | 1 + .../edziennik/api/v2/vulcan/DataVulcan.kt | 7 ++- .../edziennik/api/v2/vulcan/data/VulcanApi.kt | 12 ++--- .../edziennik/api/v2/vulcan/data/signer.kt | 47 +++++++++++++++++++ .../api/v2/vulcan/login/VulcanLoginApi.kt | 18 ++++++- 6 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/signer.kt diff --git a/app/build.gradle b/app/build.gradle index df5dde57..5e5494aa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -156,7 +156,8 @@ dependencies { debugImplementation "com.github.ChuckerTeam.Chucker:library:3.0.1" releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:3.0.1" - implementation 'com.github.wulkanowy:uonet-request-signer:master-SNAPSHOT' + //implementation 'com.github.wulkanowy:uonet-request-signer:master-SNAPSHOT' + //implementation 'com.github.kuba2k2.uonet-request-signer:android:master-63f094b14a-1' } repositories { mavenCentral() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index 178b3bc9..51cedea1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -52,6 +52,7 @@ const val VULCAN_API_USER_AGENT = "MobileUserAgent" const val VULCAN_API_APP_NAME = "VULCAN-Android-ModulUcznia" const val VULCAN_API_APP_VERSION = "19.4.1.436" const val VULCAN_API_PASSWORD = "CE75EA598C7743AD9B0B7328DED85B06" +const val VULCAN_API_PASSWORD_FAKELOG = "012345678901234567890123456789AB" val VULCAN_API_DEVICE_NAME = "Szkolny.eu ${Build.MODEL}" const val VULCAN_API_ENDPOINT_CERTIFICATE = "mobile-api/Uczen.v3.UczenStart/Certyfikat" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt index 7d3f8367..6e647a87 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt @@ -16,7 +16,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app fun isApiLoginValid() = apiCertificateExpiryTime-30 > currentTimeUnix() && apiCertificateKey.isNotNullNorEmpty() - && apiCertificatePfx.isNotNullNorEmpty() + && apiCertificatePrivate.isNotNullNorEmpty() && symbol.isNotNullNorEmpty() override fun satisfyLoginMethods() { @@ -145,6 +145,11 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app get() { mApiCertificatePfx = mApiCertificatePfx ?: loginStore.getLoginData("certificatePfx", null); return mApiCertificatePfx } set(value) { loginStore.putLoginData("certificatePfx", value); mApiCertificatePfx = value } + private var mApiCertificatePrivate: String? = null + var apiCertificatePrivate: String? + get() { mApiCertificatePrivate = mApiCertificatePrivate ?: loginStore.getLoginData("certificatePrivate", null); return mApiCertificatePrivate } + set(value) { loginStore.putLoginData("certificatePrivate", value); mApiCertificatePrivate = value } + private var mApiCertificateExpiryTime: Int? = null var apiCertificateExpiryTime: Int get() { mApiCertificateExpiryTime = mApiCertificateExpiryTime ?: loginStore.getLoginData("certificateExpiryTime", 0); return mApiCertificateExpiryTime ?: 0 } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt index b0c66a66..d826dfa2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt @@ -4,12 +4,10 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data -import android.util.Base64 import com.google.gson.JsonObject import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response import im.wangchao.mhttp.callback.JsonCallbackHandler -import io.github.wulkanowy.signer.signContent import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan @@ -17,7 +15,6 @@ import pl.szczodrzynski.edziennik.data.db.modules.teams.Team import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.models.Date -import java.io.ByteArrayInputStream import java.net.HttpURLConnection import java.util.* @@ -114,9 +111,12 @@ open class VulcanApi(open val data: DataVulcan) { .userAgent(VULCAN_API_USER_AGENT) .addHeader("RequestCertificateKey", data.apiCertificateKey) .addHeader("RequestSignatureValue", - Utils.VulcanRequestEncryptionUtils.signContent( - finalPayload.toString().toByteArray(), - ByteArrayInputStream(Base64.decode(data.apiCertificatePfx, Base64.DEFAULT)))) + try { + signContent( + data.apiCertificatePrivate ?: "", + finalPayload.toString() + ) + } catch (e: Exception) {e.printStackTrace();""}) .apply { when (method) { GET -> get() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/signer.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/signer.kt new file mode 100644 index 00000000..5f0447d8 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/signer.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) Wulkanowy kiedyś tam. + */ + +package pl.szczodrzynski.edziennik.api.v2.vulcan.data + +import android.util.Base64 +import java.io.ByteArrayInputStream +import java.security.KeyFactory +import java.security.KeyStore +import java.security.PrivateKey +import java.security.Signature +import java.security.spec.PKCS8EncodedKeySpec + +fun signContent(password: String, certificate: String?, content: String): String { + val keystore = KeyStore.getInstance("pkcs12").apply { + load(ByteArrayInputStream(Base64.decode(certificate, Base64.DEFAULT)), password.toCharArray()) + } + val signature = Signature.getInstance("SHA1WithRSA").apply { + initSign(keystore.getKey("LoginCert", password.toCharArray()) as PrivateKey) + update(content.toByteArray()) + } + return Base64.encodeToString(signature.sign(), Base64.NO_WRAP) +} + +fun signContent(privateKey: String, content: String): String { + val key = PKCS8EncodedKeySpec(Base64.decode(privateKey, Base64.DEFAULT)).let { + KeyFactory.getInstance("RSA").generatePrivate(it) + } + val signature = Signature.getInstance("SHA1WithRSA").apply { + initSign(key) + update(content.toByteArray()) + } + return Base64.encodeToString(signature.sign(), Base64.NO_WRAP) +} + +fun getPrivateKeyFromCert(password: String, certificate: String): String { + val keystore = KeyStore.getInstance("pkcs12").apply { + load(ByteArrayInputStream(Base64.decode(certificate, Base64.DEFAULT)), password.toCharArray()) + } + val keyFactory = KeyFactory.getInstance("RSA") + val keySpec = keyFactory.getKeySpec( + keystore.getKey("LoginCert", password.toCharArray()) as PrivateKey, + PKCS8EncodedKeySpec::class.java + ) + return Base64.encodeToString(keySpec.encoded, Base64.NO_WRAP) +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt index 7de629ac..0aab5950 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt @@ -9,10 +9,14 @@ import com.google.gson.JsonObject import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response import im.wangchao.mhttp.callback.JsonCallbackHandler -import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.getPrivateKeyFromCert +import pl.szczodrzynski.edziennik.currentTimeUnix +import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.isNotNullNorEmpty import pl.szczodrzynski.edziennik.utils.Utils.d import java.net.HttpURLConnection.HTTP_BAD_REQUEST import java.util.* @@ -28,6 +32,18 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) { onSuccess() } else { + if (data.apiCertificatePfx.isNotNullNorEmpty()) { + try { + data.apiCertificatePrivate = getPrivateKeyFromCert( + if (data.apiToken?.get(0) == 'F') VULCAN_API_PASSWORD_FAKELOG else VULCAN_API_PASSWORD, + data.apiCertificatePfx ?: "" + ) + onSuccess() + return@run + } catch (e: Throwable) { + e.printStackTrace() + } + } if (data.symbol.isNotNullNorEmpty() && data.apiToken.isNotNullNorEmpty() && data.apiPin.isNotNullNorEmpty()) { loginWithToken() } From 5d3bebfdce8e6c1d7e5b6bc279e258775256f414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 22 Oct 2019 22:37:02 +0200 Subject: [PATCH 080/691] [APIv2] Implement swipe to refresh with ApiService. --- .../szczodrzynski/edziennik/MainActivity.kt | 132 +++++++++++------- .../edziennik/api/v2/ApiService.kt | 12 +- .../edziennik/receivers/SzkolnyReceiver.kt | 2 + app/src/main/res/values/strings.xml | 2 + 4 files changed, 92 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt index e617b152..c27156ff 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt @@ -14,66 +14,66 @@ import android.view.Gravity import android.view.View import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.Observer -import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial -import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont -import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem -import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem -import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.* -import pl.szczodrzynski.edziennik.utils.Themes -import pl.szczodrzynski.navlib.SystemBarsUtil.Companion.COLOR_HALF_TRANSPARENT -import pl.szczodrzynski.navlib.bottomsheet.NavBottomSheet -import pl.szczodrzynski.navlib.drawer.NavDrawer -import pl.szczodrzynski.navlib.drawer.items.DrawerPrimaryItem -import pl.szczodrzynski.navlib.drawer.items.withAppTitle import androidx.appcompat.widget.PopupMenu import androidx.core.graphics.ColorUtils +import androidx.lifecycle.Observer import androidx.navigation.NavOptions import com.danimahardhika.cafebar.CafeBar import com.mikepenz.iconics.IconicsColor import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.IconicsSize +import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont import com.mikepenz.materialdrawer.model.DividerDrawerItem import com.mikepenz.materialdrawer.model.ProfileDrawerItem +import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem +import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem import com.mikepenz.materialdrawer.model.interfaces.IProfile -import me.zhanghai.android.materialprogressbar.internal.ThemeUtils +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode import pl.droidsonroids.gif.GifDrawable import pl.szczodrzynski.edziennik.App.APP_URL -import pl.szczodrzynski.edziennik.data.api.AppError +import pl.szczodrzynski.edziennik.api.v2.ApiService +import pl.szczodrzynski.edziennik.api.v2.events.* +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface.* -import pl.szczodrzynski.edziennik.data.api.interfaces.SyncCallback +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.* import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding -import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore -import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile -import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull -import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog -import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment -import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity -import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesDetailsFragment -import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment -import pl.szczodrzynski.edziennik.utils.models.NavTarget import pl.szczodrzynski.edziennik.network.ServerRequest import pl.szczodrzynski.edziennik.sync.SyncJob +import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment import pl.szczodrzynski.edziennik.ui.modules.announcements.AnnouncementsFragment import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceFragment import pl.szczodrzynski.edziennik.ui.modules.base.DebugFragment +import pl.szczodrzynski.edziennik.ui.modules.behaviour.BehaviourFragment import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment -import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment import pl.szczodrzynski.edziennik.ui.modules.grades.GradesFragment +import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment -import pl.szczodrzynski.edziennik.ui.modules.behaviour.BehaviourFragment +import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment +import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity +import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesDetailsFragment +import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch +import pl.szczodrzynski.edziennik.utils.Themes import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils.dpToPx +import pl.szczodrzynski.edziennik.utils.models.NavTarget import pl.szczodrzynski.navlib.* +import pl.szczodrzynski.navlib.SystemBarsUtil.Companion.COLOR_HALF_TRANSPARENT +import pl.szczodrzynski.navlib.bottomsheet.NavBottomSheet import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem +import pl.szczodrzynski.navlib.drawer.NavDrawer +import pl.szczodrzynski.navlib.drawer.items.DrawerPrimaryItem +import pl.szczodrzynski.navlib.drawer.items.withAppTitle import java.io.File import java.io.IOException import java.util.* @@ -474,6 +474,13 @@ class MainActivity : AppCompatActivity() { .withIcon(CommunityMaterial.Icon.cmd_android_debug_bridge) .withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_DEBUG) }) } + + EventBus.getDefault().register(this) + } + + override fun onDestroy() { + EventBus.getDefault().unregister(this) + super.onDestroy() } var profileListEmptyListener = { @@ -508,35 +515,56 @@ class MainActivity : AppCompatActivity() { fun syncCurrentFeature() { swipeRefreshLayout.isRefreshing = true Toast.makeText(this, fragmentToSyncName(navTargetId), Toast.LENGTH_SHORT).show() - val callback = object : SyncCallback { - override fun onLoginFirst(profileList: List, loginStore: LoginStore) { - - } - - override fun onSuccess(activityContext: Context, profileFull: ProfileFull) { - swipeRefreshLayout.isRefreshing = false - } - - override fun onError(activityContext: Context, error: AppError) { - swipeRefreshLayout.isRefreshing = false - app.apiEdziennik.guiShowErrorSnackbar(this@MainActivity, error) - } - - override fun onProgress(progressStep: Int) { - - } - - override fun onActionStarted(stringResId: Int) { - + ApiService.start(this) + val fragmentParam = when (navTargetId) { + DRAWER_ITEM_MESSAGES -> MessagesFragment.pageSelection + else -> 0 + } + EventBus.getDefault().postSticky( + SyncProfileRequest( + App.profileId, + listOf(navTargetId to fragmentParam) + ) + ) + } + @Subscribe(threadMode = ThreadMode.MAIN) + fun onSyncStartedEvent(event: SyncStartedEvent) { + swipeRefreshLayout.isRefreshing = true + if (event.profileId == App.profileId) { + navView.toolbar.apply { + subtitleFormat = null + subtitleFormatWithUnread = null + subtitle = getString(R.string.toolbar_subtitle_syncing) } } - val feature = fragmentToFeature(navTargetId) - if (feature == FEATURE_ALL) { - swipeRefreshLayout.isRefreshing = false - app.apiEdziennik.guiSync(app, this, App.profileId, R.string.sync_dialog_title, R.string.sync_dialog_text, R.string.sync_done) - } else { - app.apiEdziennik.guiSyncSilent(app, this, App.profileId, callback, feature) + } + @Subscribe(threadMode = ThreadMode.MAIN) + fun onSyncProgressEvent(event: SyncProgressEvent) { + if (event.profileId == App.profileId) { + navView.toolbar.apply { + subtitleFormat = null + subtitleFormatWithUnread = null + subtitle = getString(R.string.toolbar_subtitle_syncing_format, event.progress, event.progressRes?.let { getString(it) } ?: "") + } } + } + @Subscribe(threadMode = ThreadMode.MAIN) + fun onSyncProfileFinishedEvent(event: SyncProfileFinishedEvent) { + if (event.profileId == App.profileId) { + navView.toolbar.apply { + subtitleFormat = R.string.toolbar_subtitle + subtitleFormatWithUnread = R.plurals.toolbar_subtitle_with_unread + subtitle = "Gotowe" + } + } + } + @Subscribe(threadMode = ThreadMode.MAIN) + fun onSyncFinishedEvent(event: SyncFinishedEvent) { + swipeRefreshLayout.isRefreshing = false + } + @Subscribe(threadMode = ThreadMode.MAIN) + fun onSyncErrorEvent(event: SyncErrorEvent) { + } private fun fragmentToFeature(currentFragment: Int): Int { return when (currentFragment) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt index 6ec5b605..d5545fb0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt @@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.api.v2 import android.app.Service +import android.content.Context import android.content.Intent import android.os.IBinder import android.util.Log @@ -13,10 +14,7 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.api.v2.events.SyncErrorEvent -import pl.szczodrzynski.edziennik.api.v2.events.SyncFinishedEvent -import pl.szczodrzynski.edziennik.api.v2.events.SyncProfileFinishedEvent -import pl.szczodrzynski.edziennik.api.v2.events.SyncProgressEvent +import pl.szczodrzynski.edziennik.api.v2.events.* import pl.szczodrzynski.edziennik.api.v2.events.requests.* import pl.szczodrzynski.edziennik.api.v2.events.task.ErrorReportTask import pl.szczodrzynski.edziennik.api.v2.events.task.NotifyTask @@ -37,6 +35,9 @@ class ApiService : Service() { companion object { const val TAG = "ApiService" const val NOTIFICATION_API_CHANNEL_ID = "pl.szczodrzynski.edziennik.GET_DATA" + fun start(context: Context) { + context.startService(Intent(context, ApiService::class.java)) + } } private val app by lazy { applicationContext as App } @@ -211,6 +212,9 @@ class ApiService : Service() { // update the notification notification.setCurrentTask(taskRunningId, taskProfileName).post() + // post an event + EventBus.getDefault().post(SyncStartedEvent(taskProfileId)) + edziennikInterface = when (loginStore.type) { LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback) LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt index e9d3fb9d..8cf52b11 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt @@ -8,6 +8,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import org.greenrobot.eventbus.EventBus +import pl.szczodrzynski.edziennik.api.v2.ApiService import pl.szczodrzynski.edziennik.api.v2.events.requests.ServiceCloseRequest import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest @@ -15,6 +16,7 @@ import pl.szczodrzynski.edziennik.api.v2.events.requests.TaskCancelRequest class SzkolnyReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { + context?.startService(Intent(context, ApiService::class.java)) when (intent?.extras?.getString("task", null)) { "ServiceCloseRequest" -> EventBus.getDefault().post(ServiceCloseRequest()) "TaskCancelRequest" -> EventBus.getDefault().post(TaskCancelRequest(intent.extras?.getInt("taskId", -1) ?: return)) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0c9952b5..1fb474fa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -952,4 +952,6 @@ Pobieranie nieobecności nauczycieli... Pobieranie rodzajów nieobecności nauczycieli... Pobieranie słowników... + Synchronizuję... + [%d%%] %s From bfcbeb7140ce30af822c445f8bcf960fa9ed5157 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Wed, 23 Oct 2019 00:03:40 +0200 Subject: [PATCH 081/691] [APIv2/Librus] Add Synergia request --- .../edziennik/api/v2/Constants.kt | 1 + .../szczodrzynski/edziennik/api/v2/Errors.kt | 5 +- .../api/v2/librus/data/LibrusSynergia.kt | 77 +++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index 51cedea1..576b4912 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -38,6 +38,7 @@ const val LIBRUS_API_CLIENT_ID_JST = "49" const val LIBRUS_JST_DEMO_CODE = "68656A21" const val LIBRUS_JST_DEMO_PIN = "1290" +const val LIBRUS_SYNERGIA_URL = "https://synergia.librus.pl/" /** https://synergia.librus.pl/loguj/token/TOKEN/przenies */ const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/token/TOKEN/przenies" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index bad56ff6..b6fcc05d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -125,6 +125,7 @@ const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901 const val EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN = 902 const val EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN = 903 const val EXCEPTION_LIBRUS_API_REQUEST = 904 -const val EXCEPTION_MOBIDZIENNIK_WEB_REQUEST = 905 -const val EXCEPTION_VULCAN_API_REQUEST = 906 +const val EXCEPTION_LIBRUS_SYNERGIA_REQUEST = 905 +const val EXCEPTION_MOBIDZIENNIK_WEB_REQUEST = 906 +const val EXCEPTION_VULCAN_API_REQUEST = 907 const val EXCEPTION_NOTIFY_AND_SYNC = 910 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt new file mode 100644 index 00000000..9b449ce0 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt @@ -0,0 +1,77 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-21. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data + +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.callback.TextCallbackHandler +import okhttp3.Cookie +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.utils.Utils.d +import java.lang.Exception + +open class LibrusSynergia(open val data: DataLibrus) { + companion object { + const val TAG = "LibrusSynergia" + } + + val profileId + get() = data.profile?.id ?: -1 + + val profile + get() = data.profile + + fun apiGet(tag: String, endpoint: String, method: Int = GET, + parameters: Map = emptyMap(), onSuccess: (text: String) -> Unit) { + d(tag, "Request: Librus/Synergia - $LIBRUS_SYNERGIA_URL$endpoint") + + val callback = object : TextCallbackHandler() { + override fun onSuccess(text: String?, response: Response?) { + if (text.isNullOrEmpty()) { + data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY) + .withResponse(response)) + return + } + + // TODO: Error handling + + try { + onSuccess(text) + } catch (e: Exception) { + data.error(ApiError(tag, EXCEPTION_LIBRUS_SYNERGIA_REQUEST) + .withResponse(response) + .withThrowable(e) + .withApiResponse(text)) + } + } + } + + data.app.cookieJar.saveFromResponse(null, listOf( + Cookie.Builder() + .name("DZIENNIKSID") + .value(data.synergiaSessionId!!) + .domain("synergia.librus.pl") + .secure().httpOnly().build() + )) + + Request.builder() + .url("$LIBRUS_SYNERGIA_URL$endpoint") + .userAgent(LIBRUS_USER_AGENT) + .apply { + when (method) { + GET -> get() + POST -> post() + } + parameters.map { (name, value) -> + addParameter(name, value) + } + } + .callback(callback) + .build() + .enqueue() + } +} From 74ce9cd38dcb08851ad1748403be3ee69fef1a79 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Wed, 23 Oct 2019 00:05:35 +0200 Subject: [PATCH 082/691] [APIv2/Librus] Add getting homework using Synergia --- .../edziennik/api/v2/librus/LibrusFeatures.kt | 12 ++- .../api/v2/librus/data/LibrusData.kt | 5 + .../data/synergia/LibrusSynergiaHomework.kt | 94 +++++++++++++++++++ 3 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt index 48218bc0..e92a2661 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt @@ -46,6 +46,7 @@ const val ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS = 1120 const val ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS = 1130 const val ENDPOINT_LIBRUS_SYNERGIA_INFO = 2010 const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 2020 +const val ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK = 2030 const val ENDPOINT_LIBRUS_MESSAGES_RECEIVED = 3010 const val ENDPOINT_LIBRUS_MESSAGES_SENT = 3020 const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 3030 @@ -212,8 +213,15 @@ val LibrusFeatures = listOf( ENDPOINT_LIBRUS_SYNERGIA_GRADES to LOGIN_METHOD_LIBRUS_SYNERGIA ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)),*/ - - + /** + * Homework - using scrapper. + * Sync only if account has not premium access. + */ + Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf( + ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK to LOGIN_METHOD_LIBRUS_SYNERGIA + ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)).withShouldSync { data -> + !(data as DataLibrus).isPremium + }, /** * Messages inbox - using messages website. diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index 98a6b653..33ce86c2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.librus.* import pl.szczodrzynski.edziennik.api.v2.librus.data.api.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.synergia.LibrusSynergiaHomework import pl.szczodrzynski.edziennik.utils.Utils class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { @@ -83,6 +84,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_days) LibrusApiTeacherFreeDays(data) { onSuccess() } } + ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK -> { + data.startProgress(R.string.edziennik_progress_endpoint_homework) + LibrusSynergiaHomework(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt new file mode 100644 index 00000000..60868dc4 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt @@ -0,0 +1,94 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-22. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.synergia + +import org.jsoup.Jsoup +import pl.szczodrzynski.edziennik.api.v2.POST +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergia +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.events.Event +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.get +import pl.szczodrzynski.edziennik.singleOrNull +import pl.szczodrzynski.edziennik.utils.models.Date + +class LibrusSynergiaHomework(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusSynergia(data) { + companion object { + const val TAG = "LibrusSynergiaHomework" + } + + init { + apiGet(TAG, "moje_zadania", method = POST, parameters = mapOf( + "dataOd" to Date.getToday().stringY_m_d, + "dataDo" to profile!!.getSemesterEnd(profile?.currentSemester ?: 2).stringY_m_d, + "przedmiot" to -1 + + )) { text -> + val doc = Jsoup.parse(text) + + doc.select("table.myHomeworkTable > tbody").firstOrNull()?.also { homeworkTable -> + val homeworkElements = homeworkTable.children() + + val graphElements = doc.select("table[border].center td[align=left] tbody").first().children() + + homeworkElements.forEachIndexed { i, el -> + val elements = el.children() + + val subjectName = elements[0].text().trim() + val subjectId = data.subjectList.singleOrNull { it.longName == subjectName }?.id + ?: -1 + val teacherName = elements[1].text().trim() + val teacherId = data.teacherList.singleOrNull { teacherName == "${it.name} ${it.surname}" }?.id + ?: -1 + val topic = elements[2].text().trim() + val addedDate = Date.fromY_m_d(elements[4].text().trim()).inMillis + val eventDate = Date.fromY_m_d(elements[6].text().trim()) + val id = "/podglad/([0-9]+)'".toRegex().find( + elements[9].select("input").attr("onclick") + )?.get(1)?.toLong() ?: return@forEachIndexed + val startTime = data.lessonList.singleOrNull { + it.weekDay == eventDate.weekDay && it.subjectId == subjectId + }?.startTime + + val moreInfo = graphElements[2 * i + 1].select("td[title]") + .attr("title").trim() + val description = "Treść: (.*)".toRegex(RegexOption.DOT_MATCHES_ALL).find(moreInfo) + ?.get(1)?.replace("".toRegex(), "\n")?.trim() + + val notify = (profile?.empty ?: false) && Date.getToday() < eventDate + + val eventObject = Event( + profileId, + id, + eventDate, + startTime, + "$topic\n$description", + -1, + Event.TYPE_HOMEWORK, + false, + teacherId, + subjectId, + data.teamClass?.id ?: -1 + ) + + data.eventList.add(eventObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_HOMEWORK, + id, + notify, + notify, + addedDate + )) + } + } + + data.setSyncNext(ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK, SYNC_ALWAYS) + onSuccess() + } + } +} From 9fefae3da34fb5dfd11c9a23f9318df3a149079c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Wed, 23 Oct 2019 19:12:28 +0200 Subject: [PATCH 083/691] [APIv2] Improve Librus sync timers. Make saveData update app.profile. Decide when to sync lucky number. --- .../edziennik/api/v2/librus/LibrusFeatures.kt | 3 +- .../api/v2/librus/data/LibrusSynergia.kt | 5 ++- .../api/v2/librus/data/api/LibrusApiMe.kt | 4 +-- .../data/api/LibrusApiTeacherFreeDays.kt | 4 +-- .../data/synergia/LibrusSynergiaHomework.kt | 14 +++++--- .../v2/librus/firstlogin/LibrusFirstLogin.kt | 7 ++-- .../edziennik/api/v2/models/Data.kt | 35 +++++++++++++++++++ .../modules/luckynumber/LuckyNumberDao.java | 2 +- 8 files changed, 55 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt index e92a2661..7e0b26d3 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt @@ -5,7 +5,6 @@ package pl.szczodrzynski.edziennik.api.v2.librus import pl.szczodrzynski.edziennik.api.v2.* -import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData import pl.szczodrzynski.edziennik.api.v2.models.Feature const val ENDPOINT_LIBRUS_API_ME = 1001 @@ -167,7 +166,7 @@ val LibrusFeatures = listOf( */ Feature(LOGIN_TYPE_LIBRUS, FEATURE_LUCKY_NUMBER, listOf( ENDPOINT_LIBRUS_API_LUCKY_NUMBER to LOGIN_METHOD_LIBRUS_API - ), listOf(LOGIN_METHOD_LIBRUS_API)), + ), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data -> data.shouldSyncLuckyNumber() }, /** * Teacher list - using API. */ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt index 9b449ce0..32e1bc78 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt @@ -12,7 +12,6 @@ import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.utils.Utils.d -import java.lang.Exception open class LibrusSynergia(open val data: DataLibrus) { companion object { @@ -25,8 +24,8 @@ open class LibrusSynergia(open val data: DataLibrus) { val profile get() = data.profile - fun apiGet(tag: String, endpoint: String, method: Int = GET, - parameters: Map = emptyMap(), onSuccess: (text: String) -> Unit) { + fun synergiaGet(tag: String, endpoint: String, method: Int = GET, + parameters: Map = emptyMap(), onSuccess: (text: String) -> Unit) { d(tag, "Request: Librus/Synergia - $LIBRUS_SYNERGIA_URL$endpoint") val callback = object : TextCallbackHandler() { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiMe.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiMe.kt index ad1a8efb..8a5df3d0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiMe.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiMe.kt @@ -5,8 +5,8 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ME import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ME import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi class LibrusApiMe(override val data: DataLibrus, @@ -21,7 +21,7 @@ class LibrusApiMe(override val data: DataLibrus, val account = me?.getJsonObject("Account") val user = me?.getJsonObject("User") - data.isPremium = account?.getBoolean("isPremium") == true || account?.getBoolean("isPremiumDemo") == true + data.isPremium = account?.getBoolean("IsPremium") == true || account?.getBoolean("IsPremiumDemo") == true val isParent = account?.getInt("GroupId") == 5 data.profile?.accountNameLong = diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt index d4791b39..3be6fabb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt @@ -5,10 +5,10 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi -import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsence import pl.szczodrzynski.edziennik.utils.models.Date @@ -60,7 +60,7 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus, )) } - data.setSyncNext(ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS, SYNC_ALWAYS) + data.setSyncNext(ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS, 6*HOUR, DRAWER_ITEM_AGENDA) onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt index 60868dc4..5f63bcad 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt @@ -5,11 +5,12 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.synergia import org.jsoup.Jsoup +import pl.szczodrzynski.edziennik.HOUR +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK import pl.szczodrzynski.edziennik.api.v2.POST import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergia -import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.events.Event import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.get @@ -22,8 +23,12 @@ class LibrusSynergiaHomework(override val data: DataLibrus, val onSuccess: () -> } init { - apiGet(TAG, "moje_zadania", method = POST, parameters = mapOf( - "dataOd" to Date.getToday().stringY_m_d, + synergiaGet(TAG, "moje_zadania", method = POST, parameters = mapOf( + "dataOd" to + if (data.profile?.empty != false) + profile!!.getSemesterStart(1).stringY_m_d + else + Date.getToday().stringY_m_d, "dataDo" to profile!!.getSemesterEnd(profile?.currentSemester ?: 2).stringY_m_d, "przedmiot" to -1 @@ -87,7 +92,8 @@ class LibrusSynergiaHomework(override val data: DataLibrus, val onSuccess: () -> } } - data.setSyncNext(ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK, SYNC_ALWAYS) + // because this requires a synergia login (2 more requests) sync this every two hours or if explicit :D + data.setSyncNext(ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK, 2*HOUR, DRAWER_ITEM_HOMEWORK) onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt index 5a92178c..9edb39b8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt @@ -1,6 +1,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.firstlogin import org.greenrobot.eventbus.EventBus +import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.ERROR_NO_STUDENTS_IN_ACCOUNT import pl.szczodrzynski.edziennik.api.v2.LIBRUS_ACCOUNTS_URL import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_EMAIL @@ -14,10 +15,6 @@ import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.data.api.AppError.CODE_LIBRUS_DISCONNECTED import pl.szczodrzynski.edziennik.data.api.AppError.CODE_SYNERGIA_NOT_ACTIVATED import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile -import pl.szczodrzynski.edziennik.getInt -import pl.szczodrzynski.edziennik.getJsonArray -import pl.szczodrzynski.edziennik.getLong -import pl.szczodrzynski.edziennik.getString class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) { companion object { @@ -81,7 +78,7 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) { newProfile.putStudentData("accountId", accountIds[index]) newProfile.putStudentData("accountLogin", accountLogins[index]) newProfile.putStudentData("accountToken", accountTokens[index]) - newProfile.putStudentData("accountTokenTime", accountDataTime ?: 0) + newProfile.putStudentData("accountTokenTime", (accountDataTime ?: 0) + DAY) profileList.add(newProfile) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index d573068c..5baa62bb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -197,6 +197,37 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) db.profileDao().add(profile) db.loginStoreDao().add(loginStore) + if (profile.id == app.profile?.id) { + app.profile.apply { + name = profile.name + subname = profile.subname + syncEnabled = profile.syncEnabled + loggedIn = profile.loggedIn + empty = profile.empty + archived = profile.archived + studentNameLong = profile.studentNameLong + studentNameShort = profile.studentNameShort + studentNumber = profile.studentNumber + studentData = profile.studentData + accountNameLong = profile.accountNameLong + yearAverageMode = profile.yearAverageMode + currentSemester = profile.currentSemester + attendancePercentage = profile.attendancePercentage + dateSemester1Start = profile.dateSemester1Start + dateSemester2Start = profile.dateSemester2Start + dateYearEnd = profile.dateYearEnd + luckyNumberEnabled = profile.luckyNumberEnabled + luckyNumber = profile.luckyNumber + luckyNumberDate = profile.luckyNumberDate + lastFullSync = profile.lastFullSync + lastReceiversSync = profile.lastReceiversSync + } + } + if (loginStore.id == app.profile?.loginStoreId) { + app.loginStore = loginStore.data + app.profile.loginStoreData = loginStore.data + } + db.endpointTimerDao().addAll(endpointTimers) db.teacherDao().clear(profileId) db.teacherDao().addAll(teacherList.values()) @@ -293,6 +324,10 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) saveData() } + fun shouldSyncLuckyNumber(): Boolean { + return (db.luckyNumberDao().getNearestFutureNow(profileId, Date.getToday()) ?: -1) == -1 + } + fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) { val code = when (throwable) { is UnknownHostException, is SSLException, is InterruptedIOException -> CODE_NO_INTERNET diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumberDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumberDao.java index ced9b474..fa80f6f6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumberDao.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/luckynumber/LuckyNumberDao.java @@ -36,7 +36,7 @@ public abstract class LuckyNumberDao { public abstract LuckyNumber getByDateNow(int profileId, Date date); @Nullable - @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate > :date ORDER BY luckyNumberDate ASC LIMIT 1") + @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate >= :date ORDER BY luckyNumberDate DESC LIMIT 1") public abstract LuckyNumber getNearestFutureNow(int profileId, Date date); @RawQuery(observedEntities = {LuckyNumber.class}) From 7822810b912dbd810235361eb13ff130c125afc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Wed, 23 Oct 2019 19:13:54 +0200 Subject: [PATCH 084/691] [APIv2] Implement APIv2 in first login activity. --- .../edziennik/api/v2/ApiService.kt | 15 +- .../api/v2/events/SyncStartedEvent.kt | 4 +- .../events/requests/SyncProfileListRequest.kt | 7 + .../ui/modules/login/LoginSyncFragment.java | 227 ------------------ .../ui/modules/login/LoginSyncFragment.kt | 164 +++++++++++++ .../main/res/layout/fragment_login_sync.xml | 1 + 6 files changed, 189 insertions(+), 229 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileListRequest.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.java create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt index d5545fb0..64086764 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt @@ -213,7 +213,7 @@ class ApiService : Service() { notification.setCurrentTask(taskRunningId, taskProfileName).post() // post an event - EventBus.getDefault().post(SyncStartedEvent(taskProfileId)) + EventBus.getDefault().post(SyncStartedEvent(taskProfileId, profile)) edziennikInterface = when (loginStore.type) { LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback) @@ -259,6 +259,19 @@ class ApiService : Service() { sync() } + @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) + fun onSyncProfileListRequest(request: SyncProfileListRequest) { + EventBus.getDefault().removeStickyEvent(request) + Log.d(TAG, request.toString()) + + request.profileList.forEach { id -> + taskQueue += SyncProfileRequest(id, null).apply { + taskId = ++taskMaximumId + } + } + sync() + } + @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) fun onSyncProfileRequest(request: SyncProfileRequest) { EventBus.getDefault().removeStickyEvent(request) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncStartedEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncStartedEvent.kt index 86e5bb9d..471dc413 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncStartedEvent.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncStartedEvent.kt @@ -4,4 +4,6 @@ package pl.szczodrzynski.edziennik.api.v2.events -class SyncStartedEvent(val profileId: Int) \ No newline at end of file +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile + +class SyncStartedEvent(val profileId: Int, val profile: Profile? = null) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileListRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileListRequest.kt new file mode 100644 index 00000000..9daaac63 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileListRequest.kt @@ -0,0 +1,7 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-23. + */ + +package pl.szczodrzynski.edziennik.api.v2.events.requests + +class SyncProfileListRequest(val profileList: List) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.java deleted file mode 100644 index 3b8c436e..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.java +++ /dev/null @@ -1,227 +0,0 @@ -package pl.szczodrzynski.edziennik.ui.modules.login; - - -import android.content.Context; -import android.os.AsyncTask; -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.databinding.DataBindingUtil; -import androidx.fragment.app.Fragment; - -import android.text.Html; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import java.util.ArrayList; -import java.util.List; - -import androidx.navigation.NavController; -import androidx.navigation.Navigation; -import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.data.api.AppError; -import pl.szczodrzynski.edziennik.data.api.interfaces.SyncCallback; -import pl.szczodrzynski.edziennik.databinding.FragmentLoginSyncBinding; -import pl.szczodrzynski.edziennik.data.db.modules.events.EventType; -import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore; -import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; -import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; -import pl.szczodrzynski.edziennik.sync.SyncJob; -import pl.szczodrzynski.edziennik.sync.SyncService; - -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_CLASS_EVENT; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_DEFAULT; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_EXAM; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_EXCURSION; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_HOMEWORK; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_INFORMATION; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_PROJECT; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_PT_MEETING; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_READING; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_SHORT_QUIZ; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_CLASS_EVENT; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_DEFAULT; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_ESSAY; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_EXAM; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_EXCURSION; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_INFORMATION; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_PROJECT; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_PT_MEETING; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_READING; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_SHORT_QUIZ; -import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.REGISTRATION_DISABLED; -import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.REGISTRATION_ENABLED; -import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.REGISTRATION_UNSPECIFIED; -import static pl.szczodrzynski.edziennik.utils.Utils.d; - -public class LoginSyncFragment extends Fragment { - - private App app; - private NavController nav; - private FragmentLoginSyncBinding b; - private static final String TAG = "LoginSyncFragment"; - private List profileNameList = new ArrayList<>(); - private int profileIndex = 0; - - public LoginSyncFragment() { } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Inflate the layout for this fragment - if (getActivity() != null) { - app = (App) getActivity().getApplicationContext(); - nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment); - } - else { - return null; - } - b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_sync, container, false); - return b.getRoot(); - } - - private void begin() { - AsyncTask.execute(() -> { - if (getActivity() == null) { - return; - } - int profileId = app.profileLastId()+1; - final int firstProfileId = profileId; - int loginStoreId = profileId; - // profileId contains the first ID free to use - - for (LoginProfileObject profileObject: LoginActivity.profileObjects) { - int subIndex = 0; - for (Profile profile: profileObject.profileList) { - if (profileObject.selectedList.get(subIndex)) { - saveProfile( - profile, - profileObject.loginStore, - profileId, - loginStoreId - ); - profileNameList.add(profile.getName()); - profileId++; - } - subIndex++; - } - loginStoreId = profileId; - } - - for (Profile profile: app.db.profileDao().getAllNow()) { - d(TAG, profile.toString()); - } - for (LoginStore loginStore: app.db.loginStoreDao().getAllNow()) { - d(TAG, loginStore.toString()); - } - - if (app.appConfig.loginFinished) { - LoginFinishFragment.firstRun = false; - } - else { - LoginFinishFragment.firstRun = true; - app.appConfig.loginFinished = true; - app.saveConfig("loginFinished"); - } - LoginFinishFragment.firstProfileId = firstProfileId; - - getActivity().runOnUiThread(() -> { - profileIndex = 0; - b.loginSyncSubtitle1.setText(Html.fromHtml(getString(R.string.login_sync_subtitle_1_format, profileNameList.size() > profileIndex ? profileNameList.get(profileIndex) : " "))); - }); - SyncJob.run(app, firstProfileId, -1); - }); - } - - private void saveProfile(Profile profile, LoginStore loginStore, int profileId, int loginStoreId) { - profile.setRegistration(REGISTRATION_UNSPECIFIED); - if (getArguments() != null) { - if (getArguments().getBoolean("registrationAllowed", false)) { - profile.setRegistration(REGISTRATION_ENABLED); - } - else { - profile.setRegistration(REGISTRATION_DISABLED); - } - } - profile.setId(profileId); - profile.setLoginStoreId(loginStoreId); - loginStore.id = loginStoreId; - List typeList = new ArrayList<>(); - typeList.add(new EventType(profileId, TYPE_HOMEWORK, getString(R.string.event_type_homework), COLOR_HOMEWORK)); - typeList.add(new EventType(profileId, TYPE_DEFAULT, getString(R.string.event_other), COLOR_DEFAULT)); - typeList.add(new EventType(profileId, TYPE_EXAM, getString(R.string.event_exam), COLOR_EXAM)); - typeList.add(new EventType(profileId, TYPE_SHORT_QUIZ, getString(R.string.event_short_quiz), COLOR_SHORT_QUIZ)); - typeList.add(new EventType(profileId, TYPE_ESSAY, getString(R.string.event_essay), COLOR_SHORT_QUIZ)); - typeList.add(new EventType(profileId, TYPE_PROJECT, getString(R.string.event_project), COLOR_PROJECT)); - typeList.add(new EventType(profileId, TYPE_PT_MEETING, getString(R.string.event_pt_meeting), COLOR_PT_MEETING)); - typeList.add(new EventType(profileId, TYPE_EXCURSION, getString(R.string.event_excursion), COLOR_EXCURSION)); - typeList.add(new EventType(profileId, TYPE_READING, getString(R.string.event_reading), COLOR_READING)); - typeList.add(new EventType(profileId, TYPE_CLASS_EVENT, getString(R.string.event_class_event), COLOR_CLASS_EVENT)); - typeList.add(new EventType(profileId, TYPE_INFORMATION, getString(R.string.event_information), COLOR_INFORMATION)); - app.db.eventTypeDao().addAll(typeList); - app.profileSaveFull(profile, loginStore); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - assert getContext() != null; - assert getActivity() != null; - - LoginActivity.error = null; - - SyncService.customCallback = new SyncCallback() { - @Override public void onLoginFirst(List profileList, LoginStore loginStore) { } - - @Override - public void onSuccess(Context activityContext, ProfileFull profileFull) { - if (getActivity() == null) - return; - getActivity().runOnUiThread(() -> { - if (profileFull != null) { - // a profile is finished - profileIndex++; - b.loginSyncSubtitle1.setText(Html.fromHtml(getString(R.string.login_sync_subtitle_1_format, profileIndex < profileNameList.size() ? profileNameList.get(profileIndex) : profileNameList.get(profileNameList.size()-1)))); - } - else { - // all profiles are finished - nav.navigate(R.id.loginFinishFragment, null , LoginActivity.navOptions); - } - }); - } - - @Override - public void onError(Context activityContext, AppError error) { - LoginActivity.error = error; - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - nav.navigate(R.id.loginSyncErrorFragment, null, LoginActivity.navOptions); - }); - } - } - - @Override - public void onProgress(int progressStep) { - if (getActivity() == null) - return; - getActivity().runOnUiThread(() -> { - b.loginSyncProgressBar.setMax(SyncService.maxProgress); - b.loginSyncProgressBar.setProgress(SyncService.progress); - }); - } - - @Override - public void onActionStarted(int stringResId) { - if (getActivity() == null) - return; - getActivity().runOnUiThread(() -> { - b.loginSyncSubtitle2.setText(getString(R.string.login_sync_subtitle_2_format, getString(stringResId))); - }); - } - }; - - begin(); - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt new file mode 100644 index 00000000..006c8e75 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt @@ -0,0 +1,164 @@ +package pl.szczodrzynski.edziennik.ui.modules.login + + +import android.os.AsyncTask +import android.os.Bundle +import android.text.Html +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.navigation.NavController +import androidx.navigation.Navigation +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.ApiService +import pl.szczodrzynski.edziennik.api.v2.events.SyncErrorEvent +import pl.szczodrzynski.edziennik.api.v2.events.SyncFinishedEvent +import pl.szczodrzynski.edziennik.api.v2.events.SyncProgressEvent +import pl.szczodrzynski.edziennik.api.v2.events.SyncStartedEvent +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileListRequest +import pl.szczodrzynski.edziennik.data.db.modules.events.Event.* +import pl.szczodrzynski.edziennik.data.db.modules.events.EventType +import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.REGISTRATION_DISABLED +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.REGISTRATION_ENABLED +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.REGISTRATION_UNSPECIFIED +import pl.szczodrzynski.edziennik.databinding.FragmentLoginSyncBinding + +class LoginSyncFragment : Fragment() { + + private lateinit var app: App + private lateinit var activity: LoginActivity + private lateinit var b: FragmentLoginSyncBinding + private val nav: NavController by lazy { Navigation.findNavController(activity, R.id.nav_host_fragment) } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + activity = (getActivity() as LoginActivity?) ?: return null + if (context == null) + return null + app = activity.application as App + b = FragmentLoginSyncBinding.inflate(inflater) + return b.root + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onSyncStartedEvent(event: SyncStartedEvent) { + b.loginSyncSubtitle1.text = Html.fromHtml(getString(R.string.login_sync_subtitle_1_format, event.profile?.name ?: "")) + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onSyncFinishedEvent(event: SyncFinishedEvent) { + nav.navigate(R.id.loginFinishFragment, null, LoginActivity.navOptions) + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onSyncProgressEvent(event: SyncProgressEvent) { + b.loginSyncProgressBar.progress = event.progress + b.loginSyncSubtitle2.text = event.progressRes?.let { getString(it) } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onSyncErrorEvent(event: SyncErrorEvent) { + LoginActivity.error = event.error.toAppError() + nav.navigate(R.id.loginSyncErrorFragment, null, LoginActivity.navOptions) + } + + private fun begin() { + AsyncTask.execute { + var profileId = app.profileLastId() + 1 + val firstProfileId = profileId + var loginStoreId = profileId + // profileId contains the first ID free to use + + val profileIds = mutableListOf() + + for (profileObject in LoginActivity.profileObjects) { + for ((subIndex, profile) in profileObject.profileList.withIndex()) { + if (profileObject.selectedList[subIndex]) { + saveProfile( + profile, + profileObject.loginStore, + profileId, + loginStoreId + ) + profileIds += profileId + profileId++ + } + } + loginStoreId = profileId + } + + /*for (profile in app.db.profileDao().allNow) { + d(TAG, profile.toString()) + } + for (loginStore in app.db.loginStoreDao().allNow) { + d(TAG, loginStore.toString()) + }*/ + + if (app.appConfig.loginFinished) { + LoginFinishFragment.firstRun = false + } else { + LoginFinishFragment.firstRun = true + app.appConfig.loginFinished = true + app.saveConfig("loginFinished") + } + LoginFinishFragment.firstProfileId = firstProfileId + + ApiService.start(activity) + EventBus.getDefault().postSticky(SyncProfileListRequest(profileIds)) + } + } + + private fun saveProfile(profile: Profile, loginStore: LoginStore, profileId: Int, loginStoreId: Int) { + profile.registration = REGISTRATION_UNSPECIFIED + if (arguments != null) { + if (arguments!!.getBoolean("registrationAllowed", false)) { + profile.registration = REGISTRATION_ENABLED + } else { + profile.registration = REGISTRATION_DISABLED + } + } + profile.id = profileId + profile.loginStoreId = loginStoreId + loginStore.id = loginStoreId + val typeList = listOf( + EventType(profileId, TYPE_HOMEWORK, getString(R.string.event_type_homework), COLOR_HOMEWORK), + EventType(profileId, TYPE_DEFAULT, getString(R.string.event_other), COLOR_DEFAULT), + EventType(profileId, TYPE_EXAM, getString(R.string.event_exam), COLOR_EXAM), + EventType(profileId, TYPE_SHORT_QUIZ, getString(R.string.event_short_quiz), COLOR_SHORT_QUIZ), + EventType(profileId, TYPE_ESSAY, getString(R.string.event_essay), COLOR_SHORT_QUIZ), + EventType(profileId, TYPE_PROJECT, getString(R.string.event_project), COLOR_PROJECT), + EventType(profileId, TYPE_PT_MEETING, getString(R.string.event_pt_meeting), COLOR_PT_MEETING), + EventType(profileId, TYPE_EXCURSION, getString(R.string.event_excursion), COLOR_EXCURSION), + EventType(profileId, TYPE_READING, getString(R.string.event_reading), COLOR_READING), + EventType(profileId, TYPE_CLASS_EVENT, getString(R.string.event_class_event), COLOR_CLASS_EVENT), + EventType(profileId, TYPE_INFORMATION, getString(R.string.event_information), COLOR_INFORMATION) + ) + app.db.eventTypeDao().addAll(typeList) + app.profileSaveFull(profile, loginStore) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + if (!isAdded) + return + + LoginActivity.error = null + + begin() + } + + override fun onStart() { + EventBus.getDefault().register(this) + super.onStart() + } + + override fun onStop() { + super.onStop() + EventBus.getDefault().unregister(this) + } +} diff --git a/app/src/main/res/layout/fragment_login_sync.xml b/app/src/main/res/layout/fragment_login_sync.xml index ab0cce52..a0b072c6 100644 --- a/app/src/main/res/layout/fragment_login_sync.xml +++ b/app/src/main/res/layout/fragment_login_sync.xml @@ -51,6 +51,7 @@ android:layout_marginTop="8dp" android:layout_marginRight="24dp" android:layout_marginBottom="8dp" + android:max="100" android:indeterminate="false" /> From 36c810fdbecc7647fcf5ed10c5c19f83cddcb36f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Wed, 23 Oct 2019 21:11:39 +0200 Subject: [PATCH 085/691] [APIv2/Librus] Add Virtual classes, Users, Subjects. --- .../edziennik/api/v2/librus/DataLibrus.kt | 5 +++ .../v2/librus/data/api/LibrusApiClasses.kt | 8 ++-- .../v2/librus/data/api/LibrusApiSubjects.kt | 40 +++++++++++++++++++ .../api/v2/librus/data/api/LibrusApiUsers.kt | 35 ++++++++++++++++ .../data/api/LibrusApiVirtualClasses.kt | 36 +++++++++++++++++ 5 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiSubjects.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUsers.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiVirtualClasses.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/DataLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/DataLibrus.kt index 602884eb..2f2a0d10 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/DataLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/DataLibrus.kt @@ -210,4 +210,9 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app get() { mSchoolName = mSchoolName ?: profile?.getStudentData("schoolName", null); return mSchoolName } set(value) { profile?.putStudentData("schoolName", value) ?: return; mSchoolName = value } + private var mUnitId: Long? = null + var unitId: Long + get() { mUnitId = mUnitId ?: profile?.getStudentData("unitId", 0L); return mUnitId ?: 0L } + set(value) { profile?.putStudentData("unitId", value) ?: return; mUnitId = value } + } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClasses.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClasses.kt index 3f58300d..d41206b4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClasses.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClasses.kt @@ -4,11 +4,14 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api -import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.DAY import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_CLASSES import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.db.modules.teams.Team +import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.getLong +import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.utils.models.Date class LibrusApiClasses(override val data: DataLibrus, @@ -37,7 +40,7 @@ class LibrusApiClasses(override val data: DataLibrus, data.teamList.put(id, teamObject) - val unitId = studentClass.getJsonObject("Unit").getLong("Id") + data.unitId = studentClass.getJsonObject("Unit").getLong("Id") ?: 0L profile?.apply { dateSemester1Start = Date.fromY_m_d(studentClass.getString("BeginSchoolYear") @@ -46,7 +49,6 @@ class LibrusApiClasses(override val data: DataLibrus, ?: return@apply) dateYearEnd = Date.fromY_m_d(studentClass.getString("EndSchoolYear") ?: return@apply) - unitId?.let { putStudentData("unitId", it) } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiSubjects.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiSubjects.kt new file mode 100644 index 00000000..f595bf09 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiSubjects.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-23. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.DAY +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_SUBJECTS +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject +import pl.szczodrzynski.edziennik.getJsonArray +import pl.szczodrzynski.edziennik.getLong +import pl.szczodrzynski.edziennik.getString + +class LibrusApiSubjects(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiSubjects" + } + + init { + apiGet(TAG, "Subjects") { json -> + json.getJsonArray("Subjects")?.forEach { subjectEl -> + val subject = subjectEl.asJsonObject + + val id = subject.getLong("Id") ?: return@forEach + val longName = subject.getString("Name") ?: "" + val shortName = subject.getString("Short") ?: "" + + data.subjectList.put(id, Subject(profileId, id, longName, shortName)) + } + + data.subjectList.put(1, Subject(profileId, 1, "Zachowanie", "zach")) + + data.setSyncNext(ENDPOINT_LIBRUS_API_SUBJECTS, 4*DAY) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUsers.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUsers.kt new file mode 100644 index 00000000..5818cd59 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUsers.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-23. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_USERS +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher + +class LibrusApiUsers(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiUsers" + } + + init { + apiGet(TAG, "Users") { json -> + json.getJsonArray("Users")?.forEach { userEl -> + val user = userEl.asJsonObject + + val id = user.getLong("Id") ?: return@forEach + val firstName = user.getString("FirstName")?.fixWhiteSpaces() ?: "" + val lastName = user.getString("LastName")?.fixWhiteSpaces() ?: "" + + data.teacherList.put(id, Teacher(profileId, id, firstName, lastName)) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_USERS, 4*DAY) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiVirtualClasses.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiVirtualClasses.kt new file mode 100644 index 00000000..8d63f331 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiVirtualClasses.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-23. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.teams.Team + +class LibrusApiVirtualClasses(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiVirtualClasses" + } + + init { + apiGet(TAG, "VirtualClasses") { json -> + json.getJsonArray("VirtualClasses")?.forEach { virtualClassEl -> + val virtualClass = virtualClassEl.asJsonObject + + val id = virtualClass.getLong("Id") ?: return@forEach + val name = virtualClass.getString("Name") ?: "" + val teacherId = virtualClass.getJsonObject("Teacher")?.getLong("Id") ?: -1 + val code = "${data.schoolName}:$name" + + data.teamList.put(id, Team(profileId, id, name, 2, code, teacherId)) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES, 4*DAY) + onSuccess() + } + } +} From bbf8f05d3cfdb9cf66959bac76ed1d5b3fedb201 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Wed, 23 Oct 2019 22:01:23 +0200 Subject: [PATCH 086/691] [APIv2/Vulcan] Add getting notices --- .../api/v2/vulcan/data/VulcanData.kt | 5 ++ .../v2/vulcan/data/api/VulcanApiNotices.kt | 58 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 64 insertions(+) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiNotices.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt index fec47ca2..e95f87d6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt @@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.api.v2.vulcan.* import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiDictionaries import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiEvents import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiGrades +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiNotices import pl.szczodrzynski.edziennik.utils.Utils class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { @@ -55,6 +56,10 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_homework) VulcanApiEvents(data, isHomework = true) { onSuccess() } } + ENDPOINT_VULCAN_API_NOTICES -> { + data.startProgress(R.string.edziennik_progress_endpoint_notices) + VulcanApiNotices(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiNotices.kt new file mode 100644 index 00000000..c891cbd2 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiNotices.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-23 + */ + +package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api + +import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_NOTICES +import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_NOTICES +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice +import pl.szczodrzynski.edziennik.getJsonArray +import pl.szczodrzynski.edziennik.getLong +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.utils.models.Date + +class VulcanApiNotices(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { + companion object { + const val TAG = "VulcanApi" + } + + init { + apiGet(TAG, VULCAN_API_ENDPOINT_NOTICES) { json, _ -> + json.getJsonArray("Data")?.forEach { noticeEl -> + val notice = noticeEl.asJsonObject + + val id = notice.getLong("Id") ?: return@forEach + val text = notice.getString("TrescUwagi") ?: return@forEach + val teacherId = notice.getLong("IdPracownik") ?: -1 + val addedDate = Date.fromY_m_d(notice.getString("DataWpisuTekst")).inMillis + + val noticeObject = Notice( + profileId, + id, + text, + profile!!.currentSemester, + Notice.TYPE_NEUTRAL, + teacherId + ) + + data.noticeList.add(noticeObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_NOTICE, + id, + profile?.empty ?: false, + profile?.empty ?: false, + addedDate + )) + } + + data.setSyncNext(ENDPOINT_VULCAN_API_NOTICES, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1fb474fa..3deb4994 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -954,4 +954,5 @@ Pobieranie słowników... Synchronizuję... [%d%%] %s + Pobieranie uwag... From e2150e3018158a07635a06fa19eef98c23d8531c Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Wed, 23 Oct 2019 22:53:06 +0200 Subject: [PATCH 087/691] [APIv2/Librus] Add Synergia endpoint template --- .../data/synergia/LibrusSynergiaTemplate.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaTemplate.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaTemplate.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaTemplate.kt new file mode 100644 index 00000000..1cc0097e --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaTemplate.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-23 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.synergia + +import org.jsoup.Jsoup +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergia +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS + +class LibrusSynergiaTemplate(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusSynergia(data) { + companion object { + const val TAG = "LibrusSynergia" + } + + init { + /* synergiaGet(TAG, "") { text -> + val doc = Jsoup.parse(text) + + data.setSyncNext(ENDPOINT_LIBRUS_SYNERGIA_, SYNC_ALWAYS) + onSuccess() + } */ + } +} From ff7f0151464e78d3c5875ebf57a12f0efb8d9db2 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Wed, 23 Oct 2019 22:53:25 +0200 Subject: [PATCH 088/691] [APIv2/Librus] Add getting student info (student number) using Synergia --- .../api/v2/librus/data/LibrusData.kt | 5 +++ .../data/synergia/LibrusSynergiaInfo.kt | 34 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaInfo.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index 33ce86c2..0302dc2a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -8,6 +8,7 @@ import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.librus.* import pl.szczodrzynski.edziennik.api.v2.librus.data.api.* import pl.szczodrzynski.edziennik.api.v2.librus.data.synergia.LibrusSynergiaHomework +import pl.szczodrzynski.edziennik.api.v2.librus.data.synergia.LibrusSynergiaInfo import pl.szczodrzynski.edziennik.utils.Utils class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { @@ -88,6 +89,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_homework) LibrusSynergiaHomework(data) { onSuccess() } } + ENDPOINT_LIBRUS_SYNERGIA_INFO -> { + data.startProgress(R.string.edziennik_progress_endpoint_student_info) + LibrusSynergiaInfo(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaInfo.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaInfo.kt new file mode 100644 index 00000000..7777cf6a --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaInfo.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-23 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.synergia + +import org.jsoup.Jsoup +import pl.szczodrzynski.edziennik.MONTH +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_SYNERGIA_INFO +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergia + +class LibrusSynergiaInfo(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusSynergia(data) { + companion object { + const val TAG = "LibrusSynergiaInfo" + } + + init { + synergiaGet(TAG, "informacja") { text -> + val doc = Jsoup.parse(text) + + doc.select("table.form tbody").firstOrNull()?.children()?.also { info -> + val studentNumber = info[2].select("td").text().trim().toIntOrNull() + + studentNumber?.also { + data.profile?.studentNumber = it + } + } + + data.setSyncNext(ENDPOINT_LIBRUS_SYNERGIA_INFO, MONTH) + onSuccess() + } + } +} From 05ce790587c0266f330e286a88f702f83f23cb72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Wed, 23 Oct 2019 23:00:20 +0200 Subject: [PATCH 089/691] [APIv2] Add Librus Units. Add Classroom, NoticeType, AttendanceType entities. --- .../edziennik/api/v2/librus/DataLibrus.kt | 19 +++++++- .../api/v2/librus/data/api/LibrusApiUnits.kt | 44 +++++++++++++++++++ .../edziennik/data/db/AppDb.java | 42 +++++++++++++++++- .../db/modules/attendance/AttendanceType.kt | 23 ++++++++++ .../modules/attendance/AttendanceTypeDao.kt | 25 +++++++++++ .../data/db/modules/classrooms/Classroom.kt | 19 ++++++++ .../db/modules/classrooms/ClassroomDao.kt | 25 +++++++++++ .../data/db/modules/notices/NoticeType.kt | 19 ++++++++ .../data/db/modules/notices/NoticeTypeDao.kt | 25 +++++++++++ 9 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUnits.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/attendance/AttendanceType.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/attendance/AttendanceTypeDao.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/classrooms/Classroom.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/classrooms/ClassroomDao.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notices/NoticeType.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notices/NoticeTypeDao.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/DataLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/DataLibrus.kt index 2f2a0d10..600453b1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/DataLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/DataLibrus.kt @@ -215,4 +215,21 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app get() { mUnitId = mUnitId ?: profile?.getStudentData("unitId", 0L); return mUnitId ?: 0L } set(value) { profile?.putStudentData("unitId", value) ?: return; mUnitId = value } -} + private var mStartPointsSemester1: Int? = null + var startPointsSemester1: Int + get() { mStartPointsSemester1 = mStartPointsSemester1 ?: profile?.getStudentData("startPointsSemester1", 0); return mStartPointsSemester1 ?: 0 } + set(value) { profile?.putStudentData("startPointsSemester1", value) ?: return; mStartPointsSemester1 = value } + private var mStartPointsSemester2: Int? = null + var startPointsSemester2: Int + get() { mStartPointsSemester2 = mStartPointsSemester2 ?: profile?.getStudentData("startPointsSemester2", 0); return mStartPointsSemester2 ?: 0 } + set(value) { profile?.putStudentData("startPointsSemester2", value) ?: return; mStartPointsSemester2 = value } + + private var mEnablePointGrades: Boolean? = null + var enablePointGrades: Boolean + get() { mEnablePointGrades = mEnablePointGrades ?: profile?.getStudentData("enablePointGrades", true); return mEnablePointGrades ?: true } + set(value) { profile?.putStudentData("enablePointGrades", value) ?: return; mEnablePointGrades = value } + private var mEnableDescriptiveGrades: Boolean? = null + var enableDescriptiveGrades: Boolean + get() { mEnableDescriptiveGrades = mEnableDescriptiveGrades ?: profile?.getStudentData("enableDescriptiveGrades", true); return mEnableDescriptiveGrades ?: true } + set(value) { profile?.putStudentData("enableDescriptiveGrades", value) ?: return; mEnableDescriptiveGrades = value } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUnits.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUnits.kt new file mode 100644 index 00000000..78d10103 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUnits.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-23. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_UNITS +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi + +class LibrusApiUnits(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiUnits" + } + + init { run { + if (data.unitId == 0L) { + data.setSyncNext(ENDPOINT_LIBRUS_API_UNITS, 12 * DAY) + onSuccess() + return@run + } + + apiGet(TAG, "Units") { json -> + val units = json.getJsonArray("Units") + units?.singleOrNull { it.asJsonObject.getLong("Id") == data.unitId }?.also { unitEl -> + val unit = unitEl.asJsonObject + val startPoints = unit.getJsonObject("BehaviourGradesSettings")?.getJsonObject("StartPoints") + startPoints?.apply { + data.startPointsSemester1 = getInt("Semester1", defaultValue = 0) + data.startPointsSemester2 = getInt("Semester2", defaultValue = data.startPointsSemester1) + } + unit.getJsonObject("GradesSettings")?.apply { + data.enablePointGrades = getBoolean("PointGradesEnabled", true) + data.enableDescriptiveGrades = getBoolean("DescriptiveGradesEnabled", true) + } + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_UNITS, 7 * DAY) + onSuccess() + } + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java index 6d850b7b..d43d5301 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java @@ -22,6 +22,10 @@ import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer; import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimerDao; import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance; import pl.szczodrzynski.edziennik.data.db.modules.attendance.AttendanceDao; +import pl.szczodrzynski.edziennik.data.db.modules.attendance.AttendanceType; +import pl.szczodrzynski.edziennik.data.db.modules.attendance.AttendanceTypeDao; +import pl.szczodrzynski.edziennik.data.db.modules.classrooms.Classroom; +import pl.szczodrzynski.edziennik.data.db.modules.classrooms.ClassroomDao; import pl.szczodrzynski.edziennik.data.db.modules.debuglog.DebugLog; import pl.szczodrzynski.edziennik.data.db.modules.debuglog.DebugLogDao; import pl.szczodrzynski.edziennik.data.db.modules.events.Event; @@ -52,6 +56,8 @@ import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata; import pl.szczodrzynski.edziennik.data.db.modules.metadata.MetadataDao; import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice; import pl.szczodrzynski.edziennik.data.db.modules.notices.NoticeDao; +import pl.szczodrzynski.edziennik.data.db.modules.notices.NoticeType; +import pl.szczodrzynski.edziennik.data.db.modules.notices.NoticeTypeDao; import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification; import pl.szczodrzynski.edziennik.data.db.modules.notification.NotificationDao; import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; @@ -94,7 +100,10 @@ import pl.szczodrzynski.edziennik.utils.models.Date; EndpointTimer.class, LessonRange.class, Notification.class, - Metadata.class}, version = 61) + Classroom.class, + NoticeType.class, + AttendanceType.class, + Metadata.class}, version = 62) @TypeConverters({ ConverterTime.class, ConverterDate.class, @@ -129,6 +138,9 @@ public abstract class AppDb extends RoomDatabase { public abstract EndpointTimerDao endpointTimerDao(); public abstract LessonRangeDao lessonRangeDao(); public abstract NotificationDao notificationDao(); + public abstract ClassroomDao classroomDao(); + public abstract NoticeTypeDao noticeTypeDao(); + public abstract AttendanceTypeDao attendanceTypeDao(); public abstract MetadataDao metadataDao(); private static volatile AppDb INSTANCE; @@ -684,6 +696,31 @@ public abstract class AppDb extends RoomDatabase { database.execSQL("ALTER TABLE teacherAbsence ADD COLUMN teacherAbsenceName TEXT DEFAULT NULL"); } }; + private static final Migration MIGRATION_61_62 = new Migration(61, 62) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("CREATE TABLE IF NOT EXISTS classrooms (\n" + + " profileId INTEGER NOT NULL,\n" + + " id INTEGER NOT NULL,\n" + + " name TEXT NOT NULL,\n" + + " PRIMARY KEY(profileId, id)\n" + + ")"); + database.execSQL("CREATE TABLE IF NOT EXISTS noticeTypes (\n" + + " profileId INTEGER NOT NULL,\n" + + " id INTEGER NOT NULL,\n" + + " name TEXT NOT NULL,\n" + + " PRIMARY KEY(profileId, id)\n" + + ")"); + database.execSQL("CREATE TABLE IF NOT EXISTS attendanceTypes (\n" + + " profileId INTEGER NOT NULL,\n" + + " id INTEGER NOT NULL,\n" + + " name TEXT NOT NULL,\n" + + " type INTEGER NOT NULL,\n" + + " color INTEGER NOT NULL,\n" + + " PRIMARY KEY(profileId, id)\n" + + ")"); + } + }; public static AppDb getDatabase(final Context context) { @@ -742,7 +779,8 @@ public abstract class AppDb extends RoomDatabase { MIGRATION_57_58, MIGRATION_58_59, MIGRATION_59_60, - MIGRATION_60_61 + MIGRATION_60_61, + MIGRATION_61_62 ) .allowMainThreadQueries() //.fallbackToDestructiveMigration() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/attendance/AttendanceType.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/attendance/AttendanceType.kt new file mode 100644 index 00000000..701b852a --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/attendance/AttendanceType.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-23. + */ + +package pl.szczodrzynski.edziennik.data.db.modules.attendance + +import androidx.room.Entity + +@Entity(tableName = "attendanceTypes", + primaryKeys = ["profileId", "id"]) +data class AttendanceType ( + + val profileId: Int, + + val id: Long, + + val name: String, + + val type: Int, + + val color: Int + +) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/attendance/AttendanceTypeDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/attendance/AttendanceTypeDao.kt new file mode 100644 index 00000000..ec9e630a --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/attendance/AttendanceTypeDao.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-23. + */ + +package pl.szczodrzynski.edziennik.data.db.modules.attendance + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query + +@Dao +interface AttendanceTypeDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun add(attendanceType: AttendanceType) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun addAll(attendanceTypeList: List) + + @Query("DELETE FROM attendanceTypes WHERE profileId = :profileId") + fun clear(profileId: Int) + + @Query("SELECT * FROM attendanceTypes WHERE profileId = :profileId ORDER BY id ASC") + fun getAllNow(profileId: Int): List +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/classrooms/Classroom.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/classrooms/Classroom.kt new file mode 100644 index 00000000..fc2fe404 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/classrooms/Classroom.kt @@ -0,0 +1,19 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-23. + */ + +package pl.szczodrzynski.edziennik.data.db.modules.classrooms + +import androidx.room.Entity + +@Entity(tableName = "classrooms", + primaryKeys = ["profileId", "id"]) +data class Classroom ( + + val profileId: Int, + + val id: Long, + + val name: String + +) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/classrooms/ClassroomDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/classrooms/ClassroomDao.kt new file mode 100644 index 00000000..79659f97 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/classrooms/ClassroomDao.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-23. + */ + +package pl.szczodrzynski.edziennik.data.db.modules.classrooms + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query + +@Dao +interface ClassroomDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun add(classroom: Classroom) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun addAll(classroomList: List) + + @Query("DELETE FROM classrooms WHERE profileId = :profileId") + fun clear(profileId: Int) + + @Query("SELECT * FROM classrooms WHERE profileId = :profileId ORDER BY id ASC") + fun getAllNow(profileId: Int): List +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notices/NoticeType.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notices/NoticeType.kt new file mode 100644 index 00000000..a9112fbb --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notices/NoticeType.kt @@ -0,0 +1,19 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-23. + */ + +package pl.szczodrzynski.edziennik.data.db.modules.notices + +import androidx.room.Entity + +@Entity(tableName = "noticeTypes", + primaryKeys = ["profileId", "id"]) +data class NoticeType ( + + val profileId: Int, + + val id: Long, + + val name: String + +) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notices/NoticeTypeDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notices/NoticeTypeDao.kt new file mode 100644 index 00000000..5415c442 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/notices/NoticeTypeDao.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-23. + */ + +package pl.szczodrzynski.edziennik.data.db.modules.notices + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query + +@Dao +interface NoticeTypeDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun add(noticeType: NoticeType) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun addAll(noticeTypeList: List) + + @Query("DELETE FROM noticeTypes WHERE profileId = :profileId") + fun clear(profileId: Int) + + @Query("SELECT * FROM noticeTypes WHERE profileId = :profileId ORDER BY id ASC") + fun getAllNow(profileId: Int): List +} From 35ed31f6b9f653220e4f57f2bf4308cc1cb5e856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Thu, 24 Oct 2019 22:15:35 +0200 Subject: [PATCH 090/691] [APIv2/Librus] Add Attendance types, Classrooms, Event types, Notices, Notice types, PT meetings. Simplify JSON array iteration. --- .../pl/szczodrzynski/edziennik/Extensions.kt | 4 +- .../edziennik/api/v2/librus/DataLibrus.kt | 37 ++++++++++ .../edziennik/api/v2/librus/LibrusFeatures.kt | 7 +- .../api/v2/librus/data/LibrusData.kt | 56 +++++++++++++-- .../librus/data/api/LibrusApiAnnouncements.kt | 6 +- .../data/api/LibrusApiAttendanceTypes.kt | 29 +++++--- .../librus/data/api/LibrusApiAttendances.kt | 29 ++++---- .../v2/librus/data/api/LibrusApiClassrooms.kt | 44 ++++++++++++ .../v2/librus/data/api/LibrusApiEventTypes.kt | 35 +++++++++ .../api/v2/librus/data/api/LibrusApiEvents.kt | 13 ++-- .../api/v2/librus/data/api/LibrusApiGrades.kt | 9 +-- .../v2/librus/data/api/LibrusApiHomework.kt | 11 +-- .../librus/data/api/LibrusApiNoticeTypes.kt | 34 +++++++++ .../v2/librus/data/api/LibrusApiNotices.kt | 71 +++++++++++++++++++ .../v2/librus/data/api/LibrusApiPtMeetings.kt | 68 ++++++++++++++++++ .../v2/librus/data/api/LibrusApiSubjects.kt | 9 +-- .../data/api/LibrusApiTeacherFreeDayTypes.kt | 12 +--- .../data/api/LibrusApiTeacherFreeDays.kt | 11 +-- .../api/v2/librus/data/api/LibrusApiUnits.kt | 6 +- .../api/v2/librus/data/api/LibrusApiUsers.kt | 4 +- .../data/api/LibrusApiVirtualClasses.kt | 4 +- .../edziennik/api/v2/models/Data.kt | 35 ++++++--- .../data/db/modules/events/EventType.java | 4 +- .../ui/dialogs/event/EventManualDialog.java | 10 +-- .../ui/modules/login/LoginSyncFragment.kt | 22 +++--- app/src/main/res/values/strings.xml | 8 +++ 26 files changed, 464 insertions(+), 114 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClassrooms.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEventTypes.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiNoticeTypes.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiNotices.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiPtMeetings.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index 2ab17634..5edf5fc4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -14,8 +14,8 @@ import com.google.gson.JsonArray import com.google.gson.JsonElement import com.google.gson.JsonObject import im.wangchao.mhttp.Response -import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile +import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher import pl.szczodrzynski.edziennik.data.db.modules.teams.Team import pl.szczodrzynski.navlib.R import pl.szczodrzynski.navlib.crc16 @@ -48,6 +48,8 @@ fun JsonObject?.getFloat(key: String, defaultValue: Float): Float = get(key)?.le fun JsonObject?.getJsonObject(key: String, defaultValue: JsonObject): JsonObject = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonObject } ?: defaultValue fun JsonObject?.getJsonArray(key: String, defaultValue: JsonArray): JsonArray = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonArray } ?: defaultValue +fun JsonArray?.asJsonObjectList() = this?.map { it.asJsonObject } + fun CharSequence?.isNotNullNorEmpty(): Boolean { return this != null && this.isNotEmpty() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/DataLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/DataLibrus.kt index 600453b1..a18b4511 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/DataLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/DataLibrus.kt @@ -51,6 +51,43 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app } } + fun getColor(id: Int?): Int { + return when (id) { + 1 -> 0xFFF0E68C + 2 -> 0xFF87CEFA + 3 -> 0xFFB0C4DE + 4 -> 0xFFF0F8FF + 5 -> 0xFFF0FFFF + 6 -> 0xFFF5F5DC + 7 -> 0xFFFFEBCD + 8 -> 0xFFFFF8DC + 9 -> 0xFFA9A9A9 + 10 -> 0xFFBDB76B + 11 -> 0xFF8FBC8F + 12 -> 0xFFDCDCDC + 13 -> 0xFFDAA520 + 14 -> 0xFFE6E6FA + 15 -> 0xFFFFA07A + 16 -> 0xFF32CD32 + 17 -> 0xFF66CDAA + 18 -> 0xFF66CDAA + 19 -> 0xFFC0C0C0 + 20 -> 0xFFD2B48C + 21 -> 0xFF3333FF + 22 -> 0xFF7B68EE + 23 -> 0xFFBA55D3 + 24 -> 0xFFFFB6C1 + 25 -> 0xFFFF1493 + 26 -> 0xFFDC143C + 27 -> 0xFFFF0000 + 28 -> 0xFFFF8C00 + 29 -> 0xFFFFD700 + 30 -> 0xFFADFF2F + 31 -> 0xFF7CFC00 + else -> 0xff2196f3 + }.toInt() + } + /* _____ _ _ | __ \ | | | | | |__) |__ _ __| |_ __ _| | diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt index 7e0b26d3..79c7d2df 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/LibrusFeatures.kt @@ -30,11 +30,12 @@ const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES = 1033 const val ENDPOINT_LIBRUS_API_TEXT_GRADES = 1034 const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES = 1035 const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES = 1036 -const val ENDPOINT_LIBRUS_API_EVENTS = 1040 -const val ENDPOINT_LIBRUS_API_EVENT_TYPES = 1041 +const val ENDPOINT_LIBRUS_API_EVENT_TYPES = 1040 +const val ENDPOINT_LIBRUS_API_EVENTS = 1041 const val ENDPOINT_LIBRUS_API_HOMEWORK = 1050 const val ENDPOINT_LIBRUS_API_LUCKY_NUMBER = 1060 -const val ENDPOINT_LIBRUS_API_NOTICES = 1070 +const val ENDPOINT_LIBRUS_API_NOTICE_TYPES = 1070 +const val ENDPOINT_LIBRUS_API_NOTICES = 1071 const val ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES = 1080 const val ENDPOINT_LIBRUS_API_ATTENDANCES = 1081 const val ENDPOINT_LIBRUS_API_ANNOUNCEMENTS = 1090 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index 0302dc2a..73ea0387 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -45,10 +45,43 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_school_info) LibrusApiSchools(data) { onSuccess() } } + ENDPOINT_LIBRUS_API_CLASSES -> { + data.startProgress(R.string.edziennik_progress_endpoint_classes) + LibrusApiClasses(data) { onSuccess() } + } + ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES -> { + data.startProgress(R.string.edziennik_progress_endpoint_teams) + LibrusApiVirtualClasses(data) { onSuccess() } + } + ENDPOINT_LIBRUS_API_UNITS -> { + data.startProgress(R.string.edziennik_progress_endpoint_units) + LibrusApiUnits(data) { onSuccess() } + } + ENDPOINT_LIBRUS_API_USERS -> { + data.startProgress(R.string.edziennik_progress_endpoint_teachers) + LibrusApiUsers(data) { onSuccess() } + } + ENDPOINT_LIBRUS_API_SUBJECTS -> { + data.startProgress(R.string.edziennik_progress_endpoint_subjects) + LibrusApiSubjects(data) { onSuccess() } + } + ENDPOINT_LIBRUS_API_CLASSROOMS -> { + data.startProgress(R.string.edziennik_progress_endpoint_classrooms) + LibrusApiClassrooms(data) { onSuccess() } + } + // TODO push config + // TODO timetable + ENDPOINT_LIBRUS_API_NORMAL_GRADES -> { data.startProgress(R.string.edziennik_progress_endpoint_grades) LibrusApiGrades(data) { onSuccess() } } + // TODO grades + + ENDPOINT_LIBRUS_API_EVENT_TYPES -> { + data.startProgress(R.string.edziennik_progress_endpoint_event_types) + LibrusApiEventTypes(data) { onSuccess() } + } ENDPOINT_LIBRUS_API_EVENTS -> { data.startProgress(R.string.edziennik_progress_endpoint_events) LibrusApiEvents(data) { onSuccess() } @@ -57,6 +90,18 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_homework) LibrusApiHomework(data) { onSuccess() } } + ENDPOINT_LIBRUS_API_LUCKY_NUMBER -> { + data.startProgress(R.string.edziennik_progress_endpoint_lucky_number) + LibrusApiLuckyNumber(data) { onSuccess() } + } + ENDPOINT_LIBRUS_API_NOTICE_TYPES -> { + data.startProgress(R.string.edziennik_progress_endpoint_notice_types) + LibrusApiNoticeTypes(data) { onSuccess() } + } + ENDPOINT_LIBRUS_API_NOTICES -> { + data.startProgress(R.string.edziennik_progress_endpoint_notices) + LibrusApiNotices(data) { onSuccess() } + } ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES -> { data.startProgress(R.string.edziennik_progress_endpoint_attendance_types) LibrusApiAttendanceTypes(data) { onSuccess() } @@ -69,13 +114,9 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_announcements) LibrusApiAnnouncements(data) { onSuccess() } } - ENDPOINT_LIBRUS_API_LUCKY_NUMBER -> { - data.startProgress(R.string.edziennik_progress_endpoint_lucky_number) - LibrusApiLuckyNumber(data) { onSuccess() } - } - ENDPOINT_LIBRUS_API_CLASSES -> { - data.startProgress(R.string.edziennik_progress_endpoint_classes) - LibrusApiClasses(data) { onSuccess() } + ENDPOINT_LIBRUS_API_PT_MEETINGS -> { + data.startProgress(R.string.edziennik_progress_endpoint_pt_meetings) + LibrusApiPtMeetings(data) { onSuccess() } } ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES -> { data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_day_types) @@ -85,6 +126,7 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_days) LibrusApiTeacherFreeDays(data) { onSuccess() } } + ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK -> { data.startProgress(R.string.edziennik_progress_endpoint_homework) LibrusSynergiaHomework(data) { onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAnnouncements.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAnnouncements.kt index 4e93e290..e05f1ef0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAnnouncements.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAnnouncements.kt @@ -22,11 +22,9 @@ class LibrusApiAnnouncements(override val data: DataLibrus, init { apiGet(TAG, "SchoolNotices") { json -> - val announcements = json.getJsonArray("SchoolNotices") - - announcements?.forEach { announcementEl -> - val announcement = announcementEl.asJsonObject + val announcements = json.getJsonArray("SchoolNotices").asJsonObjectList() + announcements?.forEach { announcement -> val id = Utils.crc16(announcement.getString("Id")?.toByteArray() ?: return@forEach).toLong() val subject = announcement.getString("Subject") ?: "" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendanceTypes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendanceTypes.kt index aa268a80..1ee34f9f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendanceTypes.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendanceTypes.kt @@ -4,11 +4,13 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api +import android.graphics.Color import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi -import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance +import pl.szczodrzynski.edziennik.data.db.modules.attendance.AttendanceType class LibrusApiAttendanceTypes(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusApi(data) { @@ -18,24 +20,29 @@ class LibrusApiAttendanceTypes(override val data: DataLibrus, init { apiGet(TAG, "Attendances/Types") { json -> - val attendanceTypes = json.getJsonArray("Types") + val attendanceTypes = json.getJsonArray("Types").asJsonObjectList() - attendanceTypes?.forEach { attendanceTypeEl -> - val attendanceType = attendanceTypeEl.asJsonObject + attendanceTypes?.forEach { attendanceType -> + val id = attendanceType.getLong("Id") ?: return@forEach + val name = attendanceType.getString("Name") ?: "" + val color = attendanceType.getString("ColorRGB")?.let { Color.parseColor("#$it") } ?: -1 - val id = attendanceType.getInt("Id") ?: return@forEach val standardId = when (attendanceType.getBoolean("Standard") ?: false) { true -> id - false -> attendanceType.getJsonObject("StandardType")?.getInt("Id") - ?: return@forEach + false -> attendanceType.getJsonObject("StandardType")?.getLong("Id") ?: id + } + val type = when (standardId) { + 1L -> Attendance.TYPE_ABSENT + 2L -> Attendance.TYPE_BELATED + 3L -> Attendance.TYPE_ABSENT_EXCUSED + 4L -> Attendance.TYPE_RELEASED + /*100*/else -> Attendance.TYPE_PRESENT } - val name = attendanceType.getString("Name") ?: "" - - data.attendanceTypes.put(id, Pair(standardId, name)) + data.attendanceTypes.put(id, AttendanceType(profileId, id, name, type, color)) } - data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES, SYNC_ALWAYS) + data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES, 4*DAY) onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt index 8bd7f243..52a3ea26 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiAttendances.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api +import androidx.core.util.isEmpty import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ATTENDANCES @@ -21,12 +22,14 @@ class LibrusApiAttendances(override val data: DataLibrus, } init { + if (data.attendanceTypes.isEmpty()) { + data.db.attendanceTypeDao().getAllNow(profileId).toSparseArray(data.attendanceTypes) { it.id } + } + apiGet(TAG, "Attendances") { json -> - val attendances = json.getJsonArray("Attendances") - - attendances?.forEach { attendanceEl -> - val attendance = attendanceEl.asJsonObject + val attendances = json.getJsonArray("Attendances").asJsonObjectList() + attendances?.forEach { attendance -> val id = Utils.strToInt((attendance.getString("Id") ?: return@forEach) .replace("[^\\d.]".toRegex(), "")).toLong() val teacherId = attendance.getJsonObject("AddedBy")?.getLong("Id") ?: -1 @@ -37,17 +40,9 @@ class LibrusApiAttendances(override val data: DataLibrus, it.weekDay == lessonDate.weekDay && it.startTime.value == startTime.value }?.subjectId ?: -1 val semester = attendance.getInt("Semester") ?: return@forEach - var type = attendance.getJsonObject("Type")?.getInt("Id") ?: return@forEach - val attendanceType = data.attendanceTypes.get(type) - val topic = attendanceType.second - - type = when(type) { - 1 -> Attendance.TYPE_ABSENT - 2 -> Attendance.TYPE_BELATED - 3 -> Attendance.TYPE_ABSENT_EXCUSED - 4 -> Attendance.TYPE_RELEASED - else -> Attendance.TYPE_PRESENT - } + val type = attendance.getJsonObject("Type")?.getLong("Id") ?: return@forEach + val typeObject = data.attendanceTypes.get(type) + val topic = typeObject?.name ?: "" val attendanceObject = Attendance( profileId, @@ -58,13 +53,13 @@ class LibrusApiAttendances(override val data: DataLibrus, topic, lessonDate, startTime, - type + typeObject.type ) val addedDate = Date.fromIso(attendance.getString("AddDate") ?: return@forEach) data.attendanceList.add(attendanceObject) - if(type != Attendance.TYPE_PRESENT) { + if(typeObject.type != Attendance.TYPE_PRESENT) { data.metadataList.add(Metadata( profileId, Metadata.TYPE_ATTENDANCE, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClassrooms.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClassrooms.kt new file mode 100644 index 00000000..662225e0 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiClassrooms.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-24. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_CLASSROOMS +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.classrooms.Classroom +import java.util.* + +class LibrusApiClassrooms(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiClassrooms" + } + + init { + apiGet(TAG, "Classrooms") { json -> + val classrooms = json.getJsonArray("Classrooms").asJsonObjectList() + + classrooms?.forEach { classroom -> + val id = classroom.getLong("Id") ?: return@forEach + val name = classroom.getString("Name")?.toLowerCase(Locale.getDefault()) ?: "" + val symbol = classroom.getString("Symbol")?.toLowerCase(Locale.getDefault()) ?: "" + val nameShort = name.split(" ").onEach { it[0] }.joinToString() + + val friendlyName = if (name != symbol && !name.contains(symbol) && !nameShort.contains(symbol)) { + classroom.getString("Symbol") + " " + classroom.getString("Name") + } + else { + classroom.getString("Name") ?: "" + } + + data.classrooms.put(id, Classroom(profileId, id, friendlyName)) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_CLASSROOMS, 4*DAY) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEventTypes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEventTypes.kt new file mode 100644 index 00000000..42fad5d2 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEventTypes.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-24. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_EVENT_TYPES +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.events.EventType + +class LibrusApiEventTypes(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiEventTypes" + } + + init { + apiGet(TAG, "HomeWorks/Categories") { json -> + val eventTypes = json.getJsonArray("Categories").asJsonObjectList() + + eventTypes?.forEach { eventType -> + val id = eventType.getLong("Id") ?: return@forEach + val name = eventType.getString("Name") ?: "" + val color = data.getColor(eventType.getJsonObject("Color")?.getInt("Id")) + + data.eventTypes.put(id, EventType(profileId, id, name, color)) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_EVENT_TYPES, 4*DAY) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt index ed6ece5c..512a8caa 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiEvents.kt @@ -4,9 +4,10 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api +import androidx.core.util.isEmpty import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_EVENTS import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_EVENTS import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.events.Event @@ -21,12 +22,14 @@ class LibrusApiEvents(override val data: DataLibrus, } init { + if (data.eventTypes.isEmpty()) { + data.db.eventTypeDao().getAllNow(profileId).toSparseArray(data.eventTypes) { it.id } + } + apiGet(TAG, "HomeWorks") { json -> - val events = json.getJsonArray("HomeWorks") - - events?.forEach { eventEl -> - val event = eventEl.asJsonObject + val events = json.getJsonArray("HomeWorks").asJsonObjectList() + events?.forEach { event -> val id = event.getLong("Id") ?: return@forEach val eventDate = Date.fromY_m_d(event.getString("Date")) val topic = event.getString("Content") ?: "" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt index 8f4699c9..930938d0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGrades.kt @@ -1,8 +1,8 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api import pl.szczodrzynski.edziennik.* -import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADES import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADES import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade @@ -10,7 +10,6 @@ import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.models.Date - class LibrusApiGrades(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusApi(data) { companion object { @@ -19,11 +18,9 @@ class LibrusApiGrades(override val data: DataLibrus, init { apiGet(TAG, "Grades") { json -> - val grades = json.getJsonArray("Grades") - - grades?.forEach { gradeEl -> - val grade = gradeEl.asJsonObject + val grades = json.getJsonArray("Grades").asJsonObjectList() + grades?.forEach { grade -> val id = grade.getLong("Id") ?: return@forEach val categoryId = grade.getJsonObject("Category")?.getLong("Id") ?: -1 val name = grade.getString("Grade") ?: "" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiHomework.kt index c3f67c8d..ba16ebe9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiHomework.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiHomework.kt @@ -4,16 +4,13 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api +import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_HOMEWORK import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.events.Event import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata -import pl.szczodrzynski.edziennik.getJsonArray -import pl.szczodrzynski.edziennik.getJsonObject -import pl.szczodrzynski.edziennik.getLong -import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.utils.models.Date class LibrusApiHomework(override val data: DataLibrus, @@ -24,11 +21,9 @@ class LibrusApiHomework(override val data: DataLibrus, init { apiGet(TAG, "HomeWorkAssignments") { json -> - val homeworkList = json.getJsonArray("HomeWorkAssignments") - - homeworkList?.forEach { homeworkEl -> - val homework = homeworkEl.asJsonObject + val homeworkList = json.getJsonArray("HomeWorkAssignments").asJsonObjectList() + homeworkList?.forEach { homework -> val id = homework.getLong("Id") ?: return@forEach val eventDate = Date.fromY_m_d(homework.getString("DueDate")) val topic = homework.getString("Topic") + "\n" + homework.getString("Text") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiNoticeTypes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiNoticeTypes.kt new file mode 100644 index 00000000..f49b15c7 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiNoticeTypes.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-24. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NOTICE_TYPES +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.notices.NoticeType + +class LibrusApiNoticeTypes(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiNoticeTypes" + } + + init { + apiGet(TAG, "Notes/Categories") { json -> + val noticeTypes = json.getJsonArray("Categories").asJsonObjectList() + + noticeTypes?.forEach { noticeType -> + val id = noticeType.getLong("Id") ?: return@forEach + val name = noticeType.getString("CategoryName") ?: "" + + data.noticeTypes.put(id, NoticeType(profileId, id, name)) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_NOTICE_TYPES, 4*DAY) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiNotices.kt new file mode 100644 index 00000000..2f4ca5c9 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiNotices.kt @@ -0,0 +1,71 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-24. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import androidx.core.util.isEmpty +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NOTICES +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice +import pl.szczodrzynski.edziennik.utils.models.Date + +class LibrusApiNotices(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiNotices" + } + + init { + if (data.noticeTypes.isEmpty()) { + data.db.noticeTypeDao().getAllNow(profileId).toSparseArray(data.noticeTypes) { it.id } + } + + apiGet(TAG, "Notes") { json -> + val notes = json.getJsonArray("Notes").asJsonObjectList() + + notes?.forEach { note -> + val id = note.getLong("Id") ?: return@forEach + val text = note.getString("Text") ?: "" + val categoryId = note.getJsonObject("Category")?.getLong("Id") ?: -1 + val teacherId = note.getJsonObject("AddedBy")?.getLong("Id") ?: -1 + val addedDate = note.getString("Date")?.let { Date.fromY_m_d(it) } ?: return@forEach + + val type = when (note.getInt("Positive")) { + 0 -> Notice.TYPE_NEGATIVE + 1 -> Notice.TYPE_POSITIVE + /*2*/else -> Notice.TYPE_NEUTRAL + } + val categoryText = data.noticeTypes[categoryId]?.name ?: "" + val semester = profile?.dateToSemester(addedDate) ?: 1 + + val noticeObject = Notice( + profileId, + id, + categoryText+"\n"+text, + semester, + type, + teacherId + ) + + data.noticeList.add(noticeObject) + data.metadataList.add( + Metadata( + profileId, + Metadata.TYPE_NOTICE, + id, + profile?.empty ?: false, + profile?.empty ?: false, + addedDate.inMillis + )) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_NOTICES, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiPtMeetings.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiPtMeetings.kt new file mode 100644 index 00000000..2c4e5f53 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiPtMeetings.kt @@ -0,0 +1,68 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-24. + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_PT_MEETINGS +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.events.Event +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Time + +class LibrusApiPtMeetings(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiPtMeetings" + } + + init { + apiGet(TAG, "ParentTeacherConferences") { json -> + val ptMeetings = json.getJsonArray("ParentTeacherConferences").asJsonObjectList() + + ptMeetings?.forEach { meeting -> + val id = meeting.getLong("Id") ?: return@forEach + val topic = meeting.getString("Topic") ?: "" + val teacherId = meeting.getJsonObject("Teacher")?.getLong("Id") ?: -1 + val eventDate = meeting.getString("Date")?.let { Date.fromY_m_d(it) } ?: return@forEach + val startTime = meeting.getString("Time")?.let { + if (it == "00:00:00") + null + else + Time.fromH_m_s(it) + } + + val eventObject = Event( + profileId, + id, + eventDate, + startTime, + topic, + -1, + Event.TYPE_PT_MEETING, + false, + teacherId, + -1, + data.teamClass?.id ?: -1 + ) + + data.eventList.add(eventObject) + data.metadataList.add( + Metadata( + profileId, + Metadata.TYPE_EVENT, + id, + profile?.empty ?: false, + profile?.empty ?: false, + System.currentTimeMillis() + )) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_PT_MEETINGS, 12*HOUR) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiSubjects.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiSubjects.kt index f595bf09..2cfd9be2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiSubjects.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiSubjects.kt @@ -4,14 +4,11 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api -import pl.szczodrzynski.edziennik.DAY +import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_SUBJECTS import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject -import pl.szczodrzynski.edziennik.getJsonArray -import pl.szczodrzynski.edziennik.getLong -import pl.szczodrzynski.edziennik.getString class LibrusApiSubjects(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusApi(data) { @@ -21,9 +18,9 @@ class LibrusApiSubjects(override val data: DataLibrus, init { apiGet(TAG, "Subjects") { json -> - json.getJsonArray("Subjects")?.forEach { subjectEl -> - val subject = subjectEl.asJsonObject + val subjects = json.getJsonArray("Subjects").asJsonObjectList() + subjects?.forEach { subject -> val id = subject.getLong("Id") ?: return@forEach val longName = subject.getString("Name") ?: "" val shortName = subject.getString("Short") ?: "" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDayTypes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDayTypes.kt index dcfdcbeb..49692e25 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDayTypes.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDayTypes.kt @@ -4,15 +4,11 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api -import pl.szczodrzynski.edziennik.DAY +import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi -import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceType -import pl.szczodrzynski.edziennik.getJsonArray -import pl.szczodrzynski.edziennik.getLong -import pl.szczodrzynski.edziennik.getString class LibrusApiTeacherFreeDayTypes(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusApi(data) { @@ -22,11 +18,9 @@ class LibrusApiTeacherFreeDayTypes(override val data: DataLibrus, init { apiGet(TAG, "TeacherFreeDays/Types") { json -> - val teacherAbsenceTypes = json.getJsonArray("Types") - - teacherAbsenceTypes?.forEach { teacherAbsenceTypeEl -> - val teacherAbsenceType = teacherAbsenceTypeEl.asJsonObject + val teacherAbsenceTypes = json.getJsonArray("Types").asJsonObjectList() + teacherAbsenceTypes?.forEach { teacherAbsenceType -> val id = teacherAbsenceType.getLong("Id") ?: return@forEach val name = teacherAbsenceType.getString("Name") ?: return@forEach diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt index 3be6fabb..649e3377 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDays.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api +import androidx.core.util.isEmpty import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus @@ -21,12 +22,14 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus, } init { + if (data.teacherAbsenceTypes.isEmpty()) { + data.db.teacherAbsenceTypeDao().getAllNow(profileId).toSparseArray(data.teacherAbsenceTypes) { it.id } + } + apiGet(TAG, "TeacherFreeDays") { json -> - val teacherAbsences = json.getJsonArray("TeacherFreeDays") - - teacherAbsences?.forEach { teacherAbsenceEl -> - val teacherAbsence = teacherAbsenceEl.asJsonObject + val teacherAbsences = json.getJsonArray("TeacherFreeDays").asJsonObjectList() + teacherAbsences?.forEach { teacherAbsence -> val id = teacherAbsence.getLong("Id") ?: return@forEach val teacherId = teacherAbsence.getJsonObject("Teacher")?.getLong("Id") ?: return@forEach diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUnits.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUnits.kt index 78d10103..42353ad5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUnits.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUnits.kt @@ -23,9 +23,9 @@ class LibrusApiUnits(override val data: DataLibrus, } apiGet(TAG, "Units") { json -> - val units = json.getJsonArray("Units") - units?.singleOrNull { it.asJsonObject.getLong("Id") == data.unitId }?.also { unitEl -> - val unit = unitEl.asJsonObject + val units = json.getJsonArray("Units").asJsonObjectList() + + units?.singleOrNull { it.getLong("Id") == data.unitId }?.also { unit -> val startPoints = unit.getJsonObject("BehaviourGradesSettings")?.getJsonObject("StartPoints") startPoints?.apply { data.startPointsSemester1 = getInt("Semester1", defaultValue = 0) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUsers.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUsers.kt index 5818cd59..1277ec06 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUsers.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiUsers.kt @@ -18,9 +18,9 @@ class LibrusApiUsers(override val data: DataLibrus, init { apiGet(TAG, "Users") { json -> - json.getJsonArray("Users")?.forEach { userEl -> - val user = userEl.asJsonObject + val users = json.getJsonArray("Users").asJsonObjectList() + users?.forEach { user -> val id = user.getLong("Id") ?: return@forEach val firstName = user.getString("FirstName")?.fixWhiteSpaces() ?: "" val lastName = user.getString("LastName")?.fixWhiteSpaces() ?: "" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiVirtualClasses.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiVirtualClasses.kt index 8d63f331..3cc1559e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiVirtualClasses.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiVirtualClasses.kt @@ -18,9 +18,9 @@ class LibrusApiVirtualClasses(override val data: DataLibrus, init { apiGet(TAG, "VirtualClasses") { json -> - json.getJsonArray("VirtualClasses")?.forEach { virtualClassEl -> - val virtualClass = virtualClassEl.asJsonObject + val virtualClasses = json.getJsonArray("VirtualClasses").asJsonObjectList() + virtualClasses?.forEach { virtualClass -> val id = virtualClass.getLong("Id") ?: return@forEach val name = virtualClass.getString("Name") ?: "" val teacherId = virtualClass.getJsonObject("Teacher")?.getLong("Id") ?: -1 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 5baa62bb..54dfdae3 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -2,6 +2,7 @@ package pl.szczodrzynski.edziennik.api.v2.models import android.util.LongSparseArray import android.util.SparseArray +import androidx.core.util.size import com.google.gson.JsonObject import im.wangchao.mhttp.Response import pl.szczodrzynski.edziennik.App @@ -14,6 +15,8 @@ import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance +import pl.szczodrzynski.edziennik.data.db.modules.attendance.AttendanceType +import pl.szczodrzynski.edziennik.data.db.modules.classrooms.Classroom import pl.szczodrzynski.edziennik.data.db.modules.events.Event import pl.szczodrzynski.edziennik.data.db.modules.events.EventType import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade @@ -27,6 +30,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.messages.Message import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice +import pl.szczodrzynski.edziennik.data.db.modules.notices.NoticeType import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject @@ -102,7 +106,11 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) val teamList = LongSparseArray() val lessonRanges = SparseArray() val gradeCategories = LongSparseArray() - val attendanceTypes = SparseArray>() + + val classrooms = LongSparseArray() + val attendanceTypes = LongSparseArray() + val noticeTypes = LongSparseArray() + val eventTypes = LongSparseArray() val teacherAbsenceTypes = LongSparseArray() private var mTeamClass: Team? = null @@ -125,7 +133,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) var eventsToRemove: DataRemoveModel? = null val eventList = mutableListOf() - val eventTypeList = mutableListOf() var noticesToRemove: DataRemoveModel? = null val noticeList = mutableListOf() @@ -158,7 +165,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) db.teamDao().getAllNow(profileId).toSparseArray(teamList) { it.id } db.lessonRangeDao().getAllNow(profileId).toSparseArray(lessonRanges) { it.lessonNumber } db.gradeCategoryDao().getAllNow(profileId).toSparseArray(gradeCategories) { it.categoryId } - db.teacherAbsenceTypeDao().getAllNow(profileId).toSparseArray(teacherAbsenceTypes) { it.id } } } @@ -170,12 +176,18 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) subjectList.clear() teamList.clear() lessonRanges.clear() + gradeCategories.clear() + + classrooms.clear() + attendanceTypes.clear() + noticeTypes.clear() + eventTypes.clear() + teacherAbsenceTypes.clear() lessonList.clear() lessonChangeList.clear() gradeCategories.clear() gradeList.clear() - eventTypeList.clear() noticeList.clear() attendanceList.clear() announcementList.clear() @@ -239,8 +251,17 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) db.lessonRangeDao().addAll(lessonRanges.values()) db.gradeCategoryDao().clear(profileId) db.gradeCategoryDao().addAll(gradeCategories.values()) - db.teacherAbsenceTypeDao().clear(profileId) - db.teacherAbsenceTypeDao().addAll(teacherAbsenceTypes.values()) + + if (classrooms.size > 0) + db.classroomDao().addAll(classrooms.values()) + if (attendanceTypes.size > 0) + db.attendanceTypeDao().addAll(attendanceTypes.values()) + if (noticeTypes.size > 0) + db.noticeTypeDao().addAll(noticeTypes.values()) + if (eventTypes.size > 0) + db.eventTypeDao().addAll(eventTypes.values()) + if (teacherAbsenceTypes.size > 0) + db.teacherAbsenceTypeDao().addAll(teacherAbsenceTypes.values()) gradesToRemove?.let { it -> it.removeAll?.let { _ -> db.gradeDao().clear(profileId) } @@ -260,8 +281,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) db.eventDao().removeFuture(profile.id, Date.getToday()) db.eventDao().addAll(eventList) } - if (eventTypeList.isNotEmpty()) - db.eventTypeDao().addAll(eventTypeList) if (noticeList.isNotEmpty()) { db.noticeDao().clear(profile.id) db.noticeDao().addAll(noticeList) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/events/EventType.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/events/EventType.java index e06f7656..30c4794a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/events/EventType.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/events/EventType.java @@ -11,14 +11,14 @@ public class EventType { public int profileId; @ColumnInfo(name = "eventType") - public int id; + public long id; @ColumnInfo(name = "eventTypeName") public String name; @ColumnInfo(name = "eventTypeColor") public int color; - public EventType(int profileId, int id, String name, int color) { + public EventType(int profileId, long id, String name, int color) { this.profileId = profileId; this.id = id; this.name = name; diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.java index af333143..8bc27048 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.java @@ -34,8 +34,8 @@ import com.wdullaer.materialdatetimepicker.time.TimePickerDialog; import java.util.List; import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; +import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.data.api.AppError; import pl.szczodrzynski.edziennik.data.db.modules.events.Event; import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull; @@ -46,12 +46,12 @@ import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject; import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher; import pl.szczodrzynski.edziennik.data.db.modules.teams.Team; -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.network.ServerRequest; import pl.szczodrzynski.edziennik.utils.TextInputDropDown; import pl.szczodrzynski.edziennik.utils.Themes; +import pl.szczodrzynski.edziennik.utils.models.Date; +import pl.szczodrzynski.edziennik.utils.models.Time; +import pl.szczodrzynski.edziennik.utils.models.Week; import static pl.szczodrzynski.edziennik.App.APP_URL; import static pl.szczodrzynski.edziennik.data.api.AppError.CODE_OTHER; @@ -727,7 +727,7 @@ public class EventManualDialog { popup.setOnMenuItemClickListener(item -> { if (item.getGroupId() == 0 && eventTypes != null) { EventType typeObj = eventTypes.get(item.getItemId()); - eventType = typeObj.id; + eventType = (int)typeObj.id; eventColor = -1; // set -1 as it's the event type's default color registerEventManualColorPreview.setBackgroundColor(typeObj.color); // set event type's color here to show how will it look } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt index 006c8e75..1a0086fe 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt @@ -127,17 +127,17 @@ class LoginSyncFragment : Fragment() { profile.loginStoreId = loginStoreId loginStore.id = loginStoreId val typeList = listOf( - EventType(profileId, TYPE_HOMEWORK, getString(R.string.event_type_homework), COLOR_HOMEWORK), - EventType(profileId, TYPE_DEFAULT, getString(R.string.event_other), COLOR_DEFAULT), - EventType(profileId, TYPE_EXAM, getString(R.string.event_exam), COLOR_EXAM), - EventType(profileId, TYPE_SHORT_QUIZ, getString(R.string.event_short_quiz), COLOR_SHORT_QUIZ), - EventType(profileId, TYPE_ESSAY, getString(R.string.event_essay), COLOR_SHORT_QUIZ), - EventType(profileId, TYPE_PROJECT, getString(R.string.event_project), COLOR_PROJECT), - EventType(profileId, TYPE_PT_MEETING, getString(R.string.event_pt_meeting), COLOR_PT_MEETING), - EventType(profileId, TYPE_EXCURSION, getString(R.string.event_excursion), COLOR_EXCURSION), - EventType(profileId, TYPE_READING, getString(R.string.event_reading), COLOR_READING), - EventType(profileId, TYPE_CLASS_EVENT, getString(R.string.event_class_event), COLOR_CLASS_EVENT), - EventType(profileId, TYPE_INFORMATION, getString(R.string.event_information), COLOR_INFORMATION) + EventType(profileId, TYPE_HOMEWORK.toLong(), getString(R.string.event_type_homework), COLOR_HOMEWORK), + EventType(profileId, TYPE_DEFAULT.toLong(), getString(R.string.event_other), COLOR_DEFAULT), + EventType(profileId, TYPE_EXAM.toLong(), getString(R.string.event_exam), COLOR_EXAM), + EventType(profileId, TYPE_SHORT_QUIZ.toLong(), getString(R.string.event_short_quiz), COLOR_SHORT_QUIZ), + EventType(profileId, TYPE_ESSAY.toLong(), getString(R.string.event_essay), COLOR_SHORT_QUIZ), + EventType(profileId, TYPE_PROJECT.toLong(), getString(R.string.event_project), COLOR_PROJECT), + EventType(profileId, TYPE_PT_MEETING.toLong(), getString(R.string.event_pt_meeting), COLOR_PT_MEETING), + EventType(profileId, TYPE_EXCURSION.toLong(), getString(R.string.event_excursion), COLOR_EXCURSION), + EventType(profileId, TYPE_READING.toLong(), getString(R.string.event_reading), COLOR_READING), + EventType(profileId, TYPE_CLASS_EVENT.toLong(), getString(R.string.event_class_event), COLOR_CLASS_EVENT), + EventType(profileId, TYPE_INFORMATION.toLong(), getString(R.string.event_information), COLOR_INFORMATION) ) app.db.eventTypeDao().addAll(typeList) app.profileSaveFull(profile, loginStore) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3deb4994..a390c853 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -955,4 +955,12 @@ Synchronizuję... [%d%%] %s Pobieranie uwag... + Pobieranie grup klasowych... + Pobieranie informacji o jednostce... + Pobieranie listy nauczycieli... + Pobieranie listy przedmiotów... + Pobieranie listy sal lekcyjnych... + Pobieranie kategorii wydarzeń... + Pobieranie kategorii uwag... + Pobieranie zebrań z rodzicami... From 51662289157c0e581a659287fbad103c33d843f7 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Fri, 25 Oct 2019 00:12:23 +0200 Subject: [PATCH 091/691] [APIv2/Librus] Parse document in LibrusSynergia instead of endpoints --- .../edziennik/api/v2/Constants.kt | 4 +-- .../api/v2/librus/data/LibrusSynergia.kt | 17 ++++++++--- .../data/synergia/LibrusSynergiaGrades.kt | 19 ------------ .../data/synergia/LibrusSynergiaHomework.kt | 6 ++-- .../data/synergia/LibrusSynergiaInfo.kt | 4 +-- .../data/synergia/LibrusSynergiaTemplate.kt | 4 +-- .../edziennik/api/v2/vulcan/DataVulcan.kt | 30 +++++++++---------- 7 files changed, 34 insertions(+), 50 deletions(-) delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaGrades.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index 576b4912..371e3c58 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -38,11 +38,11 @@ const val LIBRUS_API_CLIENT_ID_JST = "49" const val LIBRUS_JST_DEMO_CODE = "68656A21" const val LIBRUS_JST_DEMO_PIN = "1290" -const val LIBRUS_SYNERGIA_URL = "https://synergia.librus.pl/" +const val LIBRUS_SYNERGIA_URL = "https://synergia.librus.pl" /** https://synergia.librus.pl/loguj/token/TOKEN/przenies */ const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/token/TOKEN/przenies" -const val LIBRUS_MESSAGES_URL = "https://wiadomosci.librus.pl/module/" +const val LIBRUS_MESSAGES_URL = "https://wiadomosci.librus.pl/module" const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action=" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt index 32e1bc78..e16f00e7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt @@ -8,6 +8,8 @@ import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response import im.wangchao.mhttp.callback.TextCallbackHandler import okhttp3.Cookie +import org.jsoup.Jsoup +import org.jsoup.nodes.Document import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.models.ApiError @@ -25,8 +27,8 @@ open class LibrusSynergia(open val data: DataLibrus) { get() = data.profile fun synergiaGet(tag: String, endpoint: String, method: Int = GET, - parameters: Map = emptyMap(), onSuccess: (text: String) -> Unit) { - d(tag, "Request: Librus/Synergia - $LIBRUS_SYNERGIA_URL$endpoint") + parameters: Map = emptyMap(), onSuccess: (doc: Document) -> Unit) { + d(tag, "Request: Librus/Synergia - $LIBRUS_SYNERGIA_URL/$endpoint") val callback = object : TextCallbackHandler() { override fun onSuccess(text: String?, response: Response?) { @@ -39,7 +41,8 @@ open class LibrusSynergia(open val data: DataLibrus) { // TODO: Error handling try { - onSuccess(text) + val doc = Jsoup.parse(text) + onSuccess(doc) } catch (e: Exception) { data.error(ApiError(tag, EXCEPTION_LIBRUS_SYNERGIA_REQUEST) .withResponse(response) @@ -47,6 +50,12 @@ open class LibrusSynergia(open val data: DataLibrus) { .withApiResponse(text)) } } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(ApiError(tag, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) + } } data.app.cookieJar.saveFromResponse(null, listOf( @@ -58,7 +67,7 @@ open class LibrusSynergia(open val data: DataLibrus) { )) Request.builder() - .url("$LIBRUS_SYNERGIA_URL$endpoint") + .url("$LIBRUS_SYNERGIA_URL/$endpoint") .userAgent(LIBRUS_USER_AGENT) .apply { when (method) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaGrades.kt deleted file mode 100644 index d553d127..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaGrades.kt +++ /dev/null @@ -1,19 +0,0 @@ -package pl.szczodrzynski.edziennik.api.v2.librus.data.synergia - -import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.api.v2.models.Data -import pl.szczodrzynski.edziennik.data.api.interfaces.ProgressCallback -import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore -import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile - -class LibrusSynergiaGrades(val app: App, - val profile: Profile, - val loginStore: LoginStore, - val data: Data, - val callback: ProgressCallback, - val onSuccess: () -> Unit) { - - init { - - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt index 5f63bcad..ee77b19a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt @@ -4,7 +4,6 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.synergia -import org.jsoup.Jsoup import pl.szczodrzynski.edziennik.HOUR import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK import pl.szczodrzynski.edziennik.api.v2.POST @@ -32,8 +31,7 @@ class LibrusSynergiaHomework(override val data: DataLibrus, val onSuccess: () -> "dataDo" to profile!!.getSemesterEnd(profile?.currentSemester ?: 2).stringY_m_d, "przedmiot" to -1 - )) { text -> - val doc = Jsoup.parse(text) + )) { doc -> doc.select("table.myHomeworkTable > tbody").firstOrNull()?.also { homeworkTable -> val homeworkElements = homeworkTable.children() @@ -93,7 +91,7 @@ class LibrusSynergiaHomework(override val data: DataLibrus, val onSuccess: () -> } // because this requires a synergia login (2 more requests) sync this every two hours or if explicit :D - data.setSyncNext(ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK, 2*HOUR, DRAWER_ITEM_HOMEWORK) + data.setSyncNext(ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK, 2 * HOUR, DRAWER_ITEM_HOMEWORK) onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaInfo.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaInfo.kt index 7777cf6a..cb638523 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaInfo.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaInfo.kt @@ -4,7 +4,6 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.synergia -import org.jsoup.Jsoup import pl.szczodrzynski.edziennik.MONTH import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_SYNERGIA_INFO @@ -16,8 +15,7 @@ class LibrusSynergiaInfo(override val data: DataLibrus, val onSuccess: () -> Uni } init { - synergiaGet(TAG, "informacja") { text -> - val doc = Jsoup.parse(text) + synergiaGet(TAG, "informacja") { doc -> doc.select("table.form tbody").firstOrNull()?.children()?.also { info -> val studentNumber = info[2].select("td").text().trim().toIntOrNull() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaTemplate.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaTemplate.kt index 1cc0097e..0aa850b6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaTemplate.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaTemplate.kt @@ -4,7 +4,6 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.synergia -import org.jsoup.Jsoup import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergia import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS @@ -15,8 +14,7 @@ class LibrusSynergiaTemplate(override val data: DataLibrus, val onSuccess: () -> } init { - /* synergiaGet(TAG, "") { text -> - val doc = Jsoup.parse(text) + /* synergiaGet(TAG, "") { doc -> data.setSyncNext(ENDPOINT_LIBRUS_SYNERGIA_, SYNC_ALWAYS) onSuccess() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt index 6e647a87..9b79713c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt @@ -158,26 +158,26 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app val apiUrl: String? get() { val url = when (apiToken?.substring(0, 3)) { - "3S1" -> "https://lekcjaplus.vulcan.net.pl/" - "TA1" -> "https://uonetplus-komunikacja.umt.tarnow.pl/" - "OP1" -> "https://uonetplus-komunikacja.eszkola.opolskie.pl/" - "RZ1" -> "https://uonetplus-komunikacja.resman.pl/" - "GD1" -> "https://uonetplus-komunikacja.edu.gdansk.pl/" - "KA1" -> "https://uonetplus-komunikacja.mcuw.katowice.eu/" - "KA2" -> "https://uonetplus-komunikacja-test.mcuw.katowice.eu/" - "P03" -> "https://efeb-komunikacja-pro-efebmobile.pro.vulcan.pl/" - "P01" -> "http://efeb-komunikacja.pro-hudson.win.vulcan.pl/" - "P02" -> "http://efeb-komunikacja.pro-hudsonrc.win.vulcan.pl/" - "P90" -> "http://efeb-komunikacja-pro-mwujakowska.neo.win.vulcan.pl/" - "FK1", "FS1" -> "http://api.fakelog.cf/" - "SZ9" -> "http://vulcan.szkolny.eu/" + "3S1" -> "https://lekcjaplus.vulcan.net.pl" + "TA1" -> "https://uonetplus-komunikacja.umt.tarnow.pl" + "OP1" -> "https://uonetplus-komunikacja.eszkola.opolskie.pl" + "RZ1" -> "https://uonetplus-komunikacja.resman.pl" + "GD1" -> "https://uonetplus-komunikacja.edu.gdansk.pl" + "KA1" -> "https://uonetplus-komunikacja.mcuw.katowice.eu" + "KA2" -> "https://uonetplus-komunikacja-test.mcuw.katowice.eu" + "P03" -> "https://efeb-komunikacja-pro-efebmobile.pro.vulcan.pl" + "P01" -> "http://efeb-komunikacja.pro-hudson.win.vulcan.pl" + "P02" -> "http://efeb-komunikacja.pro-hudsonrc.win.vulcan.pl" + "P90" -> "http://efeb-komunikacja-pro-mwujakowska.neo.win.vulcan.pl" + "FK1", "FS1" -> "http://api.fakelog.cf" + "SZ9" -> "http://vulcan.szkolny.eu" else -> null } - return if (url != null) "$url$symbol/" else null + return if (url != null) "$url/$symbol" else null } val fullApiUrl: String? get() { - return "${apiUrl}${schoolSymbol}/" + return "${apiUrl}/${schoolSymbol}/" } } From de82bc7e4de278a1d06b375577375a548f1e1b37 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Fri, 25 Oct 2019 00:15:26 +0200 Subject: [PATCH 092/691] [APIv2/Librus] Add Librus Messages endpoint --- app/build.gradle | 2 + .../szczodrzynski/edziennik/api/v2/Errors.kt | 1 + .../api/v2/librus/data/LibrusMessages.kt | 96 +++++++++++++++++++ .../data/messages/LibrusMessagesTemplate.kt | 23 +++++ 4 files changed, 122 insertions(+) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusMessages.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/messages/LibrusMessagesTemplate.kt diff --git a/app/build.gradle b/app/build.gradle index 5e5494aa..fe3d5cb5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -158,6 +158,8 @@ dependencies { //implementation 'com.github.wulkanowy:uonet-request-signer:master-SNAPSHOT' //implementation 'com.github.kuba2k2.uonet-request-signer:android:master-63f094b14a-1' + + implementation "org.redundent:kotlin-xml-builder:1.5.3" } repositories { mavenCentral() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index b6fcc05d..e3174798 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -129,3 +129,4 @@ const val EXCEPTION_LIBRUS_SYNERGIA_REQUEST = 905 const val EXCEPTION_MOBIDZIENNIK_WEB_REQUEST = 906 const val EXCEPTION_VULCAN_API_REQUEST = 907 const val EXCEPTION_NOTIFY_AND_SYNC = 910 +const val EXCEPTION_LIBRUS_MESSAGES_REQUEST = 911 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusMessages.kt new file mode 100644 index 00000000..c31ac8e5 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusMessages.kt @@ -0,0 +1,96 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-24 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data + +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.callback.TextCallbackHandler +import okhttp3.Cookie +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import org.jsoup.parser.Parser +import org.redundent.kotlin.xml.xml +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.utils.Utils.d + +open class LibrusMessages(open val data: DataLibrus) { + companion object { + private const val TAG = "LibrusMessages" + } + + val profileId + get() = data.profile?.id ?: -1 + + val profile + get() = data.profile + + fun messagesGet(tag: String, endpoint: String, method: Int = POST, + parameters: Map? = null, onSuccess: (doc: Document) -> Unit) { + + d(tag, "Request: Librus/Messages - $LIBRUS_MESSAGES_URL/$endpoint") + + val callback = object : TextCallbackHandler() { + override fun onSuccess(text: String?, response: Response?) { + if (text.isNullOrEmpty()) { + data.error(ApiError(LibrusSynergia.TAG, ERROR_RESPONSE_EMPTY) + .withResponse(response)) + return + } + + // TODO: Error handling + + try { + val doc = Jsoup.parse(text, "", Parser.xmlParser()) + onSuccess(doc) + } catch (e: Exception) { + data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_REQUEST) + .withResponse(response) + .withThrowable(e) + .withApiResponse(text)) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(ApiError(tag, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) + } + } + + data.app.cookieJar.saveFromResponse(null, listOf( + Cookie.Builder() + .name("DZIENNIKSID") + .value(data.messagesSessionId!!) + .domain("wiadomosci.librus.pl") + .secure().httpOnly().build() + )) + + val requestXml = xml("service") { + "data" { + for ((key, value) in parameters.orEmpty()) { + key { + -value.toString() + } + } + } + } + + Request.builder() + .url("$LIBRUS_MESSAGES_URL/$endpoint") + .userAgent(SYNERGIA_USER_AGENT) + .setTextBody(requestXml.toString(), "application/xml") + .apply { + when (method) { + GET -> get() + POST -> post() + } + } + .callback(callback) + .build() + .enqueue() + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/messages/LibrusMessagesTemplate.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/messages/LibrusMessagesTemplate.kt new file mode 100644 index 00000000..f2a84d68 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/messages/LibrusMessagesTemplate.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-25 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.messages + +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusMessages +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS + +class LibrusMessagesTemplate(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusMessages(data) { + companion object { + const val TAG = "LibrusMessages" + } + + init { + /* messagesGet(TAG, "") { doc -> + + data.setSyncNext(ENDPOINT_LIBRUS_MESSAGES_, SYNC_ALWAYS) + onSuccess() + } */ + } +} From 8c099bc137d27bee4f03e7f448b3a7ed37a498ca Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Fri, 25 Oct 2019 00:24:55 +0200 Subject: [PATCH 093/691] [APIv2/Librus] Add handling denied access in messages --- .../edziennik/api/v2/librus/data/LibrusMessages.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusMessages.kt index c31ac8e5..e964e0b4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusMessages.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusMessages.kt @@ -15,6 +15,7 @@ import org.redundent.kotlin.xml.xml import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.get import pl.szczodrzynski.edziennik.utils.Utils.d open class LibrusMessages(open val data: DataLibrus) { @@ -41,7 +42,15 @@ open class LibrusMessages(open val data: DataLibrus) { return } - // TODO: Error handling + // TODO: Finish error handling + + if ("error" in text) { + when ("(.*)".toRegex().find(text)?.get(1)) { + "eAccessDeny" -> data.error(ApiError(tag, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED) + .withResponse(response) + .withApiResponse(text)) + } + } try { val doc = Jsoup.parse(text, "", Parser.xmlParser()) From 9aef3d56df37a7c5ad94c242cd1e55003a33110c Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Fri, 25 Oct 2019 22:12:45 +0200 Subject: [PATCH 094/691] [APIv2/Librus] Fix Librus messages requests --- .../edziennik/api/v2/librus/data/LibrusMessages.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusMessages.kt index e964e0b4..f7e59550 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusMessages.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusMessages.kt @@ -6,11 +6,13 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response +import im.wangchao.mhttp.body.MediaTypeUtils import im.wangchao.mhttp.callback.TextCallbackHandler import okhttp3.Cookie import org.jsoup.Jsoup import org.jsoup.nodes.Document import org.jsoup.parser.Parser +import org.redundent.kotlin.xml.PrintOptions import org.redundent.kotlin.xml.xml import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus @@ -79,6 +81,7 @@ open class LibrusMessages(open val data: DataLibrus) { )) val requestXml = xml("service") { + "header" { } "data" { for ((key, value) in parameters.orEmpty()) { key { @@ -86,12 +89,15 @@ open class LibrusMessages(open val data: DataLibrus) { } } } - } + }.toString(PrintOptions( + singleLineTextElements = true, + useSelfClosingTags = true + )) Request.builder() .url("$LIBRUS_MESSAGES_URL/$endpoint") .userAgent(SYNERGIA_USER_AGENT) - .setTextBody(requestXml.toString(), "application/xml") + .setTextBody(requestXml, MediaTypeUtils.APPLICATION_XML) .apply { when (method) { GET -> get() From e38dc011bdd7f1afee236a2ba7611ae2a1e6318b Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Fri, 25 Oct 2019 22:13:43 +0200 Subject: [PATCH 095/691] [APIv2/Librus] Fix new homework notifications (Synergia) --- .../librus/data/synergia/LibrusSynergiaHomework.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt index ee77b19a..09f767b6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt @@ -45,7 +45,7 @@ class LibrusSynergiaHomework(override val data: DataLibrus, val onSuccess: () -> val subjectId = data.subjectList.singleOrNull { it.longName == subjectName }?.id ?: -1 val teacherName = elements[1].text().trim() - val teacherId = data.teacherList.singleOrNull { teacherName == "${it.name} ${it.surname}" }?.id + val teacherId = data.teacherList.singleOrNull { teacherName == it.fullName }?.id ?: -1 val topic = elements[2].text().trim() val addedDate = Date.fromY_m_d(elements[4].text().trim()).inMillis @@ -62,7 +62,11 @@ class LibrusSynergiaHomework(override val data: DataLibrus, val onSuccess: () -> val description = "Treść: (.*)".toRegex(RegexOption.DOT_MATCHES_ALL).find(moreInfo) ?.get(1)?.replace("".toRegex(), "\n")?.trim() - val notify = (profile?.empty ?: false) && Date.getToday() < eventDate + val notified = when (profile?.empty) { + true -> true + false -> Date.getToday() < eventDate + else -> false + } val eventObject = Event( profileId, @@ -83,8 +87,8 @@ class LibrusSynergiaHomework(override val data: DataLibrus, val onSuccess: () -> profileId, Metadata.TYPE_HOMEWORK, id, - notify, - notify, + notified, + notified, addedDate )) } From 454e8caa0d117ecb269b68d4562ac503b9e6d67c Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sat, 26 Oct 2019 00:14:14 +0200 Subject: [PATCH 096/691] [APIv2/Librus] Add getting received and sent messages --- .../szczodrzynski/edziennik/api/v2/Errors.kt | 1 + .../api/v2/librus/data/LibrusData.kt | 21 +++ .../data/messages/LibrusMessagesGetList.kt | 125 ++++++++++++++++++ .../messages/MessagesListFragment.java | 2 +- app/src/main/res/values/strings.xml | 1 + 5 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/messages/LibrusMessagesGetList.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index e3174798..31134489 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -45,6 +45,7 @@ const val ERROR_LOGIN_DATA_INVALID = 102 const val ERROR_PROFILE_MISSING = 105 const val ERROR_INVALID_LOGIN_MODE = 110 const val ERROR_LOGIN_METHOD_NOT_SATISFIED = 111 +const val ERROR_NOT_IMPLEMENTED = 112 const val ERROR_NO_STUDENTS_IN_ACCOUNT = 115 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index 73ea0387..157377f6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -7,8 +7,10 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.librus.* import pl.szczodrzynski.edziennik.api.v2.librus.data.api.* +import pl.szczodrzynski.edziennik.api.v2.librus.data.messages.LibrusMessagesGetList import pl.szczodrzynski.edziennik.api.v2.librus.data.synergia.LibrusSynergiaHomework import pl.szczodrzynski.edziennik.api.v2.librus.data.synergia.LibrusSynergiaInfo +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message import pl.szczodrzynski.edziennik.utils.Utils class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { @@ -37,6 +39,9 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { private fun useEndpoint(endpointId: Int, onSuccess: () -> Unit) { Utils.d(TAG, "Using endpoint $endpointId") when (endpointId) { + /** + * API + */ ENDPOINT_LIBRUS_API_ME -> { data.startProgress(R.string.edziennik_progress_endpoint_student_info) LibrusApiMe(data) { onSuccess() } @@ -127,6 +132,9 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { LibrusApiTeacherFreeDays(data) { onSuccess() } } + /** + * SYNERGIA + */ ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK -> { data.startProgress(R.string.edziennik_progress_endpoint_homework) LibrusSynergiaHomework(data) { onSuccess() } @@ -135,6 +143,19 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_student_info) LibrusSynergiaInfo(data) { onSuccess() } } + + /** + * MESSAGES + */ + ENDPOINT_LIBRUS_MESSAGES_RECEIVED -> { + data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) + LibrusMessagesGetList(data, type = Message.TYPE_RECEIVED) { onSuccess() } + } + ENDPOINT_LIBRUS_MESSAGES_SENT -> { + data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox) + LibrusMessagesGetList(data, type = Message.TYPE_SENT) { onSuccess() } + } + else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/messages/LibrusMessagesGetList.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/messages/LibrusMessagesGetList.kt new file mode 100644 index 00000000..d7abdad2 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/messages/LibrusMessagesGetList.kt @@ -0,0 +1,125 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-24 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.messages + +import pl.szczodrzynski.edziennik.DAY +import pl.szczodrzynski.edziennik.HOUR +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES +import pl.szczodrzynski.edziennik.api.v2.ERROR_NOT_IMPLEMENTED +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_MESSAGES_RECEIVED +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_MESSAGES_SENT +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusMessages +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message +import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher +import pl.szczodrzynski.edziennik.singleOrNull +import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.models.Date + +class LibrusMessagesGetList(override val data: DataLibrus, private val type: Int = Message.TYPE_RECEIVED, + archived: Boolean = false, val onSuccess: () -> Unit) : LibrusMessages(data) { + companion object { + const val TAG = "LibrusMessagesGetList" + } + + init { + val endpoint = when (type) { + Message.TYPE_RECEIVED -> "Inbox/action/GetList" + Message.TYPE_SENT -> "Outbox/action/GetList" + else -> null + } + + if (endpoint != null) { + messagesGet(TAG, endpoint, parameters = mapOf( + "archive" to if (archived) 1 else 0 + )) { doc -> + doc.select("GetList data").firstOrNull()?.children()?.forEach { element -> + val id = element.select("messageId").text().toLong() + val subject = element.select("topic").text().trim() + val readDateText = element.select("readDate").text().trim() + val readDate = when (readDateText.isNotBlank()) { + true -> Date.fromIso(readDateText) + else -> 0 + } + val sentDate = Date.fromIso(element.select("sendDate").text().trim()) + var senderId: Long = -1 + var receiverId: Long = -1 + + when (type) { + Message.TYPE_RECEIVED -> { + val senderFirstName = element.select("senderFirstName").text().trim() + val senderLastName = element.select("senderLastName").text().trim() + senderId = data.teacherList.singleOrNull { + it.name == senderFirstName && it.surname == senderLastName + }?.id ?: -1 + } + + Message.TYPE_SENT -> { + val receiverFirstName = element.select("receiverFirstName").text().trim() + val receiverLastName = element.select("receiverLastName").text().trim() + receiverId = data.teacherList.singleOrNull { + it.name == receiverFirstName && it.surname == receiverLastName + }?.id ?: { + val teacherObject = Teacher( + profileId, + -1 * Utils.crc16("$receiverFirstName $receiverLastName".toByteArray()).toLong(), + receiverFirstName, + receiverLastName + ) + data.teacherList.put(teacherObject.id, teacherObject) + teacherObject.id + }.invoke() + } + } + + val notified = when (type) { + Message.TYPE_SENT -> true + else -> readDate > 0 || profile?.empty ?: false + } + + val messageObject = Message( + profileId, + id, + subject, + null, + type, + senderId, + -1 + ) + + val messageRecipientObject = MessageRecipient( + profileId, + receiverId, + -1, + readDate, + id + ) + + data.messageList.add(messageObject) + data.messageRecipientList.add(messageRecipientObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_MESSAGE, + id, + notified, + notified, + sentDate + )) + } + + when (type) { + Message.TYPE_RECEIVED -> data.setSyncNext(ENDPOINT_LIBRUS_MESSAGES_RECEIVED, 2 * HOUR, DRAWER_ITEM_MESSAGES) + Message.TYPE_SENT -> data.setSyncNext(ENDPOINT_LIBRUS_MESSAGES_SENT, DAY, DRAWER_ITEM_MESSAGES) + } + onSuccess() + } + } else { + data.error(TAG, ERROR_NOT_IMPLEMENTED) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesListFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesListFragment.java index 17e77c41..c717afd2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesListFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesListFragment.java @@ -96,7 +96,7 @@ public class MessagesListFragment extends Fragment { return; } - if (app.profile.getLoginStoreType() == LOGIN_TYPE_LIBRUS && app.profile.getStudentData("accountPassword", null) == null) { + if (app.profile.getLoginStoreType() == LOGIN_TYPE_LIBRUS && app.profile.getStudentData("accountPassword", null) == null && false) { new MaterialDialog.Builder(activity) .title("Wiadomości w systemie Synergia") .content("Moduł Wiadomości w aplikacji Szkolny.eu jest przeglądarką zasobów szkolnego konta Synergia. Z tego powodu, musisz wpisać swoje hasło do tego konta, aby móc korzystać z tej funkcji.") diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a390c853..9aef94bf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -963,4 +963,5 @@ Pobieranie kategorii wydarzeń... Pobieranie kategorii uwag... Pobieranie zebrań z rodzicami... + Pobieranie wiadomości wysłanych... From 843a8e4298f76ea40c88c96229b4be681554f7c3 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sat, 26 Oct 2019 00:22:03 +0200 Subject: [PATCH 097/691] [APIv2/Librus] Make getting received messages sync always --- .../api/v2/librus/data/messages/LibrusMessagesGetList.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/messages/LibrusMessagesGetList.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/messages/LibrusMessagesGetList.kt index d7abdad2..1119a906 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/messages/LibrusMessagesGetList.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/messages/LibrusMessagesGetList.kt @@ -5,13 +5,13 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.messages import pl.szczodrzynski.edziennik.DAY -import pl.szczodrzynski.edziennik.HOUR import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES import pl.szczodrzynski.edziennik.api.v2.ERROR_NOT_IMPLEMENTED import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_MESSAGES_RECEIVED import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_MESSAGES_SENT import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusMessages +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.messages.Message import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata @@ -112,7 +112,7 @@ class LibrusMessagesGetList(override val data: DataLibrus, private val type: Int } when (type) { - Message.TYPE_RECEIVED -> data.setSyncNext(ENDPOINT_LIBRUS_MESSAGES_RECEIVED, 2 * HOUR, DRAWER_ITEM_MESSAGES) + Message.TYPE_RECEIVED -> data.setSyncNext(ENDPOINT_LIBRUS_MESSAGES_RECEIVED, SYNC_ALWAYS) Message.TYPE_SENT -> data.setSyncNext(ENDPOINT_LIBRUS_MESSAGES_SENT, DAY, DRAWER_ITEM_MESSAGES) } onSuccess() From 3e99c111bdd200edff6b5d7939bebf6601bcb56d Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sat, 26 Oct 2019 14:36:21 +0200 Subject: [PATCH 098/691] [APIv2/Librus] Add marking all announcements as read using Synergia --- .../edziennik/api/v2/ApiService.kt | 14 ++++++++++++- .../requests/AnnouncementsReadRequest.kt | 13 ++++++++++++ .../api/v2/interfaces/EdziennikInterface.kt | 3 ++- .../edziennik/api/v2/librus/Librus.kt | 15 +++++++++++++- .../api/v2/librus/data/LibrusSynergia.kt | 5 ++--- .../data/synergia/LibrusSynergiaHomework.kt | 4 +++- .../data/synergia/LibrusSynergiaInfo.kt | 4 +++- ...ibrusSynergiaMarkAllAnnouncementsAsRead.kt | 20 +++++++++++++++++++ .../data/synergia/LibrusSynergiaTemplate.kt | 4 +++- .../api/v2/mobidziennik/Mobidziennik.kt | 6 +++++- .../edziennik/api/v2/template/Template.kt | 6 +++++- .../edziennik/api/v2/vulcan/Vulcan.kt | 4 ++++ .../edziennik/receivers/SzkolnyReceiver.kt | 8 +++----- 13 files changed, 90 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/AnnouncementsReadRequest.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaMarkAllAnnouncementsAsRead.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt index 64086764..0b2ceefc 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt @@ -232,6 +232,7 @@ class ApiService : Service() { viewId = task.viewIds?.get(0)?.first) is MessageGetRequest -> edziennikInterface?.getMessage(task.messageId) is FirstLoginRequest -> edziennikInterface?.firstLogin() + is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead() } } @@ -294,6 +295,17 @@ class ApiService : Service() { sync() } + @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) + fun onAnnouncementsReadRequest(request: AnnouncementsReadRequest) { + EventBus.getDefault().removeStickyEvent(request) + Log.d(TAG, request.toString()) + + taskQueue += request.apply { + taskId = ++taskMaximumId + } + sync() + } + @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) fun onFirstLoginRequest(request: FirstLoginRequest) { EventBus.getDefault().removeStickyEvent(request) @@ -347,4 +359,4 @@ class ApiService : Service() { override fun onBind(intent: Intent?): IBinder? { return null } -} \ No newline at end of file +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/AnnouncementsReadRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/AnnouncementsReadRequest.kt new file mode 100644 index 00000000..60e35840 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/AnnouncementsReadRequest.kt @@ -0,0 +1,13 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-26 + */ + +package pl.szczodrzynski.edziennik.api.v2.events.requests + +import pl.szczodrzynski.edziennik.api.v2.models.ApiTask + +data class AnnouncementsReadRequest(override val profileId: Int) : ApiTask(profileId) { + override fun toString(): String { + return "AnnouncementsReadRequest(profileId=$profileId)" + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt index 20535a0e..935b778d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt @@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.api.v2.interfaces interface EdziennikInterface { fun sync(featureIds: List, viewId: Int? = null) fun getMessage(messageId: Int) + fun markAllAnnouncementsAsRead() fun firstLogin() fun cancel() -} \ No newline at end of file +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index 0e3d093e..dbd99c5f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -10,8 +10,11 @@ import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData +import pl.szczodrzynski.edziennik.api.v2.librus.data.synergia.LibrusSynergiaMarkAllAnnouncementsAsRead import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.LibrusFirstLogin import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin +import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi +import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.prepare @@ -64,6 +67,16 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va } + override fun markAllAnnouncementsAsRead() { + LibrusLoginApi(data) { + LibrusLoginSynergia(data) { + LibrusSynergiaMarkAllAnnouncementsAsRead(data) { + completed() + } + } + } + } + override fun firstLogin() { LibrusFirstLogin(data) { completed() @@ -105,4 +118,4 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt index e16f00e7..691c6d57 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt @@ -27,7 +27,7 @@ open class LibrusSynergia(open val data: DataLibrus) { get() = data.profile fun synergiaGet(tag: String, endpoint: String, method: Int = GET, - parameters: Map = emptyMap(), onSuccess: (doc: Document) -> Unit) { + parameters: Map = emptyMap(), onSuccess: (text: String) -> Unit) { d(tag, "Request: Librus/Synergia - $LIBRUS_SYNERGIA_URL/$endpoint") val callback = object : TextCallbackHandler() { @@ -41,8 +41,7 @@ open class LibrusSynergia(open val data: DataLibrus) { // TODO: Error handling try { - val doc = Jsoup.parse(text) - onSuccess(doc) + onSuccess(text) } catch (e: Exception) { data.error(ApiError(tag, EXCEPTION_LIBRUS_SYNERGIA_REQUEST) .withResponse(response) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt index 09f767b6..860eb958 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaHomework.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.synergia +import org.jsoup.Jsoup import pl.szczodrzynski.edziennik.HOUR import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK import pl.szczodrzynski.edziennik.api.v2.POST @@ -31,7 +32,8 @@ class LibrusSynergiaHomework(override val data: DataLibrus, val onSuccess: () -> "dataDo" to profile!!.getSemesterEnd(profile?.currentSemester ?: 2).stringY_m_d, "przedmiot" to -1 - )) { doc -> + )) { text -> + val doc = Jsoup.parse(text) doc.select("table.myHomeworkTable > tbody").firstOrNull()?.also { homeworkTable -> val homeworkElements = homeworkTable.children() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaInfo.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaInfo.kt index cb638523..7777cf6a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaInfo.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaInfo.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.synergia +import org.jsoup.Jsoup import pl.szczodrzynski.edziennik.MONTH import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_SYNERGIA_INFO @@ -15,7 +16,8 @@ class LibrusSynergiaInfo(override val data: DataLibrus, val onSuccess: () -> Uni } init { - synergiaGet(TAG, "informacja") { doc -> + synergiaGet(TAG, "informacja") { text -> + val doc = Jsoup.parse(text) doc.select("table.form tbody").firstOrNull()?.children()?.also { info -> val studentNumber = info[2].select("td").text().trim().toIntOrNull() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaMarkAllAnnouncementsAsRead.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaMarkAllAnnouncementsAsRead.kt new file mode 100644 index 00000000..5976954a --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaMarkAllAnnouncementsAsRead.kt @@ -0,0 +1,20 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-10-26 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.synergia + +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergia + +class LibrusSynergiaMarkAllAnnouncementsAsRead(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusSynergia(data) { + companion object { + const val TAG = "LibrusSynergiaMarkAllAnnouncementsAsRead" + } + + init { + synergiaGet(TAG, "ogloszenia") { + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaTemplate.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaTemplate.kt index 0aa850b6..1cc0097e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaTemplate.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/synergia/LibrusSynergiaTemplate.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.synergia +import org.jsoup.Jsoup import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergia import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS @@ -14,7 +15,8 @@ class LibrusSynergiaTemplate(override val data: DataLibrus, val onSuccess: () -> } init { - /* synergiaGet(TAG, "") { doc -> + /* synergiaGet(TAG, "") { text -> + val doc = Jsoup.parse(text) data.setSyncNext(ENDPOINT_LIBRUS_SYNERGIA_, SYNC_ALWAYS) onSuccess() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt index f94d7ec9..7656c921 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt @@ -64,6 +64,10 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto } + override fun markAllAnnouncementsAsRead() { + + } + override fun firstLogin() { MobidziennikFirstLogin(data) { completed() @@ -105,4 +109,4 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt index b9949410..61b4ce81 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt @@ -63,6 +63,10 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, } + override fun markAllAnnouncementsAsRead() { + + } + override fun firstLogin() { // TODO } @@ -102,4 +106,4 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt index 5997b72d..84cd1326 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt @@ -64,6 +64,10 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va } + override fun markAllAnnouncementsAsRead() { + + } + override fun firstLogin() { VulcanFirstLogin(data) { completed() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt index 8cf52b11..292b3875 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt @@ -9,10 +9,7 @@ import android.content.Context import android.content.Intent import org.greenrobot.eventbus.EventBus import pl.szczodrzynski.edziennik.api.v2.ApiService -import pl.szczodrzynski.edziennik.api.v2.events.requests.ServiceCloseRequest -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest -import pl.szczodrzynski.edziennik.api.v2.events.requests.TaskCancelRequest +import pl.szczodrzynski.edziennik.api.v2.events.requests.* class SzkolnyReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { @@ -22,6 +19,7 @@ class SzkolnyReceiver : BroadcastReceiver() { "TaskCancelRequest" -> EventBus.getDefault().post(TaskCancelRequest(intent.extras?.getInt("taskId", -1) ?: return)) "SyncRequest" -> EventBus.getDefault().post(SyncRequest()) "SyncProfileRequest" -> EventBus.getDefault().post(SyncProfileRequest(intent.extras?.getInt("profileId", -1) ?: return)) + "AnnouncementsReadRequest" -> EventBus.getDefault().post(AnnouncementsReadRequest(intent.extras?.getInt("profileId", -1) ?: return)) } } -} \ No newline at end of file +} From ca10ee2fe5ea4de9fff1b59f9b8ce30d54ad3db2 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sat, 26 Oct 2019 15:04:11 +0200 Subject: [PATCH 099/691] [Debug] Make debug mode for debug build --- app/src/main/java/pl/szczodrzynski/edziennik/App.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/App.java b/app/src/main/java/pl/szczodrzynski/edziennik/App.java index 374738e1..9da8ca74 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/App.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/App.java @@ -231,7 +231,7 @@ public class App extends androidx.multidex.MultiDexApplication { e.printStackTrace(); } - if ("f054761fbdb6a238".equals(deviceId)) { + if ("f054761fbdb6a238".equals(deviceId) || BuildConfig.DEBUG) { devMode = true; } else if (appConfig.devModePassword != null) { @@ -710,12 +710,8 @@ public class App extends androidx.multidex.MultiDexApplication { public void checkDevModePassword() { try { - if (Utils.AESCrypt.decrypt("nWFVxY65Pa8/aRrT7EylNAencmOD+IxUY2Gg/beiIWY=", appConfig.devModePassword).equals("ok here you go it's enabled now")) { - devMode = true; - } - else { - devMode = false; - } + devMode = Utils.AESCrypt.decrypt("nWFVxY65Pa8/aRrT7EylNAencmOD+IxUY2Gg/beiIWY=", appConfig.devModePassword).equals("ok here you go it's enabled now") + || BuildConfig.DEBUG; } catch (Exception e) { e.printStackTrace(); devMode = false; From 28e0f3487c0370100b48ab90c74a568b19a77a8f Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sat, 26 Oct 2019 15:13:03 +0200 Subject: [PATCH 100/691] [APIv2/Debug] Add debug button for marking all announcements as read --- .../edziennik/ui/modules/home/HomeFragment.java | 6 ++++++ app/src/main/res/layout/fragment_home.xml | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java index fa12919c..265c12fa 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java @@ -45,6 +45,7 @@ import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.api.v2.ApiService; +import pl.szczodrzynski.edziennik.api.v2.events.requests.AnnouncementsReadRequest; import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest; import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeFull; import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull; @@ -148,6 +149,11 @@ public class HomeFragment extends Fragment { EventBus.getDefault().postSticky(new SyncProfileRequest(app.profile.getId(), list)); })); + b.test5.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); + b.test5.setOnClickListener((v -> { + EventBus.getDefault().postSticky(new AnnouncementsReadRequest(app.profile.getId())); + })); + //((TextView)v.findViewById(R.id.nextSync)).setText(getString(R.string.next_sync_format,Time.fromMillis(app.appJobs.syncJobTime).getStringHMS())); diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index aeb2b074..e0fedecb 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -64,7 +64,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp" - android:text="Sync profile 16 - view home" + android:text="Sync profile - view home" android:visibility="gone"/> + + From 46dd543b48f2cf4945aa2fb0ffa8c3c7a55909c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 27 Oct 2019 15:30:24 +0100 Subject: [PATCH 101/691] [APIv2] Librus/Synergia request: error handling. Mobidziennik: fix login error code. --- .../szczodrzynski/edziennik/api/v2/Errors.kt | 5 ++++- .../api/v2/librus/data/LibrusPortal.kt | 2 +- .../api/v2/librus/data/LibrusSynergia.kt | 21 +++++++++++++------ .../login/MobidziennikLoginWeb.kt | 4 ++-- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index 31134489..b7c6403d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -81,7 +81,7 @@ const val ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID = 154 const val ERROR_LIBRUS_MESSAGES_ACCESS_DENIED = 155 const val ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED = 156 const val ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID = 157 -const val ERROR_LIBRUS_PORTAL_TOKEN_EXPIRED = 158 +const val ERROR_LIBRUS_PORTAL_ACCESS_DENIED = 158 const val ERROR_LIBRUS_PORTAL_API_DISABLED = 159 const val ERROR_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED = 160 const val ERROR_LIBRUS_PORTAL_OTHER = 161 @@ -97,6 +97,8 @@ const val ERROR_LOGIN_LIBRUS_PORTAL_UNSUPPORTED_GRANT = 170 const val ERROR_LOGIN_LIBRUS_PORTAL_INVALID_CLIENT_ID = 171 const val ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_INVALID = 172 const val ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_REVOKED = 173 +const val ERROR_LIBRUS_SYNERGIA_OTHER = 174 +const val ERROR_LIBRUS_SYNERGIA_MAINTENANCE = 175 const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201 const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202 @@ -110,6 +112,7 @@ const val ERROR_MOBIDZIENNIK_WEB_NO_SESSION_KEY = 212 const val ERROR_MOBIDZIENNIK_WEB_NO_SESSION_VALUE = 212 const val ERROR_MOBIDZIENNIK_WEB_NO_SERVER_ID = 213 const val ERROR_MOBIDZIENNIK_WEB_INVALID_RESPONSE = 214 +const val ERROR_LOGIN_MOBIDZIENNIK_WEB_NO_SESSION_ID = 215 const val ERROR_LOGIN_VULCAN_INVALID_SYMBOL = 301 const val ERROR_LOGIN_VULCAN_INVALID_TOKEN = 302 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt index c5cc886c..3c5e4963 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt @@ -41,7 +41,7 @@ open class LibrusPortal(open val data: DataLibrus) { error?.let { code -> when (code) { "requires_an_action" -> ERROR_LIBRUS_PORTAL_SYNERGIA_DISCONNECTED - "Access token is invalid" -> ERROR_LIBRUS_PORTAL_TOKEN_EXPIRED + "Access token is invalid" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED "ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED "Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND else -> ERROR_LIBRUS_PORTAL_OTHER diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt index 691c6d57..b45c0aa8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusSynergia.kt @@ -7,9 +7,6 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response import im.wangchao.mhttp.callback.TextCallbackHandler -import okhttp3.Cookie -import org.jsoup.Jsoup -import org.jsoup.nodes.Document import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.models.ApiError @@ -38,7 +35,19 @@ open class LibrusSynergia(open val data: DataLibrus) { return } - // TODO: Error handling + if (!text.contains("jesteś zalogowany")) { + when { + text.contains("stop.png") -> ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED + text.contains("Przerwa techniczna") -> ERROR_LIBRUS_SYNERGIA_MAINTENANCE + else -> ERROR_LIBRUS_SYNERGIA_OTHER + }.let { errorCode -> + data.error(ApiError(tag, errorCode) + .withResponse(response) + .withApiResponse(text)) + return + } + } + try { onSuccess(text) @@ -57,13 +66,13 @@ open class LibrusSynergia(open val data: DataLibrus) { } } - data.app.cookieJar.saveFromResponse(null, listOf( + /*data.app.cookieJar.saveFromResponse(null, listOf( Cookie.Builder() .name("DZIENNIKSID") .value(data.synergiaSessionId!!) .domain("synergia.librus.pl") .secure().httpOnly().build() - )) + ))*/ Request.builder() .url("$LIBRUS_SYNERGIA_URL/$endpoint") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLoginWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLoginWeb.kt index f40b4750..ccce499d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLoginWeb.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLoginWeb.kt @@ -62,7 +62,7 @@ class MobidziennikLoginWeb(val data: DataMobidziennik, val onSuccess: () -> Unit val sessionKey = cookie?.name() val sessionId = cookie?.value() if (sessionId == null) { - data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID) + data.error(ApiError(TAG, ERROR_LOGIN_MOBIDZIENNIK_WEB_NO_SESSION_ID) .withResponse(response) .withApiResponse(text)) return @@ -85,7 +85,7 @@ class MobidziennikLoginWeb(val data: DataMobidziennik, val onSuccess: () -> Unit Request.builder() .url("https://${data.loginServerName}.mobidziennik.pl/api/") - .userAgent(System.getProperty("http.agent")) + .userAgent(MOBIDZIENNIK_USER_AGENT) .contentType("application/x-www-form-urlencoded; charset=UTF-8") .addParameter("wersja", "20") .addParameter("ip", data.app.deviceId) From 10c439afad0a0af7343ddf556e6506dfc757ae60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 27 Oct 2019 15:38:43 +0100 Subject: [PATCH 102/691] [APIv2/Template] Add first login template. --- .../edziennik/api/v2/template/Template.kt | 5 ++- .../template/firstlogin/TemplateFirstLogin.kt | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/firstlogin/TemplateFirstLogin.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt index 61b4ce81..60e1c063 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt @@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.prepare import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateData +import pl.szczodrzynski.edziennik.api.v2.template.firstlogin.TemplateFirstLogin import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLogin import pl.szczodrzynski.edziennik.api.v2.templateLoginMethods import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore @@ -68,7 +69,9 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, } override fun firstLogin() { - // TODO + TemplateFirstLogin(data) { + completed() + } } override fun cancel() { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/firstlogin/TemplateFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/firstlogin/TemplateFirstLogin.kt new file mode 100644 index 00000000..f9430721 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/firstlogin/TemplateFirstLogin.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-27. + */ + +package pl.szczodrzynski.edziennik.api.v2.template.firstlogin + +import pl.szczodrzynski.edziennik.api.v2.template.DataTemplate +import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateApi +import pl.szczodrzynski.edziennik.api.v2.template.data.TemplateWeb +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile + +class TemplateFirstLogin(val data: DataTemplate, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "TemplateFirstLogin" + } + + private val web = TemplateWeb(data) + private val api = TemplateApi(data) + private val profileList = mutableListOf() + + init { + /*TemplateLoginWeb(data) { + web.webGet(TAG, "get all accounts") { text -> + //val accounts = json.getJsonArray("accounts") + + EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore)) + onSuccess() + } + }*/ + } +} From 054426c9cc645d1a03a40930d0fb144441ff0da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 27 Oct 2019 18:03:58 +0100 Subject: [PATCH 103/691] [APIv2/DB] Add profile account name, class, school year fields. --- .../pl/szczodrzynski/edziennik/Extensions.kt | 26 ++++++++++++++++++- .../edziennik/data/db/AppDb.java | 13 ++++++++-- .../data/db/modules/profiles/Profile.kt | 4 ++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index 5edf5fc4..ea33eaa1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -69,6 +69,21 @@ fun Bundle?.getString(key: String, defaultValue: String): String { return this?.getString(key, defaultValue) ?: defaultValue } +fun String.fixName(): String { + return this.fixWhiteSpaces().toProperCase() +} + +fun String.toProperCase(): String = changeStringCase(this) + +fun String.swapFirstLastName(): String { + return this.split(" ").let { + if (it.size > 1) + it[1]+" "+it[0] + else + it[0] + } +} + fun changeStringCase(s: String): String { val delimiters = " '-/" val sb = StringBuilder() @@ -86,7 +101,16 @@ fun changeStringCase(s: String): String { } fun buildFullName(firstName: String?, lastName: String?): String { - return changeStringCase("$firstName $lastName").trim() + return "$firstName $lastName".fixName() +} + +fun String.getShortName(): String { + return split(" ").let { + if (it.size > 1) + "${it[0]} ${it[1][0]}." + else + it[0] + } } fun colorFromName(context: Context, name: String?): Int { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java index d43d5301..800dcc69 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.java @@ -103,7 +103,7 @@ import pl.szczodrzynski.edziennik.utils.models.Date; Classroom.class, NoticeType.class, AttendanceType.class, - Metadata.class}, version = 62) + Metadata.class}, version = 63) @TypeConverters({ ConverterTime.class, ConverterDate.class, @@ -721,6 +721,14 @@ public abstract class AppDb extends RoomDatabase { ")"); } }; + private static final Migration MIGRATION_62_63 = new Migration(62, 63) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("ALTER TABLE profiles ADD COLUMN accountNameLong TEXT DEFAULT NULL"); + database.execSQL("ALTER TABLE profiles ADD COLUMN studentClassName TEXT DEFAULT NULL"); + database.execSQL("ALTER TABLE profiles ADD COLUMN studentSchoolYear TEXT DEFAULT NULL"); + } + }; public static AppDb getDatabase(final Context context) { @@ -780,7 +788,8 @@ public abstract class AppDb extends RoomDatabase { MIGRATION_58_59, MIGRATION_59_60, MIGRATION_60_61, - MIGRATION_61_62 + MIGRATION_61_62, + MIGRATION_62_63 ) .allowMainThreadQueries() //.fallbackToDestructiveMigration() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/profiles/Profile.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/profiles/Profile.kt index 82d07f86..fabfef68 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/profiles/Profile.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/profiles/Profile.kt @@ -56,9 +56,11 @@ open class Profile : IDrawerProfile { * If null, then it's a student account. * If not null, then it's a parent account with this name. */ - @Ignore var accountNameLong: String? = null + var studentClassName: String? = null + var studentSchoolYear: String? = null + var registration = REGISTRATION_UNSPECIFIED var gradeColorMode = COLOR_MODE_WEIGHTED From 13b970f4e8e1fe3a4617a2939bb749a32eab001f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 27 Oct 2019 19:40:50 +0100 Subject: [PATCH 104/691] [APIv2] Optimize first login. Add extracting account name, class name, school year. Add Vulcan error handling. Fix Vulcan first login. --- .../szczodrzynski/edziennik/api/v2/Errors.kt | 25 +++++++++++ .../v2/librus/firstlogin/LibrusFirstLogin.kt | 40 ++++++++---------- .../firstlogin/MobidziennikFirstLogin.kt | 41 +++++++++++-------- .../edziennik/api/v2/models/Data.kt | 4 ++ .../edziennik/api/v2/vulcan/DataVulcan.kt | 2 +- .../edziennik/api/v2/vulcan/data/VulcanApi.kt | 13 +++++- .../v2/vulcan/firstlogin/VulcanFirstLogin.kt | 39 +++++++++--------- .../api/v2/vulcan/login/VulcanLoginApi.kt | 6 ++- 8 files changed, 105 insertions(+), 65 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index b7c6403d..cc4709ce 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -122,6 +122,29 @@ const val ERROR_LOGIN_VULCAN_INVALID_PIN_1_REMAINING = 311 const val ERROR_LOGIN_VULCAN_INVALID_PIN_2_REMAINING = 312 const val ERROR_LOGIN_VULCAN_EXPIRED_TOKEN = 321 const val ERROR_LOGIN_VULCAN_OTHER = 322 +const val ERROR_LOGIN_VULCAN_ONLY_KINDERGARTEN = 330 +const val ERROR_LOGIN_VULCAN_NO_PUPILS = 331 +const val ERROR_VULCAN_API_MAINTENANCE = 340 +const val ERROR_VULCAN_API_BAD_REQUEST = 341 +const val ERROR_VULCAN_API_OTHER = 342 + +const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN = 401 +const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME = 402 +const val ERROR_LOGIN_IDZIENNIK_WEB_PASSWORD_CHANGE_NEEDED = 403 +const val ERROR_LOGIN_IDZIENNIK_WEB_MAINTENANCE = 404 +const val ERROR_LOGIN_IDZIENNIK_WEB_SERVER_ERROR = 405 +const val ERROR_LOGIN_IDZIENNIK_WEB_API_ERROR = 406 /* {"Message":"There was an error processing the request.","StackTrace":"","ExceptionType":""} */ +const val ERROR_LOGIN_IDZIENNIK_WEB_OTHER = 410 +const val ERROR_LOGIN_IDZIENNIK_WEB_API_NO_ACCESS = 411 /* {"d":{"__type":"mds.Web.mod_komunikator.WS_mod_wiadomosci+detailWiadomosci","Wiadomosc":{"_recordId":0,"DataNadania":null,"DataOdczytania":null,"Nadawca":null,"ListaOdbiorcow":[],"Tytul":null,"Text":null,"ListaZal":[]},"Bledy":{"__type":"mds.Module.Globalne+sBledy","CzyJestBlad":true,"ListaBledow":["Nie masz dostępu do tych zasobów!"],"ListaKodowBledow":[]},"czyJestWiecej":false}} */ +const val ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION = 420 +const val ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH = 421 +const val ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER = 422 +const val ERROR_IDZIENNIK_WEB_ACCESS_DENIED = 430 +const val ERROR_IDZIENNIK_WEB_OTHER = 431 +const val ERROR_IDZIENNIK_WEB_MAINTENANCE = 432 +const val ERROR_IDZIENNIK_WEB_SERVER_ERROR = 433 +const val ERROR_IDZIENNIK_WEB_PASSWORD_CHANGE_NEEDED = 434 +const val ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR = 440 const val ERROR_TEMPLATE_WEB_OTHER = 801 @@ -134,3 +157,5 @@ const val EXCEPTION_MOBIDZIENNIK_WEB_REQUEST = 906 const val EXCEPTION_VULCAN_API_REQUEST = 907 const val EXCEPTION_NOTIFY_AND_SYNC = 910 const val EXCEPTION_LIBRUS_MESSAGES_REQUEST = 911 +const val EXCEPTION_IDZIENNIK_WEB_REQUEST = 912 +const val EXCEPTION_IDZIENNIK_WEB_API_REQUEST = 913 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt index 9edb39b8..08daf09a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt @@ -39,11 +39,6 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) { return@portalGet } val accountDataTime = json.getLong("lastModification") - val accountIds = mutableListOf() - val accountLogins = mutableListOf() - val accountTokens = mutableListOf() - val accountNamesLong = mutableListOf() - val accountNamesShort = mutableListOf() for (accountEl in accounts) { val account = accountEl.asJsonObject @@ -60,26 +55,23 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) { return@portalGet } - accountIds.add(account.getInt("id") ?: continue) - accountLogins.add(account.getString("login") ?: continue) - accountTokens.add(account.getString("accessToken") ?: continue) - accountNamesLong.add(account.getString("studentName") ?: continue) - val nameParts = account.getString("studentName")?.split(" ") ?: continue - accountNamesShort.add(nameParts[0] + " " + nameParts[1][0] + ".") - } + val id = account.getInt("id") ?: continue + val login = account.getString("login") ?: continue + val token = account.getString("accessToken") ?: continue + val tokenTime = (accountDataTime ?: 0) + DAY + val name = account.getString("studentName")?.fixName() ?: "" - for (index in accountIds.indices) { - val newProfile = Profile() - newProfile.studentNameLong = accountNamesLong[index] - newProfile.studentNameShort = accountNamesShort[index] - newProfile.name = newProfile.studentNameLong - newProfile.subname = data.portalEmail - newProfile.empty = true - newProfile.putStudentData("accountId", accountIds[index]) - newProfile.putStudentData("accountLogin", accountLogins[index]) - newProfile.putStudentData("accountToken", accountTokens[index]) - newProfile.putStudentData("accountTokenTime", (accountDataTime ?: 0) + DAY) - profileList.add(newProfile) + val profile = Profile() + profile.studentNameLong = name + profile.studentNameShort = name.getShortName() + profile.name = profile.studentNameLong + profile.subname = data.portalEmail + profile.empty = true + profile.putStudentData("accountId", id) + profile.putStudentData("accountLogin", login) + profile.putStudentData("accountToken", token) + profile.putStudentData("accountTokenTime", tokenTime) + profileList.add(profile) } EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore)) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/firstlogin/MobidziennikFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/firstlogin/MobidziennikFirstLogin.kt index aa98c478..3742e34b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/firstlogin/MobidziennikFirstLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/firstlogin/MobidziennikFirstLogin.kt @@ -6,6 +6,8 @@ import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLoginWeb import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile +import pl.szczodrzynski.edziennik.fixName +import pl.szczodrzynski.edziennik.utils.models.Date class MobidziennikFirstLogin(val data: DataMobidziennik, val onSuccess: () -> Unit) { companion object { @@ -20,30 +22,33 @@ class MobidziennikFirstLogin(val data: DataMobidziennik, val onSuccess: () -> Un web.webGet(TAG, "/api/zrzutbazy") { text -> val tables = text.split("T@B#LA") - val studentIds = mutableListOf() - val studentNamesLong = mutableListOf() - val studentNamesShort = mutableListOf() - val student = tables[8].split("\n") - - for (aStudent in student) { - if (aStudent.isEmpty()) - continue - val student1 = aStudent.split("|") - if (student1.size == 2) - continue - studentIds += student1[0].toInt() - studentNamesLong.add(student1[2] + " " + student1[4]) - studentNamesShort.add(student1[2] + " " + student1[4][0] + ".") + val accountNameLong = run { + tables[0] + .split("\n") + .map { it.split("|") } + .singleOrNull { it.getOrNull(1) != "*" } + ?.let { + "${it[4]} ${it[5]}".fixName() + } } - for (index in studentIds.indices) { + tables[8].split("\n").forEach { student -> + if (student.isEmpty()) + return@forEach + val student1 = student.split("|") + if (student1.size == 2) + return@forEach + + val today = Date.getToday() val profile = Profile() - profile.studentNameLong = studentNamesLong[index] - profile.studentNameShort = studentNamesShort[index] + profile.studentNameLong = "${student1[2]} ${student1[4]}".fixName() + profile.studentNameShort = "${student1[2]} ${student1[4][0]}.".fixName() + profile.accountNameLong = if (accountNameLong == profile.studentNameLong) null else accountNameLong + profile.studentSchoolYear = "${today.year}/${today.year+1}" profile.name = profile.studentNameLong profile.subname = data.loginUsername profile.empty = true - profile.putStudentData("studentId", studentIds[index]) + profile.putStudentData("studentId", student1[0].toInt()) profileList.add(profile) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 54dfdae3..ed4a50b7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -307,6 +307,10 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) } fun notifyAndSyncEvents(onSuccess: () -> Unit) { + if (profile == null) { + onSuccess() + return + } try { DataNotifications(this) ServerSync(this) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt index 9b79713c..958ad901 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt @@ -178,6 +178,6 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app val fullApiUrl: String? get() { - return "${apiUrl}/${schoolSymbol}/" + return "$apiUrl/$schoolSymbol/" } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt index d826dfa2..f54cfdd3 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt @@ -81,7 +81,17 @@ open class VulcanApi(open val data: DataVulcan) { return } - // TODO: Vulcan error handling + if (response?.code() ?: 200 != 200) { + when (response?.code()) { + 503 -> ERROR_VULCAN_API_MAINTENANCE + 400 -> ERROR_VULCAN_API_BAD_REQUEST + else -> ERROR_VULCAN_API_OTHER + }.let { + data.error(ApiError(tag, EXCEPTION_VULCAN_API_REQUEST) + .withResponse(response) + .withApiResponse(json?.toString() ?: response?.parserErrorBody)) + } + } if (json == null) { data.error(ApiError(tag, ERROR_RESPONSE_EMPTY) @@ -127,6 +137,7 @@ open class VulcanApi(open val data: DataVulcan) { .allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST) .allowErrorCode(HttpURLConnection.HTTP_FORBIDDEN) .allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED) + .allowErrorCode(HttpURLConnection.HTTP_UNAVAILABLE) .callback(callback) .build() .enqueue() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt index 2424adc2..3d0b0a44 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt @@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.firstlogin import org.greenrobot.eventbus.EventBus +import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.ERROR_NO_STUDENTS_IN_ACCOUNT import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_STUDENT_LIST import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent @@ -13,10 +14,6 @@ import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLoginApi import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile -import pl.szczodrzynski.edziennik.getInt -import pl.szczodrzynski.edziennik.getJsonArray -import pl.szczodrzynski.edziennik.getLong -import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.utils.models.Date class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) { @@ -42,23 +39,19 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) { students.forEach { studentEl -> val student = studentEl.asJsonObject + val schoolSymbol = student.getString("JednostkaSprawozdawczaSymbol") ?: return@forEach + val schoolName = "${data.symbol}_$schoolSymbol" val studentId = student.getInt("Id") ?: return@forEach val studentLoginId = student.getInt("UzytkownikLoginId") ?: return@forEach val studentClassId = student.getInt("IdOddzial") ?: return@forEach - val studentClassNumber = student.getString("OkresPoziom") - val studentClassSymbol = student.getString("OddzialSymbol") - val studentClassName = "$studentClassNumber$studentClassSymbol" - val studentSemesterId = student.getInt("IdOkresKlasyfikacyjny") - ?: return@forEach - val studentFirstName = student.getString("Imie") - val studentLastName = student.getString("Nazwisko") - val studentNameLong = "$studentFirstName $studentLastName" - val studentNameShort = "$studentFirstName ${studentLastName?.get(0)}." - val userName = student.getString("UzytkownikNazwa") ?: "" + val studentClassName = student.getString("OkresPoziom").toString() + student.getString("OddzialSymbol") + val studentSemesterId = student.getInt("IdOkresKlasyfikacyjny") ?: return@forEach + val studentFirstName = student.getString("Imie") ?: "" + val studentLastName = student.getString("Nazwisko") ?: "" + val studentNameLong = "$studentFirstName $studentLastName".fixName() + val studentNameShort = "$studentFirstName ${studentLastName[0]}.".fixName() + val userLogin = student.getString("UzytkownikLogin") ?: "" - val schoolSymbol = student.getString("JednostkaSprawozdawczaSymbol") - ?: return@forEach - val schoolName = "${data.symbol}_$schoolSymbol" val currentSemesterStartDate = student.getLong("OkresDataOd") ?: return@forEach val currentSemesterEndDate = (student.getLong("OkresDataDo") ?: return@forEach) + 86400 @@ -67,12 +60,20 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) { val newProfile = Profile() newProfile.empty = true + val isParent = student.getString("UzytkownikRola") == "opiekun" + val userName = if (isParent) + student.getString("UzytkownikNazwa")?.swapFirstLastName()?.fixName() + else + null + newProfile.accountNameLong = userName + newProfile.studentClassName = studentClassName + val today = Date.getToday() + newProfile.studentSchoolYear = "${today.year}/${today.year+1}" + newProfile.putStudentData("studentId", studentId) newProfile.putStudentData("studentLoginId", studentLoginId) newProfile.putStudentData("studentClassId", studentClassId) - newProfile.putStudentData("studentClassName", studentClassName) newProfile.putStudentData("studentSemesterId", studentSemesterId) - newProfile.putStudentData("userName", userName) newProfile.putStudentData("schoolSymbol", schoolSymbol) newProfile.putStudentData("schoolName", schoolName) newProfile.putStudentData("currentSemesterEndDate", currentSemesterEndDate) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt index 0aab5950..3ec05738 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt @@ -54,7 +54,7 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) { }} private fun loginWithToken() { - d(TAG, "Request: Vulcan/Login/Api - ${data.apiUrl}$VULCAN_API_ENDPOINT_CERTIFICATE") + d(TAG, "Request: Vulcan/Login/Api - ${data.apiUrl}/$VULCAN_API_ENDPOINT_CERTIFICATE") val callback = object : JsonCallbackHandler() { override fun onSuccess(json: JsonObject?, response: Response?) { @@ -85,6 +85,8 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) { } } "Broken" -> ERROR_LOGIN_VULCAN_INVALID_PIN_0_REMAINING + "OnlyKindergarten" -> ERROR_LOGIN_VULCAN_ONLY_KINDERGARTEN + "NoPupils" -> ERROR_LOGIN_VULCAN_NO_PUPILS else -> ERROR_LOGIN_VULCAN_OTHER }.let { errorCode -> data.error(ApiError(TAG, errorCode) @@ -118,7 +120,7 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) { } Request.builder() - .url(data.apiUrl + VULCAN_API_ENDPOINT_CERTIFICATE) + .url("${data.apiUrl}/$VULCAN_API_ENDPOINT_CERTIFICATE") .userAgent(VULCAN_API_USER_AGENT) .addHeader("RequestMobileType", "RegisterDevice") .addParameter("PIN", data.apiPin) From 7ce7859a5f2a83f82da9c5f2e52c8f7ebfa7f294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 27 Oct 2019 19:43:43 +0100 Subject: [PATCH 105/691] [UI/Login] Display class and school year in summary, show e-register logo icon. --- .../pl/szczodrzynski/edziennik/Extensions.kt | 4 + .../modules/login/LoginSummaryFragment.java | 87 +++++++++++++++--- .../main/res/drawable/logo_dzienniczek.png | Bin 0 -> 1326 bytes app/src/main/res/drawable/logo_idziennik.png | Bin 0 -> 2632 bytes app/src/main/res/drawable/logo_librus.png | Bin 0 -> 1586 bytes .../main/res/drawable/logo_mobidziennik.png | Bin 0 -> 2567 bytes app/src/main/res/drawable/logo_synergia.png | Bin 0 -> 4291 bytes app/src/main/res/drawable/logo_vulcan.png | Bin 0 -> 1218 bytes .../res/layout/fragment_login_summary.xml | 3 +- .../layout/row_login_profile_list_item.xml | 62 +++++++++++-- app/src/main/res/values/strings.xml | 2 + 11 files changed, 135 insertions(+), 23 deletions(-) create mode 100644 app/src/main/res/drawable/logo_dzienniczek.png create mode 100644 app/src/main/res/drawable/logo_idziennik.png create mode 100644 app/src/main/res/drawable/logo_librus.png create mode 100644 app/src/main/res/drawable/logo_mobidziennik.png create mode 100644 app/src/main/res/drawable/logo_synergia.png create mode 100644 app/src/main/res/drawable/logo_vulcan.png diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index ea33eaa1..85b3dfd9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -113,6 +113,10 @@ fun String.getShortName(): String { } } +fun List.join(delimiter: String): String { + return this.joinToString(delimiter) +} + fun colorFromName(context: Context, name: String?): Int { var crc = crc16(name ?: "") crc = (crc and 0xff) or (crc shr 8) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryFragment.java index 43efd3fe..aaf6c4a0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSummaryFragment.java @@ -1,32 +1,40 @@ package pl.szczodrzynski.edziennik.ui.modules.login; -import androidx.databinding.DataBindingUtil; - import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; - import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.databinding.DataBindingUtil; +import androidx.fragment.app.Fragment; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.afollestad.materialdialogs.MaterialDialog; import java.util.ArrayList; import java.util.List; -import androidx.navigation.NavController; -import androidx.navigation.Navigation; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import pl.szczodrzynski.edziennik.App; +import pl.szczodrzynski.edziennik.ExtensionsKt; import pl.szczodrzynski.edziennik.R; +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; import pl.szczodrzynski.edziennik.databinding.FragmentLoginSummaryBinding; import pl.szczodrzynski.edziennik.databinding.RowLoginProfileListItemBinding; -import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; + +import static pl.szczodrzynski.edziennik.api.v2.LoginMethodsKt.LOGIN_MODE_LIBRUS_EMAIL; +import static pl.szczodrzynski.edziennik.api.v2.LoginMethodsKt.LOGIN_MODE_VULCAN_API; +import static pl.szczodrzynski.edziennik.api.v2.LoginMethodsKt.LOGIN_MODE_VULCAN_WEB; +import static pl.szczodrzynski.edziennik.api.v2.LoginMethodsKt.LOGIN_TYPE_IDZIENNIK; +import static pl.szczodrzynski.edziennik.api.v2.LoginMethodsKt.LOGIN_TYPE_LIBRUS; +import static pl.szczodrzynski.edziennik.api.v2.LoginMethodsKt.LOGIN_TYPE_MOBIDZIENNIK; +import static pl.szczodrzynski.edziennik.api.v2.LoginMethodsKt.LOGIN_TYPE_VULCAN; public class LoginSummaryFragment extends Fragment { @@ -63,11 +71,19 @@ public class LoginSummaryFragment extends Fragment { for (LoginProfileObject profileObject: LoginActivity.profileObjects) { int subIndex = 0; for (Profile profile: profileObject.profileList) { + List subnameList = new ArrayList<>(); + if (profile.getStudentClassName() != null) + subnameList.add(profile.getStudentClassName()); + if (profile.getStudentSchoolYear() != null) + subnameList.add(profile.getStudentSchoolYear()); ItemProfileModel profileModel = new ItemProfileModel( index, subIndex, profile.getName(), + ExtensionsKt.join(subnameList, " - "), profileObject.loginStore.type, + profileObject.loginStore.mode, + profile.getAccountNameLong() != null, profileObject.selectedList.get(subIndex) ); profileList.add(profileModel); @@ -133,14 +149,20 @@ public class LoginSummaryFragment extends Fragment { int listIndex; int listSubIndex; String name; + String subname; int loginType; + int loginMode; + boolean isParent; boolean selected; - public ItemProfileModel(int listIndex, int listSubIndex, String name, int loginType, boolean selected) { + public ItemProfileModel(int listIndex, int listSubIndex, String name, String subname, int loginType, int loginMode, boolean isParent, boolean selected) { this.listIndex = listIndex; this.listSubIndex = listSubIndex; this.name = name; + this.subname = subname; this.loginType = loginType; + this.loginMode = loginMode; + this.isParent = isParent; this.selected = selected; } } @@ -176,9 +198,50 @@ public class LoginSummaryFragment extends Fragment { b.checkBox.jumpDrawablesToCurrentState(); } LoginActivity.profileObjects.get(m.listIndex).selectedList.set(m.listSubIndex, m.selected); + }; b.checkBox.setOnClickListener(onClickListener); b.getRoot().setOnClickListener(onClickListener); + int imageRes = 0; + if (m.loginType == LOGIN_TYPE_MOBIDZIENNIK) { + imageRes = R.drawable.logo_mobidziennik; + } + else if (m.loginType == LOGIN_TYPE_IDZIENNIK) { + imageRes = R.drawable.logo_idziennik; + } + else if (m.loginType == LOGIN_TYPE_LIBRUS) { + if (m.loginMode == LOGIN_MODE_LIBRUS_EMAIL) { + imageRes = R.drawable.logo_librus; + } + else { + imageRes = R.drawable.logo_synergia; + } + } + else if (m.loginType == LOGIN_TYPE_VULCAN) { + if (m.loginMode == LOGIN_MODE_VULCAN_WEB) { + imageRes = R.drawable.logo_vulcan; + } + else if (m.loginMode == LOGIN_MODE_VULCAN_API) { + imageRes = R.drawable.logo_dzienniczek; + } + } + if (imageRes != 0) { + b.registerIcon.setImageResource(imageRes); + } + if (m.isParent) { + b.accountType.setText(R.string.login_summary_account_parent); + } + else { + b.accountType.setText(R.string.login_summary_account_child); + } + if (m.subname.trim().isEmpty()) { + b.textDetails.setText(null); + b.textDetails.setVisibility(View.GONE); + } + else { + b.textDetails.setText(m.subname); + b.textDetails.setVisibility(View.VISIBLE); + } //b.root.setOnClickListener(onClickListener); //holder.bind(b.textView, onClickListener); } diff --git a/app/src/main/res/drawable/logo_dzienniczek.png b/app/src/main/res/drawable/logo_dzienniczek.png new file mode 100644 index 0000000000000000000000000000000000000000..a8ec0993a21c979d41e7da4b257e2654eeabfae7 GIT binary patch literal 1326 zcmV+}1=0G6P)HZqLE{R8Xoyi4x-bTJhNZe9z`_JpE`20E zS1x#{3k@d5M_GVD5fEcQsI6!%WjgIV?%aFNJuYTiXbp5;g=xZ<+{N5X?*0Df`~Sc1 zf6j~m*!I>?AFvyE7I10qFu;D`wRgAooe|~azXy6*TrmnfB7t3tFnNG(V2>=*X0gUo z5{NBg07A0J<0gQc0O|}?rDgzwW93k?h`=;xG6}*chOZMd?`SvxZETh}qukrIkG>^G zXm^KEve*y+VdRo7_A)g2AU}_7Wzz4)5<%kt7$nOQr`gi~25s(NSFDj}3=O2ma+P6O z@+50IzT&g9Z!(%+jUZ}_1`=w5O}FhL<&FRXgJ@Xt)6|@PX3801)5@JBRJt)D)<$u6 z*8!HdoWNWM%^M`kTTXIM*8#Lq^#h2E!$Upq6XxO`d(~c4Y(-V=(HMbi<;vQqOT;yb)r2Hd8)iOw*327y969Z)cXNV%N8LY%rRy*=ASMu8!+~jJ6EOoA8?pJh}oh~v1^m9G+vAPmMBI=*>UhY>>2(Yu{wY9l&KbK$pbM3Fy> zJ~|jj%Gzd&C|5FyG-`Zo6e$Ic8^d*D*p7>ovh*c0zs4h-!^<9@BjA=9`sm`{WXBI7 zRcnnqT7xmA3k+cp;`;@Po<|V)C?N#K7`#FruaGY#AT5-#5Yi$v|kGu$4RUP zAeR{gX;(c@gb?I&Q%p{!F_DkfAvz2(CMwlXlrA;5EF~&M1VKb(GzFEbS{Jt4gS3(* z024nTL~XqypUYCnXOUM}2{ET?vI3e{Kfv17Urr)3STlf1-rbNnFlQ*>Cf1q zUR=j20`Nn{!Jjtc&cB~&LIA?B@5rN!1@~2CGwPy{6s8UvGUr@KD+<2D{-cla?U^T7 z+F6-)Y6hSz&6mg55olHC`Glbu*o^|9UH(>7R?sY=-?V--H^_;V3K!XyUq6lzKJ4aAv^xR zO~NdZ*bcUvY*@Q&oaK;sxpAbIOXrH-Jln>mOJ3l6Qxvk}S{W04I(6~Sm%`Q#oOl~j zHXqMt9U>TwpU+U3x&)X53fKWWo}aw9BA*#+wkC5G>md+A`~$o$0M@_w&I({B@Ej0t katIOSzuOFc{L0z?03`0%@=r@hE&u=k07*qoM6N<$f^(WtB>(^b literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/logo_idziennik.png b/app/src/main/res/drawable/logo_idziennik.png new file mode 100644 index 0000000000000000000000000000000000000000..7289caa64ff016a906a9f798ee9fa1d9668c49f0 GIT binary patch literal 2632 zcmV-O3b*x%P)nKJ#6b{8x#(bwhL_2n_hLXK!KuHO$zMA0{cJY z%6GZ$b%UbCx|;-OleXR%jh!@ITec-dqOFr@op=yA!{N+)y^xx{pwfo=(Sc{Hr*QA1b8+b@-zi$MI4ahW~x%&Yhk{MN%`~dFP$hFbw}k9nlEG z@amgyzS&uifvji0bLY^4y&(v6!z?@*|=M)W&f)CX0OZNr8l|(a~aXrZ2%aM_L)` zbv{+BQjZ^w{I-d)nW3r zp%j-cw9}fmqN+LQX8)hzvSfP|4^&5xK} zs_?;hf%nJqtohNVWNIa;OMzoay4nrCdOF3W(RNbqdQwU$qFC|gy9KU%u*}3=ApDpts z7ZAs)E*L4`sS_5bx?N7DZH9VXdO9qOjW@59!mC8woh_5edQ8lhd6)|}!%=noxABJ; zD1P_x3h(}Ph2trc;eMCFZim6N!!tcDmMQlhkWz|lF5rL1^4z>v;=xLVH9w*ns)ovm zZ3td^u7j7qa*UL#V;TSdA5{vb7RvnTR-Rk;N-Pxu%6?23f9ADSeNLZ!EK-oo1!Qvp zSI6=sZH;uh$+>|f-yCUWpv!JZRSg09#c%$?m5+)jEs2mC2)XISRUJYK@T-d7{CS!8 zZ|C{yP>L7Mw_zEQYj+B~e>>02qF-Y*YpjN=ZB+%;NU>T9DS9FImwax|l=$&W-JI%i z_XbdAW}d~V2{g+^w~`o68;0A4ZYDrzHTV6s-uF@=BW@bSbEW@%D)}1Z?YP zGzlF^jlSbLz3q}?2^cxq%2S=z-dXG$qbFHd%<;*+MSOn^zwi*Ln`pX))7piR>_ju1 zPbVa5iyYLaz0rDqR1Z!=?LZyXrd-MCw866j7CKc5#S-(`6~=BA_~@p^E0@mk<&plq z0rc|oBfR*HVdk=ZcoyT9|JI_DI$-ZN_wz1>oe#gta5CX?G z86HeCbg~Q23&<5cCNf#BeLT&3AB^LbRtQR~NU0-r6T@xCZcSsj9oxcbL_K{S8fW`V zPIei1&$u0?NG8{hu= z8GionFY;fP-(qZhmTKTr4a!t2CCaO_7^aQclE!ZBM(7qo=pY_h@@vqL0t{V{um!_C z250*W96eGYr%y1SS5$)v zVc=5@DpY-sYTy$rO|!N%jp4MSTP>LG`idxZ2jqHPkP=dkq^Dh{_n1b?ibxn$vP;Wc zy`E!ksY0o0QVI<;qZh3s!SS^FDV#&In>P##-7v9TMI1$hVU@ru;gt$hy*yPfkC$Iq zx1s6cw556O+)2LFZ_(;NzEEUorNGA%t7I0|2xJOnwi1iuNXx*qB|>c7hpB@KNU2BP zod6+_y2R9VOxq#RcARR($6L!0_+{eI#}CSc-YVx`OtbiKnU59<+sKJwnl40|A&P_2zf9q_R}9c z%l9r0@X1`B@%yVx&*zzaSfm=o+xLA+Y}y^Dn$u0_?yxwOb{Oh&Io0hj&}DBrCh2r? zHt`Ckkvw>SOhe;TUps@nZM-nrN6`z&E|>ZJjr-i1T4FKhZCcS(gT`!6>YRHz$)(YD zdX8DNI2w*AYmQNe`~DTnNFjxG@Bn=(av`uS9oy15(dP1{!DCdzn442e{NckHCT4Qv z)+%^G_z$)c0xUz);%Yq8?eeYjZHx{ku?*SJT-mjb9lmjg5CYTC`0|-9Mu$3CD+f%? zct-6mcBy8juQt zpaMikSRhfQQYr5mP$rYP7lvW+$kK@-e`;!Kyx#t%9-#R0<;yqb=jU%7RX}rdbK|eS z{`$N1TvB%@clYkyXkudGFQcQQ&!$qTc0h~cSj2J6A-=`cYSo{doczDnUVH67u3Wj2 z0ZKs4VzgZ^Zvt&2BO|@9zWVC7`}_L`4a0B`vueUH^ylX0?!WQI8}Ht@abpHp1^lgS zwjCg`0V%O@b2e0WSigy=HtZAtYa7a`Y^5G{kf^q?Z!pB>Lfna@PmBLfOtWGVi$P_4dY(it1(A+ck z4+kVe?IhhK$lh{u7-B8Rsz6i|+xzq(-zM6p#0 zmJGRe+24$K8z_Y+Al z5kB?TYuwZU6>!x+)6N&Z*!1i7g}?)zJTNp0gJJV<6p2J)DHyZ53g@+d{0*X5le~XJ zdGm|FA42StytcLmq}JEheUB;c1!FQU61)$owvJ)$6dEA-^le?OX_|u?9XZ0*-BsE` zeq_jsNXS}ZFR;1X1wHOUhzMjXGZ8y)iO*s42Om{&qlF`lTtttMlP~HwLvnJ2@+*Ni zEMB9_r{*4vp>CM0d_Zp-dySOOnBbpdv9e^UA0YdzgvyY{od1GwWPv4Gs^Is8RV?w? znmiO&C7t&Sf~0QXoE`l9TiG|~etffMTgS|JoYD6tqgrZFMo2~HHVMokInr)L8}rJ- z%S1%KTvWk&s;x#bB~x$-!~4nhUV;d7|Nh2=e3M4|#RWE-?)ZLv zV}au;av+8D4j2D<#J#K22ba_3Xc!tH)n4(K7rgt`*e;{o8H^1|Uro+at#gUqq=x8L z7lbzxP37Mt1ipA_)|Hx=&upNj_7&#Gmn@FBza!q1R}DZ9+)DY--q9T0_02^=IkSu* zX}(9|@fKNx4K6R%bsl4+Ro2VtxPM~$ESyHkl$@2#CTtz?IQ2nNwQb)K&f9ULcHO#p^+QFID2sF?ZMTcP zyIke7oWaRDLej&P@z#Xo1jj7FvPY#U%fZQ6xyCLcRbi(ZX%U$iR?wI!N}9uWn&{#8I^8p+l&MKJ@Z`eA-9 z=*Q&M?;3AAs7MdAFuYRqEoWmf$VeHj?Tk>G8APtnZGqf6kRfdva@{&lj4yPKFY6yF zF<^Y&z85QSY8by|$S}*fQj0^#6^xG@&yML-3!?gIGu}gTef{*FZ?*KX*MrZb9Ib^6 zHLvWXda49aXM@j@sA|(wDR&{c*pdoO?CrtJHIj_n;iXyG>}L8<8i`MHE@h`Eo##oi z5@fWFu8%G4QeyPiW7$RKp$$8=7$Jd*GTdmLECsjBZmk_AL8_8t{`V@=jen~4J6uyl zIwL(mh$_8(6h*puxesZefvZdEZ#i}_U-wClakqS#sb3y7-0(@gfi2@&+q6qf6Y&@9 z?-@J&S4_|KD$PFK(NrFF-~b~g^up}hf~OX1m!=|wFN>}zwfmThGAkQj^C0VpuqMmp zi|#XSw1WMo3E_2-(@BCX=$(ChVDRt*|4Y2J_p6Wgr39Tg9b2qay)Fwe`E<_7?ildr OfN(C}&i5TpCH@Dq!rycN literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/logo_mobidziennik.png b/app/src/main/res/drawable/logo_mobidziennik.png new file mode 100644 index 0000000000000000000000000000000000000000..0d4d853ff337d489d75795fe33f6fbeefc295fac GIT binary patch literal 2567 zcmV+i3i$PjP)egzx4-@E zwfDi-xNchpQZnYIRc-Lpv?1G?ea$wXFQ>2jE~g@WY4`t%_+jy9ZM+@C0WarfDxUWO z-VVUIXFd|(6 z+`2czbOy`!K{1+h%LF`rUjtI94DuoYmk=@Adf*f6J$}gr?GFTzP9?KW9na<_ROF0C z5k-p15Y8`h@TW00I*2$g8M)h!-K3{7)H4{vf6iaUz(^c<;Q%paqkP&qpkQQ#`995Q4Q>u$)qtJ9YSJk1b%-IdBqj#Cp8{NGBIXA$}a~ZJrz)A z??pg!*C!-dX4L<&oJm2d=x+B}XpI@D{NS@7!AfD!PaNFfsO zQDl;7Gfnc2;6uVxC<33~0MaUfH~s>_l-z#kx>yaEjx9~pq3tQM43Yah{5l0FjgsKT&=u)vI{%`4nF`wMvD zcL#C)QZMpLDzRbpJS?cIM8}cOkV&Ty8}1|Zqq&Y;r)Go_RBQslFm=+efHTzGcyy3D zDxppjhz<3yG0mQq9S0Z(<}7>TtT;c6YyBo*njHs56DZCPYJlOMD+Y{E@0}*4NVH;c zn&+{Rew}uCN+lDJ=jc$k>L@}DnQq3`JQz@9$nj+SxsKz}VHKnnBA2?1I$TgfVN(Na zOC{nuC32UNoqm1NsVgY2!T6}L35N63MyAq0B|{8vWMptSc}Rk)Pra#Q0>~uCSdSnS z&Zh>d5R4QUBNZrD1fK(;JVXbssxGcCKEEsjA6M1{eN|l=K|fiK4vGe)7!ZbCvt+VU z=5Q`(^`iOA+A>_}i)puz1>MX(VDJ7>{u(Qz>1(k-{>j z4+@dGko+V7taG_nmlfiXWes>>QN8+>^5DYh*nXfTYvvPBS#fKo-hZkCm#z&NBSEcy z8MHD74Kvf{F>s866mi-Tr!GQyg{l*E>0^d>MqE`^Kp=A%ydDuIT5lPO3TO{AE*=9) z85(4UKJnjcsK2QkYwm48zHA>XXWDx4;?7eT9p|!lrc_^e>&~;R$_b_U_^tq1Gy~y~ zU#HVftL@+3C5y#OwffTj&X4HQwn&U;zOjJIK8g!%+%8xiTvUgX1p4oDSB#nGH^iJ` z*%2^nm(Rw-OK(La6mWs0Qfa(y*kCvBRon-SyHuJ$j%E(v}!GP%~n`L3;B<2t@!1nHM%CJ=Z#Q zll^O=0wK?FpFhEu{xkgfGcWgK6#-FKVH26=iB=P z4|o14D)Jw!Y*IFCNvElsM3PhHksx2*Mf><83-Oaz4)Dozm|RnG<8-;6T&mob9FH5f z9m7+rn$S3{3b#xw)nzP~EiF6+ytexcJ~-2X(Xj+-$_w%E-LvtnC9_->?!A2)_8o7- zffE?0gpVgBe=W0vFPj>!3*!4z^3iT@u&Ab(vjKg$obFk&0p@q z-eau_M#{Cm5o~xWQL zECY9^O#5KX@;O-hwK?u+O{@8xH@zBFWJ@$16 zoNenOpb~W;?KTJ15y;y6o3Ny53Z-DC_B(esa9~HxvezC&zH(t59^14JEf>4FcL86V zH4)FR`-;AngeO_}S7gN4o16R!z%B1Kqv^?|%7g^Jebyv=+};aWWopCr{BQ`H$jFlU zQx%9!1;Y49%&<-k2|_c{l7g_F7Nc?bssP?U)sC|ld(m*S+09pRlYDh|Gg*@3r(Gsu z>BHf(m$^!lZgLc6B>zZmQwsw4IR)=65@+jsEqG(^8Gc}xLd0{sK~ z_j5y6^|%jG6kmcl|nUgEA;-@>&H+0=63ZtKH?RK@alII+u zT$a6*>WU(4`1T#{^c*A@Sr}4&@%jhYf1-^(Wy5Ar{$*FGnmC2XIAG6zHt?8A0h+Y$ z@aWPB)skgNwInRDEN+^Lw>I2Q0T=80E6im-+j2;qOY|M@pJ&o@&fA_jQk|-aCkQd+ ztTO4?aI@r(Zg;dYg>tU>7gv#&rB!69k$FdQjuotYcCUMW5BhTeFsRDI1~yx%p~VBn z0jS3hTecti1e>2+tRuDM_d8GOxkk!n);F(Ts`W5g5-?K4!;XU=VbiuFh;aoOdCH-} z_!fVrmW`Fej5u*ZW;Mi4ukSsF-ACK_Z7)Y{^G()0AbJw(j2_gQKkdd`?sazM9B%2v zZ{Imi0gGlF01`lp>1S(hR4*~Eow3T9GHJ3&&jxgtwssUPox}@IEa6e(BHrA0E(eGP zR198VH$i`LO^1002ovPDHLkV1ktU-wFT# literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/logo_synergia.png b/app/src/main/res/drawable/logo_synergia.png new file mode 100644 index 0000000000000000000000000000000000000000..9944ae642a2c9a80c0c7c5b4c5af7f11686af2b4 GIT binary patch literal 4291 zcmV;!5IpaRP)XeXG)c$-fsjA|fry+UprWF$L9A7*r{aNFl^H_>+T(#}f<&c5L5>|Z5IGNEV+6iwlqK#C+omShl$z@C#{ zyJ%a*FN!vl{Z~u?1x6;F)o&g8kM`4-cTqG2nq%-yCP@$>2@>4P3Jz30zx8=bnyu%y z0FC#JxR1QLcmGgl|63LruVgdh(~hOOiNpYFcfk{;otn?j}f&BJdDbq;2$+&i3m z;#UXXktK<`36Q6{>EZTMO*=W04p9CqZwEzIZVFFDlA*{7aHkJSQNS8?=&Enq)biPd zrvZQx1(CV9ujZd9hQ32D0Z4)bA;^Odgdv6n00gX_h0bb((Po0qY6Poi0T75`0em0~ zA;?1#q&or{!?Cz{@a&6xFl=CYE_7@d>g=zWZEbZ!Td;Z#_G~9ob2E^dn+aQ%1Ey3f zIFlX>$AC~26j_GIi{Sks_=hJj);o-$&OQuv^kd9B3_cJB&CsA(2D4$4Q7nzIo}t37 zy0&dhN6qEOWJv}+8#ayt2w@%+MIkje6Z2LSVs3dp9PV_)#Ia5*9PV_?Ezbu4$dZha zu0eF4Z%60Z7Wh0~NP+}L&w>!5^=(7vxz?TJgCD%nJnkJXCK>}Ih6GRyjhvUo z=9JC@p|t%CZ{t-oAFapLb1e`;0$2m59YC@IbDGsd-g)x%fGo)dZFD6d3lbzz!rZbv zl<%xW=E9s=ml=a?y*U3r)#yCe3PMq!Ia(8CLntal-u~;?G?RKlkcTPNf{G{Bp=iUB zJFX1dPc`H0yPsp+I}FySjiJkEC2RO#7&*)4qw=|}u%z3PS}rsZMCX|n^k42maLf;i zreR4>K}K;lTqSN8QcSn}Uf${ja4npR>fauPr@9fGfdi71O^g0LPKb*jL|0oDWBI_xDEXyDV&cc*xLC&%Q6mKd=+I*Ml`v#jCoBr_;IC9cZ zxAz2SmQG6c(K{AzemNO%BtgVoPjAAq$JZvUN$UStg;T#h0*M#F=vmM#4T_0;XqExZ z(hx!d`dhlucJdNNyM3@{JE6B4RcpCQ+%P#TxK`VCU7nCT3y`8Hka!WRpS>50?_Zhp zE$uwhf|I}b1dN^o#Zc1-P6C=`KmZK3dC^wY2m&CZC|h+v&O8?^X({MD+X6yJLI;>I z?}eH$Y-oKw7>FtG}~FGxa>u?Sm{yNv*+|^ce3O!J${)!(f|NHNW!7 zjmRo-LkJ7A2auvDpcopJ-`fUOA8oac_6#8~GMS*Tmo*7juzC)m@c@p#wilz_zUYlI zG%CNh6|6xQi4@ERKthm5$+qQ4FUX3vTgC(U^56GCQ0Y@PP7C_M(ulFf@T?r zVF8oAv4l6Z`2Lk}EzE%s`pf@Wz*q6DAEo4C)TW3m4GJ7CGMX$BI_(irXbq4`K%^fs z1z_|X=2tF?=AICb=1;!@ql?HLnx@gy(2>-JteJM)_oK&Qb=g&?o;dIvnm?^qt(UiY z0rVEbj3L|^0g?m}&fE+*-04x{*J`g|tatckWRT_<^tbk4q-!v#4cT+ju>IxT$XYxP zyg!t5hJ|7%jCqIAd8Q?5+>&lXMqxHYUQ7}oQ9@Q}t}6EHscwqC1|%Yrnhw_`ed=bX z6l}~NNeJ}kL9PV`R{xFmT%>bHZFxb|MN#A(Xc;7UeQNPNuB!oZ=38;DJQ_Yf(bC%@d!Jj>i6}#7i)pH4%0)!$6jR!H%(j7IP zl9dXh-7+l~Q8TG`bE*{vTXY0(u+0nkHtqqVXVG=O9rYhnY38djIu_-RR^q`|pFw_Q z3B<4fS(M__bc%w3w%({A*1*A%kuoi3PR~J-43pEU;*`;DAE;@<60Cv4S09{&r>YTJ zQi{ugjW0Zm4L^7Y2Ae4^O-E=-<(~n7In98^$DrfTAg!_|*Yi zJ=Y4YDZjD=55Dp=aw-ZUAjNQ-DT+d1WK!j*njDsC#legK>CMp@Az6?R4uob5Y!RA* zEGhW>=O1d$Ns*IJ!ImHGLix@$5W~XE9y&$9`$H;6b*k$WzZRRKsU%T~nCLOwe~O_X z%L%vh(|-+DBU}==NOLv z>&FO91T_b$WNQWPer_9NQJVhUlv}+!N#2s4x?!57phx- zRJ5rKD|WAk7!qy^PUNuYY4;ZwK!gy4Cqt?$#~Ko=Kx2(MjCqG~5f$>Twe+y5LTH0ilTtgb1K7d>i*&eD0qua7#(IPQeuW6%`u=D3XO+r@$rklN8_Pd zm0U=MRz0&B)+~pLGZa~YA;ko0ip&X&OiXJ}Q8Q_x$)RzT8ntCPZ)+&VC|$uBbqI`3 zqUy~<*!R*N^jv7y1R$NofQrZOQAG@8Nru&BS3N&5Fg9&@Gc}Wdo%D?(IPQ-cPs?{{ z60e~c8k|vwL5~-Qe)c|2y?yldG9(VlTeA>p`K~CC6h(nEFLO%3kH^HoC`h6JMSK{) z$^uO<$b#N#j4L}ZlPf_p6fW+o!NH%ti?QC}q`miob)}GRTr&k=^&B#avZJ0G^A000 zGBIt!AOWH}&< zG9+GtdwHSiDs`W4pRv7SW`I}*zILzbn&sYA1WURt;TX+y?hUJF5ghU3^Iv=f-XBhw zBU7?cAqpaN76az5U8M3S`&+srg`mk<(vdR#ss5DCY(UZa(zudv@qok_I1GBcsM~uY z;dM+73xq%zxpx)8ni;LZI%=8`9P`iUQ^u6lU^o_SCmU74s*-KXVM(_^7PY@>P!w=R z9a=uWfbqVOgl#U)q{otnR;g5e>+yyexhH0TD2BpV?+~t>xEM9Aw;HkZvAYvaImhCn zgs+$5B|>3;2qjx9V4LHN8t*)F8NT+u86DM_0lIM-7x$lm5R8@y#hc18Z+W4n3BPES zO)!{XzRfl#70Y(5jpiOv!i5h{$K3lCGe8tWW3*=o4IkI2WK`vITVYO1x!zWwc}zof zC^0**8_J+hi7wA;KKc~~+InNIMT)DWg4J_q*ndVVv8oGZ_g3M5st}N!6Q&RJp3mg*jON_d7rc(G(;_QIIx2OOth~-Z~V~ zTyb<2#9frb>NxaW?n2F;W2(7%D~qu4g-1Yh3`AZ`UM49DbS6ErOLLP3sP6p}xK`H| zce_H|04a#5-^N3=xOCvGYTmt~5LU}xZAu98$S%u+)#XTPoz~+GsQ+VC zq_8|*C6SDSS+nODT8}rV<}!-1@xUujp>W+|h#?`N)UhndV2wH~eeCX}!09~Gf~q$@ z0nN|}Roy3CCrtCbO!SYU zr=bHKHJ8!X+yx;N2BYIN?N_~_Nu9ZwwM_2H;t$%Zo1U7ja0P4N00rp1bQL`pI-s){ zV9RzQE#C!aZYHcQ2aI-eq%Hh<^P8eX`q20QkKpJe#`}ih>*&W|TQ5eg4I(rV1fd9+ z>q*SnAVpD-Q&I3CGq*hdQ%6pE!_d`%(pcgniOQxM)v%HvVem>X`Y(3@AfQ<~lJV5D zpjieKO+~sGc@bimhj7Etyxu25F|_8~qUIhM)=WpIyP|ME#pu}ZqU|eQOSVul9tL<1w4D%@2y6knG zSwBGm!2Gp~-pi>dIKl_QNjJeJ3L2rQn;b$h^q-pR?&Rx`i2vs*ai3neW!YOdeKCrr zl(o-q`VwV6Wo6xB#RQnumD$y z`{dfc-TtgSJMGGpTX$<}JY4&1<0rNMFxER%q9_Vzh6eej{K=avLlR{`QDDuqw=UeW z?B7bZR{Vx$8SxfkXtSP)@c`{7Iq#>?mg$+yTshw8~}vF zVd#7-SJ*1cs%ffTOlJ#PHKnGh`T2POphV_&r2VyX_-O54o=6bHXOkDz^#5J5n?Re1 zj8Js+>Ww@7SGotZl|+CpIV}W1)GJ*A zN*(7jCq!}Ma4{iJdq0YQs6HW zqaL(2IuoKm;fYX>(SVwko~%gmTCrT+-OuqvzxeKH0;HnpndlU1%I6DG&f3=N499Wx zijpF|4*aw?2Lgb*ccw`aAMopM3r&GG5=T7cg2O}$l9vo1yRC2h+EvuFy!tGcJ83XC zcUsyzI^~H$k?1TH2mn42+1kZYY4Oo=gNefJY`5r4jJjQ*@oQHH2l`RdY`l@zUJCpb z9I-Z~X%0k^MG1}+#xLw0Son_gxtxb|dzgb(;34##2TD%(|01zukets}_ z_QUHi)kNMqOGkjBQ@7Bly_-m`t#1H;+itA+NJ?&R7l>i>q481FbpMw)0MJT>(`Enw zM$3O^=?D-2h~?Uci%a=@0RS!znE)UlIeE1UGJ^63-w2Jh3Dnldmzn5P`Y2 + android:layout_weight="1" + tools:listitem="@layout/row_login_profile_list_item">
diff --git a/app/src/main/res/layout/row_login_profile_list_item.xml b/app/src/main/res/layout/row_login_profile_list_item.xml index 054e0ad7..322fad71 100644 --- a/app/src/main/res/layout/row_login_profile_list_item.xml +++ b/app/src/main/res/layout/row_login_profile_list_item.xml @@ -1,6 +1,5 @@ + android:gravity="center_vertical" + android:focusable="true"> - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9aef94bf..26202e3b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -964,4 +964,6 @@ Pobieranie kategorii uwag... Pobieranie zebrań z rodzicami... Pobieranie wiadomości wysłanych... + (rodzic) + (uczeń) From 4e8b80822b48cd343e3e3fa257183e447eeec481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 27 Oct 2019 19:45:14 +0100 Subject: [PATCH 106/691] [APIv2/Idziennik] Add Idziennik implementation (Web, Api login + first login) --- .../edziennik/api/v2/ApiService.kt | 2 + .../edziennik/api/v2/Constants.kt | 13 +- .../edziennik/api/v2/LoginMethods.kt | 14 +- .../szczodrzynski/edziennik/api/v2/Regexes.kt | 24 +++ .../api/v2/idziennik/DataIdziennik.kt | 109 +++++++++++++ .../edziennik/api/v2/idziennik/Idziennik.kt | 112 +++++++++++++ .../api/v2/idziennik/IdziennikFeatures.kt | 24 +++ .../api/v2/idziennik/data/IdziennikData.kt | 45 ++++++ .../api/v2/idziennik/data/IdziennikWeb.kt | 147 ++++++++++++++++++ .../firstlogin/IdziennikFirstLogin.kt | 80 ++++++++++ .../api/v2/idziennik/login/IdziennikLogin.kt | 58 +++++++ .../v2/idziennik/login/IdziennikLoginApi.kt | 22 +++ .../v2/idziennik/login/IdziennikLoginWeb.kt | 145 +++++++++++++++++ app/src/main/res/values/strings.xml | 2 + 14 files changed, 795 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikWeb.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/firstlogin/IdziennikFirstLogin.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLogin.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginApi.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt index 0b2ceefc..0f8c0d35 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt @@ -18,6 +18,7 @@ import pl.szczodrzynski.edziennik.api.v2.events.* import pl.szczodrzynski.edziennik.api.v2.events.requests.* import pl.szczodrzynski.edziennik.api.v2.events.task.ErrorReportTask import pl.szczodrzynski.edziennik.api.v2.events.task.NotifyTask +import pl.szczodrzynski.edziennik.api.v2.idziennik.Idziennik import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.librus.Librus @@ -219,6 +220,7 @@ class ApiService : Service() { LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback) LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback) LOGIN_TYPE_VULCAN -> Vulcan(app, profile, loginStore, taskCallback) + LOGIN_TYPE_IDZIENNIK -> Idziennik(app, profile, loginStore, taskCallback) LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback) else -> null } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index 371e3c58..90aa98ec 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -45,7 +45,18 @@ const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/to const val LIBRUS_MESSAGES_URL = "https://wiadomosci.librus.pl/module" const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action=" - +const val IDZIENNIK_USER_AGENT = SYNERGIA_USER_AGENT +const val IDZIENNIK_WEB_URL = "https://iuczniowie.progman.pl/idziennik" +const val IDZIENNIK_WEB_LOGIN = "login.aspx" +const val IDZIENNIK_WEB_SETTINGS = "mod_panelRodzica/Ustawienia.aspx" +const val IDZIENNIK_WEB_TIMETABLE = "mod_panelRodzica/plan/WS_Plan.asmx/pobierzPlanZajec" +const val IDZIENNIK_WEB_GRADES = "mod_panelRodzica/oceny/WS_ocenyUcznia.asmx/pobierzOcenyUcznia" +const val IDZIENNIK_WEB_MISSING_GRADES = "mod_panelRodzica/brak_ocen/WS_BrakOcenUcznia.asmx/pobierzBrakujaceOcenyUcznia" +const val IDZIENNIK_WEB_EXAMS = "mod_panelRodzica/sprawdziany/mod_sprawdzianyPanel.asmx/pobierzListe" +const val IDZIENNIK_WEB_NOTICES = "mod_panelRodzica/uwagi/WS_uwagiUcznia.asmx/pobierzUwagiUcznia" +const val IDZIENNIK_WEB_ATTENDANCE = "mod_panelRodzica/obecnosci/WS_obecnosciUcznia.asmx/pobierzObecnosciUcznia" +const val IDZIENNIK_WEB_ANNOUNCEMENTS = "mod_panelRodzica/tabOgl/WS_tablicaOgloszen.asmx/GetOgloszenia" +const val IDZIENNIK_WEB_MESSAGES_LIST = "mod_komunikator/WS_wiadomosci.asmx/PobierzListeWiadomosci" val MOBIDZIENNIK_USER_AGENT = SYSTEM_USER_AGENT diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt index a16c265e..6dd2ffb9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt @@ -4,9 +4,11 @@ package pl.szczodrzynski.edziennik.api.v2 -import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginPortal +import pl.szczodrzynski.edziennik.api.v2.idziennik.login.IdziennikLoginApi +import pl.szczodrzynski.edziennik.api.v2.idziennik.login.IdziennikLoginWeb import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginMessages +import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginPortal import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLoginWeb import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod @@ -118,6 +120,16 @@ val vulcanLoginMethods = listOf( } ) +val idziennikLoginMethods = listOf( + LoginMethod(LOGIN_TYPE_IDZIENNIK, LOGIN_METHOD_IDZIENNIK_WEB, IdziennikLoginWeb::class.java) + .withIsPossible { _, _ -> true } + .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }, + + LoginMethod(LOGIN_TYPE_IDZIENNIK, LOGIN_METHOD_IDZIENNIK_API, IdziennikLoginApi::class.java) + .withIsPossible { _, _ -> true } + .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_IDZIENNIK_WEB } +) + val templateLoginMethods = listOf( LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_WEB, TemplateLoginWeb::class.java) .withIsPossible { _, _ -> true } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt index ab0f4260..a35d95ba 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt @@ -36,4 +36,28 @@ object Regexes { val MOBIDZIENNIK_CLASS_CALENDAR by lazy { """events: (.+),$""".toRegex(RegexOption.MULTILINE) } + + + + val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy { + "".toRegex(RegexOption.DOT_MATCHES_ALL) + } + val IDZIENNIK_LOGIN_ERROR by lazy { + "id=\"spanErrorMessage\">(.*?)(.+?)".toRegex(RegexOption.DOT_MATCHES_ALL) + } + val IDZIENNIK_LOGIN_FIRST_IS_PARENT by lazy { + "id=\"ctl00_CzyRodzic\" value=\"([01])\" />".toRegex() + } + val IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR by lazy { + "name=\"ctl00\\\$dxComboRokSzkolny\".+?selected=\"selected\".*?value=\"([0-9]+)\">([0-9/]+)<".toRegex(RegexOption.DOT_MATCHES_ALL) + } + val IDZIENNIK_LOGIN_FIRST_STUDENT_SELECT by lazy { + "".toRegex(RegexOption.DOT_MATCHES_ALL) + } + val IDZIENNIK_LOGIN_FIRST_STUDENT by lazy { + "(.+?)\\s(.+?)\\s*\\((.+?),\\s*(.+?)\\)".toRegex(RegexOption.DOT_MATCHES_ALL) + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt new file mode 100644 index 00000000..faa2f517 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt @@ -0,0 +1,109 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-25. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik + +import okhttp3.Cookie +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_API +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_WEB +import pl.szczodrzynski.edziennik.api.v2.models.Data +import pl.szczodrzynski.edziennik.currentTimeUnix +import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile +import pl.szczodrzynski.edziennik.isNotNullNorEmpty + +class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) { + + fun isWebLoginValid() = loginExpiryTime-30 > currentTimeUnix() && webSessionId.isNotNullNorEmpty() && webAuth.isNotNullNorEmpty() + fun isApiLoginValid() = loginExpiryTime-30 > currentTimeUnix() && apiBearer.isNotNullNorEmpty() + + override fun satisfyLoginMethods() { + loginMethods.clear() + if (isWebLoginValid()) { + loginMethods += LOGIN_METHOD_IDZIENNIK_WEB + app.cookieJar.saveFromResponse(null, listOf( + Cookie.Builder() + .name("ASP.NET_SessionId_iDziennik") + .value(webSessionId!!) + .domain("iuczniowie.progman.pl") + .secure().httpOnly().build(), + Cookie.Builder() + .name(".ASPXAUTH") + .value(webAuth!!) + .domain("iuczniowie.progman.pl") + .secure().httpOnly().build() + )) + } + if (isApiLoginValid()) + loginMethods += LOGIN_METHOD_IDZIENNIK_API + } + + private var mLoginExpiryTime: Long? = null + var loginExpiryTime: Long + get() { mLoginExpiryTime = mLoginExpiryTime ?: loginStore.getLoginData("loginExpiryTime", 0L); return mLoginExpiryTime ?: 0L } + set(value) { loginStore.putLoginData("loginExpiryTime", value); mLoginExpiryTime = value } + + /* __ __ _ + \ \ / / | | + \ \ /\ / /__| |__ + \ \/ \/ / _ \ '_ \ + \ /\ / __/ |_) | + \/ \/ \___|_._*/ + private var mWebSchoolName: String? = null + var webSchoolName: String? + get() { mWebSchoolName = mWebSchoolName ?: loginStore.getLoginData("schoolName", null); return mWebSchoolName } + set(value) { loginStore.putLoginData("schoolName", value); mWebSchoolName = value } + private var mWebUsername: String? = null + var webUsername: String? + get() { mWebUsername = mWebUsername ?: loginStore.getLoginData("username", null); return mWebUsername } + set(value) { loginStore.putLoginData("username", value); mWebUsername = value } + private var mWebPassword: String? = null + var webPassword: String? + get() { mWebPassword = mWebPassword ?: loginStore.getLoginData("password", null); return mWebPassword } + set(value) { loginStore.putLoginData("password", value); mWebPassword = value } + + private var mWebSessionId: String? = null + var webSessionId: String? + get() { mWebSessionId = mWebSessionId ?: loginStore.getLoginData("webSessionId", null); return mWebSessionId } + set(value) { loginStore.putLoginData("webSessionId", value); mWebSessionId = value } + private var mWebAuth: String? = null + var webAuth: String? + get() { mWebAuth = mWebAuth ?: loginStore.getLoginData("webAuth", null); return mWebAuth } + set(value) { loginStore.putLoginData("webAuth", value); mWebAuth = value } + + /* _ + /\ (_) + / \ _ __ _ + / /\ \ | '_ \| | + / ____ \| |_) | | + /_/ \_\ .__/|_| + | | + |*/ + private var mApiBearer: String? = null + var apiBearer: String? + get() { mApiBearer = mApiBearer ?: loginStore.getLoginData("apiBearer", null); return mApiBearer } + set(value) { loginStore.putLoginData("apiBearer", value); mApiBearer = value } + + /* ____ _ _ + / __ \| | | | + | | | | |_| |__ ___ _ __ + | | | | __| '_ \ / _ \ '__| + | |__| | |_| | | | __/ | + \____/ \__|_| |_|\___|*/ + private var mStudentId: String? = null + var studentId: String? + get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", null); return mStudentId } + set(value) { profile?.putStudentData("studentId", value) ?: return; mStudentId = value } + + private var mRegisterId: Int? = null + var registerId: Int + get() { mRegisterId = mRegisterId ?: profile?.getStudentData("registerId", 0); return mRegisterId ?: 0 } + set(value) { profile?.putStudentData("registerId", value) ?: return; mRegisterId = value } + + private var mSchoolYearId: Int? = null + var schoolYearId: Int + get() { mSchoolYearId = mSchoolYearId ?: profile?.getStudentData("schoolYearId", 0); return mSchoolYearId ?: 0 } + set(value) { profile?.putStudentData("schoolYearId", value) ?: return; mSchoolYearId = value } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt new file mode 100644 index 00000000..2a09756f --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt @@ -0,0 +1,112 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-25. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik + +import android.util.Log +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikData +import pl.szczodrzynski.edziennik.api.v2.idziennik.firstlogin.IdziennikFirstLogin +import pl.szczodrzynski.edziennik.api.v2.idziennik.login.IdziennikLogin +import pl.szczodrzynski.edziennik.api.v2.idziennikLoginMethods +import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback +import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.api.v2.prepare +import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile +import pl.szczodrzynski.edziennik.utils.Utils + +class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { + companion object { + private const val TAG = "Idziennik" + } + + val internalErrorList = mutableListOf() + val data: DataIdziennik + + init { + data = DataIdziennik(app, profile, loginStore).apply { + callback = wrapCallback(this@Idziennik.callback) + satisfyLoginMethods() + } + } + + private fun completed() { + data.saveData() + data.notifyAndSyncEvents { + callback.onCompleted() + } + } + + /* _______ _ _ _ _ _ + |__ __| | /\ | | (_) | | | + | | | |__ ___ / \ | | __ _ ___ _ __ _| |_| |__ _ __ ___ + | | | '_ \ / _ \ / /\ \ | |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \ + | | | | | | __/ / ____ \| | (_| | (_) | | | | |_| | | | | | | | | + |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| + __/ | + |__*/ + override fun sync(featureIds: List, viewId: Int?) { + data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId) + Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") + Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") + IdziennikLogin(data) { + IdziennikData(data) { + completed() + } + } + } + + override fun getMessage(messageId: Int) { + + } + + override fun markAllAnnouncementsAsRead() { + + } + + override fun firstLogin() { + IdziennikFirstLogin(data) { + completed() + } + } + + override fun cancel() { + Utils.d(TAG, "Cancelled") + data.cancel() + } + + private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { + return object : EdziennikCallback { + override fun onCompleted() { + callback.onCompleted() + } + + override fun onProgress(step: Int) { + callback.onProgress(step) + } + + override fun onStartProgress(stringRes: Int) { + callback.onStartProgress(stringRes) + } + + override fun onError(apiError: ApiError) { + when (apiError.errorCode) { + in internalErrorList -> { + // finish immediately if the same error occurs twice during the same sync + callback.onError(apiError) + } + CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> { + internalErrorList.add(apiError.errorCode) + loginStore.removeLoginData("refreshToken") // force a clean login + //loginLibrus() + } + else -> callback.onError(apiError) + } + } + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt new file mode 100644 index 00000000..c3a01ea1 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-25. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik + +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.models.Feature + +const val ENDPOINT_IDZIENNIK_WEB_SAMPLE = 9991 +const val ENDPOINT_IDZIENNIK_WEB_SAMPLE_2 = 9992 +const val ENDPOINT_IDZIENNIK_API_SAMPLE = 9993 + +val IdziennikFeatures = listOf( + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_STUDENT_INFO, listOf( + ENDPOINT_IDZIENNIK_WEB_SAMPLE to LOGIN_METHOD_IDZIENNIK_WEB + ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_SCHOOL_INFO, listOf( + ENDPOINT_IDZIENNIK_WEB_SAMPLE_2 to LOGIN_METHOD_IDZIENNIK_WEB + ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_GRADES, listOf( + ENDPOINT_IDZIENNIK_API_SAMPLE to LOGIN_METHOD_IDZIENNIK_API + ), listOf(LOGIN_METHOD_IDZIENNIK_API)) +) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt new file mode 100644 index 00000000..4873a275 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-25. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.data + +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.utils.Utils + +class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "IdziennikData" + } + + private var cancelled = false + + init { + nextEndpoint(onSuccess) + } + + private fun nextEndpoint(onSuccess: () -> Unit) { + if (data.targetEndpointIds.isEmpty()) { + onSuccess() + return + } + if (cancelled) { + onSuccess() + return + } + useEndpoint(data.targetEndpointIds.removeAt(0)) { + nextEndpoint(onSuccess) + } + } + + private fun useEndpoint(endpointId: Int, onSuccess: () -> Unit) { + Utils.d(TAG, "Using endpoint $endpointId") + when (endpointId) { + /*ENDPOINT_IDZIENNIK_WEB_SAMPLE -> { + data.startProgress(R.string.edziennik_progress_endpoint_student_info) + IdziennikWebSample(data) { onSuccess() } + }*/ + else -> onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikWeb.kt new file mode 100644 index 00000000..f1241c0c --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikWeb.kt @@ -0,0 +1,147 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-25. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.data + +import com.google.gson.JsonObject +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.callback.JsonCallbackHandler +import im.wangchao.mhttp.callback.TextCallbackHandler +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.utils.Utils.d +import java.net.HttpURLConnection.HTTP_INTERNAL_ERROR +import java.net.HttpURLConnection.HTTP_UNAUTHORIZED + +open class IdziennikWeb(open val data: DataIdziennik) { + companion object { + const val TAG = "IdziennikWeb" + } + + val profileId + get() = data.profile?.id ?: -1 + + val profile + get() = data.profile + + fun webApiGet(tag: String, endpoint: String, parameters: Map = emptyMap(), onSuccess: (json: JsonObject) -> Unit) { + d(tag, "Request: Idziennik/Web/API - $IDZIENNIK_WEB_URL/$endpoint") + + val callback = object : JsonCallbackHandler() { + override fun onSuccess(json: JsonObject?, response: Response?) { + if (json == null && response?.parserErrorBody == null) { + data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY) + .withResponse(response)) + return + } + + when { + response?.code() == HTTP_UNAUTHORIZED -> ERROR_IDZIENNIK_WEB_ACCESS_DENIED + response?.code() == HTTP_INTERNAL_ERROR -> ERROR_IDZIENNIK_WEB_SERVER_ERROR + response?.parserErrorBody != null -> when { + response.parserErrorBody.contains("Identyfikator zgłoszenia") -> ERROR_IDZIENNIK_WEB_SERVER_ERROR + response.parserErrorBody.contains("Hasło dostępu do systemu wygasło") -> ERROR_IDZIENNIK_WEB_PASSWORD_CHANGE_NEEDED + response.parserErrorBody.contains("Trwają prace konserwacyjne") -> ERROR_IDZIENNIK_WEB_MAINTENANCE + else -> ERROR_IDZIENNIK_WEB_OTHER + } + else -> null + }?.let { errorCode -> + data.error(ApiError(TAG, errorCode) + .withApiResponse(json?.toString() ?: response?.parserErrorBody) + .withResponse(response)) + return + } + + if (json == null) { + data.error(ApiError(tag, ERROR_RESPONSE_EMPTY) + .withResponse(response)) + return + } + + try { + onSuccess(json) + } catch (e: Exception) { + data.error(ApiError(tag, EXCEPTION_IDZIENNIK_WEB_API_REQUEST) + .withResponse(response) + .withThrowable(e) + .withApiResponse(json)) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(ApiError(tag, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) + } + } + + Request.builder() + .url("$IDZIENNIK_WEB_URL/$endpoint") + .userAgent(IDZIENNIK_USER_AGENT) + .postJson() + .apply { + parameters.map { (name, value) -> + addParameter(name, value) + } + } + .allowErrorCode(HTTP_UNAUTHORIZED) + .allowErrorCode(HTTP_INTERNAL_ERROR) + .callback(callback) + .build() + .enqueue() + } + + fun webGet(tag: String, endpoint: String, onSuccess: (text: String) -> Unit) { + d(tag, "Request: Idziennik/Web - $IDZIENNIK_WEB_URL/$endpoint") + + val callback = object : TextCallbackHandler() { + override fun onSuccess(text: String?, response: Response?) { + if (text == null) { + data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY) + .withResponse(response)) + return + } + + if (!text.contains("czyWyswietlicDostepMobilny")) { + when { + text.contains("Identyfikator zgłoszenia") -> ERROR_IDZIENNIK_WEB_SERVER_ERROR + text.contains("Hasło dostępu do systemu wygasło") -> ERROR_IDZIENNIK_WEB_PASSWORD_CHANGE_NEEDED + text.contains("Trwają prace konserwacyjne") -> ERROR_IDZIENNIK_WEB_MAINTENANCE + else -> ERROR_IDZIENNIK_WEB_OTHER + }.let { errorCode -> + data.error(ApiError(TAG, errorCode) + .withApiResponse(text) + .withResponse(response)) + return + } + } + + try { + onSuccess(text) + } catch (e: Exception) { + data.error(ApiError(tag, EXCEPTION_IDZIENNIK_WEB_REQUEST) + .withResponse(response) + .withThrowable(e) + .withApiResponse(text)) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(ApiError(tag, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) + } + } + + Request.builder() + .url("$IDZIENNIK_WEB_URL/$endpoint") + .userAgent(IDZIENNIK_USER_AGENT) + .get() + .callback(callback) + .build() + .enqueue() + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/firstlogin/IdziennikFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/firstlogin/IdziennikFirstLogin.kt new file mode 100644 index 00000000..b282643b --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/firstlogin/IdziennikFirstLogin.kt @@ -0,0 +1,80 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-27. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.firstlogin + +import org.greenrobot.eventbus.EventBus +import pl.szczodrzynski.edziennik.api.v2.ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR +import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_SETTINGS +import pl.szczodrzynski.edziennik.api.v2.Regexes +import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb +import pl.szczodrzynski.edziennik.api.v2.idziennik.login.IdziennikLoginWeb +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile +import pl.szczodrzynski.edziennik.fixName +import pl.szczodrzynski.edziennik.get +import pl.szczodrzynski.edziennik.swapFirstLastName + +class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "IdziennikFirstLogin" + } + + private val web = IdziennikWeb(data) + private val profileList = mutableListOf() + + init { + IdziennikLoginWeb(data) { + web.webGet(TAG, IDZIENNIK_WEB_SETTINGS) { text -> + //val accounts = json.getJsonArray("accounts") + + val isParent = Regexes.IDZIENNIK_LOGIN_FIRST_IS_PARENT.find(text)?.get(1) != "0" + val accountNameLong = if (isParent) + Regexes.IDZIENNIK_LOGIN_FIRST_ACCOUNT_NAME.find(text)?.get(1)?.swapFirstLastName()?.fixName() + else + null + + var schoolYearName: String? = null + val schoolYear = Regexes.IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR.find(text)?.let { + schoolYearName = it[2] + it[1].toIntOrNull() + } ?: run { + data.error(ApiError(TAG, ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR) + .withApiResponse(text)) + return@webGet + } + + Regexes.IDZIENNIK_LOGIN_FIRST_STUDENT.findAll(text) + .toMutableList() + .reversed() + .forEach { match -> + val registerId = match[1].toIntOrNull() ?: return@forEach + val studentId = match[2] + val firstName = match[3] + val lastName = match[4] + val className = match[5] + " " + match[6] + + val profile = Profile() + profile.studentNameLong = "$firstName $lastName".fixName() + profile.studentNameShort = "$firstName ${lastName[0]}.".fixName() + profile.accountNameLong = accountNameLong + profile.studentClassName = className + profile.studentSchoolYear = schoolYearName + profile.name = profile.studentNameLong + profile.subname = data.webUsername + profile.empty = true + profile.putStudentData("studentId", studentId) + profile.putStudentData("registerId", registerId) + profile.putStudentData("schoolYearId", schoolYear) + profileList.add(profile) + } + + EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore)) + onSuccess() + } + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLogin.kt new file mode 100644 index 00000000..be79e3e3 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLogin.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-25. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.login + +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_API +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_WEB +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.utils.Utils + +class IdziennikLogin(val data: DataIdziennik, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "IdziennikLogin" + } + + private var cancelled = false + + init { + nextLoginMethod(onSuccess) + } + + private fun nextLoginMethod(onSuccess: () -> Unit) { + if (data.targetLoginMethodIds.isEmpty()) { + onSuccess() + return + } + if (cancelled) { + onSuccess() + return + } + useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId -> + if (usedMethodId != -1) + data.loginMethods.add(usedMethodId) + nextLoginMethod(onSuccess) + } + } + + private fun useLoginMethod(loginMethodId: Int, onSuccess: (usedMethodId: Int) -> Unit) { + // this should never be true + if (data.loginMethods.contains(loginMethodId)) { + onSuccess(-1) + return + } + Utils.d(TAG, "Using login method $loginMethodId") + when (loginMethodId) { + LOGIN_METHOD_IDZIENNIK_WEB -> { + data.startProgress(R.string.edziennik_progress_login_idziennik_web) + IdziennikLoginWeb(data) { onSuccess(loginMethodId) } + } + LOGIN_METHOD_IDZIENNIK_API -> { + data.startProgress(R.string.edziennik_progress_login_idziennik_api) + IdziennikLoginApi(data) { onSuccess(loginMethodId) } + } + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginApi.kt new file mode 100644 index 00000000..4782b980 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginApi.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-27. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.login + +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik + +class IdziennikLoginApi(val data: DataIdziennik, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "IdziennikLoginApi" + } + + init { run { + if (data.isApiLoginValid()) { + onSuccess() + } + else { + onSuccess() + } + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt new file mode 100644 index 00000000..d03c254e --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt @@ -0,0 +1,145 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-26. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.login + +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.callback.TextCallbackHandler +import okhttp3.Cookie +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.get +import pl.szczodrzynski.edziennik.getUnixDate +import pl.szczodrzynski.edziennik.utils.Utils + +class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) { + companion object { + private const val TAG = "IdziennikLoginWeb" + } + + init { run { + if (data.isWebLoginValid()) { + data.app.cookieJar.saveFromResponse(null, listOf( + Cookie.Builder() + .name("ASP.NET_SessionId_iDziennik") + .value(data.webSessionId!!) + .domain("iuczniowie.progman.pl") + .secure().httpOnly().build(), + Cookie.Builder() + .name(".ASPXAUTH") + .value(data.webAuth!!) + .domain("iuczniowie.progman.pl") + .secure().httpOnly().build() + )) + onSuccess() + } + else { + data.app.cookieJar.clearForDomain("iuczniowie.progman.pl") + if (data.webSchoolName != null && data.webUsername != null && data.webPassword != null) { + loginWithCredentials() + } + else { + data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING)) + } + } + }} + + private fun loginWithCredentials() { + Utils.d(TAG, "Request: Idziennik/Login/Web - $IDZIENNIK_WEB_URL/$IDZIENNIK_WEB_LOGIN") + + val loginCallback = object : TextCallbackHandler() { + override fun onSuccess(text: String?, response: Response?) { + if (text.isNullOrEmpty()) { + data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY) + .withResponse(response)) + return + } + + // login succeeded: there is a start page + if (text.contains("czyWyswietlicDostepMobilny")) { + val cookies = data.app.cookieJar.getForDomain("iuczniowie.progman.pl") + run { + data.webSessionId = cookies.singleOrNull { it.name() == "ASP.NET_SessionId_iDziennik" }?.name() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION + data.webAuth = cookies.singleOrNull { it.name() == ".ASPXAUTH" }?.name() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH + data.apiBearer = cookies.singleOrNull { it.name() == "Bearer" }?.name() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER + data.loginExpiryTime = response.getUnixDate() + 45 * 60 /* 45 min */ + return@run null + }?.let { errorCode -> + data.error(ApiError(TAG, errorCode) + .withApiResponse(text) + .withResponse(response)) + return + } + + onSuccess() + return + } + + val errorText = Regexes.IDZIENNIK_LOGIN_ERROR.find(text)?.get(1) + when { + errorText?.contains("nieprawidłową nazwę szkoły") == true -> ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME + errorText?.contains("nieprawidłowy login lub hasło") == true -> ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN + text.contains("Identyfikator zgłoszenia") -> ERROR_LOGIN_IDZIENNIK_WEB_SERVER_ERROR + text.contains("Hasło dostępu do systemu wygasło") -> ERROR_LOGIN_IDZIENNIK_WEB_PASSWORD_CHANGE_NEEDED + text.contains("Trwają prace konserwacyjne") -> ERROR_LOGIN_IDZIENNIK_WEB_MAINTENANCE + else -> ERROR_LOGIN_IDZIENNIK_WEB_OTHER + }.let { errorCode -> + data.error(ApiError(TAG, errorCode) + .withApiResponse(text) + .withResponse(response)) + return + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(ApiError(TAG, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) + } + } + + val getCallback = object : TextCallbackHandler() { + override fun onSuccess(text: String?, response: Response?) { + Request.builder() + .url("$IDZIENNIK_WEB_URL/$IDZIENNIK_WEB_LOGIN") + .userAgent(IDZIENNIK_USER_AGENT) + .addHeader("Origin", "https://iuczniowie.progman.pl") + .addHeader("Referer", "$IDZIENNIK_WEB_URL/$IDZIENNIK_WEB_LOGIN") + .apply { + Regexes.IDZIENNIK_LOGIN_HIDDEN_FIELDS.findAll(text ?: return@apply).forEach { + addParameter(it[1], it[2]) + } + } + .addParameter("ctl00\$ContentPlaceHolder\$nazwaPrzegladarki", IDZIENNIK_USER_AGENT) + .addParameter("ctl00\$ContentPlaceHolder\$NazwaSzkoly", data.webSchoolName) + .addParameter("ctl00\$ContentPlaceHolder\$UserName", data.webUsername) + .addParameter("ctl00\$ContentPlaceHolder\$Password", data.webPassword) + .addParameter("ctl00\$ContentPlaceHolder\$captcha", "") + .addParameter("ctl00\$ContentPlaceHolder\$Logowanie", "Zaloguj") + .post() + .allowErrorCode(502) + .callback(loginCallback) + .build() + .enqueue() + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(ApiError(TAG, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) + } + } + + Request.builder() + .url("$IDZIENNIK_WEB_URL/$IDZIENNIK_WEB_LOGIN") + .userAgent(IDZIENNIK_USER_AGENT) + .get() + .allowErrorCode(502) + .callback(getCallback) + .build() + .enqueue() + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 26202e3b..f73a26fd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -963,6 +963,8 @@ Pobieranie kategorii wydarzeń... Pobieranie kategorii uwag... Pobieranie zebrań z rodzicami... + Logowanie do iDziennika... + Logowanie do iDziennika... Pobieranie wiadomości wysłanych... (rodzic) (uczeń) From 42f70da162cf6113f8add1f5115d9a01a9d90368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 27 Oct 2019 21:00:23 +0100 Subject: [PATCH 107/691] [UI/Settings] Add option to change app language. --- .../pl/szczodrzynski/edziennik/Extensions.kt | 49 +++ .../szczodrzynski/edziennik/MainActivity.kt | 4 + .../modules/settings/SettingsNewFragment.java | 414 +++++++----------- .../edziennik/utils/models/AppConfig.java | 5 + app/src/main/res/values-en/strings.xml | 10 + app/src/main/res/values/strings.xml | 9 +- 6 files changed, 235 insertions(+), 256 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index 85b3dfd9..fcf2095b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -257,3 +257,52 @@ operator fun MatchResult.get(group: Int): String { return "" return groupValues[group] } + +fun Activity.setLanguage(language: String) { + val locale = Locale(language.toLowerCase(Locale.ROOT)) + val configuration = resources.configuration + Locale.setDefault(locale) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + configuration.setLocale(locale) + } + configuration.locale = locale + resources.updateConfiguration(configuration, resources.displayMetrics) + baseContext.resources.updateConfiguration(configuration, baseContext.resources.displayMetrics) +} + +/* + Code copied from android-28/java.util.Locale.initDefault() + */ +fun initDefaultLocale() { + run { + // user.locale gets priority + /*val languageTag: String? = System.getProperty("user.locale", "") + if (languageTag.isNotNullNorEmpty()) { + return@run Locale(languageTag) + }*/ + + // user.locale is empty + val language: String? = System.getProperty("user.language", "pl") + val region: String? = System.getProperty("user.region") + val country: String? + val variant: String? + // for compatibility, check for old user.region property + if (region != null) { + // region can be of form country, country_variant, or _variant + val i = region.indexOf('_') + if (i >= 0) { + country = region.substring(0, i) + variant = region.substring(i + 1) + } else { + country = region + variant = "" + } + } else { + country = System.getProperty("user.country", "") + variant = System.getProperty("user.variant", "") + } + return@run Locale(language) + }.let { + Locale.setDefault(it) + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt index c27156ff..41de1a6e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt @@ -236,6 +236,10 @@ class MainActivity : AppCompatActivity() { setTheme(Themes.appTheme) + app.appConfig.language?.let { + setLanguage(it) + } + setContentView(b.root) navLoading = true diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java index 932e89eb..76fb23c1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java @@ -38,28 +38,27 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Map; import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; +import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.Notifier; import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore; -import pl.szczodrzynski.edziennik.ui.modules.webpush.WebPushConfigActivity; -import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog; -import pl.szczodrzynski.edziennik.utils.models.Endpoint; -import pl.szczodrzynski.edziennik.utils.models.Time; import pl.szczodrzynski.edziennik.network.NetworkUtils; import pl.szczodrzynski.edziennik.network.ServerRequest; import pl.szczodrzynski.edziennik.receivers.BootReceiver; import pl.szczodrzynski.edziennik.sync.SyncJob; +import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog; import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment; +import pl.szczodrzynski.edziennik.ui.modules.webpush.WebPushConfigActivity; import pl.szczodrzynski.edziennik.utils.Themes; import pl.szczodrzynski.edziennik.utils.Utils; +import pl.szczodrzynski.edziennik.utils.models.Time; import static android.app.Activity.RESULT_OK; import static pl.szczodrzynski.edziennik.App.APP_URL; +import static pl.szczodrzynski.edziennik.ExtensionsKt.initDefaultLocale; import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.REGISTRATION_DISABLED; import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.REGISTRATION_ENABLED; import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.YEAR_1_AVG_2_AVG; @@ -81,8 +80,7 @@ public class SettingsNewFragment extends MaterialAboutFragment { private static final int CARD_THEME = 1; private static final int CARD_SYNC = 2; private static final int CARD_REGISTER = 3; - private static final int CARD_SYNC_CUSTOMIZE = 4; - private static final int CARD_ABOUT = 5; + private static final int CARD_ABOUT = 4; private int iconColor = Color.WHITE; private int primaryTextOnPrimaryBg = -1; private int secondaryTextOnPrimaryBg = -1; @@ -1081,136 +1079,6 @@ public class SettingsNewFragment extends MaterialAboutFragment { return items; } - /* _____ ______ _ _ _ - / ____| | ____| | | (_) | | - | (___ _ _ _ __ ___ | |__ _ __ __| |_ __ ___ _ _ __ | |_ ___ - \___ \| | | | '_ \ / __| | __| | '_ \ / _` | '_ \ / _ \| | '_ \| __/ __| - ____) | |_| | | | | (__ | |____| | | | (_| | |_) | (_) | | | | | |_\__ \ - |_____/ \__, |_| |_|\___| |______|_| |_|\__,_| .__/ \___/|_|_| |_|\__|___/ - __/ | | | - |___/ |*/ - private String getEndpointTitle(String name) { - int stringRes = -1; - switch (name) { - case "Classrooms": - stringRes = R.string.settings_sync_customize_endpoint_classrooms; - break; - case "Timetables": - case "Timetable": - stringRes = R.string.settings_sync_customize_endpoint_timetable; - break; - case "Substitutions": - stringRes = R.string.settings_sync_customize_endpoint_substitutions; - break; - case "Grades": - stringRes = R.string.settings_sync_customize_endpoint_grades; - break; - case "PointGrades": - stringRes = R.string.settings_sync_customize_endpoint_point_grades; - break; - case "Events": - stringRes = R.string.settings_sync_customize_endpoint_events; - break; - case "Homework": - stringRes = R.string.settings_sync_customize_endpoint_homework; - break; - case "LuckyNumber": - case "LuckyNumbers": - stringRes = R.string.settings_sync_customize_endpoint_lucky_numbers; - break; - case "Notices": - stringRes = R.string.settings_sync_customize_endpoint_notices; - break; - case "Attendance": - stringRes = R.string.settings_sync_customize_endpoint_attendance; - break; - case "Announcements": - stringRes = R.string.settings_sync_customize_endpoint_announcements; - break; - case "PtMeetings": - stringRes = R.string.settings_sync_customize_endpoint_pt_meetings; - break; - case "TeacherFreeDays": - stringRes = R.string.settings_sync_customize_endpoint_teacher_free_days; - break; - case "SchoolFreeDays": - stringRes = R.string.settings_sync_customize_endpoint_school_free_days; - break; - case "ClassFreeDays": - stringRes = R.string.settings_sync_customize_endpoint_class_free_days; - break; - case "MessagesInbox": - stringRes = R.string.settings_sync_customize_endpoint_messages_inbox; - break; - case "MessagesOutbox": - stringRes = R.string.settings_sync_customize_endpoint_messages_outbox; - break; - } - if (stringRes == -1) - return name; - else - return getString(stringRes); - } - private MaterialAboutSwitchItem getEndpointSwitch(String name, Endpoint endpoint) { - return new MaterialAboutSwitchItem( - getEndpointTitle(name), - endpoint.onlyFullSync ? getString(R.string.settings_sync_customize_full_sync_only) : null, - null - ) - .setChecked(endpoint.enabled) - .setOnChangeAction((isChecked, tag) -> { - endpoint.enabled = isChecked; - boolean changed = isChecked ^ endpoint.defaultActive; - if (!changed) { - if (app.profile.getChangedEndpoints() != null) { - app.profile.getChangedEndpoints().remove(name); - } - } - else { - if (app.profile.getChangedEndpoints() == null) - app.profile.setChangedEndpoints(new ArrayList<>()); - app.profile.getChangedEndpoints().add(name); - } - app.profileSaveAsync(); - return true; - }); - } - private Map configurableEndpoints = null; - private ArrayList getSyncCustomizeCard(boolean expandedOnly) { - ArrayList items = new ArrayList<>(); - if (!expandedOnly) { - items.add( - new MaterialAboutActionItem( - null, - getString(R.string.settings_sync_customize_help_subtext), - null - ) - ); - - if (app.profile.getChangedEndpoints() != null) { - for (String changedEndpoint : app.profile.getChangedEndpoints()) { - Endpoint endpoint = configurableEndpoints.get(changedEndpoint); - if (endpoint == null) - continue; - items.add(getEndpointSwitch(changedEndpoint, endpoint)); - } - } - - items.add(getMoreItem(() -> addCardItems(CARD_SYNC_CUSTOMIZE, getSyncCustomizeCard(true)))); - } - else { - for (String endpointName: configurableEndpoints.keySet()) { - if (app.profile.getChangedEndpoints() != null && app.profile.getChangedEndpoints().contains(endpointName)) - continue; - Endpoint endpoint = configurableEndpoints.get(endpointName); - if (endpoint == null) - continue; - items.add(getEndpointSwitch(endpointName, endpoint)); - } - } - return items; - } - /* _ _ /\ | | | | / \ | |__ ___ _ _| |_ @@ -1223,117 +1091,152 @@ public class SettingsNewFragment extends MaterialAboutFragment { secondaryTextOnPrimaryBg = 0xd0ffffff;//activity.getResources().getColor(R.color.secondaryTextLight); ArrayList items = new ArrayList<>(); - items.add(new MaterialAboutTitleItem.Builder() - .text(R.string.app_name) - .desc(R.string.settings_about_title_subtext) - .textColor(primaryTextOnPrimaryBg) - .descColor(secondaryTextOnPrimaryBg) - .icon(R.mipmap.ic_splash) - .build()); + if (!expandedOnly) { + items.add(new MaterialAboutTitleItem.Builder() + .text(R.string.app_name) + .desc(R.string.settings_about_title_subtext) + .textColor(primaryTextOnPrimaryBg) + .descColor(secondaryTextOnPrimaryBg) + .icon(R.mipmap.ic_splash) + .build()); - pref_about_version = new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_version_text) - .textColor(primaryTextOnPrimaryBg) - .subTextColor(secondaryTextOnPrimaryBg) - .subText(BuildConfig.VERSION_NAME+", "+BuildConfig.BUILD_TYPE) - .icon(new IconicsDrawable(activity) - .icon(CommunityMaterial.Icon2.cmd_information) - .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) - .size(IconicsSize.dp(iconSizeDp))) - .build(); - final int[] clickCounter = {0}; - pref_about_version.setOnClickAction(() -> { - if (6 - clickCounter[0] != 0) { - Toast.makeText(activity, ("\ud83d\ude02"), Toast.LENGTH_SHORT).show(); - } - refreshMaterialAboutList(); - clickCounter[0] = clickCounter[0] + 1; - if (clickCounter[0] > 6) - { - final MediaPlayer mp = MediaPlayer.create(activity, R.raw.ogarnij_sie); - mp.start(); - clickCounter[0] = 0; - } - }); - items.add(pref_about_version); + pref_about_version = new MaterialAboutActionItem.Builder() + .text(R.string.settings_about_version_text) + .textColor(primaryTextOnPrimaryBg) + .subTextColor(secondaryTextOnPrimaryBg) + .subText(BuildConfig.VERSION_NAME + ", " + BuildConfig.BUILD_TYPE) + .icon(new IconicsDrawable(activity) + .icon(CommunityMaterial.Icon2.cmd_information) + .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) + .size(IconicsSize.dp(iconSizeDp))) + .build(); + final int[] clickCounter = {0}; + pref_about_version.setOnClickAction(() -> { + if (6 - clickCounter[0] != 0) { + Toast.makeText(activity, ("\ud83d\ude02"), Toast.LENGTH_SHORT).show(); + } + refreshMaterialAboutList(); + clickCounter[0] = clickCounter[0] + 1; + if (clickCounter[0] > 6) { + final MediaPlayer mp = MediaPlayer.create(activity, R.raw.ogarnij_sie); + mp.start(); + clickCounter[0] = 0; + } + }); + items.add(pref_about_version); - items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_privacy_policy_text) - .textColor(primaryTextOnPrimaryBg) - .subTextColor(secondaryTextOnPrimaryBg) - .icon(new IconicsDrawable(activity) - .icon(CommunityMaterial.Icon2.cmd_shield_half_full) - .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) - .size(IconicsSize.dp(iconSizeDp))) - .setOnClickAction(ConvenienceBuilder.createWebsiteOnClickAction(activity, Uri.parse("https://szkolny.eu/privacy-policy"))) - .build()); + items.add(new MaterialAboutActionItem.Builder() + .text(R.string.settings_about_privacy_policy_text) + .textColor(primaryTextOnPrimaryBg) + .subTextColor(secondaryTextOnPrimaryBg) + .icon(new IconicsDrawable(activity) + .icon(CommunityMaterial.Icon2.cmd_shield_half_full) + .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) + .size(IconicsSize.dp(iconSizeDp))) + .setOnClickAction(ConvenienceBuilder.createWebsiteOnClickAction(activity, Uri.parse("https://szkolny.eu/privacy-policy"))) + .build()); - items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_discord_text) - .textColor(primaryTextOnPrimaryBg) - .subTextColor(secondaryTextOnPrimaryBg) - .subText(R.string.settings_about_discord_subtext) - .icon(new IconicsDrawable(activity) - .icon(CommunityMaterial.Icon.cmd_discord) - .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) - .size(IconicsSize.dp(iconSizeDp))) - .setOnClickAction(ConvenienceBuilder.createWebsiteOnClickAction(activity, Uri.parse("https://discord.gg/n9e8pWr"))) - .build()); + items.add(new MaterialAboutActionItem.Builder() + .text(R.string.settings_about_discord_text) + .textColor(primaryTextOnPrimaryBg) + .subTextColor(secondaryTextOnPrimaryBg) + .subText(R.string.settings_about_discord_subtext) + .icon(new IconicsDrawable(activity) + .icon(CommunityMaterial.Icon.cmd_discord) + .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) + .size(IconicsSize.dp(iconSizeDp))) + .setOnClickAction(ConvenienceBuilder.createWebsiteOnClickAction(activity, Uri.parse("https://discord.gg/n9e8pWr"))) + .build()); - items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_update_text) - .subText(R.string.settings_about_update_subtext) - .textColor(primaryTextOnPrimaryBg) - .subTextColor(secondaryTextOnPrimaryBg) - .icon(new IconicsDrawable(activity) - .icon(CommunityMaterial.Icon2.cmd_update) - .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) - .size(IconicsSize.dp(iconSizeDp))) - .setOnClickAction(() -> { - //open browser or intent here - NetworkUtils net = new NetworkUtils(app); - if (!net.isOnline()) - { + items.add(new MaterialAboutActionItem.Builder() + .text(R.string.settings_about_language_text) + .textColor(primaryTextOnPrimaryBg) + .subTextColor(secondaryTextOnPrimaryBg) + .subText(R.string.settings_about_language_subtext) + .icon(new IconicsDrawable(activity) + .icon(CommunityMaterial.Icon2.cmd_translate) + .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) + .size(IconicsSize.dp(iconSizeDp))) + .setOnClickAction(() -> { new MaterialDialog.Builder(activity) - .title(R.string.you_are_offline_title) - .content(R.string.you_are_offline_text) - .positiveText(R.string.ok) + .title(getString(R.string.settings_about_language_dialog_title)) + .content(getString(R.string.settings_about_language_dialog_text)) + .items(getString(R.string.language_system), getString(R.string.language_polish), getString(R.string.language_english)) + .itemsCallbackSingleChoice(app.appConfig.language == null ? 0 : app.appConfig.language.equals("pl") ? 1 : 2, (dialog, itemView, which, text) -> { + switch (which) { + case 0: + app.appConfig.language = null; + initDefaultLocale(); + break; + case 1: + app.appConfig.language = "pl"; + break; + case 2: + app.appConfig.language = "en"; + break; + } + app.saveConfig("language"); + activity.recreate(MainActivity.DRAWER_ITEM_SETTINGS); + return true; + }) .show(); - } - else - { - BootReceiver br = new BootReceiver(); - Intent i = new Intent(); - i.putExtra("UserChecked", true); - br.onReceive(getContext(), i); - } - }) - .build()); + }) + .build()); - items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_changelog_text) - .textColor(primaryTextOnPrimaryBg) - .subTextColor(secondaryTextOnPrimaryBg) - .icon(new IconicsDrawable(activity) - .icon(CommunityMaterial.Icon2.cmd_radar) - .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) - .size(IconicsSize.dp(iconSizeDp))) - .setOnClickAction(() -> new ChangelogDialog().show(getActivity().getSupportFragmentManager(), "whats_new")) - .build()); + items.add(getMoreItem(() -> addCardItems(CARD_ABOUT, getAboutCard(true)))); + } + else { + items.add(new MaterialAboutActionItem.Builder() + .text(R.string.settings_about_update_text) + .subText(R.string.settings_about_update_subtext) + .textColor(primaryTextOnPrimaryBg) + .subTextColor(secondaryTextOnPrimaryBg) + .icon(new IconicsDrawable(activity) + .icon(CommunityMaterial.Icon2.cmd_update) + .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) + .size(IconicsSize.dp(iconSizeDp))) + .setOnClickAction(() -> { + //open browser or intent here + NetworkUtils net = new NetworkUtils(app); + if (!net.isOnline()) { + new MaterialDialog.Builder(activity) + .title(R.string.you_are_offline_title) + .content(R.string.you_are_offline_text) + .positiveText(R.string.ok) + .show(); + } else { + BootReceiver br = new BootReceiver(); + Intent i = new Intent(); + i.putExtra("UserChecked", true); + br.onReceive(getContext(), i); + } + }) + .build()); - items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_licenses_text) - .textColor(primaryTextOnPrimaryBg) - .subTextColor(secondaryTextOnPrimaryBg) - .icon(new IconicsDrawable(activity) - .icon(CommunityMaterial.Icon.cmd_code_braces) - .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) - .size(IconicsSize.dp(iconSizeDp))) - .setOnClickAction(() -> { - Intent intent = new Intent(activity, SettingsLicenseActivity.class); - startActivity(intent); - }) - .build()); + items.add(new MaterialAboutActionItem.Builder() + .text(R.string.settings_about_changelog_text) + .textColor(primaryTextOnPrimaryBg) + .subTextColor(secondaryTextOnPrimaryBg) + .icon(new IconicsDrawable(activity) + .icon(CommunityMaterial.Icon2.cmd_radar) + .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) + .size(IconicsSize.dp(iconSizeDp))) + .setOnClickAction(() -> new ChangelogDialog().show(getActivity().getSupportFragmentManager(), "whats_new")) + .build()); + + items.add(new MaterialAboutActionItem.Builder() + .text(R.string.settings_about_licenses_text) + .textColor(primaryTextOnPrimaryBg) + .subTextColor(secondaryTextOnPrimaryBg) + .icon(new IconicsDrawable(activity) + .icon(CommunityMaterial.Icon.cmd_code_braces) + .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) + .size(IconicsSize.dp(iconSizeDp))) + .setOnClickAction(() -> { + Intent intent = new Intent(activity, SettingsLicenseActivity.class); + startActivity(intent); + }) + .build()); /*items.add(new MaterialAboutActionItem.Builder() .text(R.string.settings_about_intro_text) @@ -1357,20 +1260,21 @@ public class SettingsNewFragment extends MaterialAboutFragment { }) .build());*/ - if (App.devMode) { - items.add(new MaterialAboutActionItem.Builder() - .text(R.string.settings_about_crash_text) - .subText(R.string.settings_about_crash_subtext) - .textColor(primaryTextOnPrimaryBg) - .subTextColor(secondaryTextOnPrimaryBg) - .icon(new IconicsDrawable(activity) - .icon(CommunityMaterial.Icon.cmd_bug) - .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) - .size(IconicsSize.dp(iconSizeDp))) - .setOnClickAction(() -> { - throw new RuntimeException("MANUAL CRASH"); - }) - .build()); + if (App.devMode) { + items.add(new MaterialAboutActionItem.Builder() + .text(R.string.settings_about_crash_text) + .subText(R.string.settings_about_crash_subtext) + .textColor(primaryTextOnPrimaryBg) + .subTextColor(secondaryTextOnPrimaryBg) + .icon(new IconicsDrawable(activity) + .icon(CommunityMaterial.Icon.cmd_bug) + .color(IconicsColor.colorInt(primaryTextOnPrimaryBg)) + .size(IconicsSize.dp(iconSizeDp))) + .setOnClickAction(() -> { + throw new RuntimeException("MANUAL CRASH"); + }) + .build()); + } } return items; } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/AppConfig.java b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/AppConfig.java index 2aefccf0..b2d104e2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/AppConfig.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/AppConfig.java @@ -2,6 +2,8 @@ package pl.szczodrzynski.edziennik.utils.models; import android.util.Pair; +import androidx.annotation.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -110,4 +112,7 @@ public class AppConfig { public long appRateSnackbarTime = 0; public int mobidziennikOldMessages = -1; + + @Nullable + public String language = null; } diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 08dba8a1..ad6f407b 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -859,4 +859,14 @@ Getting school free days Show teacher absences in Agenda Mark everything as read + App language + "English " + Change app language + Notice. This feature may not work on some devices or in some parts of the app. + Discord server + Join our Discord community! + Use system language + (child) + (parent) + Syncing... diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f73a26fd..05e64564 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -953,7 +953,7 @@ Pobieranie rodzajów nieobecności nauczycieli... Pobieranie słowników... Synchronizuję... - [%d%%] %s + [%d%%] %s Pobieranie uwag... Pobieranie grup klasowych... Pobieranie informacji o jednostce... @@ -968,4 +968,11 @@ Pobieranie wiadomości wysłanych... (rodzic) (uczeń) + Język aplikacji + Polski + Zmień język aplikacji + Uwaga. Ta opcja może nie działać na niektórych urządzeniach oraz w niektórych fragmentach aplikacji. + Według systemu + Polski + English From f452a1b81cbc9f88fbafad6c8dcf7a3e324735f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 27 Oct 2019 21:25:01 +0100 Subject: [PATCH 108/691] [Utils] Update CRC16 generation method. --- .../pl/szczodrzynski/edziennik/Extensions.kt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index fcf2095b..b0167959 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -18,7 +18,6 @@ import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher import pl.szczodrzynski.edziennik.data.db.modules.teams.Team import pl.szczodrzynski.navlib.R -import pl.szczodrzynski.navlib.crc16 import pl.szczodrzynski.navlib.getColorFromRes import java.text.SimpleDateFormat import java.util.* @@ -118,7 +117,7 @@ fun List.join(delimiter: String): String { } fun colorFromName(context: Context, name: String?): Int { - var crc = crc16(name ?: "") + var crc = (name ?: "").crc16() crc = (crc and 0xff) or (crc shr 8) crc %= 16 val color = when (crc) { @@ -305,4 +304,17 @@ fun initDefaultLocale() { }.let { Locale.setDefault(it) } +} + +fun String.crc16(): Int { + var crc = 0xFFFF + for (aBuffer in this) { + crc = crc.ushr(8) or (crc shl 8) and 0xffff + crc = crc xor (aBuffer.toInt() and 0xff) // byte to int, trunc sign + crc = crc xor (crc and 0xff shr 4) + crc = crc xor (crc shl 12 and 0xffff) + crc = crc xor (crc and 0xFF shl 5 and 0xffff) + } + crc = crc and 0xffff + return crc + 32768 } \ No newline at end of file From debb0b150796f70d060bc4b24579a67851a83bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 28 Oct 2019 19:54:15 +0100 Subject: [PATCH 109/691] [APIv2/Idziennik] Fix login, add timetable. List features. --- .../szczodrzynski/edziennik/api/v2/Errors.kt | 1 + .../api/v2/idziennik/DataIdziennik.kt | 68 +++++++++- .../api/v2/idziennik/IdziennikFeatures.kt | 60 ++++++-- .../api/v2/idziennik/data/IdziennikData.kt | 11 +- .../data/web/IdziennikWebTimetable.kt | 128 ++++++++++++++++++ .../v2/idziennik/login/IdziennikLoginWeb.kt | 6 +- app/src/main/res/values/strings.xml | 1 + 7 files changed, 256 insertions(+), 19 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebTimetable.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index cc4709ce..ae9db1e7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -145,6 +145,7 @@ const val ERROR_IDZIENNIK_WEB_MAINTENANCE = 432 const val ERROR_IDZIENNIK_WEB_SERVER_ERROR = 433 const val ERROR_IDZIENNIK_WEB_PASSWORD_CHANGE_NEEDED = 434 const val ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR = 440 +const val ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA = 441 const val ERROR_TEMPLATE_WEB_OTHER = 801 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt index faa2f517..61629c3d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt @@ -4,15 +4,16 @@ package pl.szczodrzynski.edziennik.api.v2.idziennik +import androidx.core.util.set import okhttp3.Cookie -import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_API import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_WEB import pl.szczodrzynski.edziennik.api.v2.models.Data -import pl.szczodrzynski.edziennik.currentTimeUnix import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile -import pl.szczodrzynski.edziennik.isNotNullNorEmpty +import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject +import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) { @@ -106,4 +107,65 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data( var schoolYearId: Int get() { mSchoolYearId = mSchoolYearId ?: profile?.getStudentData("schoolYearId", 0); return mSchoolYearId ?: 0 } set(value) { profile?.putStudentData("schoolYearId", value) ?: return; mSchoolYearId = value } + + + + /* _ _ _ _ _ + | | | | | (_) | + | | | | |_ _| |___ + | | | | __| | / __| + | |__| | |_| | \__ \ + \____/ \__|_|_|__*/ + fun getSubject(name: String, id: Long?, shortName: String): Subject { + var subject = if (id == null) + subjectList.singleOrNull { it.longName == name } + else + subjectList.singleOrNull { it.id == id } + + if (subject == null) { + subject = Subject(profileId, id ?: name.crc16().toLong(), name, shortName) + subjectList[subject.id] = subject + } + return subject + } + + fun getTeacher(firstName: String, lastName: String): Teacher { + val teacher = teacherList.singleOrNull { it.fullName == "$firstName $lastName" } + return validateTeacher(teacher, firstName, lastName) + } + fun getTeacher(firstNameChar: Char, lastName: String): Teacher { + val teacher = teacherList.singleOrNull { it.shortName == "$firstNameChar.$lastName" } + return validateTeacher(teacher, firstNameChar.toString(), lastName) + } + fun getTeacherByLastFirst(nameLastFirst: String): Teacher { + val nameParts = nameLastFirst.split(" ") + return if (nameParts.size == 1) getTeacher(nameParts[0], "") else getTeacher(nameParts[1], nameParts[0]) + } + + fun getTeacherByFirstLast(nameFirstLast: String): Teacher { + val nameParts = nameFirstLast.split(" ") + return if (nameParts.size == 1) getTeacher(nameParts[0], "") else getTeacher(nameParts[0], nameParts[1]) + } + + fun getTeacherByFDotLast(nameFDotLast: String): Teacher { + val nameParts = nameFDotLast.split(".") + return if (nameParts.size == 1) getTeacher(nameParts[0], "") else getTeacher(nameParts[0][0], nameParts[1]) + } + + fun getTeacherByFDotSpaceLast(nameFDotSpaceLast: String): Teacher { + val nameParts = nameFDotSpaceLast.split(".") + return if (nameParts.size == 1) getTeacher(nameParts[0], "") else getTeacher(nameParts[0][0], nameParts[1]) + } + + private fun validateTeacher(teacher: Teacher?, firstName: String, lastName: String): Teacher { + (teacher ?: Teacher(profileId, -1, firstName, lastName).apply { + id = shortName.crc16().toLong() + teacherList[id] = this + }).apply { + if (firstName.length > 1) + name = firstName + surname = lastName + return this + } + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt index c3a01ea1..eac360e4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt @@ -7,18 +7,60 @@ package pl.szczodrzynski.edziennik.api.v2.idziennik import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.models.Feature -const val ENDPOINT_IDZIENNIK_WEB_SAMPLE = 9991 -const val ENDPOINT_IDZIENNIK_WEB_SAMPLE_2 = 9992 -const val ENDPOINT_IDZIENNIK_API_SAMPLE = 9993 +const val ENDPOINT_IDZIENNIK_WEB_TIMETABLE = 1030 +const val ENDPOINT_IDZIENNIK_WEB_GRADES = 1040 +const val ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES = 1050 +const val ENDPOINT_IDZIENNIK_WEB_EXAMS = 1060 +const val ENDPOINT_IDZIENNIK_WEB_NOTICES = 1070 +const val ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS = 1080 +const val ENDPOINT_IDZIENNIK_WEB_ATTENDANCE = 1090 +const val ENDPOINT_IDZIENNIK_WEB_MESSAGES_INBOX = 1110 +const val ENDPOINT_IDZIENNIK_WEB_MESSAGES_SENT = 1120 +const val ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER = 2010 +const val ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX = 2110 +const val ENDPOINT_IDZIENNIK_API_MESSAGES_SENT = 2120 val IdziennikFeatures = listOf( - Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_STUDENT_INFO, listOf( - ENDPOINT_IDZIENNIK_WEB_SAMPLE to LOGIN_METHOD_IDZIENNIK_WEB - ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), - Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_SCHOOL_INFO, listOf( - ENDPOINT_IDZIENNIK_WEB_SAMPLE_2 to LOGIN_METHOD_IDZIENNIK_WEB + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_TIMETABLE, listOf( + ENDPOINT_IDZIENNIK_WEB_TIMETABLE to LOGIN_METHOD_IDZIENNIK_WEB ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_GRADES, listOf( - ENDPOINT_IDZIENNIK_API_SAMPLE to LOGIN_METHOD_IDZIENNIK_API + ENDPOINT_IDZIENNIK_WEB_GRADES to LOGIN_METHOD_IDZIENNIK_WEB, + ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES to LOGIN_METHOD_IDZIENNIK_WEB + ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_AGENDA, listOf( + ENDPOINT_IDZIENNIK_WEB_EXAMS to LOGIN_METHOD_IDZIENNIK_WEB + ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_BEHAVIOUR, listOf( + ENDPOINT_IDZIENNIK_WEB_NOTICES to LOGIN_METHOD_IDZIENNIK_WEB + ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_ATTENDANCE, listOf( + ENDPOINT_IDZIENNIK_WEB_ATTENDANCE to LOGIN_METHOD_IDZIENNIK_WEB + ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_ANNOUNCEMENTS, listOf( + ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS to LOGIN_METHOD_IDZIENNIK_WEB + ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_MESSAGES_INBOX, listOf( + ENDPOINT_IDZIENNIK_WEB_MESSAGES_INBOX to LOGIN_METHOD_IDZIENNIK_WEB + ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_MESSAGES_SENT, listOf( + ENDPOINT_IDZIENNIK_WEB_MESSAGES_SENT to LOGIN_METHOD_IDZIENNIK_WEB + ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_MESSAGES_INBOX, listOf( + ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX to LOGIN_METHOD_IDZIENNIK_API + ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_MESSAGES_SENT, listOf( + ENDPOINT_IDZIENNIK_API_MESSAGES_SENT to LOGIN_METHOD_IDZIENNIK_API + ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + + Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_LUCKY_NUMBER, listOf( + ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER to LOGIN_METHOD_IDZIENNIK_API ), listOf(LOGIN_METHOD_IDZIENNIK_API)) ) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt index 4873a275..84d93d37 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt @@ -4,7 +4,10 @@ package pl.szczodrzynski.edziennik.api.v2.idziennik.data +import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_TIMETABLE +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.web.IdziennikWebTimetable import pl.szczodrzynski.edziennik.utils.Utils class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) { @@ -35,10 +38,10 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) { private fun useEndpoint(endpointId: Int, onSuccess: () -> Unit) { Utils.d(TAG, "Using endpoint $endpointId") when (endpointId) { - /*ENDPOINT_IDZIENNIK_WEB_SAMPLE -> { - data.startProgress(R.string.edziennik_progress_endpoint_student_info) - IdziennikWebSample(data) { onSuccess() } - }*/ + ENDPOINT_IDZIENNIK_WEB_TIMETABLE -> { + data.startProgress(R.string.edziennik_progress_endpoint_timetable) + IdziennikWebTimetable(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebTimetable.kt new file mode 100644 index 00000000..c9cf7f5f --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebTimetable.kt @@ -0,0 +1,128 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-27. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web + +import androidx.core.util.set +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA +import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_TIMETABLE +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_TIMETABLE +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.lessons.Lesson +import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange +import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange.TYPE_CANCELLED +import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange.TYPE_CHANGE +import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonRange +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Time +import pl.szczodrzynski.edziennik.utils.models.Week + +class IdziennikWebTimetable(override val data: DataIdziennik, + val onSuccess: () -> Unit) : IdziennikWeb(data) { + companion object { + private const val TAG = "IdziennikWebTimetable" + } + + init { + val weekStart = Week.getWeekStart() + if (Date.getToday().weekDay > 4) { + weekStart.stepForward(0, 0, 7) + } + + webApiGet(TAG, IDZIENNIK_WEB_TIMETABLE, mapOf( + "idPozDziennika" to data.registerId, + "pidRokSzkolny" to data.schoolYearId, + "data" to weekStart.stringY_m_d+"T10:00:00.000Z" + )) { result -> + val json = result.getJsonObject("d") ?: run { + data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA) + .withApiResponse(result)) + return@webApiGet + } + + json.getJsonArray("GodzinyLekcyjne")?.asJsonObjectList()?.forEach { range -> + val lessonRange = LessonRange( + profileId, + range.getInt("LiczbaP") ?: return@forEach, + range.getString("Poczatek")?.let { Time.fromH_m(it) } ?: return@forEach, + range.getString("Koniec")?.let { Time.fromH_m(it) } ?: return@forEach + ) + data.lessonRanges[lessonRange.lessonNumber] = lessonRange + } + + json.getJsonArray("Przedmioty")?.asJsonObjectList()?.forEach { lesson -> + val subject = data.getSubject( + lesson.getString("Nazwa") ?: return@forEach, + lesson.getLong("Id"), + lesson.getString("Skrot") ?: "" + ) + val teacher = data.getTeacherByFDotLast(lesson.getString("Nauczyciel") ?: return@forEach) + val weekDay = lesson.getInt("DzienTygodnia")?.minus(1) ?: return@forEach + val lessonRange = data.lessonRanges[lesson.getInt("Godzina")?.plus(1) ?: return@forEach] + + val lessonObject = Lesson( + profileId, + weekDay, + lessonRange.startTime, + lessonRange.endTime + ).apply { + subjectId = subject.id + teacherId = teacher.id + teamId = data.teamClass?.id ?: -1 + classroomName = lesson.getString("NazwaSali") ?: "" + } + + data.lessonList.add(lessonObject) + + val type = lesson.getInt("TypZastepstwa") ?: -1 + if (type != -1) { + // we have a lesson change to process + val lessonChangeObject = LessonChange( + profileId, + weekStart.clone().stepForward(0, 0, weekDay), + lessonObject.startTime, + lessonObject.endTime + ) + + lessonChangeObject.teamId = lessonObject.teamId + lessonChangeObject.teacherId = lessonObject.teacherId + lessonChangeObject.subjectId = lessonObject.subjectId + lessonChangeObject.classroomName = lessonObject.classroomName + when (type) { + 0 -> lessonChangeObject.type = TYPE_CANCELLED + 1, 2, 3, 4, 5 -> { + lessonChangeObject.type = TYPE_CHANGE + val newTeacher = lesson.getString("NauZastepujacy") + val newSubject = lesson.getString("PrzedmiotZastepujacy") + if (newTeacher != null) { + lessonChangeObject.teacherId = data.getTeacherByFDotLast(newTeacher).id + } + if (newSubject != null) { + lessonChangeObject.subjectId = data.getSubject(newSubject, null, "").id + } + } + } + + data.lessonChangeList.add(lessonChangeObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_LESSON_CHANGE, + lessonChangeObject.id, + profile?.empty ?: false, + profile?.empty ?: false, + System.currentTimeMillis() + )) + } + } + + data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_TIMETABLE, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt index d03c254e..775200c6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt @@ -62,9 +62,9 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) { if (text.contains("czyWyswietlicDostepMobilny")) { val cookies = data.app.cookieJar.getForDomain("iuczniowie.progman.pl") run { - data.webSessionId = cookies.singleOrNull { it.name() == "ASP.NET_SessionId_iDziennik" }?.name() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION - data.webAuth = cookies.singleOrNull { it.name() == ".ASPXAUTH" }?.name() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH - data.apiBearer = cookies.singleOrNull { it.name() == "Bearer" }?.name() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER + data.webSessionId = cookies.singleOrNull { it.name() == "ASP.NET_SessionId_iDziennik" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION + data.webAuth = cookies.singleOrNull { it.name() == ".ASPXAUTH" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH + data.apiBearer = cookies.singleOrNull { it.name() == "Bearer" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER data.loginExpiryTime = response.getUnixDate() + 45 * 60 /* 45 min */ return@run null }?.let { errorCode -> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 05e64564..10533021 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -975,4 +975,5 @@ Według systemu Polski English + Pobieranie planu lekcji... From 5e90e9aa71fea0777aff00f972efe93d182f4ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 28 Oct 2019 19:54:53 +0100 Subject: [PATCH 110/691] [APIv2/Mobidziennik] Make use of String.fixName() --- .../v2/mobidziennik/data/api/MobidziennikApiTimetable.kt | 6 +++--- .../api/v2/mobidziennik/data/api/MobidziennikApiUsers.kt | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTimetable.kt index 62f760ab..ce938865 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTimetable.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTimetable.kt @@ -8,7 +8,7 @@ import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.db.modules.lessons.Lesson import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata -import pl.szczodrzynski.edziennik.fixWhiteSpaces +import pl.szczodrzynski.edziennik.fixName import pl.szczodrzynski.edziennik.singleOrNull class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List) { @@ -26,7 +26,7 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List) { data.subjectList.singleOrNull { it.longName == lesson[5] }?.let { lessonObject.subjectId = it.id } - data.teacherList.singleOrNull { it.fullNameLastFirst == (lesson[7]+" "+lesson[6]).fixWhiteSpaces() }?.let { + data.teacherList.singleOrNull { it.fullNameLastFirst == (lesson[7]+" "+lesson[6]).fixName() }?.let { lessonObject.teacherId = it.id } data.teamList.singleOrNull { it.name == lesson[8]+lesson[9] }?.let { @@ -52,7 +52,7 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List) { data.subjectList.singleOrNull { it.longName == lesson[5] }?.let { lessonChange.subjectId = it.id } - data.teacherList.singleOrNull { it.fullNameLastFirst == (lesson[7]+" "+lesson[6]).fixWhiteSpaces() }?.let { + data.teacherList.singleOrNull { it.fullNameLastFirst == (lesson[7]+" "+lesson[6]).fixName() }?.let { lessonChange.teacherId = it.id } data.teamList.singleOrNull { it.name == lesson[8]+lesson[9] }?.let { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiUsers.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiUsers.kt index 7b3fed51..1ef8619f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiUsers.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiUsers.kt @@ -6,7 +6,7 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher -import pl.szczodrzynski.edziennik.fixWhiteSpaces +import pl.szczodrzynski.edziennik.fixName class MobidziennikApiUsers(val data: DataMobidziennik, rows: List) { init { @@ -15,9 +15,12 @@ class MobidziennikApiUsers(val data: DataMobidziennik, rows: List) { continue val cols = row.split("|") + if (cols[1] != "*") + continue + val id = cols[0].toLong() - val name = cols[4].fixWhiteSpaces() - val surname = cols[5].fixWhiteSpaces() + val name = cols[4].fixName() + val surname = cols[5].fixName() data.teachersMap.put(id, "$surname $name") data.teacherList.put(id, Teacher(data.profileId, id, name, surname)) From cfc5db2fe85f4f76e79aa1c0fdaea602b3d6a65b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 28 Oct 2019 22:12:04 +0100 Subject: [PATCH 111/691] [APIv2/Idziennik] Add Grades. --- .../api/v2/idziennik/data/IdziennikData.kt | 6 + .../idziennik/data/web/IdziennikWebGrades.kt | 155 ++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebGrades.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt index 84d93d37..ea1b4c3b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt @@ -6,7 +6,9 @@ package pl.szczodrzynski.edziennik.api.v2.idziennik.data import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_GRADES import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_TIMETABLE +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.web.IdziennikWebGrades import pl.szczodrzynski.edziennik.api.v2.idziennik.data.web.IdziennikWebTimetable import pl.szczodrzynski.edziennik.utils.Utils @@ -42,6 +44,10 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_timetable) IdziennikWebTimetable(data) { onSuccess() } } + ENDPOINT_IDZIENNIK_WEB_GRADES -> { + data.startProgress(R.string.edziennik_progress_endpoint_grades) + IdziennikWebGrades(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebGrades.kt new file mode 100644 index 00000000..e99387f6 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebGrades.kt @@ -0,0 +1,155 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web + +import android.graphics.Color +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA +import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_GRADES +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_GRADES +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.utils.models.Date + +class IdziennikWebGrades(override val data: DataIdziennik, + val onSuccess: () -> Unit) : IdziennikWeb(data) { + companion object { + private const val TAG = "IdziennikWebGrades" + } + + init { + webApiGet(TAG, IDZIENNIK_WEB_GRADES, mapOf( + "idPozDziennika" to data.registerId + )) { result -> + val json = result.getJsonObject("d") ?: run { + data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA) + .withApiResponse(result)) + return@webApiGet + } + + json.getJsonArray("Przedmioty")?.asJsonObjectList()?.onEach { subjectJson -> + val subject = data.getSubject( + subjectJson.getString("Przedmiot") ?: return@onEach, + subjectJson.getLong("IdPrzedmiotu") ?: return@onEach, + subjectJson.getString("Przedmiot") ?: return@onEach + ) + subjectJson.getJsonArray("Oceny")?.asJsonObjectList()?.forEach { grade -> + val id = grade.getLong("idK") ?: return@forEach + val category = grade.getString("Kategoria") ?: "" + val name = grade.getString("Ocena") ?: "?" + val semester = grade.getInt("Semestr") ?: 1 + val teacher = data.getTeacherByLastFirst(grade.getString("Wystawil") ?: return@forEach) + + val countToAverage = grade.getBoolean("DoSredniej") ?: true + var value = grade.getFloat("WartoscDoSred") ?: 0.0f + val weight = if (countToAverage) + grade.getFloat("Waga") ?: 0.0f + else + 0.0f + + val gradeColor = grade.getString("Kolor") ?: "" + var colorInt = 0xff2196f3.toInt() + if (gradeColor.isNotEmpty()) { + colorInt = Color.parseColor("#$gradeColor") + } + + val gradeObject = Grade( + profileId, + id, + category, + colorInt, + "", + name, + value, + weight, + semester, + teacher.id, + subject.id) + + when (grade.getInt("Typ")) { + 0 -> { + val history = grade.getJsonArray("Historia")?.asJsonObjectList() + if (history?.isNotEmpty() == true) { + var sum = gradeObject.value * gradeObject.weight + var count = gradeObject.weight + for (historyItem in history) { + val countToTheAverage = historyItem.getBoolean("DoSredniej") ?: false + value = historyItem.get("WartoscDoSred").asFloat + val weight = historyItem.get("Waga").asFloat + + if (value > 0 && countToTheAverage) { + sum += value * weight + count += weight + } + + val historyObject = Grade( + profileId, + gradeObject.id * -1, + historyItem.get("Kategoria").asString, + Color.parseColor("#" + historyItem.get("Kolor").asString), + historyItem.get("Uzasadnienie").asString, + historyItem.get("Ocena").asString, + value, + if (value > 0f && countToTheAverage) weight * -1f else 0f, + historyItem.get("Semestr").asInt, + teacher.id, + subject.id) + historyObject.parentId = gradeObject.id + + val addedDate = historyItem.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis() + + data.gradeList.add(historyObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_GRADE, + historyObject.id, + true, + true, + addedDate + )) + } + // update the current grade's value with an average of all historical grades and itself + if (sum > 0 && count > 0) { + gradeObject.value = sum / count + } + gradeObject.isImprovement = true // gradeObject is the improved grade. Originals are historyObjects + } + } + 1 -> { + gradeObject.type = Grade.TYPE_SEMESTER1_FINAL + gradeObject.name = name + gradeObject.weight = 0f + } + 2 -> { + gradeObject.type = Grade.TYPE_YEAR_FINAL + gradeObject.name = name + gradeObject.weight = 0f + } + } + + val addedDate = grade.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis() + + data.gradeList.add(gradeObject) + data.metadataList.add( + Metadata( + profileId, + Metadata.TYPE_GRADE, + id, + data.profile?.empty ?: false, + data.profile?.empty ?: false, + addedDate + )) + } + } + + data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_GRADES, SYNC_ALWAYS) + onSuccess() + } + } +} From 81c6275255ea2ec61ec56eee074e17ad2fa75594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 28 Oct 2019 23:18:59 +0100 Subject: [PATCH 112/691] [APIv2/Idziennik] Add Proposed grades, Exams, Notices, Announcements and broken Attendance. --- .../api/v2/idziennik/data/IdziennikData.kt | 27 +++- .../data/web/IdziennikWebAnnouncements.kt | 74 +++++++++ .../data/web/IdziennikWebAttendance.kt | 143 ++++++++++++++++++ .../idziennik/data/web/IdziennikWebExams.kt | 113 ++++++++++++++ .../idziennik/data/web/IdziennikWebNotices.kt | 75 +++++++++ .../data/web/IdziennikWebProposedGrades.kt | 108 +++++++++++++ app/src/main/res/values/strings.xml | 2 + 7 files changed, 537 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebAnnouncements.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebAttendance.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebExams.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebNotices.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebProposedGrades.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt index ea1b4c3b..8a78e6ee 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt @@ -5,11 +5,8 @@ package pl.szczodrzynski.edziennik.api.v2.idziennik.data import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik -import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_GRADES -import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_TIMETABLE -import pl.szczodrzynski.edziennik.api.v2.idziennik.data.web.IdziennikWebGrades -import pl.szczodrzynski.edziennik.api.v2.idziennik.data.web.IdziennikWebTimetable +import pl.szczodrzynski.edziennik.api.v2.idziennik.* +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.web.* import pl.szczodrzynski.edziennik.utils.Utils class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) { @@ -48,6 +45,26 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_grades) IdziennikWebGrades(data) { onSuccess() } } + ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES -> { + data.startProgress(R.string.edziennik_progress_endpoint_proposed_grades) + IdziennikWebProposedGrades(data) { onSuccess() } + } + ENDPOINT_IDZIENNIK_WEB_EXAMS -> { + data.startProgress(R.string.edziennik_progress_endpoint_exams) + IdziennikWebExams(data) { onSuccess() } + } + ENDPOINT_IDZIENNIK_WEB_NOTICES -> { + data.startProgress(R.string.edziennik_progress_endpoint_notices) + IdziennikWebNotices(data) { onSuccess() } + } + ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS -> { + data.startProgress(R.string.edziennik_progress_endpoint_announcements) + IdziennikWebAnnouncements(data) { onSuccess() } + } + ENDPOINT_IDZIENNIK_WEB_ATTENDANCE -> { + data.startProgress(R.string.edziennik_progress_endpoint_attendance) + IdziennikWebAttendance(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebAnnouncements.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebAnnouncements.kt new file mode 100644 index 00000000..5f700482 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebAnnouncements.kt @@ -0,0 +1,74 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web + +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA +import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_ANNOUNCEMENTS +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.utils.models.Date + +class IdziennikWebAnnouncements(override val data: DataIdziennik, + val onSuccess: () -> Unit) : IdziennikWeb(data) { + companion object { + private const val TAG = "IdziennikWebAnnouncements" + } + + init { + val param = JsonObject() + param.add("parametryFiltrow", JsonArray()) + + webApiGet(TAG, IDZIENNIK_WEB_ANNOUNCEMENTS, mapOf( + "uczenId" to (data.studentId ?: ""), + "param" to param + )) { result -> + val json = result.getJsonObject("d") ?: run { + data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA) + .withApiResponse(result)) + return@webApiGet + } + + for (jAnnouncementEl in json.getAsJsonArray("ListK")) { + val jAnnouncement = jAnnouncementEl.asJsonObject + // jAnnouncement + val announcementId = jAnnouncement.get("Id").asLong + + val rTeacher = data.getTeacherByFirstLast(jAnnouncement.get("Autor").asString) + val addedDate = java.lang.Long.parseLong(jAnnouncement.get("DataDodania").asString.replace("[^\\d]".toRegex(), "")) + val startDate = Date.fromMillis(java.lang.Long.parseLong(jAnnouncement.get("DataWydarzenia").asString.replace("[^\\d]".toRegex(), ""))) + + val announcementObject = Announcement( + profileId, + announcementId, + jAnnouncement.get("Temat").asString, + jAnnouncement.get("Tresc").asString, + startDate, + null, + rTeacher.id + ) + data.announcementList.add(announcementObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_ANNOUNCEMENT, + announcementObject.id, + profile?.empty ?: false, + profile?.empty ?: false, + addedDate + )) + } + + data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebAttendance.kt new file mode 100644 index 00000000..68823c17 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebAttendance.kt @@ -0,0 +1,143 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web + +import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA +import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_ATTENDANCE +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_ATTENDANCE +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.crc16 +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance +import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance.* +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Time + +class IdziennikWebAttendance(override val data: DataIdziennik, + val onSuccess: () -> Unit) : IdziennikWeb(data) { + companion object { + private const val TAG = "IdziennikWebAttendance" + } + + init { + getAttendance() + } + + private var attendanceYear: Int = Date.getToday().year + private var attendanceMonth: Int = Date.getToday().month + private var attendancePrevMonthChecked = false + private fun getAttendance() { + webApiGet(TAG, IDZIENNIK_WEB_ATTENDANCE, mapOf( + "idPozDziennika" to data.registerId, + "mc" to attendanceMonth, + "rok" to attendanceYear, + "dataTygodnia" to "" + )) { result -> + val json = result.getJsonObject("d") ?: run { + data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA) + .withApiResponse(result)) + return@webApiGet + } + + for (jAttendanceEl in json.getAsJsonArray("Obecnosci")) { + val jAttendance = jAttendanceEl.asJsonObject + // jAttendance + val attendanceTypeIdziennik = jAttendance.get("TypObecnosci").asInt + if (attendanceTypeIdziennik == 5 || attendanceTypeIdziennik == 7) + continue + val attendanceDate = Date.fromY_m_d(jAttendance.get("Data").asString) + val attendanceTime = Time.fromH_m(jAttendance.get("OdDoGodziny").asString) + if (attendanceDate.combineWith(attendanceTime) > System.currentTimeMillis()) + continue + + val attendanceId = jAttendance.get("IdLesson").asString.crc16().toLong() + val rSubject = data.getSubject(jAttendance.get("Przedmiot").asString, jAttendance.get("IdPrzedmiot").asLong, "") + val rTeacher = data.getTeacherByFDotSpaceLast(jAttendance.get("PrzedmiotNauczyciel").asString) + + var attendanceName = "obecność" + var attendanceType = Attendance.TYPE_CUSTOM + + when (attendanceTypeIdziennik) { + 1 /* nieobecność usprawiedliwiona */ -> { + attendanceName = "nieobecność usprawiedliwiona" + attendanceType = TYPE_ABSENT_EXCUSED + } + 2 /* spóźnienie */ -> { + attendanceName = "spóźnienie" + attendanceType = TYPE_BELATED + } + 3 /* nieobecność nieusprawiedliwiona */ -> { + attendanceName = "nieobecność nieusprawiedliwiona" + attendanceType = TYPE_ABSENT + } + 4 /* zwolnienie */, 9 /* zwolniony / obecny */ -> { + attendanceType = TYPE_RELEASED + if (attendanceTypeIdziennik == 4) + attendanceName = "zwolnienie" + if (attendanceTypeIdziennik == 9) + attendanceName = "zwolnienie / obecność" + } + 0 /* obecny */, 8 /* Wycieczka */ -> { + attendanceType = TYPE_PRESENT + if (attendanceTypeIdziennik == 8) + attendanceName = "wycieczka" + } + } + + val semester = profile?.dateToSemester(attendanceDate) ?: 1 + + val attendanceObject = Attendance( + profileId, + attendanceId, + rTeacher.id, + rSubject.id, + semester, + attendanceName, + attendanceDate, + attendanceTime, + attendanceType + ) + + data.attendanceList.add(attendanceObject) + if (attendanceObject.type != TYPE_PRESENT) { + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_ATTENDANCE, + attendanceObject.id, + profile?.empty ?: false, + profile?.empty ?: false, + System.currentTimeMillis() + )) + } + } + + val attendanceDateValue = attendanceYear * 10000 + attendanceMonth * 100 + if (profile?.empty == true && attendanceDateValue > profile?.getSemesterStart(1)?.value ?: 99999999) { + attendancePrevMonthChecked = true // do not need to check prev month later + attendanceMonth-- + if (attendanceMonth < 1) { + attendanceMonth = 12 + attendanceYear-- + } + getAttendance() + } else if (!attendancePrevMonthChecked /* get also the previous month */) { + attendanceMonth-- + if (attendanceMonth < 1) { + attendanceMonth = 12 + attendanceYear-- + } + attendancePrevMonthChecked = true + getAttendance() + } else { + data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_ATTENDANCE, SYNC_ALWAYS) + onSuccess() + } + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebExams.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebExams.kt new file mode 100644 index 00000000..eb0e2c0e --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebExams.kt @@ -0,0 +1,113 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web + +import com.google.gson.JsonObject +import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA +import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_EXAMS +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_EXAMS +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.events.Event +import pl.szczodrzynski.edziennik.data.db.modules.lessons.Lesson +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.utils.models.Date + +class IdziennikWebExams(override val data: DataIdziennik, + val onSuccess: () -> Unit) : IdziennikWeb(data) { + companion object { + private const val TAG = "IdziennikWebExams" + } + + init { + getExams() + } + + private var examsYear = Date.getToday().year + private var examsMonth = Date.getToday().month + private var examsMonthsChecked = 0 + private var examsNextMonthChecked = false // TO DO temporary // no more // idk + private fun getExams() { + val param = JsonObject() + param.addProperty("strona", 1) + param.addProperty("iloscNaStrone", "99") + param.addProperty("iloscRekordow", -1) + param.addProperty("kolumnaSort", "ss.Nazwa,sp.Data_sprawdzianu") + param.addProperty("kierunekSort", 0) + param.addProperty("maxIloscZaznaczonych", 0) + param.addProperty("panelFiltrow", 0) + + webApiGet(TAG, IDZIENNIK_WEB_EXAMS, mapOf( + "idP" to data.registerId, + "rok" to examsYear, + "miesiac" to examsMonth, + "param" to param + )) { result -> + val json = result.getJsonObject("d") ?: run { + data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA) + .withApiResponse(result)) + return@webApiGet + } + + for (jExamEl in json.getAsJsonArray("ListK")) { + val jExam = jExamEl.asJsonObject + // jExam + val eventId = jExam.get("_recordId").asLong + val rSubject = data.getSubject(jExam.get("przedmiot").asString, -1, "") + val rTeacher = data.getTeacherByLastFirst(jExam.get("wpisal").asString) + val examDate = Date.fromY_m_d(jExam.get("data").asString) + val lessonObject = Lesson.getByWeekDayAndSubject(data.lessonList, examDate.weekDay, rSubject.id) + val examTime = lessonObject?.startTime + + val eventType = if (jExam.get("rodzaj").asString == "sprawdzian/praca klasowa") Event.TYPE_EXAM else Event.TYPE_SHORT_QUIZ + val eventObject = Event( + profileId, + eventId, + examDate, + examTime, + jExam.get("zakres").asString, + -1, + eventType, + false, + rTeacher.id, + rSubject.id, + data.teamClass?.id ?: -1 + ) + + data.eventList.add(eventObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_EVENT, + eventObject.id, + profile?.empty ?: false, + profile?.empty ?: false, + System.currentTimeMillis() + )) + } + + if (profile?.empty == true && examsMonthsChecked < 3 /* how many months backwards to check? */) { + examsMonthsChecked++ + examsMonth-- + if (examsMonth < 1) { + examsMonth = 12 + examsYear-- + } + getExams() + } else if (!examsNextMonthChecked /* get also one month forward */) { + val showDate = Date.getToday().stepForward(0, 1, 0) + examsYear = showDate.year + examsMonth = showDate.month + examsNextMonthChecked = true + getExams() + } else { + data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_EXAMS, SYNC_ALWAYS) + onSuccess() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebNotices.kt new file mode 100644 index 00000000..7cdaf3af --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebNotices.kt @@ -0,0 +1,75 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web + +import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA +import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_NOTICES +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_NOTICES +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.crc16 +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice +import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice.* +import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.utils.models.Date + +class IdziennikWebNotices(override val data: DataIdziennik, + val onSuccess: () -> Unit) : IdziennikWeb(data) { + companion object { + private const val TAG = "IdziennikWebNotices" + } + + init { + webApiGet(TAG, IDZIENNIK_WEB_NOTICES, mapOf( + "idPozDziennika" to data.registerId + )) { result -> + val json = result.getJsonObject("d") ?: run { + data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA) + .withApiResponse(result)) + return@webApiGet + } + + for (jNoticeEl in json.getAsJsonArray("SUwaga")) { + val jNotice = jNoticeEl.asJsonObject + // jNotice + val noticeId = jNotice.get("id").asString.crc16().toLong() + + val rTeacher = data.getTeacherByLastFirst(jNotice.get("Nauczyciel").asString) + val addedDate = Date.fromY_m_d(jNotice.get("Data").asString) + + var nType = TYPE_NEUTRAL + val jType = jNotice.get("Typ").asString + if (jType == "n") { + nType = TYPE_NEGATIVE + } else if (jType == "p") { + nType = TYPE_POSITIVE + } + + val noticeObject = Notice( + profileId, + noticeId, + jNotice.get("Tresc").asString, + jNotice.get("Semestr").asInt, + nType, + rTeacher.id) + data.noticeList.add(noticeObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_NOTICE, + noticeObject.id, + profile?.empty ?: false, + profile?.empty ?: false, + addedDate.inMillis + )) + } + + data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_NOTICES, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebProposedGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebProposedGrades.kt new file mode 100644 index 00000000..376aa8cf --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebProposedGrades.kt @@ -0,0 +1,108 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web + +import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA +import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_MISSING_GRADES +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade +import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER1_PROPOSED +import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_YEAR_PROPOSED +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.utils.Utils.getWordGradeValue + +class IdziennikWebProposedGrades(override val data: DataIdziennik, + val onSuccess: () -> Unit) : IdziennikWeb(data) { + companion object { + private const val TAG = "IdziennikWebProposedGrades" + } + + init { + webApiGet(TAG, IDZIENNIK_WEB_MISSING_GRADES, mapOf( + "idPozDziennika" to data.registerId + )) { result -> + val json = result.getJsonObject("d") ?: run { + data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA) + .withApiResponse(result)) + return@webApiGet + } + + val jSubjects = json.getAsJsonArray("Przedmioty") + for (jSubjectEl in jSubjects) { + val jSubject = jSubjectEl.getAsJsonObject() + // jSubject + val rSubject = data.getSubject(jSubject.get("Przedmiot").getAsString(), -1, jSubject.get("Przedmiot").getAsString()) + val semester1Proposed = jSubject.get("OcenaSem1").getAsString() + val semester2Proposed = jSubject.get("OcenaSem2").getAsString() + val semester1Value = getWordGradeValue(semester1Proposed) + val semester2Value = getWordGradeValue(semester2Proposed) + val semester1Id = rSubject.id * -100 - 1 + val semester2Id = rSubject.id * -100 - 2 + + if (semester1Proposed != "") { + val gradeObject = Grade( + profileId, + semester1Id, + "", + -1, + "", + semester1Value.toString(), + semester1Value.toFloat(), + 0f, + 1, + -1, + rSubject.id) + + gradeObject.type = TYPE_SEMESTER1_PROPOSED + + data.gradeList.add(gradeObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_GRADE, + gradeObject.id, + profile?.empty ?: false, + profile?.empty ?: false, + System.currentTimeMillis() + )) + } + + if (semester2Proposed != "") { + val gradeObject = Grade( + profileId, + semester2Id, + "", + -1, + "", + semester2Value.toString(), + semester2Value.toFloat(), + 0f, + 2, + -1, + rSubject.id) + + gradeObject.type = TYPE_YEAR_PROPOSED + + data.gradeList.add(gradeObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_GRADE, + gradeObject.id, + profile?.empty ?: false, + profile?.empty ?: false, + System.currentTimeMillis() + )) + } + } + + data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 10533021..6cb98a7d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -976,4 +976,6 @@ Polski English Pobieranie planu lekcji... + Pobieranie ocen proponowanych... + Pobieranie listy sprawdzianów... From 2ad8a308b391326258a4ff96ead78988e296b428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 28 Oct 2019 23:21:57 +0100 Subject: [PATCH 113/691] [APIv2/Idziennik] Better request parameter adding. --- .../edziennik/api/v2/idziennik/data/IdziennikWeb.kt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikWeb.kt index f1241c0c..2bae8843 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikWeb.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikWeb.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.idziennik.data +import com.google.gson.JsonArray import com.google.gson.JsonObject import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response @@ -83,9 +84,19 @@ open class IdziennikWeb(open val data: DataIdziennik) { .userAgent(IDZIENNIK_USER_AGENT) .postJson() .apply { + val json = JsonObject() parameters.map { (name, value) -> - addParameter(name, value) + when (value) { + is JsonObject -> json.add(name, value) + is JsonArray -> json.add(name, value) + is String -> json.addProperty(name, value) + is Int -> json.addProperty(name, value) + is Long -> json.addProperty(name, value) + is Float -> json.addProperty(name, value) + is Char -> json.addProperty(name, value) + } } + setJsonBody(json) } .allowErrorCode(HTTP_UNAUTHORIZED) .allowErrorCode(HTTP_INTERNAL_ERROR) From d6d73b19eca46a839b729dfc4666be62b15408d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 29 Oct 2019 23:01:11 +0100 Subject: [PATCH 114/691] [APIv2/Mobidziennik] Fix a terrible mistake. --- .../mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt index 9e9c1557..6ac0cdcf 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt @@ -4,7 +4,6 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web -import pl.szczodrzynski.edziennik.App.profileId import pl.szczodrzynski.edziennik.api.v2.Regexes import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.db.modules.luckynumber.LuckyNumber @@ -29,7 +28,7 @@ class MobidziennikLuckyNumberExtractor(val data: DataMobidziennik, text: String) data.luckyNumberList.add(luckyNumberObject) data.metadataList.add( Metadata( - profileId, + data.profileId, Metadata.TYPE_LUCKY_NUMBER, luckyNumberObject.date.value.toLong(), data.profile?.empty ?: false, From f2b36035316cc753609a5532c3c3912df4865b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 29 Oct 2019 23:17:13 +0100 Subject: [PATCH 115/691] [APIv2/Idziennik] Fix Attendance and Exams. Add Lucky number. --- .../edziennik/api/v2/Constants.kt | 4 + .../szczodrzynski/edziennik/api/v2/Errors.kt | 4 +- .../szczodrzynski/edziennik/api/v2/Regexes.kt | 14 +-- .../api/v2/idziennik/DataIdziennik.kt | 7 +- .../api/v2/idziennik/data/IdziennikApi.kt | 116 ++++++++++++++++++ .../api/v2/idziennik/data/IdziennikData.kt | 5 + .../data/api/IdziennikApiCurrentRegister.kt | 94 ++++++++++++++ .../data/web/IdziennikWebAttendance.kt | 7 +- .../idziennik/data/web/IdziennikWebExams.kt | 9 +- .../v2/idziennik/login/IdziennikLoginWeb.kt | 5 +- .../edziennik/api/v2/models/Data.kt | 5 +- 11 files changed, 252 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikApi.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/api/IdziennikApiCurrentRegister.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index 90aa98ec..49b24503 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -58,6 +58,10 @@ const val IDZIENNIK_WEB_ATTENDANCE = "mod_panelRodzica/obecnosci/WS_obecnosciUcz const val IDZIENNIK_WEB_ANNOUNCEMENTS = "mod_panelRodzica/tabOgl/WS_tablicaOgloszen.asmx/GetOgloszenia" const val IDZIENNIK_WEB_MESSAGES_LIST = "mod_komunikator/WS_wiadomosci.asmx/PobierzListeWiadomosci" +val IDZIENNIK_API_USER_AGENT = SYSTEM_USER_AGENT +const val IDZIENNIK_API_URL = "https://iuczniowie.progman.pl/idziennik/api" +const val IDZIENNIK_API_CURRENT_REGISTER = "Uczniowie/\$STUDENT_ID/AktualnyDziennik" + val MOBIDZIENNIK_USER_AGENT = SYSTEM_USER_AGENT const val VULCAN_API_USER_AGENT = "MobileUserAgent" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index ae9db1e7..f1f3564c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -133,7 +133,6 @@ const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME = 402 const val ERROR_LOGIN_IDZIENNIK_WEB_PASSWORD_CHANGE_NEEDED = 403 const val ERROR_LOGIN_IDZIENNIK_WEB_MAINTENANCE = 404 const val ERROR_LOGIN_IDZIENNIK_WEB_SERVER_ERROR = 405 -const val ERROR_LOGIN_IDZIENNIK_WEB_API_ERROR = 406 /* {"Message":"There was an error processing the request.","StackTrace":"","ExceptionType":""} */ const val ERROR_LOGIN_IDZIENNIK_WEB_OTHER = 410 const val ERROR_LOGIN_IDZIENNIK_WEB_API_NO_ACCESS = 411 /* {"d":{"__type":"mds.Web.mod_komunikator.WS_mod_wiadomosci+detailWiadomosci","Wiadomosc":{"_recordId":0,"DataNadania":null,"DataOdczytania":null,"Nadawca":null,"ListaOdbiorcow":[],"Tytul":null,"Text":null,"ListaZal":[]},"Bledy":{"__type":"mds.Module.Globalne+sBledy","CzyJestBlad":true,"ListaBledow":["Nie masz dostępu do tych zasobów!"],"ListaKodowBledow":[]},"czyJestWiecej":false}} */ const val ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION = 420 @@ -146,6 +145,8 @@ const val ERROR_IDZIENNIK_WEB_SERVER_ERROR = 433 const val ERROR_IDZIENNIK_WEB_PASSWORD_CHANGE_NEEDED = 434 const val ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR = 440 const val ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA = 441 +const val ERROR_IDZIENNIK_API_ACCESS_DENIED = 450 +const val ERROR_IDZIENNIK_API_OTHER = 451 const val ERROR_TEMPLATE_WEB_OTHER = 801 @@ -160,3 +161,4 @@ const val EXCEPTION_NOTIFY_AND_SYNC = 910 const val EXCEPTION_LIBRUS_MESSAGES_REQUEST = 911 const val EXCEPTION_IDZIENNIK_WEB_REQUEST = 912 const val EXCEPTION_IDZIENNIK_WEB_API_REQUEST = 913 +const val EXCEPTION_IDZIENNIK_API_REQUEST = 914 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt index a35d95ba..532d021b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Regexes.kt @@ -40,24 +40,24 @@ object Regexes { val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy { - "".toRegex(RegexOption.DOT_MATCHES_ALL) + """""".toRegex(RegexOption.DOT_MATCHES_ALL) } val IDZIENNIK_LOGIN_ERROR by lazy { - "id=\"spanErrorMessage\">(.*?)(.*?)(.+?)".toRegex(RegexOption.DOT_MATCHES_ALL) + """Imię i nazwisko:.+?">(.+?)""".toRegex(RegexOption.DOT_MATCHES_ALL) } val IDZIENNIK_LOGIN_FIRST_IS_PARENT by lazy { - "id=\"ctl00_CzyRodzic\" value=\"([01])\" />".toRegex() + """id="ctl00_CzyRodzic" value="([01])" />""".toRegex() } val IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR by lazy { - "name=\"ctl00\\\$dxComboRokSzkolny\".+?selected=\"selected\".*?value=\"([0-9]+)\">([0-9/]+)<".toRegex(RegexOption.DOT_MATCHES_ALL) + """name="ctl00\${"$"}dxComboRokSzkolny".+?selected="selected".*?value="([0-9]+)">([0-9/]+)<""".toRegex(RegexOption.DOT_MATCHES_ALL) } val IDZIENNIK_LOGIN_FIRST_STUDENT_SELECT by lazy { - "".toRegex(RegexOption.DOT_MATCHES_ALL) + """""".toRegex(RegexOption.DOT_MATCHES_ALL) } val IDZIENNIK_LOGIN_FIRST_STUDENT by lazy { - "(.+?)\\s(.+?)\\s*\\((.+?),\\s*(.+?)\\)".toRegex(RegexOption.DOT_MATCHES_ALL) + """(.+?)\s(.+?)\s*\((.+?),\s*(.+?)\)""".toRegex(RegexOption.DOT_MATCHES_ALL) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt index 61629c3d..a774129d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/DataIdziennik.kt @@ -18,7 +18,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) { fun isWebLoginValid() = loginExpiryTime-30 > currentTimeUnix() && webSessionId.isNotNullNorEmpty() && webAuth.isNotNullNorEmpty() - fun isApiLoginValid() = loginExpiryTime-30 > currentTimeUnix() && apiBearer.isNotNullNorEmpty() + fun isApiLoginValid() = apiExpiryTime-30 > currentTimeUnix() && apiBearer.isNotNullNorEmpty() override fun satisfyLoginMethods() { loginMethods.clear() @@ -46,6 +46,11 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data( get() { mLoginExpiryTime = mLoginExpiryTime ?: loginStore.getLoginData("loginExpiryTime", 0L); return mLoginExpiryTime ?: 0L } set(value) { loginStore.putLoginData("loginExpiryTime", value); mLoginExpiryTime = value } + private var mApiExpiryTime: Long? = null + var apiExpiryTime: Long + get() { mApiExpiryTime = mApiExpiryTime ?: loginStore.getLoginData("apiExpiryTime", 0L); return mApiExpiryTime ?: 0L } + set(value) { loginStore.putLoginData("apiExpiryTime", value); mApiExpiryTime = value } + /* __ __ _ \ \ / / | | \ \ /\ / /__| |__ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikApi.kt new file mode 100644 index 00000000..66a87dfa --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikApi.kt @@ -0,0 +1,116 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-29. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.data + +import com.google.gson.JsonArray +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import im.wangchao.mhttp.Request +import im.wangchao.mhttp.Response +import im.wangchao.mhttp.callback.TextCallbackHandler +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.models.ApiError +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.utils.Utils +import java.net.HttpURLConnection + +open class IdziennikApi(open val data: DataIdziennik) { + companion object { + const val TAG = "IdziennikApi" + } + + val profileId + get() = data.profile?.id ?: -1 + + val profile + get() = data.profile + + fun apiGet(tag: String, endpointTemplate: String, method: Int = GET, parameters: Map = emptyMap(), onSuccess: (json: JsonElement) -> Unit) { + val endpoint = endpointTemplate.replace("\$STUDENT_ID", data.studentId ?: "") + Utils.d(tag, "Request: Idziennik/API - $IDZIENNIK_API_URL/$endpoint") + + val callback = object : TextCallbackHandler() { + override fun onSuccess(text: String?, response: Response?) { + if (text == null) { + data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY) + .withResponse(response)) + return + } + + val json = try { + JsonParser().parse(text) + } catch (_: Exception) { null } + + var error: String? = null + if (json == null) { + error = text + } + else if (json is JsonObject) { + error = if (response?.code() == 200) null else + json.getString("message") ?: json.toString() + } + error?.let { code -> + when (code) { + "Authorization has been denied for this request." -> ERROR_IDZIENNIK_API_ACCESS_DENIED + else -> ERROR_IDZIENNIK_API_OTHER + }.let { errorCode -> + data.error(ApiError(tag, errorCode) + .withApiResponse(text) + .withResponse(response)) + return + } + } + + try { + onSuccess(json!!) + } catch (e: Exception) { + data.error(ApiError(tag, EXCEPTION_IDZIENNIK_API_REQUEST) + .withResponse(response) + .withThrowable(e) + .withApiResponse(text)) + } + } + + override fun onFailure(response: Response?, throwable: Throwable?) { + data.error(ApiError(tag, ERROR_REQUEST_FAILURE) + .withResponse(response) + .withThrowable(throwable)) + } + } + + Request.builder() + .url("$IDZIENNIK_API_URL/$endpoint") + .userAgent(IDZIENNIK_API_USER_AGENT) + .addHeader("Authorization", "Bearer ${data.apiBearer}") + .apply { + when (method) { + GET -> get() + POST -> { + postJson() + val json = JsonObject() + parameters.map { (name, value) -> + when (value) { + is JsonObject -> json.add(name, value) + is JsonArray -> json.add(name, value) + is String -> json.addProperty(name, value) + is Int -> json.addProperty(name, value) + is Long -> json.addProperty(name, value) + is Float -> json.addProperty(name, value) + is Char -> json.addProperty(name, value) + } + } + setJsonBody(json) + } + } + } + .allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED) + .allowErrorCode(HttpURLConnection.HTTP_INTERNAL_ERROR) + .callback(callback) + .build() + .enqueue() + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt index 8a78e6ee..4f3b3e66 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt @@ -6,6 +6,7 @@ package pl.szczodrzynski.edziennik.api.v2.idziennik.data import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.idziennik.* +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.api.IdziennikApiCurrentRegister import pl.szczodrzynski.edziennik.api.v2.idziennik.data.web.* import pl.szczodrzynski.edziennik.utils.Utils @@ -65,6 +66,10 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_attendance) IdziennikWebAttendance(data) { onSuccess() } } + ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER -> { + data.startProgress(R.string.edziennik_progress_endpoint_lucky_number) + IdziennikApiCurrentRegister(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/api/IdziennikApiCurrentRegister.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/api/IdziennikApiCurrentRegister.kt new file mode 100644 index 00000000..2366e625 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/api/IdziennikApiCurrentRegister.kt @@ -0,0 +1,94 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-29. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.data.api + +import com.google.gson.JsonObject +import pl.szczodrzynski.edziennik.DAY +import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_API_CURRENT_REGISTER +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikApi +import pl.szczodrzynski.edziennik.data.db.modules.luckynumber.LuckyNumber +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.getInt +import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Time + +class IdziennikApiCurrentRegister(override val data: DataIdziennik, + val onSuccess: () -> Unit) : IdziennikApi(data) { + companion object { + private const val TAG = "IdziennikApiCurrentRegister" + } + + init { + data.profile?.luckyNumber = -1 + data.profile?.luckyNumberDate = null + + apiGet(TAG, IDZIENNIK_API_CURRENT_REGISTER) { json -> + if (json !is JsonObject) { + onSuccess() + return@apiGet + } + + var nextSync = System.currentTimeMillis() + 14*DAY*1000 + + val settings = json.getJsonObject("ustawienia")?.apply { + profile?.dateSemester1Start = getString("poczatekSemestru1")?.let { Date.fromY_m_d(it) } + profile?.dateSemester2Start = getString("koniecSemestru1")?.let { Date.fromY_m_d(it).stepForward(0, 0, 1) } + profile?.dateYearEnd = getString("koniecSemestru2")?.let { Date.fromY_m_d(it) } + } + + json.getInt("szczesliwyNumerek")?.let { luckyNumber -> + val luckyNumberDate = Date.getToday() + settings.getString("godzinaPublikacjiSzczesliwegoLosu") + ?.let { Time.fromH_m(it) } + ?.let { publishTime -> + val now = Time.getNow() + if (publishTime.value < 150000 && now.value < publishTime.value) { + nextSync = luckyNumberDate.combineWith(publishTime) + luckyNumberDate.stepForward(0, 0, -1) // the lucky number is still for yesterday + } + else if (publishTime.value >= 150000 && now.value > publishTime.value) { + luckyNumberDate.stepForward(0, 0, 1) // the lucky number is already for tomorrow + nextSync = luckyNumberDate.combineWith(publishTime) + } + else if (publishTime.value < 150000) { + nextSync = luckyNumberDate + .clone() + .stepForward(0, 0, 1) + .combineWith(publishTime) + } + else { + nextSync = luckyNumberDate.combineWith(publishTime) + } + } + + + val luckyNumberObject = LuckyNumber( + data.profileId, + Date.getToday(), + luckyNumber + ) + + data.luckyNumberList.add(luckyNumberObject) + data.metadataList.add( + Metadata( + profileId, + Metadata.TYPE_LUCKY_NUMBER, + luckyNumberObject.date.value.toLong(), + data.profile?.empty ?: false, + data.profile?.empty ?: false, + System.currentTimeMillis() + )) + } + + + data.setSyncNext(ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER, syncAt = nextSync) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebAttendance.kt index 68823c17..1bcbddac 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebAttendance.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebAttendance.kt @@ -25,13 +25,14 @@ class IdziennikWebAttendance(override val data: DataIdziennik, private const val TAG = "IdziennikWebAttendance" } + private var attendanceYear = Date.getToday().year + private var attendanceMonth = Date.getToday().month + private var attendancePrevMonthChecked = false + init { getAttendance() } - private var attendanceYear: Int = Date.getToday().year - private var attendanceMonth: Int = Date.getToday().month - private var attendancePrevMonthChecked = false private fun getAttendance() { webApiGet(TAG, IDZIENNIK_WEB_ATTENDANCE, mapOf( "idPozDziennika" to data.registerId, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebExams.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebExams.kt index eb0e2c0e..3a9a3982 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebExams.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/web/IdziennikWebExams.kt @@ -24,14 +24,15 @@ class IdziennikWebExams(override val data: DataIdziennik, private const val TAG = "IdziennikWebExams" } - init { - getExams() - } - private var examsYear = Date.getToday().year private var examsMonth = Date.getToday().month private var examsMonthsChecked = 0 private var examsNextMonthChecked = false // TO DO temporary // no more // idk + + init { + getExams() + } + private fun getExams() { val param = JsonObject() param.addProperty("strona", 1) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt index 775200c6..85f9af55 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt @@ -8,6 +8,8 @@ import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response import im.wangchao.mhttp.callback.TextCallbackHandler import okhttp3.Cookie +import pl.szczodrzynski.edziennik.HOUR +import pl.szczodrzynski.edziennik.MINUTE import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik import pl.szczodrzynski.edziennik.api.v2.models.ApiError @@ -65,7 +67,8 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) { data.webSessionId = cookies.singleOrNull { it.name() == "ASP.NET_SessionId_iDziennik" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION data.webAuth = cookies.singleOrNull { it.name() == ".ASPXAUTH" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH data.apiBearer = cookies.singleOrNull { it.name() == "Bearer" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER - data.loginExpiryTime = response.getUnixDate() + 45 * 60 /* 45 min */ + data.loginExpiryTime = response.getUnixDate() + 45 * MINUTE + data.apiExpiryTime = response.getUnixDate() + 12 * HOUR /* actually it expires after 24 hours but I'm not sure when does the token refresh. */ return@run null }?.let { errorCode -> data.error(ApiError(TAG, errorCode) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index ed4a50b7..bd38d628 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -324,7 +324,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) } } - fun setSyncNext(endpointId: Int, syncIn: Long? = null, viewId: Int? = null) { + fun setSyncNext(endpointId: Int, syncIn: Long? = null, viewId: Int? = null, syncAt: Long? = null) { EndpointTimer(profile?.id ?: -1, endpointId).apply { syncedNow() @@ -334,6 +334,9 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) else syncIn(syncIn) } + if (syncAt != null) { + nextSync = syncAt + } if (viewId != null) syncWhenView(viewId) From 01657ca002b91a5a0528b3f64acbbef60734e952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 29 Oct 2019 23:20:03 +0100 Subject: [PATCH 116/691] [APIv2/Mobidziennik] Reformat Grades a bit. --- .../api/v2/mobidziennik/data/api/MobidziennikApiGrades.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiGrades.kt index 2587245f..52d100e0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiGrades.kt @@ -70,7 +70,8 @@ class MobidziennikApiGrades(val data: DataMobidziennik, rows: List) { weight, semester, teacherId, - subjectId);gradeObject.type = type + subjectId) + gradeObject.type = type data.gradeList.add(gradeObject) data.metadataList.add( From dd99771c0b622b018c593c3b5d2a9c449e2d6366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Wed, 30 Oct 2019 21:13:49 +0100 Subject: [PATCH 117/691] [APIv2/Idziennik] Add Messages - received and sent. --- .../pl/szczodrzynski/edziennik/Extensions.kt | 7 ++ .../edziennik/api/v2/Constants.kt | 7 ++ .../api/v2/idziennik/IdziennikFeatures.kt | 10 +- .../api/v2/idziennik/data/IdziennikData.kt | 10 ++ .../data/api/IdziennikApiMessagesInbox.kt | 94 +++++++++++++++++++ .../data/api/IdziennikApiMessagesSent.kt | 85 +++++++++++++++++ 6 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/api/IdziennikApiMessagesInbox.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/api/IdziennikApiMessagesSent.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index b0167959..c03db59c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -21,6 +21,7 @@ import pl.szczodrzynski.navlib.R import pl.szczodrzynski.navlib.getColorFromRes import java.text.SimpleDateFormat import java.util.* +import java.util.zip.CRC32 fun List.byId(id: Long) = firstOrNull { it.id == id } @@ -317,4 +318,10 @@ fun String.crc16(): Int { } crc = crc and 0xffff return crc + 32768 +} + +fun String.crc32(): Long { + val crc = CRC32() + crc.update(toByteArray()) + return crc.value } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index 49b24503..b34b6900 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -5,12 +5,15 @@ package pl.szczodrzynski.edziennik.api.v2 import android.os.Build +import pl.szczodrzynski.edziennik.BuildConfig const val GET = 0 const val POST = 1 val SYSTEM_USER_AGENT = System.getProperty("http.agent") ?: "Dalvik/2.1.0 Android" +val SERVER_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME} $SYSTEM_USER_AGENT" + val LIBRUS_USER_AGENT = "$SYSTEM_USER_AGENT LibrusMobileApp" const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0" const val LIBRUS_CLIENT_ID = "wmSyUMo8llDAs4y9tJVYY92oyZ6h4lAt7KCuy0Gv" @@ -61,6 +64,10 @@ const val IDZIENNIK_WEB_MESSAGES_LIST = "mod_komunikator/WS_wiadomosci.asmx/Pobi val IDZIENNIK_API_USER_AGENT = SYSTEM_USER_AGENT const val IDZIENNIK_API_URL = "https://iuczniowie.progman.pl/idziennik/api" const val IDZIENNIK_API_CURRENT_REGISTER = "Uczniowie/\$STUDENT_ID/AktualnyDziennik" +const val IDZIENNIK_API_GRADES = "Uczniowie/\$STUDENT_ID/Oceny/" /* + semester */ +const val IDZIENNIK_API_MESSAGES_INBOX = "Wiadomosci/Odebrane" +const val IDZIENNIK_API_MESSAGES_SENT = "Wiadomosci/Wyslane" + val MOBIDZIENNIK_USER_AGENT = SYSTEM_USER_AGENT diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt index eac360e4..c83c9690 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/IdziennikFeatures.kt @@ -46,19 +46,19 @@ val IdziennikFeatures = listOf( ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS to LOGIN_METHOD_IDZIENNIK_WEB ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), - Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_MESSAGES_INBOX, listOf( + /*Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_MESSAGES_INBOX, listOf( ENDPOINT_IDZIENNIK_WEB_MESSAGES_INBOX to LOGIN_METHOD_IDZIENNIK_WEB - ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)).withPriority(2), Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_MESSAGES_SENT, listOf( ENDPOINT_IDZIENNIK_WEB_MESSAGES_SENT to LOGIN_METHOD_IDZIENNIK_WEB - ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)).withPriority(2),*/ Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_MESSAGES_INBOX, listOf( ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX to LOGIN_METHOD_IDZIENNIK_API - ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + ), listOf(LOGIN_METHOD_IDZIENNIK_API)), Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_MESSAGES_SENT, listOf( ENDPOINT_IDZIENNIK_API_MESSAGES_SENT to LOGIN_METHOD_IDZIENNIK_API - ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), + ), listOf(LOGIN_METHOD_IDZIENNIK_API)), Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_LUCKY_NUMBER, listOf( ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER to LOGIN_METHOD_IDZIENNIK_API diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt index 4f3b3e66..a3de93a7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt @@ -7,6 +7,8 @@ package pl.szczodrzynski.edziennik.api.v2.idziennik.data import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.idziennik.* import pl.szczodrzynski.edziennik.api.v2.idziennik.data.api.IdziennikApiCurrentRegister +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.api.IdziennikApiMessagesInbox +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.api.IdziennikApiMessagesSent import pl.szczodrzynski.edziennik.api.v2.idziennik.data.web.* import pl.szczodrzynski.edziennik.utils.Utils @@ -70,6 +72,14 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_lucky_number) IdziennikApiCurrentRegister(data) { onSuccess() } } + ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX -> { + data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) + IdziennikApiMessagesInbox(data) { onSuccess() } + } + ENDPOINT_IDZIENNIK_API_MESSAGES_SENT -> { + data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox) + IdziennikApiMessagesSent(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/api/IdziennikApiMessagesInbox.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/api/IdziennikApiMessagesInbox.kt new file mode 100644 index 00000000..a1352a60 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/api/IdziennikApiMessagesInbox.kt @@ -0,0 +1,94 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-30. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.data.api + +import com.google.gson.JsonArray +import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_API_MESSAGES_INBOX +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikApi +import pl.szczodrzynski.edziennik.asJsonObjectList +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_DELETED +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED +import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.getBoolean +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.utils.Utils.crc32 +import pl.szczodrzynski.edziennik.utils.models.Date + +class IdziennikApiMessagesInbox(override val data: DataIdziennik, + val onSuccess: () -> Unit) : IdziennikApi(data) { + companion object { + private const val TAG = "IdziennikApiMessagesInbox" + } + + init { + apiGet(TAG, IDZIENNIK_API_MESSAGES_INBOX) { json -> + if (json !is JsonArray) { + onSuccess() + return@apiGet + } + + json.asJsonObjectList()?.forEach { jMessage -> + val subject = jMessage.getString("tytul") + if (subject?.contains("(") == true && subject.startsWith("iDziennik - ")) + return@forEach + if (subject?.startsWith("Uwaga dla ucznia (klasa:") == true) + return@forEach + + val messageIdStr = jMessage.getString("id") + val messageId = crc32((messageIdStr + "0").toByteArray()) + + var body = "[META:$messageIdStr;-1]" + body += jMessage.getString("tresc")?.replace("\n".toRegex(), "
") + + val readDate = if (jMessage.getBoolean("odczytana") == true) Date.fromIso(jMessage.getString("wersjaRekordu")) else 0 + val sentDate = Date.fromIso(jMessage.getString("dataWyslania")) + + val sender = jMessage.getAsJsonObject("nadawca") + val rTeacher = data.getTeacher( + sender.getString("imie") ?: "", + sender.getString("nazwisko") ?: "" + ) + rTeacher.loginId = sender.getString("id") + ":" + sender.getString("usr") + + val message = Message( + profileId, + messageId, + subject, + body, + if (jMessage.getBoolean("rekordUsuniety") == true) TYPE_DELETED else TYPE_RECEIVED, + rTeacher.id, + -1 + ) + + val messageRecipient = MessageRecipient( + profileId, + -1 /* me */, + -1, + readDate, + /*messageId*/ messageId + ) + + data.messageList.add(message) + data.messageRecipientList.add(messageRecipient) + data.messageMetadataList.add(Metadata( + profileId, + Metadata.TYPE_MESSAGE, + message.id, + readDate > 0, + readDate > 0 || profile?.empty ?: false, + sentDate + )) + } + + data.setSyncNext(ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/api/IdziennikApiMessagesSent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/api/IdziennikApiMessagesSent.kt new file mode 100644 index 00000000..8a4ca455 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/api/IdziennikApiMessagesSent.kt @@ -0,0 +1,85 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-30. + */ + +package pl.szczodrzynski.edziennik.api.v2.idziennik.data.api + +import com.google.gson.JsonArray +import pl.szczodrzynski.edziennik.DAY +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES +import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_API_MESSAGES_SENT +import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik +import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_API_MESSAGES_SENT +import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikApi +import pl.szczodrzynski.edziennik.asJsonObjectList +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT +import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.utils.Utils.crc32 +import pl.szczodrzynski.edziennik.utils.models.Date + +class IdziennikApiMessagesSent(override val data: DataIdziennik, + val onSuccess: () -> Unit) : IdziennikApi(data) { + companion object { + private const val TAG = "IdziennikApiMessagesSent" + } + + init { + apiGet(TAG, IDZIENNIK_API_MESSAGES_SENT) { json -> + if (json !is JsonArray) { + onSuccess() + return@apiGet + } + + json.asJsonObjectList()?.forEach { jMessage -> + val messageIdStr = jMessage.get("id").asString + val messageId = crc32((messageIdStr + "1").toByteArray()) + + val subject = jMessage.get("tytul").asString + + var body = "[META:$messageIdStr;-1]" + body += jMessage.get("tresc").asString.replace("\n".toRegex(), "
") + + val sentDate = Date.fromIso(jMessage.get("dataWyslania").asString) + + val message = Message( + profileId, + messageId, + subject, + body, + TYPE_SENT, + -1, + -1 + ) + + for (recipientEl in jMessage.getAsJsonArray("odbiorcy")) { + val recipient = recipientEl.asJsonObject + var firstName = recipient.get("imie").asString + var lastName = recipient.get("nazwisko").asString + if (firstName.isEmpty() || lastName.isEmpty()) { + firstName = "usunięty" + lastName = "użytkownik" + } + val rTeacher = data.getTeacher(firstName, lastName) + rTeacher.loginId = recipient.get("id").asString + ":" + recipient.get("usr").asString + + val messageRecipient = MessageRecipient( + profileId, + rTeacher.id, + -1, + -1, + /*messageId*/ messageId + ) + data.messageRecipientIgnoreList.add(messageRecipient) + } + + data.messageList.add(message) + data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, sentDate)) + } + + data.setSyncNext(ENDPOINT_IDZIENNIK_API_MESSAGES_SENT, DAY, DRAWER_ITEM_MESSAGES) + onSuccess() + } + } +} From bf73c758729c333e460139f13a8db99e0cc70766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Thu, 31 Oct 2019 15:36:12 +0100 Subject: [PATCH 118/691] [APIv2/Librus] Change teacher free day types sync frequency. --- .../api/v2/librus/data/api/LibrusApiTeacherFreeDayTypes.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDayTypes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDayTypes.kt index 49692e25..eb82bbe2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDayTypes.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTeacherFreeDayTypes.kt @@ -33,7 +33,7 @@ class LibrusApiTeacherFreeDayTypes(override val data: DataLibrus, data.teacherAbsenceTypes.put(id, teacherAbsenceTypeObject) } - data.setSyncNext(ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES, 4 * DAY) + data.setSyncNext(ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES, 7 * DAY) onSuccess() } } From 38d0a173affe4a72651740a60e8b84af34a1ca1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 1 Nov 2019 21:19:30 +0100 Subject: [PATCH 119/691] [Gradle] Fix AgendaCalendarView dependencies. --- agendacalendarview/build.gradle | 2 +- app/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agendacalendarview/build.gradle b/agendacalendarview/build.gradle index 28cbb26d..824ce517 100644 --- a/agendacalendarview/build.gradle +++ b/agendacalendarview/build.gradle @@ -49,6 +49,6 @@ dependencies { // other libraries //implementation 'se.emilsjolander:stickylistheaders:2.7.0' - implementation 'com.github.edisonw:StickyListHeaders:master-SNAPSHOT' + implementation 'com.github.edisonw:StickyListHeaders:master-SNAPSHOT@aar' implementation 'io.reactivex:rxjava:1.1.1' } diff --git a/app/build.gradle b/app/build.gradle index fe3d5cb5..b05d11ce 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -142,7 +142,7 @@ dependencies { implementation "org.jsoup:jsoup:1.10.1" implementation "pl.droidsonroids.gif:android-gif-drawable:1.2.15" //implementation "se.emilsjolander:stickylistheaders:2.7.0" - implementation 'com.github.edisonw:StickyListHeaders:master-SNAPSHOT' + implementation 'com.github.edisonw:StickyListHeaders:master-SNAPSHOT@aar' implementation "uk.co.samuelwall:material-tap-target-prompt:2.14.0" implementation project(":agendacalendarview") From 0b211c4f120d62595c3c6671c886c4e369c30251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 1 Nov 2019 21:21:25 +0100 Subject: [PATCH 120/691] [APIv2/Vulcan] Fix first login. Add Attendance, Proposed grades. Complete Dictionaries. --- .../edziennik/api/v2/models/Data.kt | 1 - .../edziennik/api/v2/vulcan/DataVulcan.kt | 2 +- .../api/v2/vulcan/data/VulcanData.kt | 13 ++- .../v2/vulcan/data/api/VulcanApiAttendance.kt | 78 ++++++++++++++ .../vulcan/data/api/VulcanApiDictionaries.kt | 101 ++++++++++++++++-- .../api/v2/vulcan/data/api/VulcanApiEvents.kt | 23 +++- .../api/v2/vulcan/data/api/VulcanApiGrades.kt | 10 +- .../v2/vulcan/data/api/VulcanApiNotices.kt | 23 ++-- .../data/api/VulcanApiProposedGrades.kt | 81 ++++++++++++++ .../api/v2/vulcan/login/VulcanLoginApi.kt | 5 + 10 files changed, 310 insertions(+), 27 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiAttendance.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiProposedGrades.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index bd38d628..d96bae78 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -186,7 +186,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) lessonList.clear() lessonChangeList.clear() - gradeCategories.clear() gradeList.clear() noticeList.clear() attendanceList.clear() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt index 958ad901..a6710b8c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/DataVulcan.kt @@ -178,6 +178,6 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app val fullApiUrl: String? get() { - return "$apiUrl/$schoolSymbol/" + return "$apiUrl/$schoolSymbol" } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt index e95f87d6..4fa2ad1e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt @@ -6,10 +6,7 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.vulcan.* -import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiDictionaries -import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiEvents -import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiGrades -import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.VulcanApiNotices +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.api.* import pl.szczodrzynski.edziennik.utils.Utils class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { @@ -48,6 +45,10 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_grades) VulcanApiGrades(data) { onSuccess() } } + ENDPOINT_VULCAN_API_GRADES_SUMMARY -> { + data.startProgress(R.string.edziennik_progress_endpoint_proposed_grades) + VulcanApiProposedGrades(data) { onSuccess() } + } ENDPOINT_VULCAN_API_EVENTS -> { data.startProgress(R.string.edziennik_progress_endpoint_events) VulcanApiEvents(data, isHomework = false) { onSuccess() } @@ -60,6 +61,10 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_notices) VulcanApiNotices(data) { onSuccess() } } + ENDPOINT_VULCAN_API_ATTENDANCE -> { + data.startProgress(R.string.edziennik_progress_endpoint_attendance) + VulcanApiAttendance(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiAttendance.kt new file mode 100644 index 00000000..1722cfed --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiAttendance.kt @@ -0,0 +1,78 @@ +package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api + +import androidx.core.util.isEmpty +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_ATTENDANCE +import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_ATTENDANCE +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance +import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance.TYPE_PRESENT +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.utils.models.Date + +class VulcanApiAttendance(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { + companion object { + const val TAG = "VulcanApiAttendance" + } + + init { data.profile?.also { profile -> + if (data.attendanceTypes.isEmpty()) { + data.db.attendanceTypeDao().getAllNow(profileId).toSparseArray(data.attendanceTypes) { it.id } + } + + val startDate: String = profile.getSemesterStart(profile.currentSemester).stringY_m_d + val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d + + apiGet(TAG, VULCAN_API_ENDPOINT_ATTENDANCE, parameters = mapOf( + "DataPoczatkowa" to startDate, + "DataKoncowa" to endDate, + "IdOddzial" to data.studentClassId, + "IdUczen" to data.studentId, + "IdOkresKlasyfikacyjny" to data.studentSemesterId + )) { json, _ -> + json.getJsonObject("Data")?.getJsonArray("Frekwencje")?.forEach { attendanceEl -> + val attendance = attendanceEl.asJsonObject + + val attendanceCategory = data.attendanceTypes.get(attendance.getLong("IdKategoria") ?: return@forEach) + ?: return@forEach + + val type = attendanceCategory.type + + val id = (attendance.getInt("Dzien") ?: 0) + (attendance.getInt("Numer") ?: 0) + + val lessonDateMillis = Date.fromY_m_d(attendance.getString("DzienTekst")).inMillis + val lessonDate = Date.fromMillis(lessonDateMillis) + + val lessonSemester = profile.dateToSemester(lessonDate) + + val attendanceObject = Attendance( + profileId, + id.toLong(), + -1, + attendance.getLong("IdPrzedmiot") ?: -1, + lessonSemester, + attendance.getString("PrzedmiotNazwa") + attendanceCategory.name.let { " - $it" }, + lessonDate, + data.lessonRanges.get(attendance.getInt("IdPoraLekcji") ?: 0)?.startTime, + type) + + data.attendanceList.add(attendanceObject) + if (attendanceObject.type != TYPE_PRESENT) { + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_ATTENDANCE, + attendanceObject.id, + profile.empty, + profile.empty, + attendanceObject.lessonDate.combineWith(attendanceObject.startTime) + )) + } + } + + data.setSyncNext(ENDPOINT_VULCAN_API_ATTENDANCE, SYNC_ALWAYS) + onSuccess() + } + } ?: onSuccess()} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiDictionaries.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiDictionaries.kt index 3fb89406..b4548d9d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiDictionaries.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiDictionaries.kt @@ -5,17 +5,19 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api import com.google.gson.JsonObject +import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_DICTIONARIES import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_DICTIONARIES import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi -import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance +import pl.szczodrzynski.edziennik.data.db.modules.attendance.AttendanceType +import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory +import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonRange +import pl.szczodrzynski.edziennik.data.db.modules.notices.NoticeType import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher -import pl.szczodrzynski.edziennik.getJsonArray -import pl.szczodrzynski.edziennik.getJsonObject -import pl.szczodrzynski.edziennik.getLong -import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.utils.models.Time class VulcanApiDictionaries(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { companion object { @@ -28,8 +30,12 @@ class VulcanApiDictionaries(override val data: DataVulcan, val onSuccess: () -> elements?.getJsonArray("Pracownicy")?.forEach { saveTeacher(it.asJsonObject) } elements?.getJsonArray("Przedmioty")?.forEach { saveSubject(it.asJsonObject) } + elements?.getJsonArray("PoryLekcji")?.forEach { saveLessonRange(it.asJsonObject) } + elements?.getJsonArray("KategorieOcen")?.forEach { saveGradeCategory(it.asJsonObject) } + elements?.getJsonArray("KategorieUwag")?.forEach { saveNoticeType(it.asJsonObject) } + elements?.getJsonArray("KategorieFrekwencji")?.forEach { saveAttendanceType(it.asJsonObject) } - data.setSyncNext(ENDPOINT_VULCAN_API_DICTIONARIES, SYNC_ALWAYS) + data.setSyncNext(ENDPOINT_VULCAN_API_DICTIONARIES, 4*DAY) onSuccess() } } @@ -65,4 +71,87 @@ class VulcanApiDictionaries(override val data: DataVulcan, val onSuccess: () -> data.subjectList.put(id, subjectObject) } + + private fun saveLessonRange(lessonRange: JsonObject) { + val lessonNumber = lessonRange.getInt("Id") ?: return + val startTime = lessonRange.getString("PoczatekTekst")?.let { Time.fromH_m(it) } ?: return + val endTime = lessonRange.getString("KoniecTekst")?.let { Time.fromH_m(it) } ?: return + + val lessonRangeObject = LessonRange( + profileId, + lessonNumber, + startTime, + endTime + ) + + data.lessonRanges.put(lessonNumber, lessonRangeObject) + } + + private fun saveGradeCategory(gradeCategory: JsonObject) { + val id = gradeCategory.getLong("Id") ?: return + val name = gradeCategory.getString("Nazwa") ?: "" + + val gradeCategoryObject = GradeCategory( + profileId, + id, + 0.0f, + -1, + name + ) + + data.gradeCategories.put(id, gradeCategoryObject) + } + + private fun saveNoticeType(noticeType: JsonObject) { + val id = noticeType.getLong("Id") ?: return + val name = noticeType.getString("Nazwa") ?: "" + + val noticeTypeObject = NoticeType( + profileId, + id, + name + ) + + data.noticeTypes.put(id, noticeTypeObject) + } + + private fun saveAttendanceType(attendanceType: JsonObject) { + val id = attendanceType.getLong("Id") ?: return + val name = attendanceType.getString("Nazwa") ?: "" + + val absent = attendanceType.getBoolean("Nieobecnosc") ?: false + val excused = attendanceType.getBoolean("Usprawiedliwione") ?: false + val type = if (absent) { + if (excused) + Attendance.TYPE_ABSENT_EXCUSED + else + Attendance.TYPE_ABSENT + } + else { + val belated = attendanceType.getBoolean("Spoznienie") ?: false + val released = attendanceType.getBoolean("Zwolnienie") ?: false + val present = attendanceType.getBoolean("Obecnosc") ?: true + if (belated) + if (excused) + Attendance.TYPE_ABSENT_EXCUSED + else + Attendance.TYPE_ABSENT + else if (released) + Attendance.TYPE_RELEASED + else if (present) + Attendance.TYPE_PRESENT + else + Attendance.TYPE_CUSTOM + } + + val attendanceTypeObject = AttendanceType( + profileId, + id, + name, + type, + -1 + ) + + data.attendanceTypes.put(id, attendanceTypeObject) + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt index 71bf916c..c24129b6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt @@ -24,12 +24,25 @@ class VulcanApiEvents(override val data: DataVulcan, private val isHomework: Boo const val TAG = "VulcanApiEvents" } - init { + init { data.profile?.also { profile -> + + val startDate: String = when (profile.empty) { + true -> profile.getSemesterStart(profile.currentSemester).stringY_m_d + else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d + } + val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d + val endpoint = when (isHomework) { true -> VULCAN_API_ENDPOINT_HOMEWORK else -> VULCAN_API_ENDPOINT_EVENTS } - apiGet(TAG, endpoint) { json, _ -> + apiGet(TAG, endpoint, parameters = mapOf( + "DataPoczatkowa" to startDate, + "DataKoncowa" to endDate, + "IdOddzial" to data.studentClassId, + "IdUczen" to data.studentId, + "IdOkresKlasyfikacyjny" to data.studentSemesterId + )) { json, _ -> val events = json.getJsonArray("Data") events?.forEach { eventEl -> @@ -71,8 +84,8 @@ class VulcanApiEvents(override val data: DataVulcan, private val isHomework: Boo profileId, if (isHomework) Metadata.TYPE_HOMEWORK else Metadata.TYPE_EVENT, id, - profile?.empty ?: false, - profile?.empty ?: false, + profile.empty, + profile.empty, System.currentTimeMillis() )) } @@ -83,5 +96,5 @@ class VulcanApiEvents(override val data: DataVulcan, private val isHomework: Boo } onSuccess() } - } + } ?: onSuccess()} } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt index acf13fd8..281713ac 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiGrades.kt @@ -20,8 +20,12 @@ class VulcanApiGrades(override val data: DataVulcan, val onSuccess: () -> Unit) const val TAG = "VulcanApiGrades" } - init { - apiGet(TAG, VULCAN_API_ENDPOINT_GRADES) { json, _ -> + init { data.profile?.also { profile -> + + apiGet(TAG, VULCAN_API_ENDPOINT_GRADES, parameters = mapOf( + "IdUczen" to data.studentId, + "IdOkresKlasyfikacyjny" to data.studentSemesterId + )) { json, _ -> val grades = json.getJsonArray("Data") grades?.forEach { gradeEl -> @@ -108,5 +112,5 @@ class VulcanApiGrades(override val data: DataVulcan, val onSuccess: () -> Unit) data.setSyncNext(ENDPOINT_VULCAN_API_GRADES, SYNC_ALWAYS) onSuccess() } - } + } ?: onSuccess()} } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiNotices.kt index c891cbd2..6407dddd 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiNotices.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiNotices.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api +import androidx.core.util.isEmpty import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_NOTICES import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_NOTICES @@ -14,15 +15,23 @@ import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice import pl.szczodrzynski.edziennik.getJsonArray import pl.szczodrzynski.edziennik.getLong import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.toSparseArray import pl.szczodrzynski.edziennik.utils.models.Date class VulcanApiNotices(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { companion object { - const val TAG = "VulcanApi" + const val TAG = "VulcanApiNotices" } - init { - apiGet(TAG, VULCAN_API_ENDPOINT_NOTICES) { json, _ -> + init { data.profile?.also { profile -> + if (data.noticeTypes.isEmpty()) { + data.db.noticeTypeDao().getAllNow(profileId).toSparseArray(data.noticeTypes) { it.id } + } + + apiGet(TAG, VULCAN_API_ENDPOINT_NOTICES, parameters = mapOf( + "IdUczen" to data.studentId, + "IdOkresKlasyfikacyjny" to data.studentSemesterId + )) { json, _ -> json.getJsonArray("Data")?.forEach { noticeEl -> val notice = noticeEl.asJsonObject @@ -35,7 +44,7 @@ class VulcanApiNotices(override val data: DataVulcan, val onSuccess: () -> Unit) profileId, id, text, - profile!!.currentSemester, + profile.currentSemester, Notice.TYPE_NEUTRAL, teacherId ) @@ -45,8 +54,8 @@ class VulcanApiNotices(override val data: DataVulcan, val onSuccess: () -> Unit) profileId, Metadata.TYPE_NOTICE, id, - profile?.empty ?: false, - profile?.empty ?: false, + profile.empty, + profile.empty, addedDate )) } @@ -54,5 +63,5 @@ class VulcanApiNotices(override val data: DataVulcan, val onSuccess: () -> Unit) data.setSyncNext(ENDPOINT_VULCAN_API_NOTICES, SYNC_ALWAYS) onSuccess() } - } + } ?: onSuccess()} } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiProposedGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiProposedGrades.kt new file mode 100644 index 00000000..8289a549 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiProposedGrades.kt @@ -0,0 +1,81 @@ +package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api + +import com.google.gson.JsonArray +import pl.szczodrzynski.edziennik.HOUR +import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_GRADES_PROPOSITIONS +import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_GRADES_SUMMARY +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi +import pl.szczodrzynski.edziennik.asJsonObjectList +import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.getJsonArray +import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.utils.Utils + +class VulcanApiProposedGrades(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { + companion object { + const val TAG = "VulcanApiProposedGrades" + } + + init { data.profile?.also { profile -> + + apiGet(TAG, VULCAN_API_ENDPOINT_GRADES_PROPOSITIONS, parameters = mapOf( + "IdUczen" to data.studentId, + "IdOkresKlasyfikacyjny" to data.studentSemesterId + )) { json, _ -> + val grades = json.getJsonObject("Data") + + grades.getJsonArray("OcenyPrzewidywane")?.let { + processGradeList(it, isFinal = false) + } + + grades.getJsonArray("OcenyKlasyfikacyjne")?.let { + processGradeList(it, isFinal = true) + } + + data.setSyncNext(ENDPOINT_VULCAN_API_GRADES_SUMMARY, 6*HOUR) + onSuccess() + } + } ?: onSuccess()} + + private fun processGradeList(grades: JsonArray, isFinal: Boolean) { + grades.asJsonObjectList()?.forEach { grade -> + val name = grade.get("Wpis").asString + val value = Utils.getGradeValue(name) + val subjectId = grade.get("IdPrzedmiot").asLong + + val id = subjectId * -100 - data.studentSemesterNumber + + val color = Utils.getVulcanGradeColor(name) + + val gradeObject = Grade( + profileId, + id, + "", + color, + "", + name, + value, + 0f, + data.studentSemesterNumber, + -1, + subjectId + ) + if (data.studentSemesterNumber == 1) { + gradeObject.type = if (isFinal) Grade.TYPE_SEMESTER1_FINAL else Grade.TYPE_SEMESTER1_PROPOSED + } else { + gradeObject.type = if (isFinal) Grade.TYPE_SEMESTER2_FINAL else Grade.TYPE_SEMESTER2_PROPOSED + } + data.gradeList.add(gradeObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_GRADE, + gradeObject.id, + data.profile?.empty ?: false, + data.profile?.empty ?: false, + System.currentTimeMillis() + )) + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt index 3ec05738..ed42277d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt @@ -108,6 +108,11 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) { data.apiCertificatePfx = cert.getString("CertyfikatPfx") data.apiCertificateExpiryTime = 1598832000 data.apiToken = data.apiToken?.substring(0, 3) + data.apiCertificatePrivate = getPrivateKeyFromCert( + if (data.apiToken?.get(0) == 'F') VULCAN_API_PASSWORD_FAKELOG else VULCAN_API_PASSWORD, + data.apiCertificatePfx ?: "" + ) + data.loginStore.removeLoginData("certificatePfx") data.loginStore.removeLoginData("devicePin") onSuccess() } From e3bb60730331333676f1631c35512b307046b45c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 1 Nov 2019 21:31:26 +0100 Subject: [PATCH 121/691] [APIv2/Vulcan] Use Wulkanowy uonet-request-signer. --- app/build.gradle | 2 + .../edziennik/api/v2/vulcan/data/VulcanApi.kt | 51 ++++++++++--------- .../edziennik/api/v2/vulcan/data/signer.kt | 47 ----------------- .../api/v2/vulcan/login/VulcanLoginApi.kt | 2 +- build.gradle | 1 + 5 files changed, 30 insertions(+), 73 deletions(-) delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/signer.kt diff --git a/app/build.gradle b/app/build.gradle index b05d11ce..70a7de7f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -160,6 +160,8 @@ dependencies { //implementation 'com.github.kuba2k2.uonet-request-signer:android:master-63f094b14a-1' implementation "org.redundent:kotlin-xml-builder:1.5.3" + + implementation "io.github.wulkanowy:signer-android:0.1.0" } repositories { mavenCentral() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt index f54cfdd3..b9ebda85 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt @@ -4,17 +4,18 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data +import com.google.gson.JsonArray import com.google.gson.JsonObject import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response import im.wangchao.mhttp.callback.JsonCallbackHandler +import io.github.wulkanowy.signer.android.signContent import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan import pl.szczodrzynski.edziennik.data.db.modules.teams.Team import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils.d -import pl.szczodrzynski.edziennik.utils.models.Date import java.net.HttpURLConnection import java.util.* @@ -26,17 +27,20 @@ open class VulcanApi(open val data: DataVulcan) { val profileId get() = data.profile?.id ?: -1 - val profile - get() = data.profile - - fun apiGet(tag: String, endpoint: String, method: Int = POST, payload: JsonObject? = null, - baseUrl: Boolean = false, onSuccess: (json: JsonObject, response: Response?) -> Unit) { - val url = "${if (baseUrl) data.apiUrl else data.fullApiUrl}$endpoint" + fun apiGet( + tag: String, + endpoint: String, + method: Int = POST, + parameters: Map = emptyMap(), + baseUrl: Boolean = false, + onSuccess: (json: JsonObject, response: Response?) -> Unit + ) { + val url = "${if (baseUrl) data.apiUrl else data.fullApiUrl}/$endpoint" d(tag, "Request: Vulcan/Api - $url") if (data.teamList.size() == 0) { - profile?.getStudentData("studentClassName", null)?.also { name -> + data.profile?.getStudentData("studentClassName", null)?.also { name -> val id = Utils.crc16(name.toByteArray()).toLong() val teamObject = Team( @@ -51,28 +55,24 @@ open class VulcanApi(open val data: DataVulcan) { } } - val startDate = when (profile?.empty) { - true -> profile?.getSemesterStart(profile?.currentSemester ?: 1)?.stringY_m_d - else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d - } - val endDate = profile?.getSemesterEnd(profile?.currentSemester ?: 1)?.stringY_m_d - val finalPayload = JsonObject() - finalPayload.addProperty("IdUczen", data.studentId) - finalPayload.addProperty("IdOkresKlasyfikacyjny", data.studentSemesterId) - finalPayload.addProperty("IdOddzial", data.studentClassId) - finalPayload.addProperty("DataPoczatkowa", startDate) - finalPayload.addProperty("DataKoncowa", endDate) + parameters.map { (name, value) -> + when (value) { + is JsonObject -> finalPayload.add(name, value) + is JsonArray -> finalPayload.add(name, value) + is String -> finalPayload.addProperty(name, value) + is Int -> finalPayload.addProperty(name, value) + is Long -> finalPayload.addProperty(name, value) + is Float -> finalPayload.addProperty(name, value) + is Char -> finalPayload.addProperty(name, value) + } + } finalPayload.addProperty("RemoteMobileTimeKey", System.currentTimeMillis() / 1000) finalPayload.addProperty("TimeKey", System.currentTimeMillis() / 1000 - 1) finalPayload.addProperty("RequestId", UUID.randomUUID().toString()) finalPayload.addProperty("RemoteMobileAppVersion", VULCAN_API_APP_VERSION) finalPayload.addProperty("RemoteMobileAppName", VULCAN_API_APP_NAME) - payload?.keySet()?.forEach { - finalPayload.add(it, payload.get(it)) - } - val callback = object : JsonCallbackHandler() { override fun onSuccess(json: JsonObject?, response: Response?) { if (json == null && response?.parserErrorBody == null) { @@ -86,10 +86,11 @@ open class VulcanApi(open val data: DataVulcan) { 503 -> ERROR_VULCAN_API_MAINTENANCE 400 -> ERROR_VULCAN_API_BAD_REQUEST else -> ERROR_VULCAN_API_OTHER - }.let { - data.error(ApiError(tag, EXCEPTION_VULCAN_API_REQUEST) + }.let { errorCode -> + data.error(ApiError(tag, errorCode) .withResponse(response) .withApiResponse(json?.toString() ?: response?.parserErrorBody)) + return } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/signer.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/signer.kt deleted file mode 100644 index 5f0447d8..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/signer.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) Wulkanowy kiedyś tam. - */ - -package pl.szczodrzynski.edziennik.api.v2.vulcan.data - -import android.util.Base64 -import java.io.ByteArrayInputStream -import java.security.KeyFactory -import java.security.KeyStore -import java.security.PrivateKey -import java.security.Signature -import java.security.spec.PKCS8EncodedKeySpec - -fun signContent(password: String, certificate: String?, content: String): String { - val keystore = KeyStore.getInstance("pkcs12").apply { - load(ByteArrayInputStream(Base64.decode(certificate, Base64.DEFAULT)), password.toCharArray()) - } - val signature = Signature.getInstance("SHA1WithRSA").apply { - initSign(keystore.getKey("LoginCert", password.toCharArray()) as PrivateKey) - update(content.toByteArray()) - } - return Base64.encodeToString(signature.sign(), Base64.NO_WRAP) -} - -fun signContent(privateKey: String, content: String): String { - val key = PKCS8EncodedKeySpec(Base64.decode(privateKey, Base64.DEFAULT)).let { - KeyFactory.getInstance("RSA").generatePrivate(it) - } - val signature = Signature.getInstance("SHA1WithRSA").apply { - initSign(key) - update(content.toByteArray()) - } - return Base64.encodeToString(signature.sign(), Base64.NO_WRAP) -} - -fun getPrivateKeyFromCert(password: String, certificate: String): String { - val keystore = KeyStore.getInstance("pkcs12").apply { - load(ByteArrayInputStream(Base64.decode(certificate, Base64.DEFAULT)), password.toCharArray()) - } - val keyFactory = KeyFactory.getInstance("RSA") - val keySpec = keyFactory.getKeySpec( - keystore.getKey("LoginCert", password.toCharArray()) as PrivateKey, - PKCS8EncodedKeySpec::class.java - ) - return Base64.encodeToString(keySpec.encoded, Base64.NO_WRAP) -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt index ed42277d..ee81cb9e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLoginApi.kt @@ -9,10 +9,10 @@ import com.google.gson.JsonObject import im.wangchao.mhttp.Request import im.wangchao.mhttp.Response import im.wangchao.mhttp.callback.JsonCallbackHandler +import io.github.wulkanowy.signer.android.getPrivateKeyFromCert import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan -import pl.szczodrzynski.edziennik.api.v2.vulcan.data.getPrivateKeyFromCert import pl.szczodrzynski.edziennik.currentTimeUnix import pl.szczodrzynski.edziennik.getJsonObject import pl.szczodrzynski.edziennik.getString diff --git a/build.gradle b/build.gradle index 2e2db165..5e08c012 100644 --- a/build.gradle +++ b/build.gradle @@ -81,6 +81,7 @@ allprojects { jcenter() maven { url 'https://jitpack.io' } maven { url "https://kotlin.bintray.com/kotlinx/" } + maven { url "https://dl.bintray.com/wulkanowy/wulkanowy" } } } From 64019dccf786db58d5b4bdcd49139051ea40dc11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 1 Nov 2019 22:30:39 +0100 Subject: [PATCH 122/691] [APIv2/Vulcan] Update uonet-request-signer. Update removing profile. Fix minor issues. --- app/build.gradle | 2 +- .../edziennik/api/v2/vulcan/data/VulcanApi.kt | 2 +- .../edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt | 2 +- .../edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt | 2 +- .../pl/szczodrzynski/edziennik/data/api/Edziennik.java | 7 +++++++ 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 70a7de7f..8a721d8e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -161,7 +161,7 @@ dependencies { implementation "org.redundent:kotlin-xml-builder:1.5.3" - implementation "io.github.wulkanowy:signer-android:0.1.0" + implementation "io.github.wulkanowy:signer-android:0.1.1" } repositories { mavenCentral() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt index b9ebda85..0345c0a0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanApi.kt @@ -40,7 +40,7 @@ open class VulcanApi(open val data: DataVulcan) { d(tag, "Request: Vulcan/Api - $url") if (data.teamList.size() == 0) { - data.profile?.getStudentData("studentClassName", null)?.also { name -> + data.profile?.studentClassName?.also { name -> val id = Utils.crc16(name.toByteArray()).toLong() val teamObject = Team( diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt index c24129b6..d971118c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt @@ -63,7 +63,7 @@ class VulcanApiEvents(override val data: DataVulcan, private val isHomework: Boo else -> Event.TYPE_EXAM } } - val teamId = event.getLong("IdOddzial") ?: data.teamClass?.id ?: return@forEach + val teamId = event.getLong("IdOddzial") ?: data.teamClass?.id ?: -1 val eventObject = Event( profileId, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt index 3d0b0a44..61f38f53 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/firstlogin/VulcanFirstLogin.kt @@ -44,7 +44,7 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) { val studentId = student.getInt("Id") ?: return@forEach val studentLoginId = student.getInt("UzytkownikLoginId") ?: return@forEach val studentClassId = student.getInt("IdOddzial") ?: return@forEach - val studentClassName = student.getString("OkresPoziom").toString() + student.getString("OddzialSymbol") + val studentClassName = student.getString("OkresPoziom").toString() + (student.getString("OddzialSymbol") ?: return@forEach) val studentSemesterId = student.getInt("IdOkresKlasyfikacyjny") ?: return@forEach val studentFirstName = student.getString("Imie") ?: "" val studentLastName = student.getString("Nazwisko") ?: "" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java index e21a12dc..7b9997ca 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java @@ -1161,6 +1161,13 @@ public class Edziennik { app.db.teamDao().clear(profileId); app.db.messageRecipientDao().clear(profileId); app.db.messageDao().clear(profileId); + app.db.endpointTimerDao().clear(profileId); + app.db.attendanceTypeDao().clear(profileId); + app.db.classroomDao().clear(profileId); + app.db.lessonRangeDao().clear(profileId); + app.db.noticeTypeDao().clear(profileId); + app.db.teacherAbsenceDao().clear(profileId); + app.db.teacherAbsenceTypeDao().clear(profileId); int loginStoreId = profileObject.getLoginStoreId(); List profilesUsingLoginStore = app.db.profileDao().getIdsByLoginStoreIdNow(loginStoreId); From 33c009befe3bd6362c8bc1e2a1933bfd8bc48bec Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sat, 2 Nov 2019 00:05:14 +0100 Subject: [PATCH 123/691] [APIv2/Vulcan] Add getting received messages --- .../edziennik/api/v2/models/Data.kt | 13 +++- .../api/v2/vulcan/data/VulcanData.kt | 4 + .../v2/vulcan/data/api/VulcanApiAttendance.kt | 7 +- .../api/v2/vulcan/data/api/VulcanApiEvents.kt | 10 +-- .../vulcan/data/api/VulcanApiMessagesInbox.kt | 75 +++++++++++++++++++ 5 files changed, 94 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesInbox.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index d96bae78..8e3865b8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -60,6 +60,16 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) val profileId get() = profile?.id ?: -1 + val syncStartDate: Date + get() = when (profile?.empty) { + true -> profile.getSemesterStart(profile.currentSemester) + else -> Date.getToday().stepForward(0, -1, 0) + } + + val syncEndDate: Date + get() = profile?.getSemesterEnd(profile.currentSemester) + ?: Date.getToday().stepForward(0, 1, 0) + /** * A callback passed to all [Feature]s and [LoginMethod]s */ @@ -316,8 +326,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) db.notificationDao().addAll(notifications) onSuccess() } - } - catch (e: Exception) { + } catch (e: Exception) { error(ApiError(TAG, EXCEPTION_NOTIFY_AND_SYNC) .withThrowable(e)) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt index 4fa2ad1e..c3364192 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt @@ -65,6 +65,10 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_attendance) VulcanApiAttendance(data) { onSuccess() } } + ENDPOINT_VULCAN_API_MESSAGES_INBOX -> { + data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) + VulcanApiMessagesInbox(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiAttendance.kt index 1722cfed..20a8082f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiAttendance.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiAttendance.kt @@ -22,12 +22,9 @@ class VulcanApiAttendance(override val data: DataVulcan, val onSuccess: () -> Un data.db.attendanceTypeDao().getAllNow(profileId).toSparseArray(data.attendanceTypes) { it.id } } - val startDate: String = profile.getSemesterStart(profile.currentSemester).stringY_m_d - val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d - apiGet(TAG, VULCAN_API_ENDPOINT_ATTENDANCE, parameters = mapOf( - "DataPoczatkowa" to startDate, - "DataKoncowa" to endDate, + "DataPoczatkowa" to data.syncStartDate.stringY_m_d, + "DataKoncowa" to data.syncEndDate.stringY_m_d, "IdOddzial" to data.studentClassId, "IdUczen" to data.studentId, "IdOkresKlasyfikacyjny" to data.studentSemesterId diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt index d971118c..37578e6f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt @@ -26,19 +26,13 @@ class VulcanApiEvents(override val data: DataVulcan, private val isHomework: Boo init { data.profile?.also { profile -> - val startDate: String = when (profile.empty) { - true -> profile.getSemesterStart(profile.currentSemester).stringY_m_d - else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d - } - val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d - val endpoint = when (isHomework) { true -> VULCAN_API_ENDPOINT_HOMEWORK else -> VULCAN_API_ENDPOINT_EVENTS } apiGet(TAG, endpoint, parameters = mapOf( - "DataPoczatkowa" to startDate, - "DataKoncowa" to endDate, + "DataPoczatkowa" to data.syncStartDate.stringY_m_d, + "DataKoncowa" to data.syncEndDate.stringY_m_d, "IdOddzial" to data.studentClassId, "IdUczen" to data.studentId, "IdOkresKlasyfikacyjny" to data.studentSemesterId diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesInbox.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesInbox.kt new file mode 100644 index 00000000..50ba3411 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesInbox.kt @@ -0,0 +1,75 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-11-01 + */ + +package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_MESSAGES_RECEIVED +import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_MESSAGES_INBOX +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message +import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata + +class VulcanApiMessagesInbox(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { + companion object { + const val TAG = "VulcanApiMessagesInbox" + } + + init { + apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_RECEIVED, parameters = mapOf( + "DataPoczatkowa" to data.syncStartDate.inUnix, + "DataKoncowa" to data.syncEndDate.inUnix, + "LoginId" to data.studentLoginId, + "IdUczen" to data.studentId + )) { json, _ -> + json.getJsonArray("Data").asJsonObjectList()?.forEach { message -> + val id = message.getLong("WiadomoscId") ?: return@forEach + val subject = message.getString("Tytul") ?: "" + val body = message.getString("Tresc") ?: "" + + val senderLoginId = message.getString("NadawcaId") ?: return@forEach + val senderId = data.teacherList + .singleOrNull { it.loginId == senderLoginId }?.id ?: return@forEach + + val addedDate = message.getLong("DataWyslaniaUnixEpoch")?.let { it * 1000 } ?: -1 + val readDate = message.getLong("DataPrzeczytaniaUnixEpoch")?.let { it * 1000 } ?: -1 + + val messageObject = Message( + profileId, + id, + subject, + body, + Message.TYPE_RECEIVED, + senderId, + -1 + ) + + val messageRecipientObject = MessageRecipient( + profileId, + -1, + -1, + readDate, + id + ) + + data.messageList.add(messageObject) + data.messageRecipientList.add(messageRecipientObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_MESSAGE, + id, + readDate > 0, + readDate > 0, + addedDate + )) + } + + data.setSyncNext(ENDPOINT_VULCAN_API_MESSAGES_INBOX, SYNC_ALWAYS) + onSuccess() + } + } +} From 99ab9d586ff0210785839c6b51d571f3ec10fc9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sat, 2 Nov 2019 13:30:16 +0100 Subject: [PATCH 124/691] [APIv2] Revert changes in Data. --- .../edziennik/api/v2/models/Data.kt | 10 ---------- .../v2/vulcan/data/api/VulcanApiAttendance.kt | 7 +++++-- .../api/v2/vulcan/data/api/VulcanApiEvents.kt | 10 ++++++++-- .../v2/vulcan/data/api/VulcanApiMessagesInbox.kt | 16 ++++++++++++---- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 8e3865b8..6e5dce2e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -60,16 +60,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) val profileId get() = profile?.id ?: -1 - val syncStartDate: Date - get() = when (profile?.empty) { - true -> profile.getSemesterStart(profile.currentSemester) - else -> Date.getToday().stepForward(0, -1, 0) - } - - val syncEndDate: Date - get() = profile?.getSemesterEnd(profile.currentSemester) - ?: Date.getToday().stepForward(0, 1, 0) - /** * A callback passed to all [Feature]s and [LoginMethod]s */ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiAttendance.kt index 20a8082f..1722cfed 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiAttendance.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiAttendance.kt @@ -22,9 +22,12 @@ class VulcanApiAttendance(override val data: DataVulcan, val onSuccess: () -> Un data.db.attendanceTypeDao().getAllNow(profileId).toSparseArray(data.attendanceTypes) { it.id } } + val startDate: String = profile.getSemesterStart(profile.currentSemester).stringY_m_d + val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d + apiGet(TAG, VULCAN_API_ENDPOINT_ATTENDANCE, parameters = mapOf( - "DataPoczatkowa" to data.syncStartDate.stringY_m_d, - "DataKoncowa" to data.syncEndDate.stringY_m_d, + "DataPoczatkowa" to startDate, + "DataKoncowa" to endDate, "IdOddzial" to data.studentClassId, "IdUczen" to data.studentId, "IdOkresKlasyfikacyjny" to data.studentSemesterId diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt index 37578e6f..d971118c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiEvents.kt @@ -26,13 +26,19 @@ class VulcanApiEvents(override val data: DataVulcan, private val isHomework: Boo init { data.profile?.also { profile -> + val startDate: String = when (profile.empty) { + true -> profile.getSemesterStart(profile.currentSemester).stringY_m_d + else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d + } + val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d + val endpoint = when (isHomework) { true -> VULCAN_API_ENDPOINT_HOMEWORK else -> VULCAN_API_ENDPOINT_EVENTS } apiGet(TAG, endpoint, parameters = mapOf( - "DataPoczatkowa" to data.syncStartDate.stringY_m_d, - "DataKoncowa" to data.syncEndDate.stringY_m_d, + "DataPoczatkowa" to startDate, + "DataKoncowa" to endDate, "IdOddzial" to data.studentClassId, "IdUczen" to data.studentId, "IdOkresKlasyfikacyjny" to data.studentSemesterId diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesInbox.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesInbox.kt index 50ba3411..ef1cacfe 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesInbox.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesInbox.kt @@ -13,16 +13,24 @@ import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.messages.Message import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.utils.models.Date class VulcanApiMessagesInbox(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { companion object { const val TAG = "VulcanApiMessagesInbox" } - init { + init { data.profile?.also { profile -> + + val startDate: String = when (profile.empty) { + true -> profile.getSemesterStart(profile.currentSemester).stringY_m_d + else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d + } + val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d + apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_RECEIVED, parameters = mapOf( - "DataPoczatkowa" to data.syncStartDate.inUnix, - "DataKoncowa" to data.syncEndDate.inUnix, + "DataPoczatkowa" to startDate, + "DataKoncowa" to endDate, "LoginId" to data.studentLoginId, "IdUczen" to data.studentId )) { json, _ -> @@ -71,5 +79,5 @@ class VulcanApiMessagesInbox(override val data: DataVulcan, val onSuccess: () -> data.setSyncNext(ENDPOINT_VULCAN_API_MESSAGES_INBOX, SYNC_ALWAYS) onSuccess() } - } + } ?: onSuccess()} } From aa161b5b0e84942459076f679cef184aa33ba904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sat, 2 Nov 2019 19:20:39 +0100 Subject: [PATCH 125/691] [Login] Remove Login migration fragment. --- .../modules/login/LoginMigrationFragment.java | 637 ------------------ .../login/LoginMigrationSyncFragment.java | 132 ---- app/src/main/res/navigation/nav_login.xml | 17 - 3 files changed, 786 deletions(-) delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMigrationFragment.java delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMigrationSyncFragment.java diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMigrationFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMigrationFragment.java deleted file mode 100644 index 846d45b5..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMigrationFragment.java +++ /dev/null @@ -1,637 +0,0 @@ -package pl.szczodrzynski.edziennik.ui.modules.login; - -import android.app.Activity; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.os.AsyncTask; -import android.os.Bundle; -import android.util.LongSparseArray; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.util.Pair; -import androidx.databinding.DataBindingUtil; -import androidx.fragment.app.Fragment; -import androidx.navigation.NavController; -import androidx.navigation.Navigation; - -import com.afollestad.materialdialogs.MaterialDialog; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.reflect.TypeToken; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.BuildConfig; -import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance; -import pl.szczodrzynski.edziennik.data.db.modules.events.Event; -import pl.szczodrzynski.edziennik.data.db.modules.events.EventType; -import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade; -import pl.szczodrzynski.edziennik.data.db.modules.lessons.Lesson; -import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange; -import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore; -import pl.szczodrzynski.edziennik.data.db.modules.luckynumber.LuckyNumber; -import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata; -import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice; -import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; -import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject; -import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher; -import pl.szczodrzynski.edziennik.data.db.modules.teams.Team; -import pl.szczodrzynski.edziennik.databinding.FragmentLoginMigrationBinding; -import pl.szczodrzynski.edziennik.utils.models.Date; -import pl.szczodrzynski.edziennik.utils.models.Time; - -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_CLASS_EVENT; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_DEFAULT; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_ESSAY; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_EXAM; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_EXCURSION; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_HOMEWORK; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_INFORMATION; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_PROJECT; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_PT_MEETING; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_READING; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.COLOR_SHORT_QUIZ; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_CLASS_EVENT; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_DEFAULT; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_ESSAY; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_EXAM; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_EXCURSION; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_INFORMATION; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_PROJECT; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_PT_MEETING; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_READING; -import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_SHORT_QUIZ; -import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_DEMO; -import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_IUCZNIOWIE; -import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_LIBRUS; -import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK; -import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_VULCAN; -import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.REGISTRATION_DISABLED; -import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.REGISTRATION_ENABLED; -import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.REGISTRATION_UNSPECIFIED; - -public class LoginMigrationFragment extends Fragment { - - private App app; - private NavController nav; - private FragmentLoginMigrationBinding b; - private static final String TAG = "LoginMigration"; - - public LoginMigrationFragment() { } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Inflate the layout for this fragment - if (getActivity() != null) { - app = (App) getActivity().getApplicationContext(); - nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment); - } - else { - return null; - } - b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_migration, container, false); - return b.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - assert getContext() != null; - assert getActivity() != null; - - String profilesStr; - if (app.appConfig.lastAppVersion == 198 && (profilesStr = app.appSharedPrefs.getString("app.appConfig.profiles", null)) != null) { - Toast.makeText(app, getString(R.string.login_migration_toast), Toast.LENGTH_SHORT).show(); - AsyncTask.execute(() -> { - try { - migrate(profilesStr); - } - catch (Exception e) { - getActivity().runOnUiThread(() -> { - new MaterialDialog.Builder(getActivity()) - .title(R.string.error_occured) - .content(getString(R.string.login_migration_error_format)) - .positiveText(R.string.ok) - .onPositive(((dialog, which) -> { - dialog.dismiss(); - getActivity().setResult(Activity.RESULT_OK); - getActivity().finish(); - })) - .autoDismiss(false) - .canceledOnTouchOutside(false) - .show(); - }); - } - finally { - if (app.db.profileDao().getIdsNow().size() != 0) { - app.appSharedPrefs.edit().remove("app.appConfig.profiles").apply(); - } - app.appConfig.lastAppVersion = BuildConfig.VERSION_CODE; - app.saveConfig("lastAppVersion"); - } - }); - } - - b.doneButton.setOnClickListener((v -> { - nav.navigate(R.id.loginMigrationSyncFragment, null, LoginActivity.navOptions); - })); - } - - private String gp(SharedPreferences p, String key, String defValue) { - String s = p.getString(key, defValue); - if (s == null) - s = defValue; - return s; - } - private void migrate(String profilesStr) { - Context c = getContext(); - if (c == null) - return; - List metadataList = new ArrayList<>(); - JsonArray profiles = new JsonParser().parse(profilesStr).getAsJsonArray(); - Map loginStores = app.gson.fromJson(app.appSharedPrefs.getString("app.appConfig.loginStores", "{}"), new TypeToken>(){}.getType()); - if (loginStores == null) { - Toast.makeText(c, "Błąd odczytywania słoików z danymi.", Toast.LENGTH_SHORT).show(); - return; - } - for (JsonElement profileEl: profiles) { - JsonObject jProfile = profileEl.getAsJsonObject(); - int profileId = jProfile.get("id").getAsInt(); - SharedPreferences p = app.getSharedPreferences(String.format(getString(R.string.preference_file_register), profileId), Context.MODE_PRIVATE); - - if (!Boolean.parseBoolean(p.getString("app.register.loggedIn", Boolean.toString(false)))) - return; - Profile profile = new Profile(); - profile.setId(profileId); - profile.setName(jProfile.get("name").getAsString()); - profile.setSubname(jProfile.get("subname").getAsString()); - profile.setImage(jProfile.get("image").getAsString()); - - profile.setSyncEnabled(Boolean.parseBoolean(p.getString("app.register.syncThisProfile", Boolean.toString(profile.getSyncEnabled())))); - profile.setSyncNotifications(Boolean.parseBoolean(p.getString("app.register.syncNotificationsEnabled", Boolean.toString(profile.getSyncNotifications())))); - profile.setEnableSharedEvents(Boolean.parseBoolean(p.getString("app.register.eventsShared", Boolean.toString(profile.getEnableSharedEvents())))); - app.appConfig.countInSeconds = Boolean.parseBoolean(p.getString("app.register.countInSeconds", Boolean.toString(app.appConfig.countInSeconds))); - // so in some APIs we force a full, clean sync - profile.setEmpty(true);//Boolean.parseBoolean(p.getString("app.register.empty", Boolean.toString(profile.empty))); - profile.setArchived(false); - - String s; - s = gp(p, "app.register.studentNameLong", "\"\""); - profile.setStudentNameLong(s.replace("\"", "")); - s = gp(p, "app.register.studentNameShort", "\"\""); - profile.setStudentNameShort(s.replace("\"", "")); - profile.setStudentNumber(Integer.parseInt(gp(p, "app.register.studentNumber", "-1"))); - profile.setStudentData(new JsonParser().parse(gp(p, "app.register.studentStore", "[]")).getAsJsonObject()); - - boolean autoRegistrationDecided = Boolean.parseBoolean(p.getString("app.register.autoRegistrationDecided", "true")); - boolean autoRegistrationAllowed = Boolean.parseBoolean(p.getString("app.register.autoRegistrationAllowed", "true")); - profile.setRegistration(!autoRegistrationAllowed && !autoRegistrationDecided ? REGISTRATION_UNSPECIFIED : !autoRegistrationAllowed ? REGISTRATION_DISABLED : REGISTRATION_ENABLED); - - profile.setGradeColorMode(Integer.parseInt(gp(p, "app.register.gradeColorMode", "1"))); - profile.setAgendaViewType(Integer.parseInt(gp(p, "app.register.agendaViewType", "0"))); - - profile.setCurrentSemester(Integer.parseInt(gp(p, "app.register.currentSemester", "1"))); - - profile.setAttendancePercentage(Float.parseFloat(gp(p, "app.register.attendancePercentage", "0.0"))); - - profile.setDateSemester1Start(app.gson.fromJson(gp(p, "app.register.dateSemester1Start", ""), Date.class)); - profile.setDateSemester2Start(app.gson.fromJson(gp(p, "app.register.dateSemester2Start", ""), Date.class)); - profile.setDateYearEnd(app.gson.fromJson(gp(p, "app.register.dateYearEnd", ""), Date.class)); - - profile.setLuckyNumberEnabled(Boolean.parseBoolean(gp(p, "app.register.luckyNumberEnabled", Boolean.toString(profile.getLuckyNumberEnabled())))); - profile.setLuckyNumber(Integer.parseInt(gp(p, "app.register.luckyNumber", "-1"))); - profile.setLuckyNumberDate(app.gson.fromJson(gp(p, "app.register.luckyNumberDate", ""), Date.class)); - - - profile.setLoginStoreId(jProfile.get("loginStoreId").getAsInt()); - - - - LoginStore loginStore = new LoginStore( - profile.getLoginStoreId(), - Integer.parseInt(gp(p, "app.register.loginType", "1")), - loginStores.get(profile.getLoginStoreId()) - ); - - String teamPrefix; - switch (loginStore.type) { - case LOGIN_TYPE_MOBIDZIENNIK: - teamPrefix = loginStore.getLoginData("serverName", "MOBI_UN"); - break; - case LOGIN_TYPE_LIBRUS: - teamPrefix = profile.getStudentData("schoolName", "LIBRUS_UN"); - break; - case LOGIN_TYPE_IUCZNIOWIE: - teamPrefix = loginStore.getLoginData("schoolName", "IUCZNIOWIE_UN"); - break; - case LOGIN_TYPE_VULCAN: - teamPrefix = profile.getStudentData("schoolName", "VULCAN_UN"); - break; - case LOGIN_TYPE_DEMO: - teamPrefix = loginStore.getLoginData("serverName", "DEMO_UN"); - break; - default: - teamPrefix = "TYPE_UNKNOWN"; - break; - } - - - - - - - - - JsonArray items = new JsonParser().parse(gp(p, "app.register.users", "[]")).getAsJsonArray(); - if (items != null) { - List itemList = new ArrayList<>(); - for (JsonElement itemEl : items) { - JsonObject item = itemEl.getAsJsonObject(); - if (item.get("id").getAsLong() == -1) - continue; - itemList.add(new Teacher( - profileId, - item.get("id").getAsLong() + (loginStore.type == LOGIN_TYPE_IUCZNIOWIE ? 32768 : 0), - item.get("name").getAsString(), - item.get("surname").getAsString() - )); - } - app.db.teacherDao().addAllIgnore(itemList); - } - - items = new JsonParser().parse(gp(p, "app.register.subjects", "[]")).getAsJsonArray(); - if (items != null) { - List itemList = new ArrayList<>(); - for (JsonElement itemEl : items) { - JsonObject item = itemEl.getAsJsonObject(); - if (item.get("id").getAsLong() == -1) - continue; - itemList.add(new Subject( - profileId, - item.get("id").getAsLong() + (loginStore.type == LOGIN_TYPE_IUCZNIOWIE ? 32768 : 0), - item.get("longName").getAsString(), - item.get("shortName").getAsString() - )); - } - app.db.subjectDao().addAll(itemList); - } - - items = new JsonParser().parse(gp(p, "app.register.teams", "[]")).getAsJsonArray(); - List tItemList = new ArrayList<>(); - if (items != null) { - for (JsonElement itemEl : items) { - JsonObject item = itemEl.getAsJsonObject(); - if (item.get("id").getAsLong() == -1) - continue; - tItemList.add(new Team( - profileId, - item.get("id").getAsLong() + (loginStore.type == LOGIN_TYPE_IUCZNIOWIE ? 32768 : 0), - item.get("name").getAsString(), - item.get("type").getAsInt(), - teamPrefix+":"+item.get("name").getAsString(), - item.get("teacherId").getAsInt() - )); - } - } - JsonObject tItem = new JsonParser().parse(gp(p, "app.register.teamClass", "")).getAsJsonObject(); - tItemList.add(new Team( - profileId, - tItem.get("id").getAsLong(), - tItem.get("name").getAsString(), - tItem.get("type").getAsInt(), - teamPrefix+":"+tItem.get("name").getAsString(), - tItem.get("teacherId").getAsInt() - )); - app.db.teamDao().addAll(tItemList); - - Map> types = app.gson.fromJson(p.getString("app.register.eventTypes", "{}"), new TypeToken>>(){}.getType()); - if (types != null) { - for (Integer index : types.keySet()) { - Pair type = types.get(index); - if (type != null && type.second != null) { - int color = type.second; - switch (color) { - case 0xffdaa520: - color = COLOR_DEFAULT; - break; - case 0xffff0000: - color = COLOR_EXAM; - break; - case 0xffadff2f: - color = COLOR_SHORT_QUIZ; - break; - case 0xff4050b5: - color = COLOR_ESSAY; - break; - case 0xff673ab7: - color = COLOR_PROJECT; - break; - case 0xffabcdef: - color = COLOR_PT_MEETING; - break; - case 0xff4caf50: - color = COLOR_EXCURSION; - break; - case 0xffffeb3b: - color = COLOR_READING; - break; - } - app.db.eventTypeDao().add(new EventType(profileId, index, type.first, color)); - } - } - } - - items = new JsonParser().parse(gp(p, "app.register.events", "[]")).getAsJsonArray(); - if (items != null) { - List itemList = new ArrayList<>(); - for (JsonElement itemEl : items) { - JsonObject item = itemEl.getAsJsonObject(); - int color = item.get("color").getAsInt(); - switch (color) { - case 0xffdaa520: - case 0xffff0000: - case 0xffadff2f: - case 0xff4050b5: - case 0xff673ab7: - case 0xffabcdef: - case 0xff4caf50: - case 0xffffeb3b: - color = -1; - break; - } - long id = item.get("id").getAsLong(); - Event itemObj = new Event( - profileId, - id, - app.gson.fromJson(item.get("eventDate"), Date.class), - app.gson.fromJson(item.get("startTime"), Time.class), - item.get("topic").getAsString(), - color, - item.get("type").getAsInt(), - (loginStore.type != LOGIN_TYPE_MOBIDZIENNIK || id >= 1420070400000L) && item.get("addedManually").getAsBoolean(), - item.get("teacherId").getAsInt(), - item.get("subjectId").getAsInt(), - item.get("teamId").getAsInt()); - if (item.get("sharedBy") != null) { - itemObj.sharedBy = item.get("sharedBy").getAsString(); - itemObj.sharedByName = item.get("sharedByName").getAsString(); - } - Metadata metadataObj = new Metadata( - profileId, - Metadata.TYPE_EVENT, - itemObj.id, - item.get("seen").getAsBoolean(), - item.get("notified").getAsBoolean(), - item.get("addedDate").getAsLong() - ); - if (itemObj.id == -1) - continue; - itemList.add(itemObj); - metadataList.add(metadataObj); - } - app.db.eventDao().addAll(itemList); - } - items = new JsonParser().parse(gp(p, "app.register.homeworksNew", "[]")).getAsJsonArray(); - if (items != null) { - List itemList = new ArrayList<>(); - for (JsonElement itemEl : items) { - JsonObject item = itemEl.getAsJsonObject(); - Event itemObj = new Event( - profileId, - item.get("id").getAsLong(), - app.gson.fromJson(item.get("eventDate"), Date.class), - app.gson.fromJson(item.get("startTime"), Time.class), - item.get("topic").getAsString(), - -1, - TYPE_HOMEWORK, - item.get("addedManually").getAsBoolean(), - item.get("teacherId").getAsInt(), - item.get("subjectId").getAsInt(), - item.get("teamId").getAsInt()); - if (item.get("sharedBy") != null) { - itemObj.sharedBy = item.get("sharedBy").getAsString(); - itemObj.sharedByName = item.get("sharedByName").getAsString(); - } - Metadata metadataObj = new Metadata( - profileId, - Metadata.TYPE_HOMEWORK, - itemObj.id, - item.get("seen").getAsBoolean(), - item.get("notified").getAsBoolean(), - item.get("addedDate").getAsLong() - ); - if (itemObj.id == -1) - continue; - itemList.add(itemObj); - metadataList.add(metadataObj); - } - app.db.eventDao().addAll(itemList); - } - - items = new JsonParser().parse(gp(p, "app.register.gradeCategories", "[]")).getAsJsonArray(); - LongSparseArray> gradeCategories = new LongSparseArray<>(); - if (items != null) { - for (JsonElement itemEl : items) { - JsonObject item = itemEl.getAsJsonObject(); - if (item.get("id").getAsLong() == -1) - continue; - gradeCategories.put(item.get("id").getAsLong(), - new Pair<>(item.get("description").getAsString(), item.get("color").getAsInt())); - } - } - - items = new JsonParser().parse(gp(p, "app.register.grades", "[]")).getAsJsonArray(); - if (items != null) { - List itemList = new ArrayList<>(); - for (JsonElement itemEl : items) { - JsonObject item = itemEl.getAsJsonObject(); - long categoryId = item.get("categoryId").getAsInt(); - Pair category = gradeCategories.get(categoryId); - Grade itemObj = new Grade( - profileId, - item.get("id").getAsLong(), - category != null ? category.first : "", - category != null && category.second != null ? category.second : 0xff0000ff, - item.get("description").getAsString(), - item.get("name").getAsString(), - item.get("value").getAsFloat(), - item.get("weight").getAsInt(), - item.get("semester").getAsInt(), - item.get("teacherId").getAsInt(), - item.get("subjectId").getAsInt()); - itemObj.type = item.get("type").getAsInt(); - if (loginStore.type == LOGIN_TYPE_IUCZNIOWIE && itemObj.type != 0) - continue; - Metadata metadataObj = new Metadata( - profileId, - Metadata.TYPE_GRADE, - itemObj.id, - item.get("seen").getAsBoolean(), - item.get("notified").getAsBoolean(), - item.get("addedDate").getAsLong() - ); - if (itemObj.id == -1) - continue; - itemList.add(itemObj); - metadataList.add(metadataObj); - } - app.db.gradeDao().addAll(itemList); - } - - items = new JsonParser().parse(gp(p, "app.register.notices", "[]")).getAsJsonArray(); - if (items != null) { - List itemList = new ArrayList<>(); - for (JsonElement itemEl : items) { - JsonObject item = itemEl.getAsJsonObject(); - Notice itemObj = new Notice( - profileId, - item.get("id").getAsLong() + (loginStore.type == LOGIN_TYPE_IUCZNIOWIE ? 32768 : 0), - item.get("reason").getAsString(), - item.get("semester").getAsInt(), - item.get("type").getAsInt(), - item.get("teacherId").getAsInt()); - Metadata metadataObj = new Metadata( - profileId, - Metadata.TYPE_NOTICE, - itemObj.id, - item.get("seen").getAsBoolean(), - item.get("notified").getAsBoolean(), - item.get("addedDate").getAsLong() - ); - if (itemObj.id == -1) - continue; - itemList.add(itemObj); - metadataList.add(metadataObj); - } - app.db.noticeDao().addAll(itemList); - } - - items = new JsonParser().parse(gp(p, "app.register.attendances", "[]")).getAsJsonArray(); - if (items != null) { - List itemList = new ArrayList<>(); - for (JsonElement itemEl : items) { - JsonObject item = itemEl.getAsJsonObject(); - Attendance itemObj = new Attendance( - profileId, - item.get("id").getAsLong(), - item.get("teacherId").getAsInt(), - item.get("subjectId").getAsInt(), - item.get("semester").getAsInt(), - item.get("lessonTopic").getAsString(), - app.gson.fromJson(item.get("lessonDate"), Date.class), - app.gson.fromJson(item.get("startTime"), Time.class), - item.get("type").getAsInt()); - Metadata metadataObj = new Metadata( - profileId, - Metadata.TYPE_ATTENDANCE, - itemObj.id, - item.get("seen").getAsBoolean(), - item.get("notified").getAsBoolean(), - item.get("addedDate").getAsLong() - ); - if (itemObj.id == -1) - continue; - itemList.add(itemObj); - metadataList.add(metadataObj); - } - app.db.attendanceDao().addAll(itemList); - } - - - tItem = new JsonParser().parse(gp(p, "app.register.timetable", "{weekdays: [], lessonChanges: [], lessonAdditions: []}")).getAsJsonObject(); - if (tItem != null) { - - List itemLessonList = new ArrayList<>(); - for (JsonElement weekDayEl : tItem.getAsJsonArray("weekdays")) { - JsonObject weekDay = weekDayEl.getAsJsonObject(); - int weekDayNum = weekDay.get("weekDay").getAsInt(); - for (JsonElement itemEl : weekDay.getAsJsonArray("lessons")) { - JsonObject item = itemEl.getAsJsonObject(); - Lesson itemObj = new Lesson( - profileId, - weekDayNum, - app.gson.fromJson(item.get("startTime"), Time.class), - app.gson.fromJson(item.get("endTime"), Time.class) - ); - itemObj.classroomName = item.get("classroomName").getAsString(); - itemObj.subjectId = item.get("subjectId").getAsInt(); - itemObj.teacherId = item.get("teacherId").getAsInt(); - itemObj.teamId = item.get("teamId").getAsInt(); - itemLessonList.add(itemObj); - } - } - app.db.lessonDao().addAll(itemLessonList); - - List itemList = new ArrayList<>(); - for (JsonElement itemEl : tItem.getAsJsonArray("lessonChanges")) { - JsonObject item = itemEl.getAsJsonObject(); - LessonChange itemObj = new LessonChange( - profileId, - app.gson.fromJson(item.get("lessonDate"), Date.class), - app.gson.fromJson(item.get("startTime"), Time.class), - app.gson.fromJson(item.get("endTime"), Time.class)); - itemObj.classroomName = item.get("classroomName").getAsString(); - itemObj.subjectId = item.get("subjectId").getAsInt(); - itemObj.teacherId = item.get("teacherId").getAsInt(); - itemObj.teamId = item.get("teamId").getAsInt(); - itemObj.type = item.get("type").getAsInt(); - Metadata metadataObj = new Metadata( - profileId, - Metadata.TYPE_LESSON_CHANGE, - itemObj.id, - item.get("seen").getAsBoolean(), - item.get("notified").getAsBoolean(), - System.currentTimeMillis() - ); - if (itemObj.id == -1) - continue; - itemList.add(itemObj); - metadataList.add(metadataObj); - } - app.db.lessonChangeDao().addAll(itemList); - } - - app.db.eventTypeDao().add(new EventType(profileId, TYPE_HOMEWORK, getString(R.string.event_type_homework), COLOR_HOMEWORK)); - app.db.eventTypeDao().add(new EventType(profileId, TYPE_DEFAULT, getString(R.string.event_other), COLOR_DEFAULT)); - app.db.eventTypeDao().add(new EventType(profileId, TYPE_EXAM, getString(R.string.event_exam), COLOR_EXAM)); - app.db.eventTypeDao().add(new EventType(profileId, TYPE_SHORT_QUIZ, getString(R.string.event_short_quiz), COLOR_SHORT_QUIZ)); - app.db.eventTypeDao().add(new EventType(profileId, TYPE_ESSAY, getString(R.string.event_essay), COLOR_SHORT_QUIZ)); - app.db.eventTypeDao().add(new EventType(profileId, TYPE_PROJECT, getString(R.string.event_project), COLOR_PROJECT)); - app.db.eventTypeDao().add(new EventType(profileId, TYPE_PT_MEETING, getString(R.string.event_pt_meeting), COLOR_PT_MEETING)); - app.db.eventTypeDao().add(new EventType(profileId, TYPE_EXCURSION, getString(R.string.event_excursion), COLOR_EXCURSION)); - app.db.eventTypeDao().add(new EventType(profileId, TYPE_READING, getString(R.string.event_reading), COLOR_READING)); - app.db.eventTypeDao().add(new EventType(profileId, TYPE_CLASS_EVENT, getString(R.string.event_class_event), COLOR_CLASS_EVENT)); - app.db.eventTypeDao().add(new EventType(profileId, TYPE_INFORMATION, getString(R.string.event_information), COLOR_INFORMATION)); - - if (profile.getLuckyNumberDate() != null) { - app.db.luckyNumberDao().add(new LuckyNumber(profile.getId(), profile.getLuckyNumberDate(), profile.getLuckyNumber())); - } - app.db.profileDao().add(profile); - app.db.loginStoreDao().add(loginStore); - app.db.metadataDao().addAllIgnore(metadataList); - } - - - try { - app.appConfig.appInstalledTime = app.getPackageManager().getPackageInfo(app.getPackageName(), 0).firstInstallTime; - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - app.appConfig.appRateSnackbarTime = app.appConfig.appInstalledTime + 2 * 24 * 60 * 60 * 1000; - app.appConfig.loginFinished = true; - app.saveConfig("loginFinished", "appInstalledTime", "appRateSnackbarTime"); - } -} - diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMigrationSyncFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMigrationSyncFragment.java deleted file mode 100644 index 8adeebf2..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginMigrationSyncFragment.java +++ /dev/null @@ -1,132 +0,0 @@ -package pl.szczodrzynski.edziennik.ui.modules.login; - -import android.app.Activity; -import android.content.Context; -import android.os.AsyncTask; -import android.os.Bundle; -import android.text.Html; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import java.util.ArrayList; -import java.util.List; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.databinding.DataBindingUtil; -import androidx.fragment.app.Fragment; -import androidx.navigation.NavController; -import androidx.navigation.Navigation; -import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.data.api.AppError; -import pl.szczodrzynski.edziennik.data.api.interfaces.SyncCallback; -import pl.szczodrzynski.edziennik.databinding.FragmentLoginMigrationSyncBinding; -import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore; -import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; -import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; -import pl.szczodrzynski.edziennik.sync.SyncJob; -import pl.szczodrzynski.edziennik.sync.SyncService; - -public class LoginMigrationSyncFragment extends Fragment { - - private App app; - private NavController nav; - private FragmentLoginMigrationSyncBinding b; - private static final String TAG = "LoginMigrationSync"; - private List profileNameList = new ArrayList<>(); - private int profileIndex = 0; - - public LoginMigrationSyncFragment() { } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // Inflate the layout for this fragment - if (getActivity() != null) { - app = (App) getActivity().getApplicationContext(); - nav = Navigation.findNavController(getActivity(), R.id.nav_host_fragment); - } - else { - return null; - } - b = DataBindingUtil.inflate(inflater, R.layout.fragment_login_migration_sync, container, false); - return b.getRoot(); - } - - private void begin() { - AsyncTask.execute(() -> { - if (getActivity() == null) { - return; - } - - for (Profile profileObject: app.db.profileDao().getAllNow()) { - profileNameList.add(profileObject.getName()); - } - - getActivity().runOnUiThread(() -> { - profileIndex = 0; - b.loginSyncSubtitle1.setText(Html.fromHtml(getString(R.string.login_sync_subtitle_1_format, profileNameList.size() > profileIndex ? profileNameList.get(profileIndex) : " "))); - }); - SyncJob.run(app, -1, -1); - }); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - assert getContext() != null; - assert getActivity() != null; - - SyncService.customCallback = new SyncCallback() { - @Override public void onLoginFirst(List profileList, LoginStore loginStore) { } - - @Override - public void onSuccess(Context activityContext, ProfileFull profileFull) { - if (getActivity() == null) - return; - getActivity().runOnUiThread(() -> { - if (profileFull != null) { - // a profile is finished - profileIndex++; - b.loginSyncSubtitle1.setText(Html.fromHtml(getString(R.string.login_sync_subtitle_1_format, profileIndex < profileNameList.size() ? profileNameList.get(profileIndex) : profileNameList.get(profileNameList.size()-1)))); - } - else { - // all profiles are finished - getActivity().setResult(Activity.RESULT_OK); - getActivity().finish(); - } - }); - } - - @Override - public void onError(Context activityContext, AppError error) { - if (getActivity() == null) - return; - getActivity().setResult(Activity.RESULT_OK); - getActivity().finish(); - } - - @Override - public void onProgress(int progressStep) { - if (getActivity() == null) - return; - getActivity().runOnUiThread(() -> { - b.loginSyncProgressBar.setMax(SyncService.maxProgress); - b.loginSyncProgressBar.setProgress(SyncService.progress); - }); - } - - @Override - public void onActionStarted(int stringResId) { - if (getActivity() == null) - return; - getActivity().runOnUiThread(() -> { - b.loginSyncSubtitle2.setText(getString(R.string.login_sync_subtitle_2_format, getString(stringResId))); - }); - } - }; - - begin(); - } -} - diff --git a/app/src/main/res/navigation/nav_login.xml b/app/src/main/res/navigation/nav_login.xml index 825b646f..902daba7 100644 --- a/app/src/main/res/navigation/nav_login.xml +++ b/app/src/main/res/navigation/nav_login.xml @@ -21,9 +21,6 @@ - - - - - Date: Sat, 2 Nov 2019 19:25:44 +0100 Subject: [PATCH 126/691] [Login] Remove Login migration fragment (fix). --- .../edziennik/ui/modules/login/LoginActivity.java | 15 +++++---------- .../ui/modules/login/LoginChooserFragment.java | 6 ++---- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginActivity.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginActivity.java index 5d5b210d..6b8e5ac1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginActivity.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginActivity.java @@ -1,17 +1,18 @@ package pl.szczodrzynski.edziennik.ui.modules.login; -import androidx.databinding.DataBindingUtil; import android.os.Bundle; + import androidx.appcompat.app.AppCompatActivity; +import androidx.databinding.DataBindingUtil; +import androidx.navigation.NavDestination; +import androidx.navigation.NavOptions; +import androidx.navigation.Navigation; import com.afollestad.materialdialogs.MaterialDialog; import java.util.ArrayList; import java.util.List; -import androidx.navigation.NavDestination; -import androidx.navigation.NavOptions; -import androidx.navigation.Navigation; import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.data.api.AppError; @@ -34,12 +35,6 @@ public class LoginActivity extends AppCompatActivity { @Override public void onBackPressed() { NavDestination destination = Navigation.findNavController(this, R.id.nav_host_fragment).getCurrentDestination(); - if (destination != null && destination.getId() == R.id.loginMigrationFragment) { - return; - } - if (destination != null && destination.getId() == R.id.loginMigrationSyncFragment) { - return; - } if (destination != null && destination.getId() == R.id.loginSyncErrorFragment) { return; } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.java index a2547b3a..7f5a2c49 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.java @@ -12,10 +12,11 @@ import androidx.databinding.DataBindingUtil; import androidx.fragment.app.Fragment; import androidx.navigation.NavController; import androidx.navigation.Navigation; + import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackActivity; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.databinding.FragmentLoginChooserBinding; +import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackActivity; import static android.app.Activity.RESULT_CANCELED; @@ -57,9 +58,6 @@ public class LoginChooserFragment extends Fragment { b.cancelButton.setVisibility(View.VISIBLE); b.cancelButton.setOnClickListener((v -> nav.navigateUp())); } - else if (app.appConfig.lastAppVersion < 1991) { - nav.navigate(R.id.loginMigrationFragment, null, LoginActivity.navOptions); - } else if (app.appConfig.loginFinished) { // we are navigated here from AppDrawer b.cancelButton.setVisibility(View.VISIBLE); From 07863fed6ffb1a6c6168f7643ae96b9b1eeb7472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 3 Nov 2019 15:01:12 +0100 Subject: [PATCH 127/691] [Sync] Implement APIv2 auto sync using WorkManager. --- app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 4 - .../java/pl/szczodrzynski/edziennik/App.java | 29 +- .../pl/szczodrzynski/edziennik/Extensions.kt | 4 +- .../szczodrzynski/edziennik/MainActivity.kt | 54 +++- .../pl/szczodrzynski/edziennik/Notifier.java | 15 +- .../edziennik/WidgetTimetable.java | 7 +- .../edziennik/api/v2/ApiService.kt | 4 + .../edziennik/data/api/Edziennik.java | 11 +- .../edziennik/receivers/BootReceiver.java | 7 +- .../edziennik/receivers/JobsCreator.java | 22 -- .../edziennik/receivers/SzkolnyReceiver.kt | 17 +- .../edziennik/sync/AppManagerDetectedEvent.kt | 3 + .../sync/MyFirebaseMessagingService.java | 7 +- .../szczodrzynski/edziennik/sync/SyncJob.java | 153 ----------- .../edziennik/sync/SyncService.java | 258 ------------------ .../edziennik/sync/SyncWorker.kt | 99 +++++++ .../ui/modules/home/HomeFragment.java | 56 +--- .../modules/settings/SettingsNewFragment.java | 22 +- .../szczodrzynski/edziennik/utils/Utils.java | 14 +- .../edziennik/utils/models/AppConfig.java | 4 + .../luckynumber/WidgetLuckyNumber.java | 7 +- .../notifications/WidgetNotifications.java | 7 +- app/src/main/res/layout/fragment_home.xml | 89 ++---- app/src/main/res/values/strings.xml | 4 + build.gradle | 1 + 26 files changed, 274 insertions(+), 626 deletions(-) delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/receivers/JobsCreator.java create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/sync/AppManagerDetectedEvent.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncJob.java delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncService.java create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncWorker.kt diff --git a/app/build.gradle b/app/build.gradle index 8a721d8e..df417d05 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -162,6 +162,8 @@ dependencies { implementation "org.redundent:kotlin-xml-builder:1.5.3" implementation "io.github.wulkanowy:signer-android:0.1.1" + + implementation "androidx.work:work-runtime-ktx:${versions.work}" } repositories { mavenCentral() diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9ffe0db4..2d1ad42c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -222,10 +222,6 @@ - diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/App.java b/app/src/main/java/pl/szczodrzynski/edziennik/App.java index 9da8ca74..230bce79 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/App.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/App.java @@ -20,10 +20,13 @@ import android.util.Base64; import android.util.Log; import android.util.Pair; +import androidx.annotation.RequiresApi; +import androidx.appcompat.app.AppCompatDelegate; +import androidx.work.Configuration; + import com.chuckerteam.chucker.api.ChuckerCollector; import com.chuckerteam.chucker.api.ChuckerInterceptor; import com.chuckerteam.chucker.api.RetentionManager; -import com.evernote.android.job.JobManager; import com.google.android.gms.security.ProviderInstaller; import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; @@ -55,8 +58,6 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; -import androidx.annotation.RequiresApi; -import androidx.appcompat.app.AppCompatDelegate; import cat.ereza.customactivityoncrash.config.CaocConfig; import im.wangchao.mhttp.MHttp; import im.wangchao.mhttp.internal.cookie.PersistentCookieJar; @@ -66,7 +67,6 @@ import me.leolin.shortcutbadger.ShortcutBadger; import okhttp3.ConnectionSpec; import okhttp3.OkHttpClient; import okhttp3.TlsVersion; -import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity; import pl.szczodrzynski.edziennik.data.api.Edziennik; import pl.szczodrzynski.edziennik.data.api.Iuczniowie; import pl.szczodrzynski.edziennik.data.api.Librus; @@ -77,24 +77,32 @@ import pl.szczodrzynski.edziennik.data.db.modules.debuglog.DebugLog; import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore; import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; -import pl.szczodrzynski.edziennik.utils.models.AppConfig; import pl.szczodrzynski.edziennik.network.NetworkUtils; import pl.szczodrzynski.edziennik.network.TLSSocketFactory; -import pl.szczodrzynski.edziennik.receivers.JobsCreator; -import pl.szczodrzynski.edziennik.sync.SyncJob; +import pl.szczodrzynski.edziennik.sync.SyncWorker; +import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity; import pl.szczodrzynski.edziennik.utils.PermissionChecker; import pl.szczodrzynski.edziennik.utils.Themes; import pl.szczodrzynski.edziennik.utils.Utils; +import pl.szczodrzynski.edziennik.utils.models.AppConfig; import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_LIBRUS; import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK; import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_VULCAN; -public class App extends androidx.multidex.MultiDexApplication { +public class App extends androidx.multidex.MultiDexApplication implements Configuration.Provider { private static final String TAG = "App"; public static int profileId = -1; private Context mContext; + @Override + public Configuration getWorkManagerConfiguration() { + return new Configuration.Builder() + .setMinimumLoggingLevel(Log.VERBOSE) + .build(); + } + + public static final int REQUEST_TIMEOUT = 10 * 1000; // notifications @@ -301,12 +309,11 @@ public class App extends androidx.multidex.MultiDexApplication { //profileLoadById(appSharedPrefs.getInt("current_profile_id", 1)); - JobManager.create(this).addJobCreator(new JobsCreator()); if (appConfig.registerSyncEnabled) { - SyncJob.schedule(this); + SyncWorker.Companion.scheduleNext(this); } else { - SyncJob.clear(); + SyncWorker.Companion.cancelNext(this); } db.metadataDao().countUnseen().observeForever(count -> { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index c03db59c..7ea38470 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -324,4 +324,6 @@ fun String.crc32(): Long { val crc = CRC32() crc.update(toByteArray()) return crc.value -} \ No newline at end of file +} + +fun Long.formatDate(format: String = "yyyy-MM-dd HH:mm:ss"): String = SimpleDateFormat(format).format(this) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt index 41de1a6e..cc04e8d0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt @@ -2,10 +2,7 @@ package pl.szczodrzynski.edziennik import android.app.Activity import android.app.ActivityManager -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter +import android.content.* import android.graphics.BitmapFactory import android.graphics.drawable.BitmapDrawable import android.os.* @@ -19,6 +16,7 @@ import androidx.core.graphics.ColorUtils import androidx.lifecycle.Observer import androidx.navigation.NavOptions import com.danimahardhika.cafebar.CafeBar +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.mikepenz.iconics.IconicsColor import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.IconicsSize @@ -37,11 +35,12 @@ import pl.szczodrzynski.edziennik.App.APP_URL import pl.szczodrzynski.edziennik.api.v2.ApiService import pl.szczodrzynski.edziennik.api.v2.events.* import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface.* import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.* import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding import pl.szczodrzynski.edziennik.network.ServerRequest -import pl.szczodrzynski.edziennik.sync.SyncJob +import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment import pl.szczodrzynski.edziennik.ui.modules.announcements.AnnouncementsFragment @@ -370,7 +369,7 @@ class MainActivity : AppCompatActivity() { isStoragePermissionGranted() - SyncJob.schedule(app) + //SyncWorker.scheduleNext(app) // APP BACKGROUND if (app.appConfig.appBackground != null) { @@ -499,7 +498,7 @@ class MainActivity : AppCompatActivity() { profileListEmptyListener() } DRAWER_PROFILE_SYNC_ALL -> { - SyncJob.run(app) + ApiService.startAndRequest(this, SyncRequest()) } else -> { loadTarget(id) @@ -569,6 +568,47 @@ class MainActivity : AppCompatActivity() { @Subscribe(threadMode = ThreadMode.MAIN) fun onSyncErrorEvent(event: SyncErrorEvent) { + } + @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) + fun onAppManagerDetectedEvent(event: AppManagerDetectedEvent) { + if (app.appConfig.dontShowAppManagerDialog) + return + MaterialAlertDialogBuilder(this) + .setTitle(R.string.app_manager_dialog_title) + .setMessage(R.string.app_manager_dialog_text) + .setPositiveButton(R.string.ok) { dialog, which -> + try { + val intent = Intent() + intent.component = ComponentName( + "com.huawei.systemmanager", + "com.huawei.systemmanager.appcontrol.activity.StartupAppControlActivity" + ) + startActivity(intent) + } catch (e: Exception) { + e.printStackTrace() + try { + val intent = Intent() + intent.component = ComponentName( + "com.asus.mobilemanager", + "com.asus.mobilemanager.MainActivity" + ) + startActivity(intent) + } catch (e: Exception) { + try { + startActivity(Intent(android.provider.Settings.ACTION_SETTINGS)) + } catch (e: Exception) { + e.printStackTrace() + Toast.makeText(this, R.string.app_manager_open_failed, Toast.LENGTH_SHORT).show() + } + } + } + } + .setNeutralButton(R.string.dont_ask_again) { dialog, which -> + app.appConfig.dontShowAppManagerDialog = true + app.saveConfig("dontShowAppManagerDialog") + } + .setCancelable(false) + .show() } private fun fragmentToFeature(currentFragment: Int): Int { return when (currentFragment) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java b/app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java index 7b316ed8..660647b9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Notifier.java @@ -19,14 +19,11 @@ import java.util.List; import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; import pl.szczodrzynski.edziennik.receivers.BootReceiver; -import pl.szczodrzynski.edziennik.sync.SyncJob; -import pl.szczodrzynski.edziennik.sync.SyncService; import pl.szczodrzynski.edziennik.utils.models.Date; import pl.szczodrzynski.edziennik.utils.models.Time; import static androidx.core.app.NotificationCompat.PRIORITY_DEFAULT; import static androidx.core.app.NotificationCompat.PRIORITY_MAX; -import static pl.szczodrzynski.edziennik.sync.SyncService.ACTION_CANCEL; public class Notifier { @@ -127,17 +124,17 @@ public class Notifier { | |__| | (_| | || (_| | | |__| | __/ |_ |_____/ \__,_|\__\__,_| \_____|\___|\_*/ public Notification notificationGetDataShow(int maxProgress) { - Intent notificationIntent = new Intent(app.getContext(), SyncService.class); + /*Intent notificationIntent = new Intent(app.getContext(), SyncService.class); notificationIntent.setAction(ACTION_CANCEL); PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0, - notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT); + notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);*/ getDataNotificationBuilder = new NotificationCompat.Builder(app, GROUP_KEY_GET_DATA) .setSmallIcon(android.R.drawable.stat_sys_download) .setColor(notificationColor) .setContentTitle(app.getString(R.string.notification_get_data_title)) .setContentText(app.getString(R.string.notification_get_data_text)) - .addAction(R.drawable.ic_notification, app.getString(R.string.notification_get_data_cancel), pendingIntent) + //.addAction(R.drawable.ic_notification, app.getString(R.string.notification_get_data_cancel), pendingIntent) //.setGroup(GROUP_KEY_GET_DATA) .setOngoing(true) .setProgress(maxProgress, 0, false) @@ -209,10 +206,8 @@ public class Notifier { @Override protected void onHandleIntent(Intent intent) { - SyncJob.run((App) getApplication(), intent.getExtras().getInt("failedProfileId", -1), -1); - NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - assert notificationManager != null; - notificationManager.cancel(ID_GET_DATA_ERROR); + + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.java b/app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.java index 21496078..7a6875d8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.java @@ -28,6 +28,8 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; +import pl.szczodrzynski.edziennik.api.v2.ApiService; +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest; import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull; import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange; import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull; @@ -38,7 +40,6 @@ import pl.szczodrzynski.edziennik.utils.models.ItemWidgetTimetableModel; import pl.szczodrzynski.edziennik.utils.models.Time; import pl.szczodrzynski.edziennik.utils.models.Week; import pl.szczodrzynski.edziennik.widgets.WidgetConfig; -import pl.szczodrzynski.edziennik.sync.SyncJob; import pl.szczodrzynski.edziennik.widgets.timetable.LessonDetailsActivity; import pl.szczodrzynski.edziennik.widgets.timetable.WidgetTimetableService; @@ -65,8 +66,8 @@ public class WidgetTimetable extends AppWidgetProvider { @Override public void onReceive(Context context, Intent intent) { - if (ACTION_SYNC_DATA.equals(intent.getAction())){ - SyncJob.run((App) context.getApplicationContext()); + if (ACTION_SYNC_DATA.equals(intent.getAction())) { + ApiService.Companion.startAndRequest(context, new SyncRequest()); } super.onReceive(context, intent); } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt index 0f8c0d35..d2d39796 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt @@ -39,6 +39,10 @@ class ApiService : Service() { fun start(context: Context) { context.startService(Intent(context, ApiService::class.java)) } + fun startAndRequest(context: Context, request: Any) { + context.startService(Intent(context, ApiService::class.java)) + EventBus.getDefault().postSticky(request) + } } private val app by lazy { applicationContext as App } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java index 7b9997ca..c73ccd80 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Edziennik.java @@ -63,7 +63,6 @@ import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; import pl.szczodrzynski.edziennik.data.db.modules.teams.Team; import pl.szczodrzynski.edziennik.network.ServerRequest; -import pl.szczodrzynski.edziennik.sync.SyncJob; import pl.szczodrzynski.edziennik.utils.Themes; import pl.szczodrzynski.edziennik.utils.models.Date; import pl.szczodrzynski.edziennik.utils.models.Notification; @@ -116,7 +115,6 @@ import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notificati import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_SERVER_MESSAGE; import static pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.TYPE_TIMETABLE_LESSON_CHANGE; import static pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.REGISTRATION_ENABLED; -import static pl.szczodrzynski.edziennik.sync.SyncService.PROFILE_MAX_PROGRESS; import static pl.szczodrzynski.edziennik.utils.Utils.d; import static pl.szczodrzynski.edziennik.utils.Utils.ns; @@ -592,12 +590,7 @@ public class Edziennik { } public void notifyAndReload() { - app.notifier.postAll(null); - app.saveConfig(); - SyncJob.schedule(app); - Intent i = new Intent(Intent.ACTION_MAIN) - .putExtra("reloadProfileId", -1); - app.sendBroadcast(i); + // TODO \/ Intent intent = new Intent(app.getContext(), WidgetTimetable.class); intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); @@ -751,7 +744,7 @@ public class Edziennik { MaterialDialog progressDialog = new MaterialDialog.Builder(activity) .title(dialogTitle) .content(dialogText) - .progress(false, PROFILE_MAX_PROGRESS, false) + .progress(false, 110, false) .canceledOnTouchOutside(false) .show(); SyncCallback guiSyncCallback = new SyncCallback() { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/BootReceiver.java b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/BootReceiver.java index 694bead6..b1524237 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/BootReceiver.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/BootReceiver.java @@ -11,16 +11,17 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Build; -import androidx.core.content.FileProvider; import android.widget.Toast; +import androidx.core.content.FileProvider; + import java.io.File; import java.util.List; import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.network.ServerRequest; -import pl.szczodrzynski.edziennik.sync.SyncJob; +import pl.szczodrzynski.edziennik.sync.SyncWorker; import pl.szczodrzynski.edziennik.utils.Utils; import static android.content.Context.DOWNLOAD_SERVICE; @@ -81,7 +82,7 @@ public class BootReceiver extends BroadcastReceiver { } else { - SyncJob.schedule(app); + SyncWorker.Companion.scheduleNext(app); if (app.networkUtils.isOnline()) { checkUpdate(context, intent); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/JobsCreator.java b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/JobsCreator.java deleted file mode 100644 index 0c96d14e..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/JobsCreator.java +++ /dev/null @@ -1,22 +0,0 @@ -package pl.szczodrzynski.edziennik.receivers; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.evernote.android.job.Job; -import com.evernote.android.job.JobCreator; - -import pl.szczodrzynski.edziennik.sync.SyncJob; - -public class JobsCreator implements JobCreator { - @Override - @Nullable - public Job create(@NonNull String tag) { - switch (tag) { - case SyncJob.TAG: - return new SyncJob(); - default: - return null; - } - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt index 292b3875..f3a6ad36 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt @@ -7,19 +7,20 @@ package pl.szczodrzynski.edziennik.receivers import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import org.greenrobot.eventbus.EventBus import pl.szczodrzynski.edziennik.api.v2.ApiService -import pl.szczodrzynski.edziennik.api.v2.events.requests.* +import pl.szczodrzynski.edziennik.api.v2.events.requests.ServiceCloseRequest +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest +import pl.szczodrzynski.edziennik.api.v2.events.requests.TaskCancelRequest class SzkolnyReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { - context?.startService(Intent(context, ApiService::class.java)) + context ?: return when (intent?.extras?.getString("task", null)) { - "ServiceCloseRequest" -> EventBus.getDefault().post(ServiceCloseRequest()) - "TaskCancelRequest" -> EventBus.getDefault().post(TaskCancelRequest(intent.extras?.getInt("taskId", -1) ?: return)) - "SyncRequest" -> EventBus.getDefault().post(SyncRequest()) - "SyncProfileRequest" -> EventBus.getDefault().post(SyncProfileRequest(intent.extras?.getInt("profileId", -1) ?: return)) - "AnnouncementsReadRequest" -> EventBus.getDefault().post(AnnouncementsReadRequest(intent.extras?.getInt("profileId", -1) ?: return)) + "ServiceCloseRequest" -> ApiService.startAndRequest(context, ServiceCloseRequest()) + "TaskCancelRequest" -> ApiService.startAndRequest(context, TaskCancelRequest(intent.extras?.getInt("taskId", -1) ?: return)) + "SyncRequest" -> ApiService.startAndRequest(context, SyncRequest()) + "SyncProfileRequest" -> ApiService.startAndRequest(context, SyncProfileRequest(intent.extras?.getInt("profileId", -1) ?: return)) } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/AppManagerDetectedEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/sync/AppManagerDetectedEvent.kt new file mode 100644 index 00000000..10d93dfc --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/sync/AppManagerDetectedEvent.kt @@ -0,0 +1,3 @@ +package pl.szczodrzynski.edziennik.sync + +class AppManagerDetectedEvent(val failedWorkTimestamps: List) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java b/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java index 312724b7..17102d23 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java @@ -17,6 +17,9 @@ import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.R; +import pl.szczodrzynski.edziennik.api.v2.ApiService; +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest; +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest; import pl.szczodrzynski.edziennik.data.db.modules.events.Event; import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull; import pl.szczodrzynski.edziennik.data.db.modules.events.EventType; @@ -111,7 +114,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { app.notifier.postAll(profile); app.saveConfig("notifications");*/ d(TAG, "Syncing profile " + profile.getId()); - SyncJob.run(app, -1, profile.getId()); + ApiService.Companion.startAndRequest(app, new SyncProfileRequest(profile.getId(), null)); } else { /*app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message")) .withProfileData(profile.id, profile.name) @@ -122,7 +125,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { app.notifier.postAll(profile); app.saveConfig("notifications");*/ d(TAG, "Syncing profile " + profile.getId()); - SyncJob.run(app, -1, profile.getId()); + ApiService.Companion.startAndRequest(app, new SyncProfileRequest(profile.getId(), null)); } } }); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncJob.java b/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncJob.java deleted file mode 100644 index 3deddb25..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncJob.java +++ /dev/null @@ -1,153 +0,0 @@ -package pl.szczodrzynski.edziennik.sync; - -import android.content.Context; - -import androidx.annotation.NonNull; - -import android.net.ConnectivityManager; -import android.net.NetworkInfo; - -import com.evernote.android.job.Job; -import com.evernote.android.job.JobManager; -import com.evernote.android.job.JobRequest; - -import java.util.Set; - -import pl.szczodrzynski.edziennik.App; - -import static pl.szczodrzynski.edziennik.utils.Utils.d; - -public class SyncJob extends Job { - - public static final String TAG = "SyncJob"; - private static int retryCount = 0; - - public static void schedule(App app) { - schedule(app, false); - } - - /** - * Forces a SyncJob to be scheduled. Does nothing if it's already scheduled. - */ - public static void schedule(App app, boolean internetError) { - int count = count(); - if (count > 0) { - d(TAG, "Job is already scheduled"); - return; - } - if (app.appConfig.registerSyncEnabled) { - //Toast.makeText(app, "Scheduling an alarm", Toast.LENGTH_SHORT).show(); - long timeout = 1000 * app.appConfig.registerSyncInterval; - if (internetError) { - retryCount++; - timeout = 1000 * (30*retryCount*retryCount); // 30sec, 2min, 4min 30sec, 8min, 12min 30sec, ... - timeout = Math.min(timeout, 30 * 60 * 1000); // max 30min between retries - if (timeout > 1000 * app.appConfig.registerSyncInterval) { - timeout = 1000 * app.appConfig.registerSyncInterval; - } - } - - new JobRequest.Builder(SyncJob.TAG) - .setExecutionWindow((long) (timeout - (0.1 * timeout)), (long) (timeout + (0.1 * timeout))) - //.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED) - .build() - .schedule(); - if (!internetError) { - retryCount = 0; - } - } - else { - clear(); - } - } - public static int count() { - Set jobRequests = JobManager.instance().getAllJobRequestsForTag(SyncJob.TAG); - if (App.devMode) { - for (JobRequest jobRequest: jobRequests) { - d(TAG, "JOBLIST: Found at "+jobRequest.getStartMs()+" - "+jobRequest.getEndMs()); - } - } - return jobRequests.size(); - } - public static void clear() { - JobManager.instance().cancelAllForTag(SyncJob.TAG); - } - - /** - * Deletes every scheduled SyncJob and then reschedules it. - */ - public static void update(App app) { - update(app, false); - } - public static void update(App app, boolean internetError) { - app.debugLogAsync("SyncJob update internetError="+internetError); - clear(); - schedule(app, internetError); - } - public static void run(App app) { - app.debugLogAsync("SyncJob run with no IDs"); - clear(); - runService(app, app.getContext(), -1, -1); - /*new JobRequest.Builder(SyncJob.TAG) - .startNow() - .build() - .schedule();*/ - } - - public static void run(App app, int firstProfileId, int targetProfileId) { - app.debugLogAsync("SyncJob run with firstProfileId="+firstProfileId+", targetProfileId="+targetProfileId); - clear(); - runService(app, app.getContext(), firstProfileId, targetProfileId); - /* PersistableBundleCompat extras = new PersistableBundleCompat(); - extras.putInt("firstProfileId", firstProfileId); - extras.putInt("targetProfileId", targetProfileId); - new JobRequest.Builder(SyncJob.TAG) - .startNow() - .setExtras(extras) - .build() - .schedule();*/ - } - - @Override - @NonNull - protected Result onRunJob(@NonNull Params params) { - //Log.d(TAG, "Job is running!"); - - App app = (App) getContext().getApplicationContext(); - - int firstProfileId = params.getExtras().getInt("firstProfileId", -1); - int targetProfileId = params.getExtras().getInt("targetProfileId", -1); - - runService(app, getContext(), firstProfileId, targetProfileId); - - return Result.SUCCESS; - } - - private static void runService(App app, Context context, int firstProfileId, int targetProfileId) { - - SyncService.firstProfileId = firstProfileId; - SyncService.targetProfileId = targetProfileId; - - app.debugLog("SyncJob runService with firstProfileId="+firstProfileId+", targetProfileId="+targetProfileId); - - boolean connected; - - ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - if (app.appConfig.registerSyncOnlyWifi) { - NetworkInfo mWifi = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - connected = mWifi != null && mWifi.isConnected(); - } - else { - NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo(); - connected = netInfo != null && netInfo.isConnected(); - } - - if (!connected) { - app.debugLog("SyncJob cancelling: no internet"); - update(app, true); - } - else { - SyncService.start(context); - } - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncService.java b/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncService.java deleted file mode 100644 index e3eddf3d..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncService.java +++ /dev/null @@ -1,258 +0,0 @@ -package pl.szczodrzynski.edziennik.sync; - -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.Handler; -import android.os.IBinder; -import androidx.annotation.Nullable; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.data.api.AppError; -import pl.szczodrzynski.edziennik.data.api.interfaces.SyncCallback; -import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore; -import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; -import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; - -import static pl.szczodrzynski.edziennik.Notifier.ID_GET_DATA; -import static pl.szczodrzynski.edziennik.Notifier.ID_GET_DATA_ERROR; -import static pl.szczodrzynski.edziennik.utils.Utils.d; - -public class SyncService extends Service { - public static final String TAG = "SyncService"; - private App app; - public static boolean running = false; - private List profiles; - public static final int PROFILE_MAX_PROGRESS = 110; - private Profile profile = null; - private int profileId = -1; - public static int progress = 0; - private int profileProgress = 0; - public static int maxProgress; - public static int firstProfileId = -1; - public static int targetProfileId = -1; - public static final String ACTION_CANCEL = "SyncService.ACTION_CANCEL"; - public static SyncCallback customCallback = null; - private static final int PROFILE_TIMEOUT = 40000; - - private Handler timeoutHandler = new Handler(); - private Runnable timeoutRunnable = new Runnable() { - public void run() { - if (!running) - return; - error(AppError.stringErrorCode(app, AppError.CODE_TIMEOUT, ""), profile); - finishDownload(); - } - }; - - public static void start(Context context) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - context.startForegroundService(new Intent(context, SyncService.class)); - } else { - context.startService(new Intent(context, SyncService.class)); - } - } - - @Override - public void onCreate() { - super.onCreate(); - app = (App) getApplicationContext(); - app.debugLog("SyncService created"); - startForeground(ID_GET_DATA, app.notifier.notificationGetDataShow(0)); - } - - private void reNotify() { - //app.notifier.notificationCancel(ID_GET_DATA_ERROR); - app.notifier.notificationPost(ID_GET_DATA, app.notifier.notificationGetDataProgress(progress, maxProgress)); - } - private void error(String error, Profile profile) { - app.notifier.notificationPost(ID_GET_DATA_ERROR, app.notifier.notificationGetDataError(profile == null ? "" : profile.getName(), error, profile == null ? -1 : profile.getId())); - } - - long millisStart; - - private void downloadData() { - if (profiles.size() > 0) { - profile = profiles.get(0); - profileId = profile.getId(); - app.debugLog("SyncService downloading profileId="+profileId); - profiles.remove(0); - // mamy profileID wiec nie trzeba nic ładować. DAO musi używać profileId - millisStart = System.currentTimeMillis(); - app.notifier.notificationPost(ID_GET_DATA, app.notifier.notificationGetDataProfile(profile.getName())); - app.apiEdziennik.sync(app, SyncService.this, new SyncCallback() { - @Override - public void onLoginFirst(List profileList, LoginStore loginStore) { - if (customCallback != null) - customCallback.onLoginFirst(profileList, loginStore); - } - - @Override - public void onSuccess(Context activityContext, ProfileFull profile) { - profileProgress++; - progress = profileProgress*PROFILE_MAX_PROGRESS; - updateTimeoutHandler(); - reNotify(); - app.debugLog("SyncService profileId="+profileId+" done in "+(System.currentTimeMillis() - millisStart)+"ms"); - if (customCallback != null) - customCallback.onSuccess(activityContext, profile); - downloadData(); - } - - @Override - public void onError(Context activityContext, AppError error) { - error(error.asReadableString(activityContext), profile); - if (customCallback != null) - customCallback.onError(activityContext, error); - if (error.errorCode == AppError.CODE_NO_INTERNET) { - finishDownload(CODE_NO_INTERNET); // use finishDownload instead of scheduleNext so it always changes the profile back to previousProfile - } else { - finishDownload(CODE_ERROR); - } - } - - @Override - public void onProgress(int progressStep) { - if (customCallback != null) - customCallback.onProgress(Math.min(progressStep, PROFILE_MAX_PROGRESS)); - progress = Math.min(progress+progressStep, (profileProgress+1)*PROFILE_MAX_PROGRESS); - reNotify(); - } - - @Override - public void onActionStarted(int stringResId) { - if (customCallback != null) - customCallback.onActionStarted(stringResId); - app.notifier.notificationPost(ID_GET_DATA, app.notifier.notificationGetDataAction(stringResId)); - } - }, profileId); - } - else { - finishDownload(); - } - } - - private void updateTimeoutHandler() { - timeoutHandler.removeCallbacks(timeoutRunnable); - timeoutHandler.postDelayed(timeoutRunnable, PROFILE_TIMEOUT); - } - - private static final int CODE_OK = 0; - private static final int CODE_ERROR = 1; - private static final int CODE_NO_INTERNET = 2; - private void finishDownload() { - finishDownload(CODE_OK); - } - private void finishDownload(int errorCode) { - timeoutHandler.removeCallbacks(timeoutRunnable); - - app.apiEdziennik.notifyAndReload(); - - app.debugLog("SyncService finishing with profileId="+profileId); - if (errorCode == CODE_OK) { - app.notifier.notificationCancel(ID_GET_DATA_ERROR); - } - SyncJob.update((App) getApplication(), errorCode == CODE_NO_INTERNET); - stopSelf(); - if (customCallback != null && errorCode == CODE_OK) { - customCallback.onSuccess(null, null); - } - customCallback = null; - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - d(TAG, "Got an intent. Service is running "+running); - if (intent != null && intent.getAction() != null && intent.getAction().equals(ACTION_CANCEL)) { - d(TAG, "ACTION_CANCEL intent"); - finishDownload(); - running = true; - } - - if (running) { - app.debugLog("SyncService already running; return"); - return Service.START_STICKY; - } - running = true; - - timeoutHandler.postDelayed(timeoutRunnable, PROFILE_TIMEOUT); - - profiles = app.db.profileDao().getProfilesForSyncNow(); - - app.debugLog("SyncService source profileList="+ Arrays.toString(profiles.toArray())+". firstProfileId="+firstProfileId+", targetProfileId="+targetProfileId); - - if (profiles.size() == 0) { - stopSelf(); - return Service.START_STICKY; - } - - if (firstProfileId != -1) { - d(TAG, "Profile list from first "+firstProfileId); - Collection toRemove = new ArrayList<>(); - for (Profile profile: profiles) { - if (firstProfileId == profile.getId()) { - break; - } - toRemove.add(profile); - } - profiles.removeAll(toRemove); - firstProfileId = -1; - } - if (targetProfileId != -1) { - d(TAG, "Profile list only target "+targetProfileId); - Profile targetProfile = null; - for (Profile profile: profiles) { - if (targetProfileId == profile.getId()) { - targetProfile = profile; - break; - } - } - profiles.clear(); - if (targetProfile != null) - profiles.add(targetProfile); - targetProfileId = -1; - } - - app.debugLog("SyncService target profileList="+ Arrays.toString(profiles.toArray())); - - progress = 0; - profileProgress = 0; - maxProgress = profiles.size()*PROFILE_MAX_PROGRESS; - - app.notifier.notificationPost(ID_GET_DATA, app.notifier.notificationGetDataShow(maxProgress)); - - if (!app.networkUtils.isOnline()) { - app.debugLog("SyncService no internet; update,stop"); - error(AppError.stringErrorCode(app, AppError.CODE_NO_INTERNET, ""), profiles.size() >= 1 ? profiles.get(0) : null); - SyncJob.update((App) getApplication(), true); - if (customCallback != null) { - customCallback.onError(getApplicationContext(), new AppError(TAG, 241, AppError.CODE_NO_INTERNET, null, (Throwable) null)); - } - stopSelf(); - return Service.START_STICKY; - } - - downloadData(); - - return Service.START_STICKY; - } - - @Override - public void onDestroy() { - super.onDestroy(); - app.debugLog("SyncService destroyed"); - running = false; - } - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return null; - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncWorker.kt b/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncWorker.kt new file mode 100644 index 00000000..3c0e032e --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncWorker.kt @@ -0,0 +1,99 @@ +package pl.szczodrzynski.edziennik.sync + +import android.annotation.SuppressLint +import android.content.Context +import android.os.AsyncTask +import androidx.work.* +import androidx.work.impl.WorkManagerImpl +import org.greenrobot.eventbus.EventBus +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.MINUTE +import pl.szczodrzynski.edziennik.api.v2.ApiService +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest +import pl.szczodrzynski.edziennik.formatDate +import pl.szczodrzynski.edziennik.utils.Utils.d +import java.util.concurrent.TimeUnit + +class SyncWorker(val context: Context, val params: WorkerParameters) : Worker(context, params) { + companion object { + const val TAG = "SyncWorker" + + /** + * Schedule the sync job only if it's not already scheduled. + */ + @SuppressLint("RestrictedApi") + fun scheduleNext(app: App) { + AsyncTask.execute { + val workManager = WorkManager.getInstance(app) as WorkManagerImpl + val scheduledWork = workManager.workDatabase.workSpecDao().scheduledWork + scheduledWork.forEach { + d(TAG, "Work: ${it.id} at ${(it.periodStartTime+it.initialDelay).formatDate()}. State = ${it.state} (finished = ${it.state.isFinished})") + } + // remove finished work and other than SyncWorker + scheduledWork.removeAll { it.workerClassName != SyncWorker::class.java.canonicalName || it.isPeriodic || it.state.isFinished } + d(TAG, "Found ${scheduledWork.size} unfinished work") + // remove all enqueued work that had to (but didn't) run at some point in the past (at least 1min ago) + val failedWork = scheduledWork.filter { it.state == WorkInfo.State.ENQUEUED && it.periodStartTime+it.initialDelay < System.currentTimeMillis() - 1*MINUTE*1000 } + d(TAG, "${failedWork.size} work requests failed to start (out of ${scheduledWork.size} requests)") + if (failedWork.isNotEmpty()) { + d(TAG, "App Manager detected!") + EventBus.getDefault().postSticky(AppManagerDetectedEvent(failedWork.map { it.periodStartTime+it.initialDelay })) + } + if (scheduledWork.size-failedWork.size < 1) { + d(TAG, "No pending work found, scheduling next:") + rescheduleNext(app) + } + } + } + + /** + * Cancel any existing sync jobs and schedule a new one. + * + * If [registerSyncEnabled] is not true, just cancel every job. + */ + fun rescheduleNext(app: App) { + cancelNext(app) + val enableSync = app.appConfig.registerSyncEnabled + if (!enableSync) { + return + } + val onlyWifi = app.appConfig.registerSyncOnlyWifi + val syncInterval = app.appConfig.registerSyncInterval.toLong() + + val syncAt = System.currentTimeMillis() + syncInterval*1000 + d(TAG, "Scheduling work at ${syncAt.formatDate()}") + + val constraints = Constraints.Builder() + .setRequiredNetworkType( + if (onlyWifi) + NetworkType.UNMETERED + else + NetworkType.CONNECTED) + .build() + + val syncWorkRequest = OneTimeWorkRequestBuilder() + .setInitialDelay(syncInterval, TimeUnit.SECONDS) + .setConstraints(constraints) + .addTag(TAG) + .build() + + WorkManager.getInstance(app).enqueue(syncWorkRequest) + } + + /** + * Cancel any scheduled sync job. + */ + fun cancelNext(app: App) { + d(TAG, "Cancelling work by tag $TAG") + WorkManager.getInstance(app).cancelAllWorkByTag(TAG) + //WorkManager.getInstance(app).pruneWork() // do not prune the work in order to look for failed tasks + } + } + + override fun doWork(): Result { + d(TAG, "Running worker ID ${params.id}") + ApiService.startAndRequest(context, SyncRequest()) + rescheduleNext(context as App) + return Result.success() + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java index 265c12fa..d5aca94b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java @@ -27,6 +27,7 @@ import androidx.annotation.PluralsRes; import androidx.core.graphics.ColorUtils; import androidx.databinding.DataBindingUtil; import androidx.fragment.app.Fragment; +import androidx.work.WorkManager; import com.afollestad.materialdialogs.MaterialDialog; import com.mikepenz.iconics.IconicsColor; @@ -34,19 +35,13 @@ import com.mikepenz.iconics.IconicsDrawable; import com.mikepenz.iconics.IconicsSize; import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial; -import org.greenrobot.eventbus.EventBus; - import java.util.ArrayList; import java.util.List; -import kotlin.Pair; import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.api.v2.ApiService; -import pl.szczodrzynski.edziennik.api.v2.events.requests.AnnouncementsReadRequest; -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest; import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeFull; import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull; import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; @@ -67,8 +62,6 @@ import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem; import static pl.szczodrzynski.edziennik.App.UPDATES_ON_PLAY_STORE; import static pl.szczodrzynski.edziennik.MainActivity.DRAWER_ITEM_GRADES; -import static pl.szczodrzynski.edziennik.MainActivity.DRAWER_ITEM_HOME; -import static pl.szczodrzynski.edziennik.MainActivity.DRAWER_ITEM_MESSAGES; import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER1_FINAL; import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER1_PROPOSED; import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER2_FINAL; @@ -76,7 +69,6 @@ import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMES import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_YEAR_FINAL; import static pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_YEAR_PROPOSED; import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK; -import static pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT; public class HomeFragment extends Fragment { private static final String TAG = "HomeFragment"; @@ -104,55 +96,13 @@ public class HomeFragment extends Fragment { if (app == null || app.profile == null || activity == null || b == null || !isAdded()) return; - /*b.refreshLayout.setOnRefreshListener(() -> { - activity.syncCurrentFeature(MainActivity.DRAWER_ITEM_HOME, b.refreshLayout); - });*/ - /*b.refreshLayout.setOnTouchListener((v, event) -> { - d(TAG, "event "+event); - event.setSource(0x10000000); // set a unique source - activity.swipeRefreshLayout.onTouchEvent(event); - return true; - });*/ - /*b.refreshLayout.setOnDragListener((v, event) -> { - activity.swipeRefreshLayout.onDragEvent(event); - return true; - });*/ + b.devMode.setVisibility(App.devMode ? View.VISIBLE : View.GONE); - b.composeButton.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); b.composeButton.setOnClickListener((v -> { startActivity(new Intent(activity, MessagesComposeActivity.class)); })); - b.testButton.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); - b.testButton.setOnClickListener((v -> { - app.startService(new Intent(app, ApiService.class)); - })); - - b.test2.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); - b.test2.setOnClickListener((v -> { - List> list = new ArrayList<>(); - list.add(new Pair<>(DRAWER_ITEM_HOME, 0)); - EventBus.getDefault().postSticky(new SyncProfileRequest(app.profile.getId(), list)); - })); - - b.test3.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); - b.test3.setOnClickListener((v -> { - List> list = new ArrayList<>(); - list.add(new Pair<>(DRAWER_ITEM_MESSAGES, TYPE_SENT)); - EventBus.getDefault().postSticky(new SyncProfileRequest(app.profile.getId(), list)); - })); - - b.test4.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); - b.test4.setOnClickListener((v -> { - List> list = new ArrayList<>(); - list.add(new Pair<>(DRAWER_ITEM_GRADES, 0)); - EventBus.getDefault().postSticky(new SyncProfileRequest(app.profile.getId(), list)); - })); - - b.test5.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); - b.test5.setOnClickListener((v -> { - EventBus.getDefault().postSticky(new AnnouncementsReadRequest(app.profile.getId())); - })); + b.pruneWorkButton.setOnClickListener((v -> WorkManager.getInstance(app).pruneWork())); //((TextView)v.findViewById(R.id.nextSync)).setText(getString(R.string.next_sync_format,Time.fromMillis(app.appJobs.syncJobTime).getStringHMS())); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java index 76fb23c1..c442bdc6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java @@ -48,7 +48,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore; import pl.szczodrzynski.edziennik.network.NetworkUtils; import pl.szczodrzynski.edziennik.network.ServerRequest; import pl.szczodrzynski.edziennik.receivers.BootReceiver; -import pl.szczodrzynski.edziennik.sync.SyncJob; +import pl.szczodrzynski.edziennik.sync.SyncWorker; import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog; import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment; import pl.szczodrzynski.edziennik.ui.modules.webpush.WebPushConfigActivity; @@ -511,6 +511,7 @@ public class SettingsNewFragment extends MaterialAboutFragment { .setOnChangeAction((isChecked, tag) -> { app.appConfig.registerSyncOnlyWifi = isChecked; app.saveConfig("registerSyncOnlyWifi"); + SyncWorker.Companion.rescheduleNext(app); return true; }); } @@ -532,6 +533,10 @@ public class SettingsNewFragment extends MaterialAboutFragment { syncCardIntervalItem.setChecked(app.appConfig.registerSyncEnabled); syncCardIntervalItem.setOnClickAction(() -> { List intervalNames = new ArrayList<>(); + if (App.devMode) { + intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_seconds, 30)); + intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_minutes, 5)); + } intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_minutes, 30)); intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_minutes, 45)); intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_hours, 1)); @@ -540,6 +545,10 @@ public class SettingsNewFragment extends MaterialAboutFragment { intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_hours, 3)); intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_hours, 4)); List intervals = new ArrayList<>(); + if (App.devMode) { + intervals.add(30); + intervals.add(5 * 60); + } intervals.add(30 * 60); intervals.add(45 * 60); intervals.add(60 * 60); @@ -564,20 +573,12 @@ public class SettingsNewFragment extends MaterialAboutFragment { app.appConfig.registerSyncEnabled = true; // app.appConfig modifications have to surround syncCardIntervalItem and those ifs app.saveConfig("registerSyncInterval", "registerSyncEnabled"); - SyncJob.clear(); - SyncJob.schedule(app); + SyncWorker.Companion.rescheduleNext(app); return true; }) .show(); }); syncCardIntervalItem.setOnChangeAction((isChecked, tag) -> { - if (isChecked) { - SyncJob.update(app); - } - else { - SyncJob.clear(); - } - if (isChecked && !app.appConfig.registerSyncEnabled) { addCardItem(CARD_SYNC, 1, getSyncCardWifiItem()); } @@ -586,6 +587,7 @@ public class SettingsNewFragment extends MaterialAboutFragment { } app.appConfig.registerSyncEnabled = isChecked; app.saveConfig("registerSyncEnabled"); + SyncWorker.Companion.rescheduleNext(app); return true; }); items.add(syncCardIntervalItem); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/Utils.java b/app/src/main/java/pl/szczodrzynski/edziennik/utils/Utils.java index f7bd5c0f..1dadf492 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/Utils.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/Utils.java @@ -14,16 +14,18 @@ import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.MediaStore; -import androidx.annotation.AttrRes; -import androidx.annotation.ColorInt; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import android.util.Base64; import android.util.Log; import android.util.TypedValue; import android.webkit.MimeTypeMap; import android.widget.Toast; +import androidx.annotation.AttrRes; +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.FileProvider; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; @@ -64,8 +66,6 @@ import javax.crypto.ShortBufferException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import androidx.core.content.FileProvider; - import pl.szczodrzynski.edziennik.App; public class Utils { @@ -106,7 +106,7 @@ public class Utils { public static void d(String TAG, String message) { if (App.devMode) { - Log.d(TAG, message); + Log.d("Szkolny/"+TAG, message); //debugLog.add(TAG+": "+message); } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/AppConfig.java b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/AppConfig.java index b2d104e2..d9310ccb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/AppConfig.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/AppConfig.java @@ -2,6 +2,7 @@ package pl.szczodrzynski.edziennik.utils.models; import android.util.Pair; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.util.ArrayList; @@ -115,4 +116,7 @@ public class AppConfig { @Nullable public String language = null; + + @NonNull + public boolean dontShowAppManagerDialog = false; } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/widgets/luckynumber/WidgetLuckyNumber.java b/app/src/main/java/pl/szczodrzynski/edziennik/widgets/luckynumber/WidgetLuckyNumber.java index 60f4dce1..051657c0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/widgets/luckynumber/WidgetLuckyNumber.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/widgets/luckynumber/WidgetLuckyNumber.java @@ -22,11 +22,12 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; +import pl.szczodrzynski.edziennik.R; +import pl.szczodrzynski.edziennik.api.v2.ApiService; +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest; import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; import pl.szczodrzynski.edziennik.utils.models.Date; -import pl.szczodrzynski.edziennik.sync.SyncJob; import pl.szczodrzynski.edziennik.widgets.WidgetConfig; import static pl.szczodrzynski.edziennik.utils.Utils.getCellsForSize; @@ -39,7 +40,7 @@ public class WidgetLuckyNumber extends AppWidgetProvider { @Override public void onReceive(Context context, Intent intent) { if (ACTION_SYNC_DATA.equals(intent.getAction())){ - SyncJob.run((App) context.getApplicationContext()); + ApiService.Companion.startAndRequest(context, new SyncRequest()); } super.onReceive(context, intent); } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/widgets/notifications/WidgetNotifications.java b/app/src/main/java/pl/szczodrzynski/edziennik/widgets/notifications/WidgetNotifications.java index 2cd238a1..05b37adb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/widgets/notifications/WidgetNotifications.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/widgets/notifications/WidgetNotifications.java @@ -22,9 +22,10 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; -import pl.szczodrzynski.edziennik.sync.SyncJob; +import pl.szczodrzynski.edziennik.R; +import pl.szczodrzynski.edziennik.api.v2.ApiService; +import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest; import pl.szczodrzynski.edziennik.widgets.WidgetConfig; public class WidgetNotifications extends AppWidgetProvider { @@ -35,7 +36,7 @@ public class WidgetNotifications extends AppWidgetProvider { @Override public void onReceive(Context context, Intent intent) { if (ACTION_SYNC_DATA.equals(intent.getAction())){ - SyncJob.run((App) context.getApplicationContext()); + ApiService.Companion.startAndRequest(context, new SyncRequest()); } super.onReceive(context, intent); } diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index e0fedecb..cedad80b 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -1,6 +1,5 @@ @@ -31,69 +30,41 @@ android:layout_margin="8dp" tools:text="TextView" /> - - - + android:orientation="vertical" + tools:visibility="visible"> + + - - - - - - - - - + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6cb98a7d..ae409e30 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -978,4 +978,8 @@ Pobieranie planu lekcji... Pobieranie ocen proponowanych... Pobieranie listy sprawdzianów... + Wykryto problem z synchronizacją + Na urządzeniu prawdopodobnie zainstalowany jest menedżer aplikacji, który może powodować problemy z synchronizacją automatyczną.\n\nNależy wyłączyć w ustawieniach telefonu optymalizację baterii dla aplikacji Szkolny.eu.\n\nKliknij OK, aby przejść do ustawień telefonu. + Nie pytaj ponownie + Nie udało się otworzyć ustawień diff --git a/build.gradle b/build.gradle index 5e08c012..4c67499d 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,7 @@ buildscript { room : "2.2.0-beta01", lifecycle : "2.2.0-alpha04", + work : "2.2.0", firebase : '17.2.0', firebasemessaging: "20.0.0", From d789d08f319aa157ededb6c51a78a911500f4841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 3 Nov 2019 16:10:30 +0100 Subject: [PATCH 128/691] [Sync] Fix auto sync when screen is off/device is in Doze mode. --- .../java/pl/szczodrzynski/edziennik/App.java | 2 +- .../szczodrzynski/edziennik/MainActivity.kt | 3 ++- .../edziennik/receivers/BootReceiver.java | 2 +- .../edziennik/sync/SyncWorker.kt | 23 +++++++++++++------ .../modules/settings/SettingsNewFragment.java | 4 ++-- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/App.java b/app/src/main/java/pl/szczodrzynski/edziennik/App.java index 230bce79..b72c713e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/App.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/App.java @@ -310,7 +310,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config //profileLoadById(appSharedPrefs.getInt("current_profile_id", 1)); if (appConfig.registerSyncEnabled) { - SyncWorker.Companion.scheduleNext(this); + SyncWorker.Companion.scheduleNext(this, false); } else { SyncWorker.Companion.cancelNext(this); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt index cc04e8d0..b6a56582 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt @@ -41,6 +41,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.* import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding import pl.szczodrzynski.edziennik.network.ServerRequest import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent +import pl.szczodrzynski.edziennik.sync.SyncWorker import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment import pl.szczodrzynski.edziennik.ui.modules.announcements.AnnouncementsFragment @@ -369,7 +370,7 @@ class MainActivity : AppCompatActivity() { isStoragePermissionGranted() - //SyncWorker.scheduleNext(app) + SyncWorker.scheduleNext(app) // APP BACKGROUND if (app.appConfig.appBackground != null) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/BootReceiver.java b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/BootReceiver.java index b1524237..f1427ff3 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/BootReceiver.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/BootReceiver.java @@ -82,7 +82,7 @@ public class BootReceiver extends BroadcastReceiver { } else { - SyncWorker.Companion.scheduleNext(app); + SyncWorker.Companion.scheduleNext(app, false); if (app.networkUtils.isOnline()) { checkUpdate(context, intent); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncWorker.kt b/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncWorker.kt index 3c0e032e..4405c10b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncWorker.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncWorker.kt @@ -22,7 +22,7 @@ class SyncWorker(val context: Context, val params: WorkerParameters) : Worker(co * Schedule the sync job only if it's not already scheduled. */ @SuppressLint("RestrictedApi") - fun scheduleNext(app: App) { + fun scheduleNext(app: App, rescheduleIfFailedFound: Boolean = true) { AsyncTask.execute { val workManager = WorkManager.getInstance(app) as WorkManagerImpl val scheduledWork = workManager.workDatabase.workSpecDao().scheduledWork @@ -35,13 +35,22 @@ class SyncWorker(val context: Context, val params: WorkerParameters) : Worker(co // remove all enqueued work that had to (but didn't) run at some point in the past (at least 1min ago) val failedWork = scheduledWork.filter { it.state == WorkInfo.State.ENQUEUED && it.periodStartTime+it.initialDelay < System.currentTimeMillis() - 1*MINUTE*1000 } d(TAG, "${failedWork.size} work requests failed to start (out of ${scheduledWork.size} requests)") - if (failedWork.isNotEmpty()) { - d(TAG, "App Manager detected!") - EventBus.getDefault().postSticky(AppManagerDetectedEvent(failedWork.map { it.periodStartTime+it.initialDelay })) + if (rescheduleIfFailedFound) { + if (failedWork.isNotEmpty()) { + d(TAG, "App Manager detected!") + EventBus.getDefault().postSticky(AppManagerDetectedEvent(failedWork.map { it.periodStartTime + it.initialDelay })) + } + if (scheduledWork.size - failedWork.size < 1) { + d(TAG, "No pending work found, scheduling next:") + rescheduleNext(app) + } } - if (scheduledWork.size-failedWork.size < 1) { - d(TAG, "No pending work found, scheduling next:") - rescheduleNext(app) + else { + d(TAG, "NOT rescheduling: waiting to open the activity") + if (scheduledWork.size < 1) { + d(TAG, "No work found *at all*, scheduling next:") + rescheduleNext(app) + } } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java index c442bdc6..9929ec74 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java @@ -535,7 +535,7 @@ public class SettingsNewFragment extends MaterialAboutFragment { List intervalNames = new ArrayList<>(); if (App.devMode) { intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_seconds, 30)); - intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_minutes, 5)); + intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_minutes, 2)); } intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_minutes, 30)); intervalNames.add(HomeFragment.plural(activity, R.plurals.time_till_minutes, 45)); @@ -547,7 +547,7 @@ public class SettingsNewFragment extends MaterialAboutFragment { List intervals = new ArrayList<>(); if (App.devMode) { intervals.add(30); - intervals.add(5 * 60); + intervals.add(2 * 60); } intervals.add(30 * 60); intervals.add(45 * 60); From 22726f85665ece8c9f60afa0ae61e368d810aeff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 3 Nov 2019 21:26:48 +0100 Subject: [PATCH 129/691] [ApiService] Refactor the service, requesting, cancelling. Make compatible with other API tasks. --- .../szczodrzynski/edziennik/MainActivity.kt | 45 +-- .../edziennik/WidgetTimetable.java | 5 +- .../edziennik/api/v2/ApiService.kt | 296 +++++------------- .../edziennik/api/v2/EdziennikNotification.kt | 16 +- .../edziennik/api/v2/EndpointChooser.kt | 3 + ...hedEvent.kt => ApiTaskAllFinishedEvent.kt} | 2 +- ...SyncErrorEvent.kt => ApiTaskErrorEvent.kt} | 2 +- ...nishedEvent.kt => ApiTaskFinishedEvent.kt} | 2 +- .../api/v2/events/ApiTaskProgressEvent.kt | 7 + ...StartedEvent.kt => ApiTaskStartedEvent.kt} | 2 +- .../api/v2/events/SyncProgressEvent.kt | 7 - .../requests/AnnouncementsReadRequest.kt | 13 - .../v2/events/requests/FirstLoginRequest.kt | 10 - .../v2/events/requests/MessageGetRequest.kt | 13 - .../events/requests/SyncProfileListRequest.kt | 7 - .../v2/events/requests/SyncProfileRequest.kt | 13 - .../api/v2/events/requests/SyncRequest.kt | 7 - .../api/v2/events/task/EdziennikTask.kt | 90 ++++++ .../api/v2/events/task/ErrorReportTask.kt | 24 +- .../edziennik/api/v2/events/task/IApiTask.kt | 35 +++ .../api/v2/events/task/NotifyTask.kt | 18 +- .../edziennik/api/v2/idziennik/Idziennik.kt | 11 +- .../api/v2/idziennik/data/IdziennikData.kt | 5 +- .../api/v2/idziennik/login/IdziennikLogin.kt | 1 + .../api/v2/interfaces/EndpointCallback.kt | 2 +- .../edziennik/api/v2/librus/Librus.kt | 11 +- .../api/v2/librus/data/LibrusData.kt | 1 + .../librus/data/api/LibrusApiLuckyNumber.kt | 17 +- .../api/v2/librus/login/LibrusLogin.kt | 1 + .../api/v2/mobidziennik/Mobidziennik.kt | 11 +- .../v2/mobidziennik/MobidziennikFeatures.kt | 9 +- .../v2/mobidziennik/data/MobidziennikData.kt | 1 + .../mobidziennik/login/MobidziennikLogin.kt | 1 + .../edziennik/api/v2/models/ApiError.kt | 6 + .../edziennik/api/v2/models/ApiTask.kt | 9 - .../edziennik/api/v2/models/Data.kt | 11 +- .../edziennik/api/v2/template/Template.kt | 11 +- .../api/v2/template/data/TemplateData.kt | 5 +- .../api/v2/template/login/TemplateLogin.kt | 4 +- .../edziennik/api/v2/vulcan/Vulcan.kt | 11 +- .../api/v2/vulcan/data/VulcanData.kt | 5 +- .../api/v2/vulcan/login/VulcanLogin.kt | 1 + .../data/db/modules/timetable/Lesson.kt | 40 +++ .../edziennik/receivers/SzkolnyReceiver.kt | 7 +- .../sync/MyFirebaseMessagingService.java | 8 +- .../edziennik/sync/SyncWorker.kt | 5 +- .../modules/login/LoginProgressFragment.java | 13 +- .../ui/modules/login/LoginSyncFragment.kt | 28 +- .../ui/modules/messages/MessagesFragment.kt | 70 ++++- .../messages/MessagesListFragment.java | 77 +---- .../modules/settings/SettingsNewFragment.java | 4 +- .../luckynumber/WidgetLuckyNumber.java | 5 +- .../notifications/WidgetNotifications.java | 5 +- app/src/main/res/values/strings.xml | 1 + 54 files changed, 509 insertions(+), 505 deletions(-) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/{SyncFinishedEvent.kt => ApiTaskAllFinishedEvent.kt} (78%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/{SyncErrorEvent.kt => ApiTaskErrorEvent.kt} (78%) rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/{SyncProfileFinishedEvent.kt => ApiTaskFinishedEvent.kt} (67%) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskProgressEvent.kt rename app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/{SyncStartedEvent.kt => ApiTaskStartedEvent.kt} (69%) delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProgressEvent.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/AnnouncementsReadRequest.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/FirstLoginRequest.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/MessageGetRequest.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileListRequest.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileRequest.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncRequest.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/EdziennikTask.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/IApiTask.kt delete mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiTask.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/Lesson.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt index b6a56582..183d00de 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt @@ -6,7 +6,6 @@ import android.content.* import android.graphics.BitmapFactory import android.graphics.drawable.BitmapDrawable import android.os.* -import android.util.Log import android.view.Gravity import android.view.View import android.widget.Toast @@ -34,8 +33,7 @@ import pl.droidsonroids.gif.GifDrawable import pl.szczodrzynski.edziennik.App.APP_URL import pl.szczodrzynski.edziennik.api.v2.ApiService import pl.szczodrzynski.edziennik.api.v2.events.* -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest +import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface.* import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.* import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding @@ -64,6 +62,7 @@ import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch import pl.szczodrzynski.edziennik.utils.Themes import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.dpToPx import pl.szczodrzynski.edziennik.utils.models.NavTarget import pl.szczodrzynski.navlib.* @@ -77,7 +76,7 @@ import pl.szczodrzynski.navlib.drawer.items.withAppTitle import java.io.File import java.io.IOException import java.util.* - +import kotlin.math.roundToInt class MainActivity : AppCompatActivity() { companion object { @@ -499,7 +498,7 @@ class MainActivity : AppCompatActivity() { profileListEmptyListener() } DRAWER_PROFILE_SYNC_ALL -> { - ApiService.startAndRequest(this, SyncRequest()) + EdziennikTask.sync().enqueue(this) } else -> { loadTarget(id) @@ -525,14 +524,14 @@ class MainActivity : AppCompatActivity() { else -> 0 } EventBus.getDefault().postSticky( - SyncProfileRequest( + EdziennikTask.syncProfile( App.profileId, listOf(navTargetId to fragmentParam) ) ) } @Subscribe(threadMode = ThreadMode.MAIN) - fun onSyncStartedEvent(event: SyncStartedEvent) { + fun onSyncStartedEvent(event: ApiTaskStartedEvent) { swipeRefreshLayout.isRefreshing = true if (event.profileId == App.profileId) { navView.toolbar.apply { @@ -543,17 +542,21 @@ class MainActivity : AppCompatActivity() { } } @Subscribe(threadMode = ThreadMode.MAIN) - fun onSyncProgressEvent(event: SyncProgressEvent) { + fun onSyncProgressEvent(event: ApiTaskProgressEvent) { if (event.profileId == App.profileId) { navView.toolbar.apply { subtitleFormat = null subtitleFormatWithUnread = null - subtitle = getString(R.string.toolbar_subtitle_syncing_format, event.progress, event.progressRes?.let { getString(it) } ?: "") + subtitle = if (event.progress < 0f) + event.progressText ?: "" + else + getString(R.string.toolbar_subtitle_syncing_format, event.progress.roundToInt(), event.progressText ?: "") + } } } @Subscribe(threadMode = ThreadMode.MAIN) - fun onSyncProfileFinishedEvent(event: SyncProfileFinishedEvent) { + fun onSyncProfileFinishedEvent(event: ApiTaskFinishedEvent) { if (event.profileId == App.profileId) { navView.toolbar.apply { subtitleFormat = R.string.toolbar_subtitle @@ -563,11 +566,11 @@ class MainActivity : AppCompatActivity() { } } @Subscribe(threadMode = ThreadMode.MAIN) - fun onSyncFinishedEvent(event: SyncFinishedEvent) { + fun onSyncFinishedEvent(event: ApiTaskAllFinishedEvent) { swipeRefreshLayout.isRefreshing = false } @Subscribe(threadMode = ThreadMode.MAIN) - fun onSyncErrorEvent(event: SyncErrorEvent) { + fun onSyncErrorEvent(event: ApiTaskErrorEvent) { } @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) @@ -657,11 +660,11 @@ class MainActivity : AppCompatActivity() { } private fun handleIntent(extras: Bundle?) { - Log.d(TAG, "handleIntent() {") + d(TAG, "handleIntent() {") extras?.keySet()?.forEach { key -> - Log.d(TAG, " \"$key\": "+extras.get(key)) + d(TAG, " \"$key\": "+extras.get(key)) } - Log.d(TAG, "}") + d(TAG, "}") if (extras?.containsKey("reloadProfileId") == true) { val reloadProfileId = extras.getInt("reloadProfileId", -1) @@ -785,7 +788,7 @@ class MainActivity : AppCompatActivity() { fun loadProfile(id: Int) = loadProfile(id, navTargetId) fun loadProfile(id: Int, arguments: Bundle?) = loadProfile(id, navTargetId, arguments) fun loadProfile(id: Int, drawerSelection: Int, arguments: Bundle? = null) { - Log.d("NavDebug", "loadProfile(id = $id, drawerSelection = $drawerSelection)") + d("NavDebug", "loadProfile(id = $id, drawerSelection = $drawerSelection)") if (app.profile != null && App.profileId == id) { drawer.currentProfile = app.profile.id loadTarget(drawerSelection, arguments) @@ -825,7 +828,7 @@ class MainActivity : AppCompatActivity() { } } private fun loadTarget(target: NavTarget, arguments: Bundle? = null) { - Log.d("NavDebug", "loadItem(id = ${target.id})") + d("NavDebug", "loadItem(id = ${target.id})") bottomSheet.close() bottomSheet.removeAllContextual() @@ -838,7 +841,7 @@ class MainActivity : AppCompatActivity() { navView.bottomBar.fabExtended = false navView.bottomBar.setFabOnClickListener(null) - Log.d("NavDebug", "Navigating from ${navTarget.fragmentClass?.java?.simpleName} to ${target.fragmentClass?.java?.simpleName}") + d("NavDebug", "Navigating from ${navTarget.fragmentClass?.java?.simpleName} to ${target.fragmentClass?.java?.simpleName}") val fragment = target.fragmentClass?.java?.newInstance() ?: return fragment.arguments = arguments @@ -897,9 +900,9 @@ class MainActivity : AppCompatActivity() { } } - Log.d("NavDebug", "Current fragment ${navTarget.fragmentClass?.java?.simpleName}, pop to home ${navTarget.popToHome}, back stack:") + d("NavDebug", "Current fragment ${navTarget.fragmentClass?.java?.simpleName}, pop to home ${navTarget.popToHome}, back stack:") navBackStack.forEachIndexed { index, target2 -> - Log.d("NavDebug", " - $index: ${target2.fragmentClass?.java?.simpleName}") + d("NavDebug", " - $index: ${target2.fragmentClass?.java?.simpleName}") } transaction.replace(R.id.fragment, fragment) @@ -990,7 +993,7 @@ class MainActivity : AppCompatActivity() { } fun setDrawerItems() { - Log.d("NavDebug", "setDrawerItems() app.profile = ${app.profile ?: "null"}") + d("NavDebug", "setDrawerItems() app.profile = ${app.profile ?: "null"}") val drawerItems = arrayListOf>() val drawerProfiles = arrayListOf() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.java b/app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.java index 7a6875d8..7dca917e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/WidgetTimetable.java @@ -28,8 +28,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; -import pl.szczodrzynski.edziennik.api.v2.ApiService; -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest; +import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask; import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull; import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange; import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull; @@ -67,7 +66,7 @@ public class WidgetTimetable extends AppWidgetProvider { @Override public void onReceive(Context context, Intent intent) { if (ACTION_SYNC_DATA.equals(intent.getAction())) { - ApiService.Companion.startAndRequest(context, new SyncRequest()); + EdziennikTask.Companion.sync().enqueue(context); } super.onReceive(context, intent); } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt index d2d39796..66ea5b1e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt @@ -8,29 +8,22 @@ import android.app.Service import android.content.Context import android.content.Intent import android.os.IBinder -import android.util.Log import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.events.* -import pl.szczodrzynski.edziennik.api.v2.events.requests.* +import pl.szczodrzynski.edziennik.api.v2.events.requests.ServiceCloseRequest +import pl.szczodrzynski.edziennik.api.v2.events.requests.TaskCancelRequest +import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask import pl.szczodrzynski.edziennik.api.v2.events.task.ErrorReportTask +import pl.szczodrzynski.edziennik.api.v2.events.task.IApiTask import pl.szczodrzynski.edziennik.api.v2.events.task.NotifyTask -import pl.szczodrzynski.edziennik.api.v2.idziennik.Idziennik import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback -import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface -import pl.szczodrzynski.edziennik.api.v2.librus.Librus -import pl.szczodrzynski.edziennik.api.v2.mobidziennik.Mobidziennik import pl.szczodrzynski.edziennik.api.v2.models.ApiError -import pl.szczodrzynski.edziennik.api.v2.models.ApiTask -import pl.szczodrzynski.edziennik.api.v2.template.Template -import pl.szczodrzynski.edziennik.api.v2.vulcan.Vulcan -import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore -import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile -import kotlin.math.max +import pl.szczodrzynski.edziennik.utils.Utils.d import kotlin.math.min +import kotlin.math.roundToInt class ApiService : Service() { companion object { @@ -47,23 +40,23 @@ class ApiService : Service() { private val app by lazy { applicationContext as App } - private val taskQueue = mutableListOf() + private val finishingTaskQueue = mutableListOf( + NotifyTask(), + ErrorReportTask() + ) + private val taskQueue = mutableListOf() private val errorList = mutableListOf() - private var queueHasErrorReportTask = false - private var queueHasNotifyTask = false private var serviceClosed = false private var taskCancelled = false - private var taskRunningObject: ApiTask? = null // for debug purposes - private var taskRunning = false + private var taskIsRunning = false + private var taskRunning: IApiTask? = null // for debug purposes private var taskRunningId = -1 private var taskMaximumId = 0 - private var edziennikInterface: EdziennikInterface? = null private var taskProfileId = -1 - private var taskProfileName: String? = null - private var taskProgress = 0 - private var taskProgressRes: Int? = null + private var taskProgress = -1f + private var taskProgressText: String? = null private val notification by lazy { EdziennikNotification(this) } @@ -75,74 +68,56 @@ class ApiService : Service() { |______\__,_/___|_|\___|_| |_|_| |_|_|_|\_\ \_____\__,_|_|_|_.__/ \__,_|\___|_|\*/ private val taskCallback = object : EdziennikCallback { override fun onCompleted() { - edziennikInterface = null - if (taskRunningObject is SyncProfileRequest) { - // post an event if this task is a sync, not e.g. first login or message getting - if (!taskCancelled) { - EventBus.getDefault().post(SyncProfileFinishedEvent(taskProfileId)) - } - // add a notifying task to create data notifications of this profile - addNotifyTask() - } - notification.setIdle().post() - taskRunningObject = null - taskRunning = false + d(TAG, "Task $taskRunningId (profile $taskProfileId) - $taskProgressText - finished") + //if (!taskCancelled) { + EventBus.getDefault().post(ApiTaskFinishedEvent(taskProfileId)) + //} + taskIsRunning = false taskRunningId = -1 - sync() + taskRunning = null + taskProfileId = -1 + taskProgress = -1f + taskProgressText = null + + notification.setIdle().post() + runTask() } override fun onError(apiError: ApiError) { - if (!queueHasErrorReportTask) { - queueHasErrorReportTask = true - taskQueue += ErrorReportTask().apply { - taskId = ++taskMaximumId - } - } + d(TAG, "Task $taskRunningId threw an error - $apiError") apiError.profileId = taskProfileId - EventBus.getDefault().post(SyncErrorEvent(apiError)) + EventBus.getDefault().post(ApiTaskErrorEvent(apiError)) errorList.add(apiError) apiError.throwable?.printStackTrace() if (apiError.isCritical) { - // if this error ends the sync, post an error notification - // if this is a sync task, create a notifying task - if (taskRunningObject is SyncProfileRequest) { - // add a notifying task to create data notifications of this profile - addNotifyTask() - } notification.setCriticalError().post() - taskRunningObject = null - taskRunning = false + taskRunning = null + taskIsRunning = false taskRunningId = -1 - sync() + runTask() } else { notification.addError().post() } } - override fun onProgress(step: Int) { + override fun onProgress(step: Float) { + if (step <= 0) + return + if (taskProgress < 0) + taskProgress = 0f taskProgress += step - taskProgress = min(100, taskProgress) - EventBus.getDefault().post(SyncProgressEvent(taskProfileId, taskProfileName, taskProgress, taskProgressRes)) + taskProgress = min(100f, taskProgress) + d(TAG, "Task $taskRunningId progress: ${taskProgress.roundToInt()}%") + EventBus.getDefault().post(ApiTaskProgressEvent(taskProfileId, taskProgress, taskProgressText)) notification.setProgress(taskProgress).post() } override fun onStartProgress(stringRes: Int) { - taskProgressRes = stringRes - EventBus.getDefault().post(SyncProgressEvent(taskProfileId, taskProfileName, taskProgress, taskProgressRes)) - notification.setProgressRes(taskProgressRes!!).post() - } - - fun addNotifyTask() { - if (!queueHasNotifyTask) { - queueHasNotifyTask = true - taskQueue.add( - if (queueHasErrorReportTask) max(taskQueue.size-1, 0) else taskQueue.size, - NotifyTask().apply { - taskId = ++taskMaximumId - } - ) - } + taskProgressText = getString(stringRes) + d(TAG, "Task $taskRunningId progress: $taskProgressText") + EventBus.getDefault().post(ApiTaskProgressEvent(taskProfileId, taskProgress, taskProgressText)) + notification.setProgressText(taskProgressText).post() } } @@ -152,98 +127,42 @@ class ApiService : Service() { | |/ _` / __| |/ / / _ \ \/ / _ \/ __| | | | __| |/ _ \| '_ \ | | (_| \__ \ < | __/> < __/ (__| |_| | |_| | (_) | | | | |_|\__,_|___/_|\_\ \___/_/\_\___|\___|\__,_|\__|_|\___/|_| |*/ - private fun sync() { - if (taskRunning) + private fun runTask() { + if (taskIsRunning) return - if (taskQueue.size <= 0 || serviceClosed) { + if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && finishingTaskQueue.isEmpty())) { serviceClosed = false allCompleted() return } - val task = taskQueue.removeAt(0) - taskCancelled = false - taskRunning = true + val task = if (taskQueue.isEmpty()) finishingTaskQueue.removeAt(0) else taskQueue.removeAt(0) + task.taskId = ++taskMaximumId + task.prepare(app) + taskIsRunning = true taskRunningId = task.taskId - taskRunningObject = task + taskRunning = task + taskProfileId = task.profileId + taskProgress = -1f + taskProgressText = task.taskName - if (task is ErrorReportTask) { - queueHasErrorReportTask = false - task.run(notification, errorList) - taskRunningObject = null - taskRunning = false - taskRunningId = -1 - sync() - return - } - - if (task is NotifyTask) { - queueHasNotifyTask = false - task.run(app) - taskRunningObject = null - taskRunning = false - taskRunningId = -1 - sync() - return - } - - val profile: Profile? - val loginStore: LoginStore - if (task is FirstLoginRequest) { - // get the requested profile and login store - profile = null - loginStore = task.loginStore - // save the profile ID and name as the current task's - taskProfileId = -1 - taskProfileName = getString(R.string.edziennik_notification_api_first_login_title) - } - else { - // get the requested profile and login store - profile = app.db.profileDao().getByIdNow(task.profileId) - if (profile == null || !profile.syncEnabled) { - return - } - loginStore = app.db.loginStoreDao().getByIdNow(profile.loginStoreId) - if (loginStore == null) { - return - } - // save the profile ID and name as the current task's - taskProfileId = profile.id - taskProfileName = profile.name - } - taskProgress = 0 - taskProgressRes = null + d(TAG, "Executing task $taskRunningId ($taskProgressText) - $task") // update the notification - notification.setCurrentTask(taskRunningId, taskProfileName).post() + notification.setCurrentTask(taskRunningId, taskProgressText).post() // post an event - EventBus.getDefault().post(SyncStartedEvent(taskProfileId, profile)) - - edziennikInterface = when (loginStore.type) { - LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback) - LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback) - LOGIN_TYPE_VULCAN -> Vulcan(app, profile, loginStore, taskCallback) - LOGIN_TYPE_IDZIENNIK -> Idziennik(app, profile, loginStore, taskCallback) - LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback) - else -> null - } - if (edziennikInterface == null) { - return - } + EventBus.getDefault().post(ApiTaskStartedEvent(taskProfileId, task.profile)) when (task) { - is SyncProfileRequest -> edziennikInterface?.sync( - featureIds = task.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) } ?: Features.getAllIds(), - viewId = task.viewIds?.get(0)?.first) - is MessageGetRequest -> edziennikInterface?.getMessage(task.messageId) - is FirstLoginRequest -> edziennikInterface?.firstLogin() - is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead() + is EdziennikTask -> task.run(app, taskCallback) + is NotifyTask -> task.run(app, taskCallback) + is ErrorReportTask -> task.run(app, taskCallback, notification, errorList) } } private fun allCompleted() { - EventBus.getDefault().post(SyncFinishedEvent()) + EventBus.getDefault().post(ApiTaskAllFinishedEvent()) stopSelf() } @@ -254,91 +173,50 @@ class ApiService : Service() { | |___\ V / __/ | | | |_| |_) | |_| \__ \ |______\_/ \___|_| |_|\__|____/ \__,_|__*/ @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) - fun onSyncRequest(request: SyncRequest) { - EventBus.getDefault().removeStickyEvent(request) - Log.d(TAG, request.toString()) + fun onApiTask(task: IApiTask) { + EventBus.getDefault().removeStickyEvent(task) + d(TAG, task.toString()) - app.db.profileDao().idsForSyncNow.forEach { id -> - taskQueue += SyncProfileRequest(id, null).apply { - taskId = ++taskMaximumId + if (task is EdziennikTask) { + when (task.request) { + is EdziennikTask.SyncRequest -> app.db.profileDao().idsForSyncNow.forEach { + taskQueue += EdziennikTask.syncProfile(it) + } + is EdziennikTask.SyncProfileListRequest -> task.request.profileList.forEach { + taskQueue += EdziennikTask.syncProfile(it) + } + else -> { + taskQueue += task + } } } - sync() - } - - @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) - fun onSyncProfileListRequest(request: SyncProfileListRequest) { - EventBus.getDefault().removeStickyEvent(request) - Log.d(TAG, request.toString()) - - request.profileList.forEach { id -> - taskQueue += SyncProfileRequest(id, null).apply { - taskId = ++taskMaximumId - } + else { + taskQueue += task } - sync() - } - - @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) - fun onSyncProfileRequest(request: SyncProfileRequest) { - EventBus.getDefault().removeStickyEvent(request) - Log.d(TAG, request.toString()) - - taskQueue += request.apply { - taskId = ++taskMaximumId + d(TAG, "EventBus received an IApiTask: $task") + d(TAG, "Current queue:") + taskQueue.forEach { + d(TAG, " - $it") } - sync() - } - - @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) - fun onMessageGetRequest(request: MessageGetRequest) { - EventBus.getDefault().removeStickyEvent(request) - Log.d(TAG, request.toString()) - - taskQueue += request.apply { - taskId = ++taskMaximumId - } - sync() - } - - @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) - fun onAnnouncementsReadRequest(request: AnnouncementsReadRequest) { - EventBus.getDefault().removeStickyEvent(request) - Log.d(TAG, request.toString()) - - taskQueue += request.apply { - taskId = ++taskMaximumId - } - sync() - } - - @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) - fun onFirstLoginRequest(request: FirstLoginRequest) { - EventBus.getDefault().removeStickyEvent(request) - Log.d(TAG, request.toString()) - - taskQueue += request.apply { - taskId = ++taskMaximumId - } - sync() + runTask() } @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) fun onTaskCancelRequest(request: TaskCancelRequest) { EventBus.getDefault().removeStickyEvent(request) - Log.d(TAG, request.toString()) + d(TAG, request.toString()) taskCancelled = true - edziennikInterface?.cancel() + taskRunning?.cancel() } @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) fun onServiceCloseRequest(request: ServiceCloseRequest) { EventBus.getDefault().removeStickyEvent(request) - Log.d(TAG, request.toString()) + d(TAG, request.toString()) serviceClosed = true taskCancelled = true - edziennikInterface?.cancel() + taskRunning?.cancel() stopSelf() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EdziennikNotification.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EdziennikNotification.kt index eeeb1968..2db9ecf1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EdziennikNotification.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EdziennikNotification.kt @@ -10,9 +10,9 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationCompat.PRIORITY_LOW import androidx.core.app.NotificationCompat.PRIORITY_MIN import pl.szczodrzynski.edziennik.R +import kotlin.math.roundToInt class EdziennikNotification(val context: Context) { @@ -92,18 +92,18 @@ class EdziennikNotification(val context: Context) { return this } - fun setProgress(progress: Int): EdziennikNotification { - notificationBuilder.setProgress(100, progress, false) + fun setProgress(progress: Float): EdziennikNotification { + notificationBuilder.setProgress(100, progress.roundToInt(), progress < 0f) return this } - fun setProgressRes(progressRes: Int): EdziennikNotification { - notificationBuilder.setContentTitle(context.getString(progressRes)) + fun setProgressText(progressText: String?): EdziennikNotification { + notificationBuilder.setContentTitle(progressText) return this } - fun setCurrentTask(taskId: Int, profileName: String?): EdziennikNotification { - notificationBuilder.setProgress(100, 0, false) - notificationBuilder.setContentTitle(context.getString(R.string.edziennik_notification_api_sync_title_format, profileName)) + fun setCurrentTask(taskId: Int, progressText: String?): EdziennikNotification { + notificationBuilder.setProgress(100, 0, true) + notificationBuilder.setContentTitle(progressText) notificationBuilder.apply { val str = errorCountText() setStyle(NotificationCompat.BigTextStyle().bigText(str)) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EndpointChooser.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EndpointChooser.kt index 9a8a5bed..c4a97f98 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EndpointChooser.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/EndpointChooser.kt @@ -77,4 +77,7 @@ fun Data.prepare(loginMethods: List, features: List, featu data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList() data.targetEndpointIds.sort() + + progressCount = targetLoginMethodIds.size + targetEndpointIds.size + progressStep = if (progressCount <= 0) 0f else 100f / progressCount.toFloat() } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncFinishedEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskAllFinishedEvent.kt similarity index 78% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncFinishedEvent.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskAllFinishedEvent.kt index 14b6f682..1bef5708 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncFinishedEvent.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskAllFinishedEvent.kt @@ -4,4 +4,4 @@ package pl.szczodrzynski.edziennik.api.v2.events -class SyncFinishedEvent \ No newline at end of file +class ApiTaskAllFinishedEvent \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncErrorEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskErrorEvent.kt similarity index 78% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncErrorEvent.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskErrorEvent.kt index 27dcff2c..5b76dfe5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncErrorEvent.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskErrorEvent.kt @@ -6,4 +6,4 @@ package pl.szczodrzynski.edziennik.api.v2.events import pl.szczodrzynski.edziennik.api.v2.models.ApiError -class SyncErrorEvent(val error: ApiError) \ No newline at end of file +class ApiTaskErrorEvent(val error: ApiError) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProfileFinishedEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskFinishedEvent.kt similarity index 67% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProfileFinishedEvent.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskFinishedEvent.kt index 087b97f6..546fe3ae 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProfileFinishedEvent.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskFinishedEvent.kt @@ -4,4 +4,4 @@ package pl.szczodrzynski.edziennik.api.v2.events -class SyncProfileFinishedEvent(val profileId: Int) \ No newline at end of file +class ApiTaskFinishedEvent(val profileId: Int) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskProgressEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskProgressEvent.kt new file mode 100644 index 00000000..e639a0ac --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskProgressEvent.kt @@ -0,0 +1,7 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.events + +class ApiTaskProgressEvent(val profileId: Int, val progress: Float, val progressText: String?) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncStartedEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskStartedEvent.kt similarity index 69% rename from app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncStartedEvent.kt rename to app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskStartedEvent.kt index 471dc413..b8af894e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncStartedEvent.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/ApiTaskStartedEvent.kt @@ -6,4 +6,4 @@ package pl.szczodrzynski.edziennik.api.v2.events import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile -class SyncStartedEvent(val profileId: Int, val profile: Profile? = null) \ No newline at end of file +class ApiTaskStartedEvent(val profileId: Int, val profile: Profile? = null) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProgressEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProgressEvent.kt deleted file mode 100644 index 83cf57d1..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/SyncProgressEvent.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2019-9-28. - */ - -package pl.szczodrzynski.edziennik.api.v2.events - -class SyncProgressEvent(val profileId: Int, val profileName: String?, val progress: Int, val progressRes: Int?) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/AnnouncementsReadRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/AnnouncementsReadRequest.kt deleted file mode 100644 index 60e35840..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/AnnouncementsReadRequest.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) Kacper Ziubryniewicz 2019-10-26 - */ - -package pl.szczodrzynski.edziennik.api.v2.events.requests - -import pl.szczodrzynski.edziennik.api.v2.models.ApiTask - -data class AnnouncementsReadRequest(override val profileId: Int) : ApiTask(profileId) { - override fun toString(): String { - return "AnnouncementsReadRequest(profileId=$profileId)" - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/FirstLoginRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/FirstLoginRequest.kt deleted file mode 100644 index 75e92793..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/FirstLoginRequest.kt +++ /dev/null @@ -1,10 +0,0 @@ -package pl.szczodrzynski.edziennik.api.v2.events.requests - -import pl.szczodrzynski.edziennik.api.v2.models.ApiTask -import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore - -data class FirstLoginRequest(val loginStore: LoginStore) : ApiTask(-1) { - override fun toString(): String { - return "FirstLoginRequest(loginStore=$loginStore)" - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/MessageGetRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/MessageGetRequest.kt deleted file mode 100644 index 50527290..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/MessageGetRequest.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2019-9-28. - */ - -package pl.szczodrzynski.edziennik.api.v2.events.requests - -import pl.szczodrzynski.edziennik.api.v2.models.ApiTask - -data class MessageGetRequest(override val profileId: Int, val messageId: Int) : ApiTask(profileId) { - override fun toString(): String { - return "MessageGetRequest(profileId=$profileId, messageId=$messageId)" - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileListRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileListRequest.kt deleted file mode 100644 index 9daaac63..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileListRequest.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2019-10-23. - */ - -package pl.szczodrzynski.edziennik.api.v2.events.requests - -class SyncProfileListRequest(val profileList: List) \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileRequest.kt deleted file mode 100644 index 34247a6a..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncProfileRequest.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2019-9-28. - */ - -package pl.szczodrzynski.edziennik.api.v2.events.requests - -import pl.szczodrzynski.edziennik.api.v2.models.ApiTask - -data class SyncProfileRequest(override val profileId: Int, val viewIds: List>? = null) : ApiTask(profileId) { - override fun toString(): String { - return "SyncProfileRequest(profileId=$profileId, viewIds=$viewIds)" - } -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncRequest.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncRequest.kt deleted file mode 100644 index ef42a557..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/requests/SyncRequest.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2019-9-28. - */ - -package pl.szczodrzynski.edziennik.api.v2.events.requests - -class SyncRequest \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/EdziennikTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/EdziennikTask.kt new file mode 100644 index 00000000..0e8e945b --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/EdziennikTask.kt @@ -0,0 +1,90 @@ +package pl.szczodrzynski.edziennik.api.v2.events.task + +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.idziennik.Idziennik +import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback +import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface +import pl.szczodrzynski.edziennik.api.v2.librus.Librus +import pl.szczodrzynski.edziennik.api.v2.mobidziennik.Mobidziennik +import pl.szczodrzynski.edziennik.api.v2.template.Template +import pl.szczodrzynski.edziennik.api.v2.vulcan.Vulcan +import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore + +open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) { + companion object { + private const val TAG = "EdziennikTask" + + fun firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore)) + fun sync() = EdziennikTask(-1, SyncRequest()) + fun syncProfile(profileId: Int, viewIds: List>? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds)) + fun syncProfileList(profileList: List) = EdziennikTask(-1, SyncProfileListRequest(profileList)) + fun messageGet(profileId: Int, messageId: Int) = EdziennikTask(profileId, MessageGetRequest(messageId)) + fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest()) + } + + private lateinit var loginStore: LoginStore + + override fun prepare(app: App) { + if (request is FirstLoginRequest) { + // get the requested profile and login store + this.profile = null + loginStore = request.loginStore + // save the profile ID and name as the current task's + taskName = app.getString(R.string.edziennik_notification_api_first_login_title) + } + else { + // get the requested profile and login store + val profile = app.db.profileDao().getByIdNow(profileId) + this.profile = profile + if (profile == null || !profile.syncEnabled) { + return + } + val loginStore = app.db.loginStoreDao().getByIdNow(profile.loginStoreId) ?: return + this.loginStore = loginStore + // save the profile ID and name as the current task's + taskName = app.getString(R.string.edziennik_notification_api_sync_title_format, profile.name) + } + } + + private var edziennikInterface: EdziennikInterface? = null + + fun run(app: App, taskCallback: EdziennikCallback) { + edziennikInterface = when (loginStore.type) { + LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback) + LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback) + LOGIN_TYPE_VULCAN -> Vulcan(app, profile, loginStore, taskCallback) + LOGIN_TYPE_IDZIENNIK -> Idziennik(app, profile, loginStore, taskCallback) + LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback) + else -> null + } + if (edziennikInterface == null) { + return + } + + when (request) { + is SyncProfileRequest -> edziennikInterface?.sync( + featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) } ?: Features.getAllIds(), + viewId = request.viewIds?.get(0)?.first) + is MessageGetRequest -> edziennikInterface?.getMessage(request.messageId) + is FirstLoginRequest -> edziennikInterface?.firstLogin() + is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead() + } + } + + override fun cancel() { + edziennikInterface?.cancel() + } + + override fun toString(): String { + return "EdziennikTask(profileId=$profileId, request=$request, edziennikInterface=$edziennikInterface)" + } + + data class FirstLoginRequest(val loginStore: LoginStore) + class SyncRequest + data class SyncProfileRequest(val viewIds: List>? = null) + data class SyncProfileListRequest(val profileList: List) + data class MessageGetRequest(val messageId: Int) + class AnnouncementsReadRequest +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/ErrorReportTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/ErrorReportTask.kt index aa1a52cf..3643d544 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/ErrorReportTask.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/ErrorReportTask.kt @@ -4,23 +4,31 @@ package pl.szczodrzynski.edziennik.api.v2.events.task +import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.ApiService import pl.szczodrzynski.edziennik.api.v2.EdziennikNotification +import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.models.ApiError -import pl.szczodrzynski.edziennik.api.v2.models.ApiTask import pl.szczodrzynski.edziennik.utils.Utils -class ErrorReportTask : ApiTask(-1) { - fun run(notification: EdziennikNotification, errorList: MutableList) { - notification - .setCurrentTask(taskId, null) - .setProgressRes(R.string.edziennik_notification_api_error_report_title) - .post() +class ErrorReportTask : IApiTask(-1) { + override fun prepare(app: App) { + taskName = app.getString(R.string.edziennik_notification_api_error_report_title) + } + + override fun cancel() { + + } + + fun run(app: App, taskCallback: EdziennikCallback, notification: EdziennikNotification, errorList: MutableList) { errorList.forEach { error -> Utils.d(ApiService.TAG, "Error ${error.tag} profile ${error.profileId}: code ${error.errorCode}") } errorList.clear() - notification.setIdle().post() + + taskCallback.onCompleted() } + + } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/IApiTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/IApiTask.kt new file mode 100644 index 00000000..0deab23e --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/IApiTask.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-9-28. + */ + +package pl.szczodrzynski.edziennik.api.v2.events.task + +import android.content.Context +import android.content.Intent +import org.greenrobot.eventbus.EventBus +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.api.v2.ApiService +import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile + +abstract class IApiTask(open val profileId: Int) { + var taskId: Int = 0 + var profile: Profile? = null + var taskName: String? = null + + /** + * A method called before running the task. + * It is synchronous and its main task is + * to prepare the correct task name. + */ + abstract fun prepare(app: App) + abstract fun cancel() + + fun enqueue(context: Context) { + context.startService(Intent(context, ApiService::class.java)) + EventBus.getDefault().postSticky(this) + } + + override fun toString(): String { + return "IApiTask(profileId=$profileId, taskId=$taskId, profile=$profile, taskName=$taskName)" + } +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/NotifyTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/NotifyTask.kt index 753040a0..edd7a6fa 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/NotifyTask.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/NotifyTask.kt @@ -8,14 +8,22 @@ import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.Notifier.ID_NOTIFICATIONS import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.api.v2.models.ApiTask +import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.utils.models.Notification import kotlin.math.min -class NotifyTask : ApiTask(-1) { - fun run(app: App) { +class NotifyTask : IApiTask(-1) { + override fun prepare(app: App) { + taskName = app.getString(R.string.edziennik_notification_api_notify_title) + } + + override fun cancel() { + + } + + fun run(app: App, taskCallback: EdziennikCallback) { val list = app.db.notificationDao().getNotPostedNow() - val notificationList = list.subList(0, min(8, list.size)) + val notificationList = list.subList(0, min(15, list.size)) var unreadCount = list.size @@ -75,5 +83,7 @@ class NotifyTask : ApiTask(-1) { } app.db.notificationDao().setAllPosted() + + taskCallback.onCompleted() } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt index 2a09756f..03b1bbf0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt @@ -4,7 +4,6 @@ package pl.szczodrzynski.edziennik.api.v2.idziennik -import android.util.Log import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikData @@ -17,7 +16,7 @@ import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.prepare import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile -import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.Utils.d class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { companion object { @@ -51,8 +50,8 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, |__*/ override fun sync(featureIds: List, viewId: Int?) { data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId) - Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") - Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") + d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") + d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") IdziennikLogin(data) { IdziennikData(data) { completed() @@ -75,7 +74,7 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, } override fun cancel() { - Utils.d(TAG, "Cancelled") + d(TAG, "Cancelled") data.cancel() } @@ -85,7 +84,7 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, callback.onCompleted() } - override fun onProgress(step: Int) { + override fun onProgress(step: Float) { callback.onProgress(step) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt index a3de93a7..c1ab81f3 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt @@ -17,8 +17,6 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) { private const val TAG = "IdziennikData" } - private var cancelled = false - init { nextEndpoint(onSuccess) } @@ -28,11 +26,12 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) { onSuccess() return } - if (cancelled) { + if (data.cancelled) { onSuccess() return } useEndpoint(data.targetEndpointIds.removeAt(0)) { + data.progress(data.progressStep) nextEndpoint(onSuccess) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLogin.kt index be79e3e3..ff6adb6e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLogin.kt @@ -31,6 +31,7 @@ class IdziennikLogin(val data: DataIdziennik, val onSuccess: () -> Unit) { return } useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId -> + data.progress(data.progressStep) if (usedMethodId != -1) data.loginMethods.add(usedMethodId) nextLoginMethod(onSuccess) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EndpointCallback.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EndpointCallback.kt index a4449fde..daddcb9a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EndpointCallback.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EndpointCallback.kt @@ -13,6 +13,6 @@ import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod */ interface EndpointCallback { fun onError(apiError: ApiError) - fun onProgress(step: Int) + fun onProgress(step: Float) fun onStartProgress(stringRes: Int) } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index dbd99c5f..937fe1a6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -4,7 +4,6 @@ package pl.szczodrzynski.edziennik.api.v2.librus -import android.util.Log import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback @@ -20,7 +19,7 @@ import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.prepare import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile -import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.Utils.d class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { companion object { @@ -54,8 +53,8 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va |__*/ override fun sync(featureIds: List, viewId: Int?) { data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId) - Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") - Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") + d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") + d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") LibrusLogin(data) { LibrusData(data) { completed() @@ -84,7 +83,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va } override fun cancel() { - Utils.d(TAG, "Cancelled") + d(TAG, "Cancelled") data.cancel() } @@ -94,7 +93,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va callback.onCompleted() } - override fun onProgress(step: Int) { + override fun onProgress(step: Float) { callback.onProgress(step) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index 157377f6..ef811453 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -32,6 +32,7 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { return } useEndpoint(data.targetEndpointIds.removeAt(0)) { + data.progress(data.progressStep) nextEndpoint(onSuccess) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt index c7289b88..93579a02 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiLuckyNumber.kt @@ -4,16 +4,17 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api +import pl.szczodrzynski.edziennik.DAY import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_LUCKY_NUMBER import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi -import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.luckynumber.LuckyNumber import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.getInt import pl.szczodrzynski.edziennik.getJsonObject import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Time class LibrusApiLuckyNumber(override val data: DataLibrus, val onSuccess: () -> Unit) : LibrusApi(data) { @@ -25,18 +26,26 @@ class LibrusApiLuckyNumber(override val data: DataLibrus, data.profile?.luckyNumber = -1 data.profile?.luckyNumberDate = null + var nextSync = System.currentTimeMillis() + 2*DAY*1000 + apiGet(TAG, "LuckyNumbers") { json -> if (json.isJsonNull) { //profile?.luckyNumberEnabled = false } else { json.getJsonObject("LuckyNumber")?.also { luckyNumberEl -> + val luckyNumberDate = Date.fromY_m_d(luckyNumberEl.getString("LuckyNumberDay")) ?: Date.getToday() + val luckyNumber = luckyNumberEl.getInt("LuckyNumber") ?: -1 val luckyNumberObject = LuckyNumber( profileId, - Date.fromY_m_d(luckyNumberEl.getString("LuckyNumberDay")) ?: Date.getToday(), - luckyNumberEl.getInt("LuckyNumber") ?: -1 + luckyNumberDate, + luckyNumber ) + //if (luckyNumberDate > Date.getToday()) { + nextSync = luckyNumberDate.combineWith(Time(15, 0, 0)) + //} + data.luckyNumberList.add(luckyNumberObject) data.metadataList.add( Metadata( @@ -50,7 +59,7 @@ class LibrusApiLuckyNumber(override val data: DataLibrus, } } - data.setSyncNext(ENDPOINT_LIBRUS_API_LUCKY_NUMBER, SYNC_ALWAYS) + data.setSyncNext(ENDPOINT_LIBRUS_API_LUCKY_NUMBER, syncAt = nextSync) onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLogin.kt index 6cc54567..cfcd6fd6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLogin.kt @@ -33,6 +33,7 @@ class LibrusLogin(val data: DataLibrus, val onSuccess: () -> Unit) { return } useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId -> + data.progress(data.progressStep) if (usedMethodId != -1) data.loginMethods.add(usedMethodId) nextLoginMethod(onSuccess) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt index 7656c921..337da3b2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt @@ -4,7 +4,6 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik -import android.util.Log import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback @@ -17,7 +16,7 @@ import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.prepare import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile -import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.Utils.d class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { companion object { @@ -51,8 +50,8 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto |__*/ override fun sync(featureIds: List, viewId: Int?) { data.prepare(mobidziennikLoginMethods, MobidziennikFeatures, featureIds, viewId) - Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") - Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") + d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") + d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") MobidziennikLogin(data) { MobidziennikData(data) { completed() @@ -75,7 +74,7 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto } override fun cancel() { - Utils.d(TAG, "Cancelled") + d(TAG, "Cancelled") data.cancel() } @@ -85,7 +84,7 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto callback.onCompleted() } - override fun onProgress(step: Int) { + override fun onProgress(step: Float) { callback.onProgress(step) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt index 788ad4af..057ce2b9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt @@ -5,7 +5,6 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik import pl.szczodrzynski.edziennik.api.v2.* -import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_PUSH_CONFIG import pl.szczodrzynski.edziennik.api.v2.models.Feature const val ENDPOINT_MOBIDZIENNIK_API_MAIN = 1000 @@ -22,17 +21,17 @@ const val ENDPOINT_MOBIDZIENNIK_API2_MAIN = 3000 val MobidziennikFeatures = listOf( // always synced - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ALWAYS_NEEDED, listOf( + /*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ALWAYS_NEEDED, listOf( ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB, ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), + ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),*/ // push config - Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_PUSH_CONFIG, listOf( + /*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_PUSH_CONFIG, listOf( ENDPOINT_MOBIDZIENNIK_API2_MAIN to LOGIN_METHOD_MOBIDZIENNIK_API2 ), listOf(LOGIN_METHOD_MOBIDZIENNIK_API2)).withShouldSync { data -> data.app.appConfig.fcmTokens[LOGIN_TYPE_MOBIDZIENNIK]?.second?.contains(data.profileId) == false - }, + },*/ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt index 553c1e0d..e287d1db 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt @@ -32,6 +32,7 @@ class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) { return } useEndpoint(data.targetEndpointIds.removeAt(0)) { + data.progress(data.progressStep) nextEndpoint(onSuccess) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLogin.kt index e6b17e1a..95e6cd95 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/login/MobidziennikLogin.kt @@ -31,6 +31,7 @@ class MobidziennikLogin(val data: DataMobidziennik, val onSuccess: () -> Unit) { return } useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId -> + data.progress(data.progressStep) if (usedMethodId != -1) data.loginMethods.add(usedMethodId) nextLoginMethod(onSuccess) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt index 0f5a484e..d819eac3 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiError.kt @@ -51,4 +51,10 @@ class ApiError(val tag: String, val errorCode: Int) { errorCode, response, throwable, apiResponse ) } + + override fun toString(): String { + return "ApiError(tag='$tag', errorCode=$errorCode, profileId=$profileId, throwable=$throwable, apiResponse=$apiResponse, request=$request, response=$response, isCritical=$isCritical)" + } + + } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiTask.kt deleted file mode 100644 index df289ea6..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/ApiTask.kt +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (c) Kuba Szczodrzyński 2019-9-28. - */ - -package pl.szczodrzynski.edziennik.api.v2.models - -open class ApiTask(open val profileId: Int) { - var taskId: Int = 0 -} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 6e5dce2e..41e3e5b3 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -91,6 +91,15 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) * to run. */ var targetEndpointIds = mutableListOf() + /** + * A count of all network requests to do. + */ + var progressCount: Int = 0 + /** + * A number by which the progress will be incremented, every time + * a login method/endpoint finishes its job. + */ + var progressStep: Float = 0f /** * A map of endpoint IDs to JSON objects, specifying their arguments bundle. @@ -386,7 +395,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) callback.onError(apiError) } - fun progress(step: Int) { + fun progress(step: Float) { callback.onProgress(step) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt index 60e1c063..9e80c2bf 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt @@ -4,7 +4,6 @@ package pl.szczodrzynski.edziennik.api.v2.template -import android.util.Log import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback @@ -17,7 +16,7 @@ import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLogin import pl.szczodrzynski.edziennik.api.v2.templateLoginMethods import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile -import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.Utils.d class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { companion object { @@ -51,8 +50,8 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, |__*/ override fun sync(featureIds: List, viewId: Int?) { data.prepare(templateLoginMethods, TemplateFeatures, featureIds, viewId) - Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") - Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") + d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") + d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") TemplateLogin(data) { TemplateData(data) { completed() @@ -75,7 +74,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, } override fun cancel() { - Utils.d(TAG, "Cancelled") + d(TAG, "Cancelled") data.cancel() } @@ -85,7 +84,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, callback.onCompleted() } - override fun onProgress(step: Int) { + override fun onProgress(step: Float) { callback.onProgress(step) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/TemplateData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/TemplateData.kt index 9aef7cbe..494ea1d9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/TemplateData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/data/TemplateData.kt @@ -19,8 +19,6 @@ class TemplateData(val data: DataTemplate, val onSuccess: () -> Unit) { private const val TAG = "TemplateData" } - private var cancelled = false - init { nextEndpoint(onSuccess) } @@ -30,11 +28,12 @@ class TemplateData(val data: DataTemplate, val onSuccess: () -> Unit) { onSuccess() return } - if (cancelled) { + if (data.cancelled) { onSuccess() return } useEndpoint(data.targetEndpointIds.removeAt(0)) { + data.progress(data.progressStep) nextEndpoint(onSuccess) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/login/TemplateLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/login/TemplateLogin.kt index 1f08ef44..82937c4a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/login/TemplateLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/login/TemplateLogin.kt @@ -5,7 +5,8 @@ package pl.szczodrzynski.edziennik.api.v2.template.login import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.api.v2.* +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_TEMPLATE_API +import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_TEMPLATE_WEB import pl.szczodrzynski.edziennik.api.v2.template.DataTemplate import pl.szczodrzynski.edziennik.utils.Utils @@ -30,6 +31,7 @@ class TemplateLogin(val data: DataTemplate, val onSuccess: () -> Unit) { return } useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId -> + data.progress(data.progressStep) if (usedMethodId != -1) data.loginMethods.add(usedMethodId) nextLoginMethod(onSuccess) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt index 84cd1326..209fec47 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt @@ -4,7 +4,6 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan -import android.util.Log import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback @@ -17,7 +16,7 @@ import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLogin import pl.szczodrzynski.edziennik.api.v2.vulcanLoginMethods import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile -import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.Utils.d class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { companion object { @@ -51,8 +50,8 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va |__*/ override fun sync(featureIds: List, viewId: Int?) { data.prepare(vulcanLoginMethods, VulcanFeatures, featureIds, viewId) - Log.d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") - Log.d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") + d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") + d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") VulcanLogin(data) { VulcanData(data) { completed() @@ -75,7 +74,7 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va } override fun cancel() { - Utils.d(TAG, "Cancelled") + d(TAG, "Cancelled") data.cancel() } @@ -85,7 +84,7 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va callback.onCompleted() } - override fun onProgress(step: Int) { + override fun onProgress(step: Float) { callback.onProgress(step) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt index c3364192..4b6c69dc 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt @@ -14,8 +14,6 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { private const val TAG = "VulcanData" } - private var cancelled = false - init { nextEndpoint(onSuccess) } @@ -25,11 +23,12 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { onSuccess() return } - if (cancelled) { + if (data.cancelled) { onSuccess() return } useEndpoint(data.targetEndpointIds.removeAt(0)) { + data.progress(data.progressStep) nextEndpoint(onSuccess) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLogin.kt index 8cfdb960..cb3db3eb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/login/VulcanLogin.kt @@ -30,6 +30,7 @@ class VulcanLogin(val data: DataVulcan, val onSuccess: () -> Unit) { return } useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId -> + data.progress(data.progressStep) if (usedMethodId != -1) data.loginMethods.add(usedMethodId) nextLoginMethod(onSuccess) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/Lesson.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/Lesson.kt new file mode 100644 index 00000000..0feaaa6f --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/Lesson.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-10-25. + */ + +package pl.szczodrzynski.edziennik.data.db.modules.timetable + +import androidx.room.ColumnInfo +import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Time + +open class Lesson(val profileId: Int) { + companion object { + const val TYPE_NORMAL = 0 + const val TYPE_CANCELLED = 1 + const val TYPE_CHANGE = 2 + const val TYPE_SHIFTED_SOURCE = 3 /* source lesson */ + const val TYPE_SHIFTED_TARGET = 4 /* target lesson */ + } + + @ColumnInfo(name = "lessonType") + var type: Int = TYPE_NORMAL + + var date: Date? = null + var lessonNumber: Int? = null + var startTime: Time? = null + var endTime: Time? = null + var teacherId: Long? = null + var subjectId: Long? = null + var teamId: Long? = null + var classroom: String? = null + + var oldDate: Date? = null + var oldLessonNumber: Int? = null + var oldStartTime: Time? = null + var oldEndTime: Time? = null + var oldTeacherId: Long? = null + var oldSubjectId: Long? = null + var oldTeamId: Long? = null + var oldClassroom: String? = null +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt index f3a6ad36..eb629818 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/receivers/SzkolnyReceiver.kt @@ -9,9 +9,8 @@ import android.content.Context import android.content.Intent import pl.szczodrzynski.edziennik.api.v2.ApiService import pl.szczodrzynski.edziennik.api.v2.events.requests.ServiceCloseRequest -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest import pl.szczodrzynski.edziennik.api.v2.events.requests.TaskCancelRequest +import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask class SzkolnyReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { @@ -19,8 +18,8 @@ class SzkolnyReceiver : BroadcastReceiver() { when (intent?.extras?.getString("task", null)) { "ServiceCloseRequest" -> ApiService.startAndRequest(context, ServiceCloseRequest()) "TaskCancelRequest" -> ApiService.startAndRequest(context, TaskCancelRequest(intent.extras?.getInt("taskId", -1) ?: return)) - "SyncRequest" -> ApiService.startAndRequest(context, SyncRequest()) - "SyncProfileRequest" -> ApiService.startAndRequest(context, SyncProfileRequest(intent.extras?.getInt("profileId", -1) ?: return)) + "SyncRequest" -> EdziennikTask.sync().enqueue(context) + "SyncProfileRequest" -> EdziennikTask.syncProfile(intent.extras?.getInt("profileId", -1) ?: return).enqueue(context) } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java b/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java index 17102d23..6347b1e1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java @@ -17,9 +17,7 @@ import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.api.v2.ApiService; -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileRequest; -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest; +import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask; import pl.szczodrzynski.edziennik.data.db.modules.events.Event; import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull; import pl.szczodrzynski.edziennik.data.db.modules.events.EventType; @@ -114,7 +112,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { app.notifier.postAll(profile); app.saveConfig("notifications");*/ d(TAG, "Syncing profile " + profile.getId()); - ApiService.Companion.startAndRequest(app, new SyncProfileRequest(profile.getId(), null)); + EdziennikTask.Companion.syncProfile(profile.getId(), null).enqueue(app); } else { /*app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message")) .withProfileData(profile.id, profile.name) @@ -125,7 +123,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { app.notifier.postAll(profile); app.saveConfig("notifications");*/ d(TAG, "Syncing profile " + profile.getId()); - ApiService.Companion.startAndRequest(app, new SyncProfileRequest(profile.getId(), null)); + EdziennikTask.Companion.syncProfile(profile.getId(), null).enqueue(app); } } }); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncWorker.kt b/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncWorker.kt index 4405c10b..e06cd6f5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncWorker.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/sync/SyncWorker.kt @@ -8,8 +8,7 @@ import androidx.work.impl.WorkManagerImpl import org.greenrobot.eventbus.EventBus import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.MINUTE -import pl.szczodrzynski.edziennik.api.v2.ApiService -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest +import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask import pl.szczodrzynski.edziennik.formatDate import pl.szczodrzynski.edziennik.utils.Utils.d import java.util.concurrent.TimeUnit @@ -101,7 +100,7 @@ class SyncWorker(val context: Context, val params: WorkerParameters) : Worker(co override fun doWork(): Result { d(TAG, "Running worker ID ${params.id}") - ApiService.startAndRequest(context, SyncRequest()) + EdziennikTask.sync().enqueue(context) rescheduleNext(context as App) return Result.success() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java index 943a14e5..f7b84db8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java @@ -1,6 +1,5 @@ package pl.szczodrzynski.edziennik.ui.modules.login; -import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -21,10 +20,9 @@ import org.greenrobot.eventbus.ThreadMode; import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.api.v2.ApiService; +import pl.szczodrzynski.edziennik.api.v2.events.ApiTaskErrorEvent; import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent; -import pl.szczodrzynski.edziennik.api.v2.events.SyncErrorEvent; -import pl.szczodrzynski.edziennik.api.v2.events.requests.FirstLoginRequest; +import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask; import pl.szczodrzynski.edziennik.data.api.AppError; import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore; import pl.szczodrzynski.edziennik.databinding.FragmentLoginProgressBinding; @@ -63,13 +61,15 @@ public class LoginProgressFragment extends Fragment { } @Subscribe(threadMode = ThreadMode.MAIN) - public void onSyncErrorEvent(SyncErrorEvent event) { + public void onSyncErrorEvent(ApiTaskErrorEvent event) { LoginActivity.error = event.getError().toAppError(); if (getActivity() == null) return; nav.navigateUp(); } + // TODO add progress bar in login + @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { assert getContext() != null; @@ -89,8 +89,7 @@ public class LoginProgressFragment extends Fragment { LoginStore loginStore = new LoginStore(-1, loginType, new JsonObject()); loginStore.copyFrom(args); - getContext().startService(new Intent(app, ApiService.class)); - EventBus.getDefault().postSticky(new FirstLoginRequest(loginStore)); + EdziennikTask.Companion.firstLogin(loginStore).enqueue(getContext()); } @Override diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt index 1a0086fe..696505e7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginSyncFragment.kt @@ -15,12 +15,11 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.R -import pl.szczodrzynski.edziennik.api.v2.ApiService -import pl.szczodrzynski.edziennik.api.v2.events.SyncErrorEvent -import pl.szczodrzynski.edziennik.api.v2.events.SyncFinishedEvent -import pl.szczodrzynski.edziennik.api.v2.events.SyncProgressEvent -import pl.szczodrzynski.edziennik.api.v2.events.SyncStartedEvent -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncProfileListRequest +import pl.szczodrzynski.edziennik.api.v2.events.ApiTaskAllFinishedEvent +import pl.szczodrzynski.edziennik.api.v2.events.ApiTaskErrorEvent +import pl.szczodrzynski.edziennik.api.v2.events.ApiTaskProgressEvent +import pl.szczodrzynski.edziennik.api.v2.events.ApiTaskStartedEvent +import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask import pl.szczodrzynski.edziennik.data.db.modules.events.Event.* import pl.szczodrzynski.edziennik.data.db.modules.events.EventType import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore @@ -29,6 +28,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.REG import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.REGISTRATION_ENABLED import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.REGISTRATION_UNSPECIFIED import pl.szczodrzynski.edziennik.databinding.FragmentLoginSyncBinding +import kotlin.math.roundToInt class LoginSyncFragment : Fragment() { @@ -47,23 +47,24 @@ class LoginSyncFragment : Fragment() { } @Subscribe(threadMode = ThreadMode.MAIN) - fun onSyncStartedEvent(event: SyncStartedEvent) { + fun onSyncStartedEvent(event: ApiTaskStartedEvent) { b.loginSyncSubtitle1.text = Html.fromHtml(getString(R.string.login_sync_subtitle_1_format, event.profile?.name ?: "")) } @Subscribe(threadMode = ThreadMode.MAIN) - fun onSyncFinishedEvent(event: SyncFinishedEvent) { + fun onSyncFinishedEvent(event: ApiTaskAllFinishedEvent) { nav.navigate(R.id.loginFinishFragment, null, LoginActivity.navOptions) } @Subscribe(threadMode = ThreadMode.MAIN) - fun onSyncProgressEvent(event: SyncProgressEvent) { - b.loginSyncProgressBar.progress = event.progress - b.loginSyncSubtitle2.text = event.progressRes?.let { getString(it) } + fun onSyncProgressEvent(event: ApiTaskProgressEvent) { + b.loginSyncProgressBar.progress = event.progress.roundToInt() + b.loginSyncProgressBar.isIndeterminate = event.progress < 0f + b.loginSyncSubtitle2.text = event.progressText } @Subscribe(threadMode = ThreadMode.MAIN) - fun onSyncErrorEvent(event: SyncErrorEvent) { + fun onSyncErrorEvent(event: ApiTaskErrorEvent) { LoginActivity.error = event.error.toAppError() nav.navigate(R.id.loginSyncErrorFragment, null, LoginActivity.navOptions) } @@ -109,8 +110,7 @@ class LoginSyncFragment : Fragment() { } LoginFinishFragment.firstProfileId = firstProfileId - ApiService.start(activity) - EventBus.getDefault().postSticky(SyncProfileListRequest(profileIds)) + EdziennikTask.syncProfileList(profileIds).enqueue(activity) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesFragment.kt index db7fae3b..b2a513fe 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesFragment.kt @@ -1,6 +1,8 @@ package pl.szczodrzynski.edziennik.ui.modules.messages import android.os.Bundle +import android.text.Html +import android.text.InputType import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -8,11 +10,20 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentPagerAdapter import androidx.viewpager.widget.ViewPager +import com.afollestad.materialdialogs.MaterialDialog +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.MainActivity -import pl.szczodrzynski.edziennik.databinding.FragmentMessagesBinding +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_LIBRUS +import pl.szczodrzynski.edziennik.api.v2.events.ApiTaskErrorEvent +import pl.szczodrzynski.edziennik.api.v2.events.ApiTaskFinishedEvent +import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask import pl.szczodrzynski.edziennik.data.db.modules.messages.Message +import pl.szczodrzynski.edziennik.databinding.FragmentMessagesBinding import pl.szczodrzynski.edziennik.utils.Themes import java.util.* @@ -80,6 +91,61 @@ class MessagesFragment : Fragment() { }) b.tabLayout.setupWithViewPager(b.viewPager) + + if (app.profile.loginStoreType == LOGIN_TYPE_LIBRUS && app.profile.getStudentData("accountPassword", null) == null) { + MaterialDialog.Builder(activity) + .title("Wiadomości w systemie Synergia") + .content("Moduł Wiadomości w aplikacji Szkolny.eu jest przeglądarką zasobów szkolnego konta Synergia. Z tego powodu, musisz wpisać swoje hasło do tego konta, aby móc korzystać z tej funkcji.") + .positiveText(R.string.ok) + .onPositive { dialog, which -> + MaterialDialog.Builder(activity) + .title("Zaloguj się") + .content(Html.fromHtml("Podaj hasło do konta Synergia z loginem " + app.profile.getStudentData("accountLogin", "???") + "")) + .inputType(InputType.TYPE_TEXT_VARIATION_PASSWORD) + .input(null, null) { dialog1, input -> + app.profile.putStudentData("accountPassword", input.toString()) + app.profileSaveFullAsync(app.profile) + EdziennikTask.syncProfile(App.profileId, listOf( + DRAWER_ITEM_MESSAGES to Message.TYPE_RECEIVED, + DRAWER_ITEM_MESSAGES to Message.TYPE_SENT + )).enqueue(context!!) + } + .positiveText(R.string.ok) + .negativeText(R.string.cancel) + .show() + } + .show() + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onSyncProfileFinishedEvent(event: ApiTaskFinishedEvent) { + if (event.profileId == App.profileId) { + app.profileSaveFullAsync(app.profile) + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onSyncErrorEvent(event: ApiTaskErrorEvent) { + app.profile.removeStudentData("accountPassword") + app.profileSaveFullAsync(app.profile) + /*MaterialDialog.Builder(activity) + .title(R.string.login_failed) + .content(R.string.login_failed_text) + .positiveText(R.string.ok) + .neutralText(R.string.report) + .onNeutral { dialog2, which1 -> app.apiEdziennik.guiReportError(getActivity(), error, null) } + .show()*/ + } + + override fun onStart() { + EventBus.getDefault().register(this) + super.onStart() + } + + override fun onStop() { + super.onStop() + EventBus.getDefault().unregister(this) } internal class Adapter(manager: FragmentManager) : FragmentPagerAdapter(manager) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesListFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesListFragment.java index c717afd2..ae3500b2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesListFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessagesListFragment.java @@ -1,13 +1,9 @@ package pl.szczodrzynski.edziennik.ui.modules.messages; -import android.content.Context; import android.graphics.Rect; import android.os.AsyncTask; import android.os.Bundle; -import android.os.Handler; -import android.text.Html; -import android.text.InputType; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -21,28 +17,19 @@ import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; -import com.afollestad.materialdialogs.MaterialDialog; - import java.util.ArrayList; import java.util.List; import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.MainActivity; -import pl.szczodrzynski.edziennik.data.api.AppError; -import pl.szczodrzynski.edziennik.data.api.Edziennik; -import pl.szczodrzynski.edziennik.data.api.interfaces.SyncCallback; -import pl.szczodrzynski.edziennik.databinding.MessagesListBinding; -import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore; +import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.data.db.modules.messages.Message; import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull; import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipientFull; -import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; -import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; +import pl.szczodrzynski.edziennik.databinding.MessagesListBinding; import pl.szczodrzynski.edziennik.utils.Themes; import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; -import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_LIBRUS; import static pl.szczodrzynski.edziennik.utils.Utils.d; public class MessagesListFragment extends Fragment { @@ -96,66 +83,6 @@ public class MessagesListFragment extends Fragment { return; } - if (app.profile.getLoginStoreType() == LOGIN_TYPE_LIBRUS && app.profile.getStudentData("accountPassword", null) == null && false) { - new MaterialDialog.Builder(activity) - .title("Wiadomości w systemie Synergia") - .content("Moduł Wiadomości w aplikacji Szkolny.eu jest przeglądarką zasobów szkolnego konta Synergia. Z tego powodu, musisz wpisać swoje hasło do tego konta, aby móc korzystać z tej funkcji.") - .positiveText(R.string.ok) - .onPositive(((dialog, which) -> { - new MaterialDialog.Builder(activity) - .title("Zaloguj się") - .content(Html.fromHtml("Podaj hasło do konta Synergia z loginem "+app.profile.getStudentData("accountLogin", "???")+"")) - .inputType(InputType.TYPE_TEXT_VARIATION_PASSWORD) - .input(null, null, (dialog1, input) -> { - app.profile.putStudentData("accountPassword", input.toString()); - Edziennik.getApi(app, app.profile.getLoginStoreType()).syncMessages(activity, new SyncCallback() { - @Override - public void onLoginFirst(List profileList, LoginStore loginStore) { - - } - - @Override - public void onSuccess(Context activityContext, ProfileFull profileFull) { - app.profile.putStudentData("accountPassword", input.toString()); - app.profileSaveFullAsync(profileFull); - ((MainActivity) activityContext).recreate(); - } - - @Override - public void onError(Context activityContext, AppError error) { - new Handler(activityContext.getMainLooper()).post(() -> { - app.profile.removeStudentData("accountPassword"); - app.profileSaveFullAsync(app.profile); - new MaterialDialog.Builder(activity) - .title(R.string.login_failed) - .content(R.string.login_failed_text) - .positiveText(R.string.ok) - .neutralText(R.string.report) - .onNeutral(((dialog2, which1) -> { - app.apiEdziennik.guiReportError(getActivity(), error, null); - })) - .show(); - }); - } - - @Override - public void onProgress(int progressStep) { - - } - - @Override - public void onActionStarted(int stringResId) { - - } - }, app.profile); - }) - .positiveText(R.string.ok) - .negativeText(R.string.cancel) - .show(); - })) - .show(); - } - if (getArguments() != null) { messageType = getArguments().getInt("messageType", Message.TYPE_RECEIVED); } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java index 9929ec74..dad2df64 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java @@ -236,7 +236,7 @@ public class SettingsNewFragment extends MaterialAboutFragment { } else { - items.add( + /*items.add( new MaterialAboutSwitchItem( getString(R.string.settings_profile_notify_text), getString(R.string.settings_profile_notify_subtext), @@ -251,7 +251,7 @@ public class SettingsNewFragment extends MaterialAboutFragment { app.profileSaveAsync(); return true; })) - ); + );*/ items.add( new MaterialAboutActionItem( diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/widgets/luckynumber/WidgetLuckyNumber.java b/app/src/main/java/pl/szczodrzynski/edziennik/widgets/luckynumber/WidgetLuckyNumber.java index 051657c0..e5522880 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/widgets/luckynumber/WidgetLuckyNumber.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/widgets/luckynumber/WidgetLuckyNumber.java @@ -24,8 +24,7 @@ import java.lang.reflect.Method; import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.api.v2.ApiService; -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest; +import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask; import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile; import pl.szczodrzynski.edziennik.utils.models.Date; import pl.szczodrzynski.edziennik.widgets.WidgetConfig; @@ -40,7 +39,7 @@ public class WidgetLuckyNumber extends AppWidgetProvider { @Override public void onReceive(Context context, Intent intent) { if (ACTION_SYNC_DATA.equals(intent.getAction())){ - ApiService.Companion.startAndRequest(context, new SyncRequest()); + EdziennikTask.Companion.sync().enqueue(context); } super.onReceive(context, intent); } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/widgets/notifications/WidgetNotifications.java b/app/src/main/java/pl/szczodrzynski/edziennik/widgets/notifications/WidgetNotifications.java index 05b37adb..ea65b7aa 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/widgets/notifications/WidgetNotifications.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/widgets/notifications/WidgetNotifications.java @@ -24,8 +24,7 @@ import java.lang.reflect.Method; import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.api.v2.ApiService; -import pl.szczodrzynski.edziennik.api.v2.events.requests.SyncRequest; +import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask; import pl.szczodrzynski.edziennik.widgets.WidgetConfig; public class WidgetNotifications extends AppWidgetProvider { @@ -36,7 +35,7 @@ public class WidgetNotifications extends AppWidgetProvider { @Override public void onReceive(Context context, Intent intent) { if (ACTION_SYNC_DATA.equals(intent.getAction())){ - ApiService.Companion.startAndRequest(context, new SyncRequest()); + EdziennikTask.Companion.sync().enqueue(context); } super.onReceive(context, intent); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ae409e30..7b31bc00 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -982,4 +982,5 @@ Na urządzeniu prawdopodobnie zainstalowany jest menedżer aplikacji, który może powodować problemy z synchronizacją automatyczną.\n\nNależy wyłączyć w ustawieniach telefonu optymalizację baterii dla aplikacji Szkolny.eu.\n\nKliknij OK, aby przejść do ustawień telefonu. Nie pytaj ponownie Nie udało się otworzyć ustawień + Tworzenie powiadomień From 399ae7e3dc97ad739408f3903816a8ef451f5035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 3 Nov 2019 21:30:51 +0100 Subject: [PATCH 130/691] [APIv2/Idziennik] Update Web login expiry time. --- .../edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt index 85f9af55..88729206 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/login/IdziennikLoginWeb.kt @@ -67,7 +67,7 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) { data.webSessionId = cookies.singleOrNull { it.name() == "ASP.NET_SessionId_iDziennik" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION data.webAuth = cookies.singleOrNull { it.name() == ".ASPXAUTH" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH data.apiBearer = cookies.singleOrNull { it.name() == "Bearer" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER - data.loginExpiryTime = response.getUnixDate() + 45 * MINUTE + data.loginExpiryTime = response.getUnixDate() + 30 * MINUTE /* after about 40 minutes the login didn't work already */ data.apiExpiryTime = response.getUnixDate() + 12 * HOUR /* actually it expires after 24 hours but I'm not sure when does the token refresh. */ return@run null }?.let { errorCode -> From 385fe21d16bd16f0d49f29e30629914584abcf99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 5 Nov 2019 11:27:29 +0100 Subject: [PATCH 131/691] [APIv2/Librus] Implement error handling. Catch exceptions in ApiService. --- .../edziennik/api/v2/ApiService.kt | 13 ++- .../szczodrzynski/edziennik/api/v2/Errors.kt | 1 + .../edziennik/api/v2/librus/Librus.kt | 96 ++++++++++++++++--- .../api/v2/librus/data/LibrusPortal.kt | 5 +- .../edziennik/api/v2/models/Data.kt | 2 - .../data/db/modules/login/LoginStore.java | 11 ++- 6 files changed, 101 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt index 66ea5b1e..162ecde1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/ApiService.kt @@ -90,6 +90,7 @@ class ApiService : Service() { errorList.add(apiError) apiError.throwable?.printStackTrace() if (apiError.isCritical) { + taskRunning?.cancel() notification.setCriticalError().post() taskRunning = null taskIsRunning = false @@ -154,10 +155,14 @@ class ApiService : Service() { // post an event EventBus.getDefault().post(ApiTaskStartedEvent(taskProfileId, task.profile)) - when (task) { - is EdziennikTask -> task.run(app, taskCallback) - is NotifyTask -> task.run(app, taskCallback) - is ErrorReportTask -> task.run(app, taskCallback, notification, errorList) + try { + when (task) { + is EdziennikTask -> task.run(app, taskCallback) + is NotifyTask -> task.run(app, taskCallback) + is ErrorReportTask -> task.run(app, taskCallback, notification, errorList) + } + } catch (e: Exception) { + taskCallback.onError(ApiError(TAG, EXCEPTION_API_TASK).withThrowable(e)) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt index f1f3564c..c2daa722 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Errors.kt @@ -150,6 +150,7 @@ const val ERROR_IDZIENNIK_API_OTHER = 451 const val ERROR_TEMPLATE_WEB_OTHER = 801 +const val EXCEPTION_API_TASK = 900 const val EXCEPTION_LOGIN_LIBRUS_API_TOKEN = 901 const val EXCEPTION_LOGIN_LIBRUS_PORTAL_TOKEN = 902 const val EXCEPTION_LIBRUS_PORTAL_SYNERGIA_TOKEN = 903 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index 937fe1a6..eeb2b759 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -5,7 +5,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 +import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData @@ -14,9 +14,7 @@ import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.LibrusFirstLogin import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia -import pl.szczodrzynski.edziennik.api.v2.librusLoginMethods import pl.szczodrzynski.edziennik.api.v2.models.ApiError -import pl.szczodrzynski.edziennik.api.v2.prepare import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.utils.Utils.d @@ -53,12 +51,28 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va |__*/ override fun sync(featureIds: List, viewId: Int?) { data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId) - d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") - d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") + login() + } + + private fun login() { + d(TAG, "Trying to login with ${data.targetLoginMethodIds}") + if (internalErrorList.isNotEmpty()) { + d(TAG, " - Internal errors:") + internalErrorList.forEach { d(TAG, " - code $it") } + } LibrusLogin(data) { - LibrusData(data) { - completed() - } + data() + } + } + + private fun data() { + d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") + if (internalErrorList.isNotEmpty()) { + d(TAG, " - Internal errors:") + internalErrorList.forEach { d(TAG, " - code $it") } + } + LibrusData(data) { + completed() } } @@ -102,15 +116,67 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va } override fun onError(apiError: ApiError) { + if (apiError.errorCode in internalErrorList) { + // finish immediately if the same error occurs twice during the same sync + callback.onError(apiError) + return + } + internalErrorList.add(apiError.errorCode) when (apiError.errorCode) { - in internalErrorList -> { - // finish immediately if the same error occurs twice during the same sync - callback.onError(apiError) + ERROR_LIBRUS_PORTAL_ACCESS_DENIED -> { + data.loginMethods.remove(LOGIN_METHOD_LIBRUS_PORTAL) + data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_PORTAL) + data.targetLoginMethodIds.sort() + data.portalTokenExpiryTime = 0 + login() } - CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> { - internalErrorList.add(apiError.errorCode) - loginStore.removeLoginData("refreshToken") // force a clean login - //loginLibrus() + ERROR_LIBRUS_API_ACCESS_DENIED, + ERROR_LIBRUS_API_TOKEN_EXPIRED -> { + data.loginMethods.remove(LOGIN_METHOD_LIBRUS_API) + data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_API) + data.targetLoginMethodIds.sort() + data.apiTokenExpiryTime = 0 + login() + } + ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED -> { + data.loginMethods.remove(LOGIN_METHOD_LIBRUS_SYNERGIA) + data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_SYNERGIA) + data.targetLoginMethodIds.sort() + data.synergiaSessionIdExpiryTime = 0 + login() + } + ERROR_LIBRUS_MESSAGES_ACCESS_DENIED -> { + data.loginMethods.remove(LOGIN_METHOD_LIBRUS_MESSAGES) + data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_MESSAGES) + data.targetLoginMethodIds.sort() + data.messagesSessionIdExpiryTime = 0 + login() + } + ERROR_LOGIN_LIBRUS_PORTAL_NO_CODE, + ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING, + ERROR_LOGIN_LIBRUS_PORTAL_CODE_REVOKED, + ERROR_LOGIN_LIBRUS_PORTAL_CODE_EXPIRED -> { + login() + } + ERROR_LOGIN_LIBRUS_PORTAL_NO_REFRESH, + ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_REVOKED, + ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_INVALID -> { + data.portalRefreshToken = null + login() + } + ERROR_LOGIN_LIBRUS_SYNERGIA_TOKEN_INVALID, + ERROR_LOGIN_LIBRUS_SYNERGIA_NO_TOKEN, + ERROR_LOGIN_LIBRUS_SYNERGIA_NO_SESSION_ID -> { + login() + } + ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID -> { + login() + } + // TODO PORTAL CAPTCHA + ERROR_LIBRUS_API_TIMETABLE_NOT_PUBLIC, + ERROR_LIBRUS_API_LUCKY_NUMBER_NOT_ACTIVE, + ERROR_LIBRUS_API_NOTES_NOT_ACTIVE -> { + data() } else -> callback.onError(apiError) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt index 3c5e4963..b86a6956 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt @@ -44,7 +44,10 @@ open class LibrusPortal(open val data: DataLibrus) { "Access token is invalid" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED "ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED "Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND - else -> ERROR_LIBRUS_PORTAL_OTHER + else -> when (json.getString("hint")) { + "Error while decoding to JSON" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED + else -> ERROR_LIBRUS_PORTAL_OTHER + } }.let { errorCode -> data.error(ApiError(tag, errorCode) .withApiResponse(json) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 41e3e5b3..394aaeaf 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -390,8 +390,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) } fun error(apiError: ApiError) { - if (apiError.isCritical) - cancel() callback.onError(apiError) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/login/LoginStore.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/login/LoginStore.java index f3d1b585..567f84db 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/login/LoginStore.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/login/LoginStore.java @@ -8,6 +8,7 @@ import androidx.room.Entity; import androidx.room.Ignore; import com.google.gson.JsonElement; +import com.google.gson.JsonNull; import com.google.gson.JsonObject; import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull; @@ -73,7 +74,7 @@ public class LoginStore { if (data == null) return defaultValue; JsonElement element = data.get(key); - if (element != null) { + if (element != null && !(element instanceof JsonNull)) { return element.getAsString(); } return defaultValue; @@ -83,7 +84,7 @@ public class LoginStore { if (data == null) return defaultValue; JsonElement element = data.get(key); - if (element != null) { + if (element != null && !(element instanceof JsonNull)) { return element.getAsInt(); } return defaultValue; @@ -93,7 +94,7 @@ public class LoginStore { if (data == null) return defaultValue; JsonElement element = data.get(key); - if (element != null) { + if (element != null && !(element instanceof JsonNull)) { return element.getAsLong(); } return defaultValue; @@ -103,7 +104,7 @@ public class LoginStore { if (data == null) return defaultValue; JsonElement element = data.get(key); - if (element != null) { + if (element != null && !(element instanceof JsonNull)) { return element.getAsFloat(); } return defaultValue; @@ -112,7 +113,7 @@ public class LoginStore { if (data == null) return defaultValue; JsonElement element = data.get(key); - if (element != null) { + if (element != null && !(element instanceof JsonNull)) { return element.getAsBoolean(); } return defaultValue; From 23d55ec5717f0745540b4311db38ee3219e79590 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Tue, 5 Nov 2019 17:30:24 +0100 Subject: [PATCH 132/691] [APIv2/Vulcan] Add getting sent messages --- .../api/v2/vulcan/data/VulcanData.kt | 4 + .../vulcan/data/api/VulcanApiMessagesInbox.kt | 119 +++++++++--------- .../vulcan/data/api/VulcanApiMessagesSent.kt | 99 +++++++++++++++ .../v2/vulcan/data/api/VulcanApiTemplate.kt | 10 +- 4 files changed, 170 insertions(+), 62 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesSent.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt index 4b6c69dc..e7bcff7f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt @@ -68,6 +68,10 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) VulcanApiMessagesInbox(data) { onSuccess() } } + ENDPOINT_VULCAN_API_MESSAGES_SENT -> { + data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox) + VulcanApiMessagesSent(data) { onSuccess() } + } else -> onSuccess() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesInbox.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesInbox.kt index ef1cacfe..085db051 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesInbox.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesInbox.kt @@ -11,6 +11,7 @@ import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_MESSAGES_INB import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.messages.Message +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.utils.models.Date @@ -20,64 +21,68 @@ class VulcanApiMessagesInbox(override val data: DataVulcan, val onSuccess: () -> const val TAG = "VulcanApiMessagesInbox" } - init { data.profile?.also { profile -> + init { + data.profile?.also { profile -> - val startDate: String = when (profile.empty) { - true -> profile.getSemesterStart(profile.currentSemester).stringY_m_d - else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d - } - val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d - - apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_RECEIVED, parameters = mapOf( - "DataPoczatkowa" to startDate, - "DataKoncowa" to endDate, - "LoginId" to data.studentLoginId, - "IdUczen" to data.studentId - )) { json, _ -> - json.getJsonArray("Data").asJsonObjectList()?.forEach { message -> - val id = message.getLong("WiadomoscId") ?: return@forEach - val subject = message.getString("Tytul") ?: "" - val body = message.getString("Tresc") ?: "" - - val senderLoginId = message.getString("NadawcaId") ?: return@forEach - val senderId = data.teacherList - .singleOrNull { it.loginId == senderLoginId }?.id ?: return@forEach - - val addedDate = message.getLong("DataWyslaniaUnixEpoch")?.let { it * 1000 } ?: -1 - val readDate = message.getLong("DataPrzeczytaniaUnixEpoch")?.let { it * 1000 } ?: -1 - - val messageObject = Message( - profileId, - id, - subject, - body, - Message.TYPE_RECEIVED, - senderId, - -1 - ) - - val messageRecipientObject = MessageRecipient( - profileId, - -1, - -1, - readDate, - id - ) - - data.messageList.add(messageObject) - data.messageRecipientList.add(messageRecipientObject) - data.metadataList.add(Metadata( - profileId, - Metadata.TYPE_MESSAGE, - id, - readDate > 0, - readDate > 0, - addedDate - )) + val startDate: String = when (profile.empty) { + true -> profile.getSemesterStart(profile.currentSemester).stringY_m_d + else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d } + val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d - data.setSyncNext(ENDPOINT_VULCAN_API_MESSAGES_INBOX, SYNC_ALWAYS) - onSuccess() - } - } ?: onSuccess()} + apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_RECEIVED, parameters = mapOf( + "DataPoczatkowa" to startDate, + "DataKoncowa" to endDate, + "LoginId" to data.studentLoginId, + "IdUczen" to data.studentId + )) { json, _ -> + json.getJsonArray("Data").asJsonObjectList()?.forEach { message -> + val id = message.getLong("WiadomoscId") ?: return@forEach + val subject = message.getString("Tytul") ?: "" + val body = message.getString("Tresc") ?: "" + + val senderLoginId = message.getString("NadawcaId") ?: return@forEach + val senderId = data.teacherList + .singleOrNull { it.loginId == senderLoginId }?.id ?: return@forEach + + val sentDate = message.getLong("DataWyslaniaUnixEpoch")?.let { it * 1000 } + ?: -1 + val readDate = message.getLong("DataPrzeczytaniaUnixEpoch")?.let { it * 1000 } + ?: -1 + + val messageObject = Message( + profileId, + id, + subject, + body, + TYPE_RECEIVED, + senderId, + -1 + ) + + val messageRecipientObject = MessageRecipient( + profileId, + -1, + -1, + readDate, + id + ) + + data.messageList.add(messageObject) + data.messageRecipientList.add(messageRecipientObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_MESSAGE, + id, + readDate > 0, + readDate > 0, + sentDate + )) + } + + data.setSyncNext(ENDPOINT_VULCAN_API_MESSAGES_INBOX, SYNC_ALWAYS) + onSuccess() + } + } ?: onSuccess() + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesSent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesSent.kt new file mode 100644 index 00000000..a56e4f67 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiMessagesSent.kt @@ -0,0 +1,99 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-11-5 + */ + +package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES +import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_MESSAGES_SENT +import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_MESSAGES_SENT +import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message +import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT +import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata +import pl.szczodrzynski.edziennik.utils.models.Date + +class VulcanApiMessagesSent(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { + companion object { + const val TAG = "VulcanApiMessagesSent" + } + + init { + data.profile?.also { profile -> + val startDate: String = when (profile.empty) { + true -> profile.getSemesterStart(profile.currentSemester).stringY_m_d + else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d + } + val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d + + apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_SENT, parameters = mapOf( + "DataPoczatkowa" to startDate, + "DataKoncowa" to endDate, + "LoginId" to data.studentLoginId, + "IdUczen" to data.studentId + )) { json, _ -> + json.getJsonArray("Data")?.asJsonObjectList()?.forEach { message -> + val id = message.getLong("WiadomoscId") ?: return@forEach + val subject = message.getString("Tytul") ?: "" + val body = message.getString("Tresc") ?: "" + val readBy = message.getInt("Przeczytane") ?: 0 + val unreadBy = message.getInt("Nieprzeczytane") ?: 0 + val sentDate = message.getLong("DataWyslaniaUnixEpoch")?.let { it * 1000 } ?: -1 + + val messageObject = Message( + profileId, + id, + subject, + body, + TYPE_SENT, + -1, + -1 + ) + + message.getJsonArray("Adresaci")?.asJsonObjectList() + ?.forEachIndexed { _, recipient -> + + val recipientLoginId = recipient.getString("LoginId") + ?: return@forEachIndexed + val recipientId = data.teacherList.singleOrNull { it.loginId == recipientLoginId }?.id + ?: return@forEachIndexed + + val readDate: Long = when (readBy) { + 0 -> 0 + else -> when (unreadBy) { + 0 -> 1 + else -> -1 + } + } + + val messageRecipientObject = MessageRecipient( + profileId, + recipientId, + -1, + readDate, + id + ) + + data.messageRecipientList.add(messageRecipientObject) + } + + data.messageList.add(messageObject) + data.metadataList.add(Metadata( + profileId, + Metadata.TYPE_MESSAGE, + id, + true, + true, + sentDate + )) + } + + data.setSyncNext(ENDPOINT_VULCAN_API_MESSAGES_SENT, 1 * DAY, DRAWER_ITEM_MESSAGES) + onSuccess() + } + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiTemplate.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiTemplate.kt index 2c2bb824..c62a0de8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiTemplate.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/api/VulcanApiTemplate.kt @@ -6,8 +6,6 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi -import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS -import pl.szczodrzynski.edziennik.getJsonArray class VulcanApiTemplate(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { companion object { @@ -15,10 +13,12 @@ class VulcanApiTemplate(override val data: DataVulcan, val onSuccess: () -> Unit } init { - /* apiGet(TAG, VULCAN_API_ENDPOINT_) { json, _ -> + /* data.profile?.also { profile -> + apiGet(TAG, VULCAN_API_ENDPOINT_) { json, _ -> - data.setSyncNext(ENDPOINT_VULCAN_API_, SYNC_ALWAYS) - onSuccess() + data.setSyncNext(ENDPOINT_VULCAN_API_, SYNC_ALWAYS) + onSuccess() + } } */ } } From a049effa618cf37b1822a1311f07fb30289ed997 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Tue, 5 Nov 2019 18:16:42 +0100 Subject: [PATCH 133/691] [APIv2/Librus] Add getting normal grade categories --- .../api/v2/librus/data/LibrusData.kt | 4 ++ .../data/api/LibrusApiGradeCategories.kt | 48 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 53 insertions(+) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGradeCategories.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index ef811453..1d07ffd1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -82,6 +82,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_grades) LibrusApiGrades(data) { onSuccess() } } + ENDPOINT_LIBRUS_API_NORMAL_GC -> { + data.startProgress(R.string.edziennik_progress_endpoint_grade_categories) + LibrusApiGradeCategories(data) { onSuccess() } + } // TODO grades ENDPOINT_LIBRUS_API_EVENT_TYPES -> { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGradeCategories.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGradeCategories.kt new file mode 100644 index 00000000..b91eae8a --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiGradeCategories.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-11-5 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import android.graphics.Color +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NORMAL_GC +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory + +class LibrusApiGradeCategories(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiGradeCategories" + } + + init { + apiGet(TAG, "Grades/Categories") { json -> + json.getJsonArray("Categories")?.asJsonObjectList()?.forEach { category -> + val id = category.getLong("Id") ?: return@forEach + val name = category.getString("Name") ?: "" + val weight = when (category.getBoolean("CountToTheAverage")) { + true -> category.getFloat("Weight") ?: 0f + else -> 0f + } + val color = category.getJsonObject("Color")?.getInt("Id") + ?.let { data.getColor(it) } ?: Color.BLUE + + val gradeCategoryObject = GradeCategory( + profileId, + id, + weight, + color, + name + ) + + data.gradeCategories.put(id, gradeCategoryObject) + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GC, SYNC_ALWAYS) + onSuccess() + } + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7b31bc00..a08886af 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -930,6 +930,7 @@ Pokazuj nieobecności nauczycieli w Terminarzu Pobieram informacje o szkole... Pobieranie ocen ucznia... + Pobieranie kategorii ocen... Logowanie do Template WEB... Logowanie do Template API... Logowanie do MobiDziennika... From b72324805fc2e32b7f6d2b60234877224c0666ab Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Tue, 5 Nov 2019 18:20:41 +0100 Subject: [PATCH 134/691] [APIv2] Move onSuccess from callback to an argument --- .../api/v2/idziennik/data/IdziennikData.kt | 20 +++---- .../api/v2/librus/data/LibrusData.kt | 52 +++++++++---------- .../v2/mobidziennik/data/MobidziennikData.kt | 4 +- .../api/v2/vulcan/data/VulcanData.kt | 18 +++---- 4 files changed, 47 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt index c1ab81f3..64064a83 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/data/IdziennikData.kt @@ -41,43 +41,43 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) { when (endpointId) { ENDPOINT_IDZIENNIK_WEB_TIMETABLE -> { data.startProgress(R.string.edziennik_progress_endpoint_timetable) - IdziennikWebTimetable(data) { onSuccess() } + IdziennikWebTimetable(data, onSuccess) } ENDPOINT_IDZIENNIK_WEB_GRADES -> { data.startProgress(R.string.edziennik_progress_endpoint_grades) - IdziennikWebGrades(data) { onSuccess() } + IdziennikWebGrades(data, onSuccess) } ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES -> { data.startProgress(R.string.edziennik_progress_endpoint_proposed_grades) - IdziennikWebProposedGrades(data) { onSuccess() } + IdziennikWebProposedGrades(data, onSuccess) } ENDPOINT_IDZIENNIK_WEB_EXAMS -> { data.startProgress(R.string.edziennik_progress_endpoint_exams) - IdziennikWebExams(data) { onSuccess() } + IdziennikWebExams(data, onSuccess) } ENDPOINT_IDZIENNIK_WEB_NOTICES -> { data.startProgress(R.string.edziennik_progress_endpoint_notices) - IdziennikWebNotices(data) { onSuccess() } + IdziennikWebNotices(data, onSuccess) } ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS -> { data.startProgress(R.string.edziennik_progress_endpoint_announcements) - IdziennikWebAnnouncements(data) { onSuccess() } + IdziennikWebAnnouncements(data, onSuccess) } ENDPOINT_IDZIENNIK_WEB_ATTENDANCE -> { data.startProgress(R.string.edziennik_progress_endpoint_attendance) - IdziennikWebAttendance(data) { onSuccess() } + IdziennikWebAttendance(data, onSuccess) } ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER -> { data.startProgress(R.string.edziennik_progress_endpoint_lucky_number) - IdziennikApiCurrentRegister(data) { onSuccess() } + IdziennikApiCurrentRegister(data, onSuccess) } ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX -> { data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) - IdziennikApiMessagesInbox(data) { onSuccess() } + IdziennikApiMessagesInbox(data, onSuccess) } ENDPOINT_IDZIENNIK_API_MESSAGES_SENT -> { data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox) - IdziennikApiMessagesSent(data) { onSuccess() } + IdziennikApiMessagesSent(data, onSuccess) } else -> onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index 1d07ffd1..7075e552 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -45,96 +45,96 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { */ ENDPOINT_LIBRUS_API_ME -> { data.startProgress(R.string.edziennik_progress_endpoint_student_info) - LibrusApiMe(data) { onSuccess() } + LibrusApiMe(data, onSuccess) } ENDPOINT_LIBRUS_API_SCHOOLS -> { data.startProgress(R.string.edziennik_progress_endpoint_school_info) - LibrusApiSchools(data) { onSuccess() } + LibrusApiSchools(data, onSuccess) } ENDPOINT_LIBRUS_API_CLASSES -> { data.startProgress(R.string.edziennik_progress_endpoint_classes) - LibrusApiClasses(data) { onSuccess() } + LibrusApiClasses(data, onSuccess) } ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES -> { data.startProgress(R.string.edziennik_progress_endpoint_teams) - LibrusApiVirtualClasses(data) { onSuccess() } + LibrusApiVirtualClasses(data, onSuccess) } ENDPOINT_LIBRUS_API_UNITS -> { data.startProgress(R.string.edziennik_progress_endpoint_units) - LibrusApiUnits(data) { onSuccess() } + LibrusApiUnits(data, onSuccess) } ENDPOINT_LIBRUS_API_USERS -> { data.startProgress(R.string.edziennik_progress_endpoint_teachers) - LibrusApiUsers(data) { onSuccess() } + LibrusApiUsers(data, onSuccess) } ENDPOINT_LIBRUS_API_SUBJECTS -> { data.startProgress(R.string.edziennik_progress_endpoint_subjects) - LibrusApiSubjects(data) { onSuccess() } + LibrusApiSubjects(data, onSuccess) } ENDPOINT_LIBRUS_API_CLASSROOMS -> { data.startProgress(R.string.edziennik_progress_endpoint_classrooms) - LibrusApiClassrooms(data) { onSuccess() } + LibrusApiClassrooms(data, onSuccess) } // TODO push config // TODO timetable ENDPOINT_LIBRUS_API_NORMAL_GRADES -> { data.startProgress(R.string.edziennik_progress_endpoint_grades) - LibrusApiGrades(data) { onSuccess() } + LibrusApiGrades(data, onSuccess) } ENDPOINT_LIBRUS_API_NORMAL_GC -> { data.startProgress(R.string.edziennik_progress_endpoint_grade_categories) - LibrusApiGradeCategories(data) { onSuccess() } + LibrusApiGradeCategories(data, onSuccess) } // TODO grades ENDPOINT_LIBRUS_API_EVENT_TYPES -> { data.startProgress(R.string.edziennik_progress_endpoint_event_types) - LibrusApiEventTypes(data) { onSuccess() } + LibrusApiEventTypes(data, onSuccess) } ENDPOINT_LIBRUS_API_EVENTS -> { data.startProgress(R.string.edziennik_progress_endpoint_events) - LibrusApiEvents(data) { onSuccess() } + LibrusApiEvents(data, onSuccess) } ENDPOINT_LIBRUS_API_HOMEWORK -> { data.startProgress(R.string.edziennik_progress_endpoint_homework) - LibrusApiHomework(data) { onSuccess() } + LibrusApiHomework(data, onSuccess) } ENDPOINT_LIBRUS_API_LUCKY_NUMBER -> { data.startProgress(R.string.edziennik_progress_endpoint_lucky_number) - LibrusApiLuckyNumber(data) { onSuccess() } + LibrusApiLuckyNumber(data, onSuccess) } ENDPOINT_LIBRUS_API_NOTICE_TYPES -> { data.startProgress(R.string.edziennik_progress_endpoint_notice_types) - LibrusApiNoticeTypes(data) { onSuccess() } + LibrusApiNoticeTypes(data, onSuccess) } ENDPOINT_LIBRUS_API_NOTICES -> { data.startProgress(R.string.edziennik_progress_endpoint_notices) - LibrusApiNotices(data) { onSuccess() } + LibrusApiNotices(data, onSuccess) } ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES -> { data.startProgress(R.string.edziennik_progress_endpoint_attendance_types) - LibrusApiAttendanceTypes(data) { onSuccess() } + LibrusApiAttendanceTypes(data, onSuccess) } ENDPOINT_LIBRUS_API_ATTENDANCES -> { data.startProgress(R.string.edziennik_progress_endpoint_attendance) - LibrusApiAttendances(data) { onSuccess() } + LibrusApiAttendances(data, onSuccess) } ENDPOINT_LIBRUS_API_ANNOUNCEMENTS -> { data.startProgress(R.string.edziennik_progress_endpoint_announcements) - LibrusApiAnnouncements(data) { onSuccess() } + LibrusApiAnnouncements(data, onSuccess) } ENDPOINT_LIBRUS_API_PT_MEETINGS -> { data.startProgress(R.string.edziennik_progress_endpoint_pt_meetings) - LibrusApiPtMeetings(data) { onSuccess() } + LibrusApiPtMeetings(data, onSuccess) } ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES -> { data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_day_types) - LibrusApiTeacherFreeDayTypes(data) { onSuccess() } + LibrusApiTeacherFreeDayTypes(data, onSuccess) } ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS -> { data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_days) - LibrusApiTeacherFreeDays(data) { onSuccess() } + LibrusApiTeacherFreeDays(data, onSuccess) } /** @@ -142,11 +142,11 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { */ ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK -> { data.startProgress(R.string.edziennik_progress_endpoint_homework) - LibrusSynergiaHomework(data) { onSuccess() } + LibrusSynergiaHomework(data, onSuccess) } ENDPOINT_LIBRUS_SYNERGIA_INFO -> { data.startProgress(R.string.edziennik_progress_endpoint_student_info) - LibrusSynergiaInfo(data) { onSuccess() } + LibrusSynergiaInfo(data, onSuccess) } /** @@ -154,11 +154,11 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { */ ENDPOINT_LIBRUS_MESSAGES_RECEIVED -> { data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) - LibrusMessagesGetList(data, type = Message.TYPE_RECEIVED) { onSuccess() } + LibrusMessagesGetList(data, type = Message.TYPE_RECEIVED, onSuccess = onSuccess) } ENDPOINT_LIBRUS_MESSAGES_SENT -> { data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox) - LibrusMessagesGetList(data, type = Message.TYPE_SENT) { onSuccess() } + LibrusMessagesGetList(data, type = Message.TYPE_SENT, onSuccess = onSuccess) } else -> onSuccess() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt index e287d1db..e6f2aca5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/MobidziennikData.kt @@ -42,7 +42,7 @@ class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) { when (endpointId) { ENDPOINT_MOBIDZIENNIK_API_MAIN -> { data.startProgress(R.string.edziennik_progress_endpoint_data) - MobidziennikApi(data) { onSuccess() } + MobidziennikApi(data, onSuccess) } ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX -> { data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) @@ -75,4 +75,4 @@ class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) { else -> onSuccess() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt index e7bcff7f..432a21d2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/data/VulcanData.kt @@ -38,39 +38,39 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { when (endpointId) { ENDPOINT_VULCAN_API_DICTIONARIES -> { data.startProgress(R.string.edziennik_progress_endpoint_dictionaries) - VulcanApiDictionaries(data) { onSuccess() } + VulcanApiDictionaries(data, onSuccess) } ENDPOINT_VULCAN_API_GRADES -> { data.startProgress(R.string.edziennik_progress_endpoint_grades) - VulcanApiGrades(data) { onSuccess() } + VulcanApiGrades(data, onSuccess) } ENDPOINT_VULCAN_API_GRADES_SUMMARY -> { data.startProgress(R.string.edziennik_progress_endpoint_proposed_grades) - VulcanApiProposedGrades(data) { onSuccess() } + VulcanApiProposedGrades(data, onSuccess) } ENDPOINT_VULCAN_API_EVENTS -> { data.startProgress(R.string.edziennik_progress_endpoint_events) - VulcanApiEvents(data, isHomework = false) { onSuccess() } + VulcanApiEvents(data, isHomework = false, onSuccess = onSuccess) } ENDPOINT_VULCAN_API_HOMEWORK -> { data.startProgress(R.string.edziennik_progress_endpoint_homework) - VulcanApiEvents(data, isHomework = true) { onSuccess() } + VulcanApiEvents(data, isHomework = true, onSuccess = onSuccess) } ENDPOINT_VULCAN_API_NOTICES -> { data.startProgress(R.string.edziennik_progress_endpoint_notices) - VulcanApiNotices(data) { onSuccess() } + VulcanApiNotices(data, onSuccess) } ENDPOINT_VULCAN_API_ATTENDANCE -> { data.startProgress(R.string.edziennik_progress_endpoint_attendance) - VulcanApiAttendance(data) { onSuccess() } + VulcanApiAttendance(data, onSuccess) } ENDPOINT_VULCAN_API_MESSAGES_INBOX -> { data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) - VulcanApiMessagesInbox(data) { onSuccess() } + VulcanApiMessagesInbox(data, onSuccess) } ENDPOINT_VULCAN_API_MESSAGES_SENT -> { data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox) - VulcanApiMessagesSent(data) { onSuccess() } + VulcanApiMessagesSent(data, onSuccess) } else -> onSuccess() } From 14cd548dffb3fbd2c5bb369ae84012e855425b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 5 Nov 2019 18:49:40 +0100 Subject: [PATCH 135/691] [Sync] Add more AppManager intents to launch. --- .../szczodrzynski/edziennik/MainActivity.kt | 39 ++++++++----------- .../edziennik/utils/AppManagerIntentList.kt | 24 ++++++++++++ 2 files changed, 41 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/utils/AppManagerIntentList.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt index 183d00de..11f7155b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt @@ -2,10 +2,15 @@ package pl.szczodrzynski.edziennik import android.app.Activity import android.app.ActivityManager -import android.content.* +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.PackageManager import android.graphics.BitmapFactory import android.graphics.drawable.BitmapDrawable import android.os.* +import android.provider.Settings import android.view.Gravity import android.view.View import android.widget.Toast @@ -64,6 +69,7 @@ import pl.szczodrzynski.edziennik.utils.Themes import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.dpToPx +import pl.szczodrzynski.edziennik.utils.appManagerIntentList import pl.szczodrzynski.edziennik.utils.models.NavTarget import pl.szczodrzynski.navlib.* import pl.szczodrzynski.navlib.SystemBarsUtil.Companion.COLOR_HALF_TRANSPARENT @@ -582,29 +588,18 @@ class MainActivity : AppCompatActivity() { .setMessage(R.string.app_manager_dialog_text) .setPositiveButton(R.string.ok) { dialog, which -> try { - val intent = Intent() - intent.component = ComponentName( - "com.huawei.systemmanager", - "com.huawei.systemmanager.appcontrol.activity.StartupAppControlActivity" - ) - startActivity(intent) - } catch (e: Exception) { - e.printStackTrace() - try { - val intent = Intent() - intent.component = ComponentName( - "com.asus.mobilemanager", - "com.asus.mobilemanager.MainActivity" - ) - startActivity(intent) - } catch (e: Exception) { - try { - startActivity(Intent(android.provider.Settings.ACTION_SETTINGS)) - } catch (e: Exception) { - e.printStackTrace() - Toast.makeText(this, R.string.app_manager_open_failed, Toast.LENGTH_SHORT).show() + for (intent in appManagerIntentList) { + if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) { + startActivity(intent) } } + } catch (e: Exception) { + try { + startActivity(Intent(Settings.ACTION_SETTINGS)) + } catch (e: Exception) { + e.printStackTrace() + Toast.makeText(this, R.string.app_manager_open_failed, Toast.LENGTH_SHORT).show() + } } } .setNeutralButton(R.string.dont_ask_again) { dialog, which -> diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/AppManagerIntentList.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/AppManagerIntentList.kt new file mode 100644 index 00000000..5e802dba --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/AppManagerIntentList.kt @@ -0,0 +1,24 @@ +package pl.szczodrzynski.edziennik.utils + +import android.content.ComponentName +import android.content.Intent + +val appManagerIntentList = listOf( + Intent().setClassName("com.miui.powerkeeper", "com.miui.powerkeeper.ui.HiddenAppsConfigActivity") + .putExtra("packageName", "pl.szczodrzynski.edziennik") + .putExtra("package_label", "Szkolny.eu"), + Intent().setComponent(ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")), + Intent().setComponent(ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity")), + Intent().setComponent(ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")), + Intent().setComponent(ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.appcontrol.activity.StartupAppControlActivity")), + Intent().setComponent(ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity")), + Intent().setComponent(ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")), + Intent().setComponent(ComponentName("com.coloros.safecenter", "com.coloros.safecenter.startupapp.StartupAppListActivity")), + Intent().setComponent(ComponentName("com.oppo.safe", "com.oppo.safe.permission.startup.StartupAppListActivity")), + Intent().setComponent(ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity")), + Intent().setComponent(ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.BgStartUpManager")), + Intent().setComponent(ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity")), + Intent().setComponent(ComponentName("com.samsung.android.lool", "com.samsung.android.sm.ui.battery.BatteryActivity")), + Intent().setComponent(ComponentName("com.htc.pitroad", "com.htc.pitroad.landingpage.activity.LandingPageActivity")), + Intent().setComponent(ComponentName("com.asus.mobilemanager", "com.asus.mobilemanager.MainActivity")) +) \ No newline at end of file From 39c8a743bbeff5c2534ffcf5d91767b605517bb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 5 Nov 2019 21:42:16 +0100 Subject: [PATCH 136/691] [Login/Librus] Add Librus captcha activity/dialog. --- app/src/main/AndroidManifest.xml | 52 ++++---- .../ui/modules/home/HomeFragment.java | 10 ++ .../login/LoginLibrusCaptchaActivity.kt | 116 ++++++++++++++++++ app/src/main/res/layout/fragment_home.xml | 32 ++++- app/src/main/res/values/strings.xml | 1 + 5 files changed, 180 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusCaptchaActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2d1ad42c..dd139fc5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,10 +15,14 @@ android:theme="@style/SplashTheme" android:usesCleartextTraffic="true" tools:ignore="UnusedAttribute"> + @@ -29,7 +33,7 @@ @@ -39,7 +43,7 @@ android:label="@string/app_name" android:theme="@style/AppTheme" /> @@ -101,22 +105,18 @@ android:excludeFromRecents="true" android:noHistory="true" android:theme="@style/AppTheme.NoDisplay" /> - - - - @@ -169,7 +169,6 @@ android:name="android.appwidget.provider" android:resource="@xml/widget_notifications_info" /> - @@ -188,7 +187,6 @@ - @@ -196,14 +194,7 @@ - - - - - - + + + + + + + + + + + + + + @@ -222,14 +230,6 @@ - - - - - - @@ -244,4 +244,4 @@ - + \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java index d5aca94b..8f606cac 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/HomeFragment.java @@ -30,6 +30,7 @@ import androidx.fragment.app.Fragment; import androidx.work.WorkManager; import com.afollestad.materialdialogs.MaterialDialog; +import com.chuckerteam.chucker.api.Chucker; import com.mikepenz.iconics.IconicsColor; import com.mikepenz.iconics.IconicsDrawable; import com.mikepenz.iconics.IconicsSize; @@ -50,6 +51,7 @@ import pl.szczodrzynski.edziennik.databinding.CardLuckyNumberBinding; import pl.szczodrzynski.edziennik.databinding.CardUpdateBinding; import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding; import pl.szczodrzynski.edziennik.receivers.BootReceiver; +import pl.szczodrzynski.edziennik.ui.modules.login.LoginLibrusCaptchaActivity; import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeActivity; import pl.szczodrzynski.edziennik.utils.Colors; import pl.szczodrzynski.edziennik.utils.Themes; @@ -104,6 +106,14 @@ public class HomeFragment extends Fragment { b.pruneWorkButton.setOnClickListener((v -> WorkManager.getInstance(app).pruneWork())); + b.runChucker.setOnClickListener((v -> { + startActivity(Chucker.getLaunchIntent(activity, Chucker.SCREEN_HTTP)); + })); + + b.librusCaptchaButton.setOnClickListener((v -> { + startActivity(new Intent(activity, LoginLibrusCaptchaActivity.class)); + })); + //((TextView)v.findViewById(R.id.nextSync)).setText(getString(R.string.next_sync_format,Time.fromMillis(app.appJobs.syncJobTime).getStringHMS())); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusCaptchaActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusCaptchaActivity.kt new file mode 100644 index 00000000..3db5ca45 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginLibrusCaptchaActivity.kt @@ -0,0 +1,116 @@ +package pl.szczodrzynski.edziennik.ui.modules.login + +import android.annotation.SuppressLint +import android.graphics.Color +import android.os.Build +import android.os.Bundle +import android.util.Base64 +import android.webkit.JavascriptInterface +import android.webkit.WebView +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import com.afollestad.materialdialogs.MaterialDialog +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.LIBRUS_USER_AGENT +import pl.szczodrzynski.edziennik.utils.Themes +import pl.szczodrzynski.edziennik.utils.Utils.hexFromColorInt +import java.nio.charset.Charset + +class LoginLibrusCaptchaActivity : AppCompatActivity() { + companion object { + private const val TAG = "LoginLibrusCaptchaActivity" + } + + private lateinit var webView: WebView + private lateinit var dialog: AlertDialog + private lateinit var jsInterface: CaptchaCallbackInterface + + @SuppressLint("AddJavascriptInterface", "SetJavaScriptEnabled") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setTheme(Themes.appThemeNoDisplay) + setFinishOnTouchOutside(false) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + WebView.setWebContentsDebuggingEnabled(true) + } + + val base64Content = """ +PCFET0NUWVBFIGh0bWw+PGh0bWw+PGhlYWQ+PHNjcmlwdCBzcmM9Imh0dHBzOi8vd3d3Lmdvb2ds +ZS5jb20vcmVjYXB0Y2hhL2FwaS5qcz9vbmxvYWQ9cmVhZHkmcmVuZGVyPWV4cGxpY2l0Ij48L3Nj +cmlwdD48L2hlYWQ+PGJvZHk+PGJyPjxjZW50ZXIgaWQ9ImdyIj48L2NlbnRlcj48YnI+PHNjcmlw +dD5mdW5jdGlvbiByZWFkeSgpe2dyZWNhcHRjaGEucmVuZGVyKCdncicse3NpdGVrZXk6JzZMZjQ4 +bW9VQUFBQUFCOUNsaGR2SHI0NmdSV1ItQ04zMUNYUVBHMlUnLHRoZW1lOidUSEVNRScsY2FsbGJh +Y2s6ZnVuY3Rpb24oZSl7d2luZG93LmlmLmNhbGxiYWNrKGUpO30sImV4cGlyZWQtY2FsbGJhY2si +OmZ1bmN0aW9uKCl7d2luZG93LmlmLmV4cGlyZWRDYWxsYmFjayhlKTt9LCJlcnJvci1jYWxsYmFj +ayI6ZnVuY3Rpb24oKXt3aW5kb3cuaWYuZXJyb3JDYWxsYmFjayhlKTt9fSk7fTwvc2NyaXB0Pjwv +Ym9keT48L2h0bWw+""" + + val backgroundColor = if (Themes.isDark) 0x424242 else 0xffffff + val backgroundColorString = hexFromColorInt(backgroundColor) + + val htmlContent = Base64.decode(base64Content, Base64.DEFAULT) + .toString(Charset.defaultCharset()) + .replace("COLOR", backgroundColorString, true) + .replace("THEME", if (Themes.isDark) "dark" else "light") + + jsInterface = object : CaptchaCallbackInterface { + @JavascriptInterface + override fun callback(recaptchaResponse: String) { + MaterialDialog.Builder(this@LoginLibrusCaptchaActivity) + .title("Captcha checked") + .content("Response: $recaptchaResponse") + .positiveText("OK") + .show() + } + + @JavascriptInterface + override fun expiredCallback() { + MaterialDialog.Builder(this@LoginLibrusCaptchaActivity) + .title("Captcha expired") + .content("Captcha expired") + .positiveText("OK") + .show() + } + + @JavascriptInterface + override fun errorCallback() { + MaterialDialog.Builder(this@LoginLibrusCaptchaActivity) + .title("Captcha error") + .content("Captcha error") + .positiveText("OK") + .show() + } + } + + webView = WebView(this).apply { + //setBackgroundColor((backgroundColor.toLong() or 0xff000000).toInt()) + setBackgroundColor(Color.TRANSPARENT) + settings.javaScriptEnabled = true + settings.userAgentString = LIBRUS_USER_AGENT + addJavascriptInterface(jsInterface, "if") + loadDataWithBaseURL("https://portal.librus.pl/rodzina/login/", htmlContent, "text/html", "UTF-8", null) + setLayerType(WebView.LAYER_TYPE_SOFTWARE, null) + } + + dialog = MaterialAlertDialogBuilder(this) + .setTitle(R.string.login_librus_captcha_title) + .setView(webView) + .setNegativeButton(R.string.cancel) { dialog, _ -> + dialog.dismiss() + finish() + } + .setCancelable(false) + .show() + } + + interface CaptchaCallbackInterface { + @JavascriptInterface + fun callback(recaptchaResponse: String) + @JavascriptInterface + fun expiredCallback() + @JavascriptInterface + fun errorCallback() + } +} diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index cedad80b..10b278a4 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -36,24 +36,46 @@ android:layout_height="wrap_content" android:visibility="gone" android:orientation="vertical" - tools:visibility="visible"> + tools:visibility="visible" + tools:ignore="HardcodedText"> + + + + + + android:layout_height="wrap_content" + android:gravity="center"> + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a08886af..dafa8099 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -984,4 +984,5 @@ Nie pytaj ponownie Nie udało się otworzyć ustawień Tworzenie powiadomień + Librus - logowanie From ca1c691bf076347d568f33193524dd2712b8533f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 5 Nov 2019 22:12:15 +0100 Subject: [PATCH 137/691] [3.9.0-dev] Update build.gradle and changelog --- app/src/main/assets/pl-changelog.html | 54 ++------------------------- build.gradle | 4 +- 2 files changed, 6 insertions(+), 52 deletions(-) diff --git a/app/src/main/assets/pl-changelog.html b/app/src/main/assets/pl-changelog.html index eab231b6..d44c0246 100644 --- a/app/src/main/assets/pl-changelog.html +++ b/app/src/main/assets/pl-changelog.html @@ -31,57 +31,11 @@ -

Wersja 3.1.1, 2019-10-09

+

Wersja 4.0, 2019-jeszcze-nie-wiem-kiedy

    -
  • Librus: poprawiona synchronizacja kategorii i kolorów ocen.
  • -
  • Zmieniony kolor dolnego paska w ciemnym motywie.
  • -
  • Zaktualizowany licznik czasu lekcji.
  • -
- -

Wersja 3.1, 2019-09-29

-
    -
  • Poprawiony interfejs zadań domowych.
  • -
  • Librus: wyświetlanie komentarzy ocen.
  • -
  • Librus: wyświetlanie nieobecności nauczycieli w Terminarzu.
  • -
  • Librus: usprawniona synchronizacja ocen.
  • -
  • Poprawki angielskiego tłumaczenia.
  • -
- -

Wersja 3.0.3, 2019-09-26

-
    -
  • Librus: poprawka kilku błędów synchronizacji.
  • -
  • Vulcan: prawidłowe oznaczanie wiadomości jako przeczytana.
  • -
  • Vulcan: poprawiona synchronizacja wiadomości i frekwencji.
  • -
  • Vulcan: poprawka błędów logowania.
  • -
- -

Wersja 3.0.2, 2019-09-24

-
    -
  • Librus: pobieranie Bieżących ocen opisowych.
  • -
  • Poprawki UI: kolor ikon paska statusu w jasnym motywie.
  • -
  • Poprawka braku skanera QR do przekazywania powiadomień.
  • -
  • Poprawka wyboru koloru i daty własnego wydarzenia, które crashowały aplikację.
  • -
- -

Wersja 3.0.1, 2019-09-19

-
    -
  • Librus: Poprawa błędu synchronizacji.
  • -
  • Poprawki UI związane z paskiem nawigacji.
  • -
  • Mobidziennik: Pobieranie ocen w niektórych przedmiotach.
  • -
- -

Wersja 3.0, 2019-09-13

-
    -
  • Nowy wygląd i sposób nawigacji w całej aplikacji.
  • -
  • Menu nawigacji można teraz otworzyć przyciskiem na dolnym pasku. Pociągnięcie w górę tego paska wyświetla menu kontekstowe dotyczące danego widoku.
  • -
  • Założyliśmy serwer Discord! https://discord.gg/n9e8pWr
  • -
    -
  • Librus: poprawka powielonych ogłoszeń szkolnych.
  • -
  • Naprawiłem błąd nieskończonej synchronizacji w Vulcanie.
  • -
  • Naprawiłem crash launchera przy dodaniu widgetu.
  • -
  • Naprawiłem częste crashe związane z widokiem kalendarza.
  • -
  • Nowe, ładniejsze (choć trochę) motywy kolorów.
  • -
  • Dużo drobnych poprawek UI i działania aplikacji.
  • +
  • UWAGA. To jest wersja in-development. Wiele funkcji może nie działać prawidłowo (lub wcale), co oznacza tylko że nie zostały jeszcze przeniesione + z wersji 3.x. Proszę o cierpliwość oraz nie udostępnianie tej wersji nikomu.
  • +
  • Bardzo dużo zmian
+ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/timetable_lesson.xml b/app/src/main/res/layout/timetable_lesson.xml new file mode 100644 index 00000000..4b857f8f --- /dev/null +++ b/app/src/main/res/layout/timetable_lesson.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index b7deaa68..972b8254 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -3,4 +3,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dafa8099..dd006b4d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -985,4 +985,10 @@ Nie udało się otworzyć ustawień Tworzenie powiadomień Librus - logowanie + Dzisiaj + Lekcja odwołana + Zastępstwo + Zastępstwo: zamiast %s + Lekcja przeniesiona na godz. %s + Lekcja przeniesiona na %s, godz. %s diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 116c7a40..6e2b64de 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,4 +1,4 @@ - + From 563f08b0ab56808e70e4c931e80b837af5a9d128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 10 Nov 2019 18:53:45 +0100 Subject: [PATCH 143/691] [UI] Update Timetable lesson layout. Add lesson number text. --- .../timetable/v2/day/TimetableDayFragment.kt | 6 +-- .../drawable/timetable_lesson_annotation.xml | 6 ++- .../timetable_subject_color_rounded.xml | 10 ++++ app/src/main/res/layout/timetable_lesson.xml | 52 ++++++++++++++----- 4 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 app/src/main/res/drawable/timetable_subject_color_rounded.xml diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt index 92044328..bc5637eb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt @@ -129,10 +129,10 @@ class TimetableDayFragment(val date: Date) : Fragment() { //lb.subjectName.typeface = Typeface.create("sans-serif-light", Typeface.BOLD) when (lesson.type) { Lesson.TYPE_NORMAL -> { - lb.annotation.visibility = View.GONE + lb.annotationVisible = false } Lesson.TYPE_CANCELLED -> { - lb.annotation.visibility = View.VISIBLE + lb.annotationVisible = true lb.annotation.setText(R.string.timetable_lesson_cancelled) lb.annotation.background.colorFilter = PorterDuffColorFilter( getColorFromAttr(activity, R.attr.timetable_lesson_cancelled_color), @@ -141,7 +141,7 @@ class TimetableDayFragment(val date: Date) : Fragment() { //lb.subjectName.typeface = Typeface.DEFAULT } Lesson.TYPE_CHANGE -> { - lb.annotation.visibility = View.VISIBLE + lb.annotationVisible = true if (lesson.subjectId != lesson.oldSubjectId && lesson.teacherId != lesson.oldTeacherId) { lb.annotation.setText( R.string.timetable_lesson_change_format, diff --git a/app/src/main/res/drawable/timetable_lesson_annotation.xml b/app/src/main/res/drawable/timetable_lesson_annotation.xml index b22dc045..4c89f473 100644 --- a/app/src/main/res/drawable/timetable_lesson_annotation.xml +++ b/app/src/main/res/drawable/timetable_lesson_annotation.xml @@ -1,7 +1,11 @@ + + - + \ No newline at end of file diff --git a/app/src/main/res/drawable/timetable_subject_color_rounded.xml b/app/src/main/res/drawable/timetable_subject_color_rounded.xml new file mode 100644 index 00000000..5466dfa2 --- /dev/null +++ b/app/src/main/res/drawable/timetable_subject_color_rounded.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/timetable_lesson.xml b/app/src/main/res/layout/timetable_lesson.xml index 4b857f8f..5fadc319 100644 --- a/app/src/main/res/layout/timetable_lesson.xml +++ b/app/src/main/res/layout/timetable_lesson.xml @@ -2,6 +2,11 @@ + + + + android:visibility="@{annotationVisible ? View.VISIBLE : View.GONE}" + tools:text="Zastępstwo: zamiast lekcji język polski z Adam Dodatkowy" + tools:visibility="gone" /> + android:orientation="horizontal" + android:baselineAligned="false"> + + app:autoSizeMaxTextSize="16sp" + app:autoSizeMinTextSize="12sp" + app:autoSizeTextType="uniform" + tools:maxLines="2" + tools:text="pracownia urządzeń techniki komputerowej" /> + + + android:paddingEnd="8dp"> From 0bcd190714abe785b6fcde20897eb871d209b4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 10 Nov 2019 20:27:26 +0100 Subject: [PATCH 144/691] [APIv2] Add Librus Fake login. --- .../szczodrzynski/edziennik/api/v2/Constants.kt | 8 ++++++++ .../edziennik/api/v2/LoginMethods.kt | 4 ++-- .../edziennik/api/v2/librus/data/LibrusApi.kt | 4 ++-- .../api/v2/librus/data/LibrusPortal.kt | 4 ++-- .../v2/librus/firstlogin/LibrusFirstLogin.kt | 3 ++- .../api/v2/librus/login/LibrusLoginPortal.kt | 17 +++++++++-------- .../v2/librus/login/SynergiaTokenExtractor.kt | 4 ++-- .../edziennik/api/v2/models/Data.kt | 8 ++++---- .../ui/modules/login/LoginChooserFragment.java | 6 ++++++ .../ui/modules/login/LoginProgressFragment.java | 5 +++++ .../main/res/layout/fragment_login_chooser.xml | 10 ++++++++++ app/src/main/res/layout/timetable_lesson.xml | 7 +++++-- 12 files changed, 57 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt index b34b6900..ef594568 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/Constants.kt @@ -14,6 +14,14 @@ val SYSTEM_USER_AGENT = System.getProperty("http.agent") ?: "Dalvik/2.1.0 Androi val SERVER_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME} $SYSTEM_USER_AGENT" +const val FAKE_LIBRUS_API = "http://librus.szkolny.eu/api" +const val FAKE_LIBRUS_PORTAL = "http://librus.szkolny.eu" +const val FAKE_LIBRUS_AUTHORIZE = "http://librus.szkolny.eu/authorize.php" +const val FAKE_LIBRUS_LOGIN = "http://librus.szkolny.eu/login_action.php" +const val FAKE_LIBRUS_TOKEN = "http://librus.szkolny.eu/access_token.php" +const val FAKE_LIBRUS_ACCOUNT = "/synergia_accounts_fresh.php?login=" +const val FAKE_LIBRUS_ACCOUNTS = "/synergia_accounts.php" + val LIBRUS_USER_AGENT = "$SYSTEM_USER_AGENT LibrusMobileApp" const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0" const val LIBRUS_CLIENT_ID = "wmSyUMo8llDAs4y9tJVYY92oyZ6h4lAt7KCuy0Gv" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt index 6dd2ffb9..1d4b6288 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/LoginMethods.kt @@ -66,13 +66,13 @@ val librusLoginMethods = listOf( }, LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, LibrusLoginSynergia::class.java) - .withIsPossible { _, _ -> true } + .withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") } .withRequiredLoginMethod { profile, _ -> if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED }, LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, LibrusLoginMessages::class.java) - .withIsPossible { _, _ -> true } + .withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") } .withRequiredLoginMethod { profile, _ -> if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt index d913e303..4f8d12eb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusApi.kt @@ -28,7 +28,7 @@ open class LibrusApi(open val data: DataLibrus) { fun apiGet(tag: String, endpoint: String, method: Int = GET, payload: JsonObject? = null, onSuccess: (json: JsonObject) -> Unit) { - d(tag, "Request: Librus/Api - $LIBRUS_API_URL/$endpoint") + d(tag, "Request: Librus/Api - ${if (data.fakeLogin) FAKE_LIBRUS_API else LIBRUS_API_URL}/$endpoint") val callback = object : JsonCallbackHandler() { override fun onSuccess(json: JsonObject?, response: Response?) { @@ -90,7 +90,7 @@ open class LibrusApi(open val data: DataLibrus) { } Request.builder() - .url("$LIBRUS_API_URL/$endpoint") + .url("${if (data.fakeLogin) FAKE_LIBRUS_API else LIBRUS_API_URL}/$endpoint") .userAgent(LIBRUS_USER_AGENT) .addHeader("Authorization", "Bearer ${data.apiAccessToken}") .apply { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt index b86a6956..97fdf33d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusPortal.kt @@ -24,7 +24,7 @@ open class LibrusPortal(open val data: DataLibrus) { fun portalGet(tag: String, endpoint: String, method: Int = GET, payload: JsonObject? = null, onSuccess: (json: JsonObject, response: Response?) -> Unit) { - d(tag, "Request: Librus/Portal - $LIBRUS_PORTAL_URL$endpoint") + d(tag, "Request: Librus/Portal - ${if (data.fakeLogin) FAKE_LIBRUS_PORTAL else LIBRUS_PORTAL_URL}$endpoint") val callback = object : JsonCallbackHandler() { override fun onSuccess(json: JsonObject?, response: Response?) { @@ -81,7 +81,7 @@ open class LibrusPortal(open val data: DataLibrus) { } Request.builder() - .url(LIBRUS_PORTAL_URL + endpoint) + .url((if (data.fakeLogin) FAKE_LIBRUS_PORTAL else LIBRUS_PORTAL_URL) + endpoint) .userAgent(LIBRUS_USER_AGENT) .addHeader("Authorization", "Bearer ${data.portalAccessToken}") .apply { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt index 08daf09a..41d77921 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/firstlogin/LibrusFirstLogin.kt @@ -3,6 +3,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.firstlogin import org.greenrobot.eventbus.EventBus import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.ERROR_NO_STUDENTS_IN_ACCOUNT +import pl.szczodrzynski.edziennik.api.v2.FAKE_LIBRUS_ACCOUNTS import pl.szczodrzynski.edziennik.api.v2.LIBRUS_ACCOUNTS_URL import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_EMAIL import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent @@ -29,7 +30,7 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) { if (data.loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) { // email login: use Portal for account list LibrusLoginPortal(data) { - portal.portalGet(TAG, LIBRUS_ACCOUNTS_URL) { json, response -> + portal.portalGet(TAG, if (data.fakeLogin) FAKE_LIBRUS_ACCOUNTS else LIBRUS_ACCOUNTS_URL) { json, response -> val accounts = json.getJsonArray("accounts") if (accounts == null || accounts.size() < 1) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLoginPortal.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLoginPortal.kt index 67730e01..dc129626 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLoginPortal.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/LibrusLoginPortal.kt @@ -7,14 +7,15 @@ import im.wangchao.mhttp.Response import im.wangchao.mhttp.body.MediaTypeUtils import im.wangchao.mhttp.callback.JsonCallbackHandler import im.wangchao.mhttp.callback.TextCallbackHandler -import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.models.ApiError -import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.getInt +import pl.szczodrzynski.edziennik.getString +import pl.szczodrzynski.edziennik.getUnixDate import pl.szczodrzynski.edziennik.utils.Utils.d import java.net.HttpURLConnection.HTTP_UNAUTHORIZED -import java.util.ArrayList +import java.util.* import java.util.regex.Pattern class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) { @@ -42,7 +43,7 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) { } else { data.app.cookieJar.clearForDomain("portal.librus.pl") - authorize(LIBRUS_AUTHORIZE_URL) + authorize(if (data.fakeLogin) FAKE_LIBRUS_AUTHORIZE else LIBRUS_AUTHORIZE_URL) } }} @@ -86,10 +87,10 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) { } private fun login(csrfToken: String) { - d(TAG, "Request: Librus/Login/Portal - $LIBRUS_LOGIN_URL") + d(TAG, "Request: Librus/Login/Portal - ${if (data.fakeLogin) FAKE_LIBRUS_LOGIN else LIBRUS_LOGIN_URL}") Request.builder() - .url(LIBRUS_LOGIN_URL) + .url(if (data.fakeLogin) FAKE_LIBRUS_LOGIN else LIBRUS_LOGIN_URL) .userAgent(LIBRUS_USER_AGENT) .addParameter("email", data.portalEmail) .addParameter("password", data.portalPassword) @@ -135,7 +136,7 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) { private var refreshTokenFailed = false private fun accessToken(code: String?, refreshToken: String?) { - d(TAG, "Request: Librus/Login/Portal - $LIBRUS_TOKEN_URL") + d(TAG, "Request: Librus/Login/Portal - ${if (data.fakeLogin) FAKE_LIBRUS_TOKEN else LIBRUS_TOKEN_URL}") val onSuccess = { json: JsonObject, response: Response? -> data.portalAccessToken = json.getString("access_token") @@ -204,7 +205,7 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) { } Request.builder() - .url(LIBRUS_TOKEN_URL) + .url(if (data.fakeLogin) FAKE_LIBRUS_TOKEN else LIBRUS_TOKEN_URL) .userAgent(LIBRUS_USER_AGENT) .addParams(params) .post() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt index 27fdbe6a..0d80cd65 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/login/SynergiaTokenExtractor.kt @@ -43,7 +43,7 @@ class SynergiaTokenExtractor(override val data: DataLibrus, val onSuccess: () -> val accountLogin = data.apiLogin ?: return false data.portalAccessToken ?: return false - d(TAG, "Request: Librus/SynergiaTokenExtractor - $LIBRUS_ACCOUNT_URL$accountLogin") + d(TAG, "Request: Librus/SynergiaTokenExtractor - ${if (data.fakeLogin) FAKE_LIBRUS_ACCOUNT else LIBRUS_ACCOUNT_URL}$accountLogin") val onSuccess = { json: JsonObject, response: Response? -> // synergiaAccount is executed when a synergia token needs a refresh @@ -67,7 +67,7 @@ class SynergiaTokenExtractor(override val data: DataLibrus, val onSuccess: () -> } } - portalGet(TAG, LIBRUS_ACCOUNT_URL+accountLogin, onSuccess = onSuccess) + portalGet(TAG, (if (data.fakeLogin) FAKE_LIBRUS_ACCOUNT else LIBRUS_ACCOUNT_URL)+accountLogin, onSuccess = onSuccess) return true } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index bac36618..2db316d3 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -5,7 +5,7 @@ import android.util.SparseArray import androidx.core.util.size import com.google.gson.JsonObject import im.wangchao.mhttp.Response -import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.DataNotifications import pl.szczodrzynski.edziennik.api.v2.EXCEPTION_NOTIFY_AND_SYNC import pl.szczodrzynski.edziennik.api.v2.ServerSync @@ -38,11 +38,8 @@ import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsence import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceType import pl.szczodrzynski.edziennik.data.db.modules.teams.Team -import pl.szczodrzynski.edziennik.singleOrNull -import pl.szczodrzynski.edziennik.toSparseArray import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.models.Date -import pl.szczodrzynski.edziennik.values import java.io.InterruptedIOException import java.net.SocketTimeoutException import java.net.UnknownHostException @@ -167,6 +164,9 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) val db: AppDb by lazy { app.db } init { + if (BuildConfig.DEBUG) { + fakeLogin = loginStore.hasLoginData("fakeLogin") + } clear() if (profile != null) { endpointTimers = db.endpointTimerDao().getAllNow(profile.id).toMutableList() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.java index 7f5a2c49..2aff483c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.java @@ -14,6 +14,7 @@ import androidx.navigation.NavController; import androidx.navigation.Navigation; import pl.szczodrzynski.edziennik.App; +import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.databinding.FragmentLoginChooserBinding; import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackActivity; @@ -26,6 +27,7 @@ public class LoginChooserFragment extends Fragment { private NavController nav; private FragmentLoginChooserBinding b; private static final String TAG = "LoginTemplate"; + public static boolean fakeLogin = false; public LoginChooserFragment() { } @@ -71,6 +73,10 @@ public class LoginChooserFragment extends Fragment { b.cancelButton.setVisibility(View.GONE); } + b.fakeLogin.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); + b.fakeLogin.setChecked(fakeLogin); + b.fakeLogin.setOnCheckedChangeListener((v, isChecked) -> fakeLogin = isChecked); + b.helpButton.setOnClickListener((v -> { startActivity(new Intent(getActivity(), FeedbackActivity.class)); })); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java index f7b84db8..08c7e919 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java @@ -19,6 +19,7 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import pl.szczodrzynski.edziennik.App; +import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.api.v2.events.ApiTaskErrorEvent; import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent; @@ -89,6 +90,10 @@ public class LoginProgressFragment extends Fragment { LoginStore loginStore = new LoginStore(-1, loginType, new JsonObject()); loginStore.copyFrom(args); + if (BuildConfig.DEBUG && LoginChooserFragment.fakeLogin) { + loginStore.putLoginData("fakeLogin", true); + } + EdziennikTask.Companion.firstLogin(loginStore).enqueue(getContext()); } diff --git a/app/src/main/res/layout/fragment_login_chooser.xml b/app/src/main/res/layout/fragment_login_chooser.xml index 78201b71..9b7937fa 100644 --- a/app/src/main/res/layout/fragment_login_chooser.xml +++ b/app/src/main/res/layout/fragment_login_chooser.xml @@ -151,6 +151,16 @@ + + + diff --git a/app/src/main/res/layout/timetable_lesson.xml b/app/src/main/res/layout/timetable_lesson.xml index 5fadc319..47892c39 100644 --- a/app/src/main/res/layout/timetable_lesson.xml +++ b/app/src/main/res/layout/timetable_lesson.xml @@ -3,6 +3,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> + + @@ -89,12 +91,13 @@ android:layout_gravity="center_vertical" android:fontFamily="sans-serif-condensed-light" android:includeFontPadding="false" - android:layout_marginTop="@{annotationVisible ? -4 : 4}" - android:layout_marginBottom="@{annotationVisible ? -4 : 0}" + android:paddingStart="4dp" android:paddingEnd="4dp" android:text="9" tools:textSize="28sp"/> + From 5fa74093170bac316c3a928eb4b4e6487ae9d222 Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sun, 10 Nov 2019 16:47:32 +0100 Subject: [PATCH 145/691] [APIv2/Librus] Add getting normal lessons --- .../api/v2/librus/data/LibrusData.kt | 6 +- .../v2/librus/data/api/LibrusApiTimetables.kt | 60 +++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index 7075e552..a34ea854 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -76,8 +76,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { LibrusApiClassrooms(data, onSuccess) } // TODO push config - // TODO timetable - + ENDPOINT_LIBRUS_API_TIMETABLES -> { + data.startProgress(R.string.edziennik_progress_endpoint_timetable) + LibrusApiTimetables(data, onSuccess) + } ENDPOINT_LIBRUS_API_NORMAL_GRADES -> { data.startProgress(R.string.edziennik_progress_endpoint_grades) LibrusApiGrades(data, onSuccess) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt new file mode 100644 index 00000000..61fd8c13 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) Kacper Ziubryniewicz 2019-11-10 + */ + +package pl.szczodrzynski.edziennik.api.v2.librus.data.api + +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus +import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_TIMETABLES +import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson +import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Time + +class LibrusApiTimetables(override val data: DataLibrus, + val onSuccess: () -> Unit) : LibrusApi(data) { + companion object { + const val TAG = "LibrusApiTimetables" + } + + init { + apiGet(TAG, "Timetables") { json -> + json.getJsonObject("Timetable")?.also { timetables -> + timetables.keySet().forEach { dateStr -> + val date = Date.fromY_m_d(dateStr) + + timetables.getJsonArray(dateStr)?.asJsonObjectList()?.forEach { lesson -> + val lessonNumber = lesson.getInt("LessonNo") + val startTime = lesson.getString("HourFrom")?.let { Time.fromH_m(it) } + val endTime = lesson.getString("HourTo")?.let { Time.fromH_m(it) } + val teacherId = lesson.getJsonObject("Teacher")?.getLong("Id") + val subjectId = lesson.getJsonObject("Subject")?.getLong("Id") + val classId = lesson.getJsonObject("Class")?.getLong("Id") + val virtualClassId = lesson.getJsonObject("VirtualClass")?.getLong("Id") + val teamId = virtualClassId ?: classId + val classroomId = lesson.getJsonObject("Classroom")?.getLong("Id") + val classroom = data.classrooms.singleOrNull { it.id == classroomId }?.name + + val lessonObject = Lesson(profileId).apply { + this.date = date + this.lessonNumber = lessonNumber + this.startTime = startTime + this.endTime = endTime + this.teacherId = teacherId + this.subjectId = subjectId + this.teamId = teamId + this.classroom = classroom + } + + // TODO add to the database + } + } + } + + data.setSyncNext(ENDPOINT_LIBRUS_API_TIMETABLES, SYNC_ALWAYS) + onSuccess() + } + } +} From 4eeaa54a47ccaf54435aea7d9ee8b7ec2934c30a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sun, 10 Nov 2019 22:57:19 +0100 Subject: [PATCH 146/691] [Timetable] Implement Librus timetable with lesson changes and shifts. Update UI. --- .../pl/szczodrzynski/edziennik/Extensions.kt | 2 +- .../api/v2/librus/data/LibrusData.kt | 1 + .../v2/librus/data/api/LibrusApiTimetables.kt | 163 ++++++++++++++---- .../data/db/modules/timetable/LessonFull.kt | 6 + .../data/db/modules/timetable/TimetableDao.kt | 3 +- .../timetable/v2/day/TimetableDayFragment.kt | 58 ++++++- .../edziennik/utils/models/Date.java | 6 + .../edziennik/utils/models/Time.java | 7 + app/src/main/res/layout/timetable_lesson.xml | 11 +- app/src/main/res/values/attrs.xml | 3 +- app/src/main/res/values/strings.xml | 4 + app/src/main/res/values/styles.xml | 6 +- 12 files changed, 231 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index bb471068..5c251a37 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -390,5 +390,5 @@ fun List.concat(delimiter: String? = null): CharSequence { } fun TextView.setText(@StringRes resid: Int, vararg formatArgs: Any) { - text = context.getString(resid, formatArgs) + text = context.getString(resid, *formatArgs) } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt index a34ea854..940d48fe 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/LibrusData.kt @@ -80,6 +80,7 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_timetable) LibrusApiTimetables(data, onSuccess) } + ENDPOINT_LIBRUS_API_NORMAL_GRADES -> { data.startProgress(R.string.edziennik_progress_endpoint_grades) LibrusApiGrades(data, onSuccess) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt index 61fd8c13..ceb4b2b2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt @@ -1,54 +1,37 @@ /* - * Copyright (c) Kacper Ziubryniewicz 2019-11-10 + * Copyright (c) Kuba Szczodrzyński 2019-11-10. */ package pl.szczodrzynski.edziennik.api.v2.librus.data.api +import com.google.gson.JsonObject import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_TIMETABLES import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Time class LibrusApiTimetables(override val data: DataLibrus, - val onSuccess: () -> Unit) : LibrusApi(data) { + val onSuccess: () -> Unit) : LibrusApi(data) { companion object { const val TAG = "LibrusApiTimetables" } init { apiGet(TAG, "Timetables") { json -> - json.getJsonObject("Timetable")?.also { timetables -> - timetables.keySet().forEach { dateStr -> - val date = Date.fromY_m_d(dateStr) + val days = json.getJsonObject("Timetable") - timetables.getJsonArray(dateStr)?.asJsonObjectList()?.forEach { lesson -> - val lessonNumber = lesson.getInt("LessonNo") - val startTime = lesson.getString("HourFrom")?.let { Time.fromH_m(it) } - val endTime = lesson.getString("HourTo")?.let { Time.fromH_m(it) } - val teacherId = lesson.getJsonObject("Teacher")?.getLong("Id") - val subjectId = lesson.getJsonObject("Subject")?.getLong("Id") - val classId = lesson.getJsonObject("Class")?.getLong("Id") - val virtualClassId = lesson.getJsonObject("VirtualClass")?.getLong("Id") - val teamId = virtualClassId ?: classId - val classroomId = lesson.getJsonObject("Classroom")?.getLong("Id") - val classroom = data.classrooms.singleOrNull { it.id == classroomId }?.name - - val lessonObject = Lesson(profileId).apply { - this.date = date - this.lessonNumber = lessonNumber - this.startTime = startTime - this.endTime = endTime - this.teacherId = teacherId - this.subjectId = subjectId - this.teamId = teamId - this.classroom = classroom - } - - // TODO add to the database + days?.entrySet()?.forEach { (dateString, dayEl) -> + val lessonDate = dateString?.let { Date.fromY_m_d(it) } ?: return@forEach + val day = dayEl?.asJsonArray + day?.forEach { lessonRangeEl -> + val lessonRange = lessonRangeEl?.asJsonArray?.asJsonObjectList() + lessonRange?.forEachIndexed { index, lesson -> + parseLesson(lessonDate, lesson) } } } @@ -57,4 +40,126 @@ class LibrusApiTimetables(override val data: DataLibrus, onSuccess() } } + + private fun parseLesson(lessonDate: Date, lesson: JsonObject) { + val lessonObject = Lesson(profileId, lesson.hashCode().toLong()) + + val isSubstitution = lesson.getBoolean("IsSubstitutionClass") ?: false + val isCancelled = lesson.getBoolean("IsCanceled") ?: false + + val lessonNo = lesson.getInt("LessonNo") ?: return + val startTime = lesson.getString("HourFrom")?.let { Time.fromH_m(it) } ?: return + val endTime = lesson.getString("HourTo")?.let { Time.fromH_m(it) } ?: return + val subjectId = lesson.getJsonObject("Subject")?.getLong("Id") + val teacherId = lesson.getJsonObject("Teacher")?.getLong("Id") + val classroomId = lesson.getJsonObject("Classroom")?.getLong("Id") ?: -1 + val virtualClassId = lesson.getJsonObject("VirtualClass")?.getLong("Id") + val teamId = lesson.getJsonObject("Class")?.getLong("Id") ?: virtualClassId + + if (isSubstitution && isCancelled) { + // shifted lesson - source + val newDate = lesson.getString("NewDate")?.let { Date.fromY_m_d(it) } ?: return + val newLessonNo = lesson.getInt("NewLessonNo") ?: return + val newStartTime = lesson.getString("NewHourFrom")?.let { Time.fromH_m(it) } ?: return + val newEndTime = lesson.getString("NewHourTo")?.let { Time.fromH_m(it) } ?: return + val newSubjectId = lesson.getJsonObject("NewSubject")?.getLong("Id") + val newTeacherId = lesson.getJsonObject("NewTeacher")?.getLong("Id") + val newClassroomId = lesson.getJsonObject("NewClassroom")?.getLong("Id") ?: -1 + val newVirtualClassId = lesson.getJsonObject("NewVirtualClass")?.getLong("Id") + val newTeamId = lesson.getJsonObject("NewClass")?.getLong("Id") ?: newVirtualClassId + + lessonObject.let { + it.type = Lesson.TYPE_SHIFTED_SOURCE + it.oldDate = lessonDate + it.oldLessonNumber = lessonNo + it.oldStartTime = startTime + it.oldEndTime = endTime + it.oldSubjectId = subjectId + it.oldTeacherId = teacherId + it.oldTeamId = teamId + it.oldClassroom = data.classrooms[classroomId]?.name + + it.date = newDate + it.lessonNumber = newLessonNo + it.startTime = newStartTime + it.endTime = newEndTime + it.subjectId = newSubjectId + it.teacherId = newTeacherId + it.teamId = newTeamId + it.classroom = data.classrooms[newClassroomId]?.name + } + } + else if (isSubstitution) { + // lesson change OR shifted lesson - target + val oldDate = lesson.getString("OrgDate")?.let { Date.fromY_m_d(it) } ?: return + val oldLessonNo = lesson.getInt("OrgLessonNo") ?: return + val oldStartTime = lesson.getString("OrgHourFrom")?.let { Time.fromH_m(it) } ?: return + val oldEndTime = lesson.getString("OrgHourTo")?.let { Time.fromH_m(it) } ?: return + val oldSubjectId = lesson.getJsonObject("OrgSubject")?.getLong("Id") + val oldTeacherId = lesson.getJsonObject("OrgTeacher")?.getLong("Id") + val oldClassroomId = lesson.getJsonObject("OrgClassroom")?.getLong("Id") ?: -1 + val oldVirtualClassId = lesson.getJsonObject("OrgVirtualClass")?.getLong("Id") + val oldTeamId = lesson.getJsonObject("OrgClass")?.getLong("Id") ?: oldVirtualClassId + + lessonObject.let { + it.type = if (lessonDate == oldDate && lessonNo == oldLessonNo) Lesson.TYPE_CHANGE else Lesson.TYPE_SHIFTED_TARGET + it.oldDate = oldDate + it.oldLessonNumber = oldLessonNo + it.oldStartTime = oldStartTime + it.oldEndTime = oldEndTime + it.oldSubjectId = oldSubjectId + it.oldTeacherId = oldTeacherId + it.oldTeamId = oldTeamId + it.oldClassroom = data.classrooms[oldClassroomId]?.name + + it.date = lessonDate + it.lessonNumber = lessonNo + it.startTime = startTime + it.endTime = endTime + it.subjectId = subjectId + it.teacherId = teacherId + it.teamId = teamId + it.classroom = data.classrooms[classroomId]?.name + } + } + else if (isCancelled) { + lessonObject.let { + it.type = Lesson.TYPE_CANCELLED + it.oldDate = lessonDate + it.oldLessonNumber = lessonNo + it.oldStartTime = startTime + it.oldEndTime = endTime + it.oldSubjectId = subjectId + it.oldTeacherId = teacherId + it.oldTeamId = teamId + it.oldClassroom = data.classrooms[classroomId]?.name + } + } + else { + lessonObject.let { + it.type = Lesson.TYPE_NORMAL + it.date = lessonDate + it.lessonNumber = lessonNo + it.startTime = startTime + it.endTime = endTime + it.subjectId = subjectId + it.teacherId = teacherId + it.teamId = teamId + it.classroom = data.classrooms[classroomId]?.name + } + } + + if (lessonObject.type != Lesson.TYPE_NORMAL) { + data.metadataList.add( + Metadata( + data.profileId, + Metadata.TYPE_LESSON_CHANGE, + lessonObject.id, + data.profile?.empty ?: false, + data.profile?.empty ?: false, + System.currentTimeMillis() + )) + } + data.lessonNewList += lessonObject + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/LessonFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/LessonFull.kt index ee573ac2..2586ea4d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/LessonFull.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/LessonFull.kt @@ -17,6 +17,12 @@ class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) { return oldDate return date ?: oldDate } + val displayLessonNumber: Int? + get() { + if (type == TYPE_SHIFTED_SOURCE) + return oldLessonNumber + return lessonNumber ?: oldLessonNumber + } val displayStartTime: Time? get() { if (type == TYPE_SHIFTED_SOURCE) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/TimetableDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/TimetableDao.kt index ee565eff..f9ab8b84 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/TimetableDao.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/TimetableDao.kt @@ -35,7 +35,8 @@ interface TimetableDao { LEFT JOIN teachers AS oldT ON timetable.profileId = oldT.profileId AND timetable.oldTeacherId = oldT.teacherId LEFT JOIN teams AS oldG ON timetable.profileId = oldG.profileId AND timetable.oldTeamId = oldG.teamId LEFT JOIN metadata ON id = thingId AND thingType = ${Metadata.TYPE_LESSON_CHANGE} AND metadata.profileId = timetable.profileId - WHERE timetable.profileId = :profileId AND (type != 3 AND date = :date) OR (type = 3 AND oldDate = :date) + WHERE timetable.profileId = :profileId AND (type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date) + ORDER BY type """) fun getForDate(profileId: Int, date: Date) : LiveData> } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt index bc5637eb..817774cf 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt @@ -122,7 +122,13 @@ class TimetableDayFragment(val date: Date) : Fragment() { }.concat(arrowRight) - lb.subjectName.text = lesson.displaySubjectName?.let { if (lesson.type == Lesson.TYPE_CANCELLED) it.asStrikethroughSpannable().asColoredSpannable(colorSecondary) else it } + lb.lessonNumber = lesson.displayLessonNumber + lb.subjectName.text = lesson.displaySubjectName?.let { + if (lesson.type == Lesson.TYPE_CANCELLED || lesson.type == Lesson.TYPE_SHIFTED_SOURCE) + it.asStrikethroughSpannable().asColoredSpannable(colorSecondary) + else + it + } lb.detailsFirst.text = listOfNotEmpty(timeRange, classroomInfo).concat(bullet) lb.detailsSecond.text = listOfNotEmpty(teacherInfo, teamInfo).concat(bullet) @@ -165,7 +171,55 @@ class TimetableDayFragment(val date: Date) : Fragment() { } lb.annotation.background.colorFilter = PorterDuffColorFilter( - getColorFromAttr(activity, R.attr.timetable_lesson_cancelled_color), + getColorFromAttr(activity, R.attr.timetable_lesson_change_color), + PorterDuff.Mode.SRC_ATOP + ) + } + Lesson.TYPE_SHIFTED_SOURCE -> { + lb.annotationVisible = true + if (lesson.date != lesson.oldDate) { + lb.annotation.setText( + R.string.timetable_lesson_shifted_other_day, + lesson.date?.stringY_m_d ?: "?", + lesson.startTime?.stringHM ?: "?" + ) + } + else if (lesson.startTime != lesson.oldStartTime) { + lb.annotation.setText( + R.string.timetable_lesson_shifted_same_day, + lesson.startTime?.stringHM ?: "?" + ) + } + else { + lb.annotation.setText(R.string.timetable_lesson_shifted) + } + + lb.annotation.background.colorFilter = PorterDuffColorFilter( + getColorFromAttr(activity, R.attr.timetable_lesson_shifted_source_color), + PorterDuff.Mode.SRC_ATOP + ) + } + Lesson.TYPE_SHIFTED_TARGET -> { + lb.annotationVisible = true + if (lesson.date != lesson.oldDate) { + lb.annotation.setText( + R.string.timetable_lesson_shifted_from_other_day, + lesson.oldDate?.stringY_m_d ?: "?", + lesson.oldStartTime?.stringHM ?: "?" + ) + } + else if (lesson.startTime != lesson.oldStartTime) { + lb.annotation.setText( + R.string.timetable_lesson_shifted_from_same_day, + lesson.oldStartTime?.stringHM ?: "?" + ) + } + else { + lb.annotation.setText(R.string.timetable_lesson_shifted_from) + } + + lb.annotation.background.colorFilter = PorterDuffColorFilter( + getColorFromAttr(activity, R.attr.timetable_lesson_shifted_target_color), PorterDuff.Mode.SRC_ATOP ) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Date.java b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Date.java index a4ab40fe..7877e1f9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Date.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Date.java @@ -1,6 +1,7 @@ package pl.szczodrzynski.edziennik.utils.models; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.text.DateFormat; import java.util.Calendar; @@ -218,6 +219,11 @@ public class Date implements Comparable { return this.getValue() - o.getValue(); } + @Override + public boolean equals(@Nullable Object obj) { + return obj instanceof Date && this.getValue() == ((Date) obj).getValue(); + } + @Override public String toString() { return "Date{" + diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Time.java b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Time.java index 07b1c422..866b530d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Time.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Time.java @@ -1,5 +1,7 @@ package pl.szczodrzynski.edziennik.utils.models; +import androidx.annotation.Nullable; + import java.util.Calendar; public class Time { @@ -173,6 +175,11 @@ public class Time { return (currentTime.getValue() >= startTime.getValue() && currentTime.getValue() <= endTime.getValue()); } + @Override + public boolean equals(@Nullable Object obj) { + return obj instanceof Time && this.getValue() == ((Time) obj).getValue(); + } + @Override public String toString() { return "Time{" + diff --git a/app/src/main/res/layout/timetable_lesson.xml b/app/src/main/res/layout/timetable_lesson.xml index 47892c39..25a3db56 100644 --- a/app/src/main/res/layout/timetable_lesson.xml +++ b/app/src/main/res/layout/timetable_lesson.xml @@ -8,6 +8,9 @@ + + android:text="@{Integer.toString(lessonNumber)}" + android:textSize="28sp" + android:visibility="@{lessonNumber != null ? View.VISIBLE : View.GONE}" + tools:text="3"/> diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 972b8254..2fbb3d4e 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -6,5 +6,6 @@ - + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dd006b4d..bffcc7f6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -991,4 +991,8 @@ Zastępstwo: zamiast %s Lekcja przeniesiona na godz. %s Lekcja przeniesiona na %s, godz. %s + Lekcja przeniesiona z godz. %s + Lekcja przeniesiona z dnia %s, godz. %s + Lekcja przeniesiona na inny termin + Lekcja przeniesiona z innego terminu diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 6e2b64de..b2164075 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -99,7 +99,8 @@ @drawable/timetable_lesson_bg_light #9f9f9f #ffb300 - #4caf50 + #A1887F + #4caf50 From d4e9e1730fd328f6491183d7a1c595227734c388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 11 Nov 2019 14:11:05 +0100 Subject: [PATCH 147/691] [APIv2] Implement getting timetable for one week. Support arguments in EdziennikTask. Create new DataRemoveModel. --- .../pl/szczodrzynski/edziennik/Extensions.kt | 19 +- .../api/v2/events/task/EdziennikTask.kt | 10 +- .../edziennik/api/v2/idziennik/Idziennik.kt | 4 +- .../api/v2/interfaces/EdziennikInterface.kt | 4 +- .../edziennik/api/v2/librus/Librus.kt | 9 +- .../v2/librus/data/api/LibrusApiTimetables.kt | 28 ++- .../api/v2/mobidziennik/Mobidziennik.kt | 4 +- .../edziennik/api/v2/models/Data.kt | 22 +- .../api/v2/models/DataRemoveModel.kt | 49 +++-- .../edziennik/api/v2/template/Template.kt | 4 +- .../edziennik/api/v2/vulcan/Vulcan.kt | 4 +- .../data/db/modules/timetable/Lesson.kt | 1 + .../data/db/modules/timetable/TimetableDao.kt | 7 + .../sync/MyFirebaseMessagingService.java | 4 +- .../modules/timetable/v2/TimetableFragment.kt | 9 + .../timetable/v2/day/TimetableDayFragment.kt | 27 ++- app/src/main/res/drawable/ic_no_timetable.xml | 61 ++++++ app/src/main/res/drawable/ic_sync.xml | 45 ++++ app/src/main/res/drawable/ic_timetable.xml | 202 ++++++++++++++++++ .../main/res/layout/fragment_timetable_v2.xml | 46 +++- .../res/layout/fragment_timetable_v2_day.xml | 134 ++++++++++-- 21 files changed, 626 insertions(+), 67 deletions(-) create mode 100644 app/src/main/res/drawable/ic_no_timetable.xml create mode 100644 app/src/main/res/drawable/ic_sync.xml create mode 100644 app/src/main/res/drawable/ic_timetable.xml diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index 5c251a37..c9e2d7c6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -391,4 +391,21 @@ fun List.concat(delimiter: String? = null): CharSequence { fun TextView.setText(@StringRes resid: Int, vararg formatArgs: Any) { text = context.getString(resid, *formatArgs) -} \ No newline at end of file +} + +fun JsonObject(vararg properties: Pair): JsonObject { + return JsonObject().apply { + for (property in properties) { + when (property.second) { + is JsonElement -> add(property.first, property.second as JsonElement) + is String -> addProperty(property.first, property.second as String) + is Char -> addProperty(property.first, property.second as Char) + is Number -> addProperty(property.first, property.second as Number) + is Boolean -> addProperty(property.first, property.second as Boolean) + } + } + } +} + +fun JsonArray?.isNullOrEmpty(): Boolean = (this?.size() ?: 0) == 0 +fun JsonArray.isEmpty(): Boolean = this.size() == 0 \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/EdziennikTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/EdziennikTask.kt index 0e8e945b..6fd170ca 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/EdziennikTask.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/events/task/EdziennikTask.kt @@ -1,5 +1,6 @@ package pl.szczodrzynski.edziennik.api.v2.events.task +import com.google.gson.JsonObject import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.api.v2.* @@ -18,7 +19,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa fun firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore)) fun sync() = EdziennikTask(-1, SyncRequest()) - fun syncProfile(profileId: Int, viewIds: List>? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds)) + fun syncProfile(profileId: Int, viewIds: List>? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds, arguments)) fun syncProfileList(profileList: List) = EdziennikTask(-1, SyncProfileListRequest(profileList)) fun messageGet(profileId: Int, messageId: Int) = EdziennikTask(profileId, MessageGetRequest(messageId)) fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest()) @@ -50,7 +51,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa private var edziennikInterface: EdziennikInterface? = null - fun run(app: App, taskCallback: EdziennikCallback) { + internal fun run(app: App, taskCallback: EdziennikCallback) { edziennikInterface = when (loginStore.type) { LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback) LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback) @@ -66,7 +67,8 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa when (request) { is SyncProfileRequest -> edziennikInterface?.sync( featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) } ?: Features.getAllIds(), - viewId = request.viewIds?.get(0)?.first) + viewId = request.viewIds?.get(0)?.first, + arguments = request.arguments) is MessageGetRequest -> edziennikInterface?.getMessage(request.messageId) is FirstLoginRequest -> edziennikInterface?.firstLogin() is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead() @@ -83,7 +85,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa data class FirstLoginRequest(val loginStore: LoginStore) class SyncRequest - data class SyncProfileRequest(val viewIds: List>? = null) + data class SyncProfileRequest(val viewIds: List>? = null, val arguments: JsonObject? = null) data class SyncProfileListRequest(val profileList: List) data class MessageGetRequest(val messageId: Int) class AnnouncementsReadRequest diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt index 03b1bbf0..3667a2c6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/idziennik/Idziennik.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.idziennik +import com.google.gson.JsonObject import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikData @@ -48,7 +49,8 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| __/ | |__*/ - override fun sync(featureIds: List, viewId: Int?) { + override fun sync(featureIds: List, viewId: Int?, arguments: JsonObject?) { + data.arguments = arguments data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId) d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt index 935b778d..80596d2a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/interfaces/EdziennikInterface.kt @@ -4,8 +4,10 @@ package pl.szczodrzynski.edziennik.api.v2.interfaces +import com.google.gson.JsonObject + interface EdziennikInterface { - fun sync(featureIds: List, viewId: Int? = null) + fun sync(featureIds: List, viewId: Int? = null, arguments: JsonObject? = null) fun getMessage(messageId: Int) fun markAllAnnouncementsAsRead() fun firstLogin() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt index eeb2b759..dc55fa1a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/Librus.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus +import com.google.gson.JsonObject import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback @@ -49,7 +50,8 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| __/ | |__*/ - override fun sync(featureIds: List, viewId: Int?) { + override fun sync(featureIds: List, viewId: Int?, arguments: JsonObject?) { + data.arguments = arguments data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId) login() } @@ -173,7 +175,10 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va login() } // TODO PORTAL CAPTCHA - ERROR_LIBRUS_API_TIMETABLE_NOT_PUBLIC, + ERROR_LIBRUS_API_TIMETABLE_NOT_PUBLIC -> { + loginStore.putLoginData("timetableNotPublic", true) + data() + } ERROR_LIBRUS_API_LUCKY_NUMBER_NOT_ACTIVE, ERROR_LIBRUS_API_NOTES_NOT_ACTIVE -> { data() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt index ceb4b2b2..898e5ea7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt @@ -4,11 +4,13 @@ package pl.szczodrzynski.edziennik.api.v2.librus.data.api +import androidx.core.util.isEmpty import com.google.gson.JsonObject import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_TIMETABLES import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi +import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson @@ -22,20 +24,42 @@ class LibrusApiTimetables(override val data: DataLibrus, } init { - apiGet(TAG, "Timetables") { json -> + if (data.classrooms.isEmpty()) { + data.db.classroomDao().getAllNow(profileId).toSparseArray(data.classrooms) { it.id } + } + + val currentWeekStart = Date.getToday().let { it.stepForward(0, 0, -it.weekDay) } + val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d + apiGet(TAG, "Timetables?weekStart=$getDate") { json -> val days = json.getJsonObject("Timetable") days?.entrySet()?.forEach { (dateString, dayEl) -> - val lessonDate = dateString?.let { Date.fromY_m_d(it) } ?: return@forEach val day = dayEl?.asJsonArray + + val lessonDate = dateString?.let { Date.fromY_m_d(it) } ?: return@forEach + + var lessonsFound = false day?.forEach { lessonRangeEl -> val lessonRange = lessonRangeEl?.asJsonArray?.asJsonObjectList() + if (lessonRange?.isNullOrEmpty() == false) + lessonsFound = true lessonRange?.forEachIndexed { index, lesson -> parseLesson(lessonDate, lesson) } } + + if (day.isNullOrEmpty() || !lessonsFound) { + data.lessonNewList += Lesson(profileId, lessonDate.inUnix).apply { + type = Lesson.TYPE_NO_LESSONS + date = lessonDate + } + } } + val weekStart = Date.fromY_m_d(getDate) + val weekEnd = weekStart.clone().stepForward(0, 0, 6) + + data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd)) data.setSyncNext(ENDPOINT_LIBRUS_API_TIMETABLES, SYNC_ALWAYS) onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt index 337da3b2..7a652392 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/Mobidziennik.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik +import com.google.gson.JsonObject import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback @@ -48,7 +49,8 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| __/ | |__*/ - override fun sync(featureIds: List, viewId: Int?) { + override fun sync(featureIds: List, viewId: Int?, arguments: JsonObject?) { + data.arguments = arguments data.prepare(mobidziennikLoginMethods, MobidziennikFeatures, featureIds, viewId) d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 2db316d3..a176ecd1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -57,6 +57,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) val profileId get() = profile?.id ?: -1 + var arguments: JsonObject? = null + /** * A callback passed to all [Feature]s and [LoginMethod]s */ @@ -130,24 +132,20 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) mTeamClass = value } - var lessonsToRemove: DataRemoveModel? = null + var toRemove = mutableListOf() + val lessonList = mutableListOf() val lessonChangeList = mutableListOf() val lessonNewList = mutableListOf() - var gradesToRemove: DataRemoveModel? = null val gradeList = mutableListOf() - var eventsToRemove: DataRemoveModel? = null val eventList = mutableListOf() - var noticesToRemove: DataRemoveModel? = null val noticeList = mutableListOf() - var attendancesToRemove: DataRemoveModel? = null val attendanceList = mutableListOf() - var announcementsToRemove: DataRemoveModel? = null val announcementList = mutableListOf() val luckyNumberList = mutableListOf() @@ -250,6 +248,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) app.profile.loginStoreData = loginStore.data } + // always present and not empty, during every sync db.endpointTimerDao().addAll(endpointTimers) db.teacherDao().clear(profileId) db.teacherDao().addAll(teacherList.values()) @@ -262,6 +261,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) db.gradeCategoryDao().clear(profileId) db.gradeCategoryDao().addAll(gradeCategories.values()) + // may be empty - extracted from DB on demand, by an endpoint if (classrooms.size > 0) db.classroomDao().addAll(classrooms.values()) if (attendanceTypes.size > 0) @@ -273,11 +273,15 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) if (teacherAbsenceTypes.size > 0) db.teacherAbsenceTypeDao().addAll(teacherAbsenceTypes.values()) - gradesToRemove?.let { it -> - it.removeAll?.let { _ -> db.gradeDao().clear(profileId) } - it.removeSemester?.let { semester -> db.gradeDao().clearForSemester(profileId, semester) } + // clear DB with DataRemoveModels added by endpoints + for (model in toRemove) { + when (model) { + is DataRemoveModel.Timetable -> model.commit(profileId, db.timetableDao()) + is DataRemoveModel.Grades -> model.commit(profileId, db.gradeDao()) + } } + // not extracted from DB - always new data if (lessonList.isNotEmpty()) { db.lessonDao().clear(profile.id) db.lessonDao().addAll(lessonList) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataRemoveModel.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataRemoveModel.kt index 1e97d733..c23b28d9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataRemoveModel.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/DataRemoveModel.kt @@ -4,28 +4,37 @@ package pl.szczodrzynski.edziennik.api.v2.models +import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeDao +import pl.szczodrzynski.edziennik.data.db.modules.timetable.TimetableDao import pl.szczodrzynski.edziennik.utils.models.Date -class DataRemoveModel { - var removeAll: Boolean? = null - var removeSemester: Int? = null - var removeDateFrom: Date? = null - var removeDateTo: Date? = null - - constructor() { - this.removeAll = true +open class DataRemoveModel { + class Timetable(private val dateFrom: Date?, private val dateTo: Date?) : DataRemoveModel() { + companion object { + fun from(dateFrom: Date) = Timetable(dateFrom, null) + fun to(dateTo: Date) = Timetable(null, dateTo) + fun between(dateFrom: Date, dateTo: Date) = Timetable(dateFrom, dateTo) + } + fun commit(profileId: Int, dao: TimetableDao) { + if (dateFrom != null && dateTo != null) { + dao.clearBetweenDates(profileId, dateFrom, dateTo) + } + else { + dateFrom?.let { dateFrom -> dao.clearFromDate(profileId, dateFrom) } + dateTo?.let { dateTo -> dao.clearToDate(profileId, dateTo) } + } + } } - - constructor(semester: Int) { - this.removeSemester = semester - } - - constructor(dateFrom: Date?, dateTo: Date) { - this.removeDateFrom = dateFrom - this.removeDateTo = dateTo - } - - constructor(dateFrom: Date) { - this.removeDateFrom = dateFrom + class Grades(val all: Boolean, val semester: Int?) : DataRemoveModel() { + companion object { + fun all() = Grades(true, null) + fun semester(semester: Int) = Grades(false, semester) + } + fun commit(profileId: Int, dao: GradeDao) { + if (all) { + dao.clear(profileId) + } + semester?.let { dao.clearForSemester(profileId, it) } + } } } \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt index 9e80c2bf..a7d44574 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/template/Template.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.template +import com.google.gson.JsonObject import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback @@ -48,7 +49,8 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| __/ | |__*/ - override fun sync(featureIds: List, viewId: Int?) { + override fun sync(featureIds: List, viewId: Int?, arguments: JsonObject?) { + data.arguments = arguments data.prepare(templateLoginMethods, TemplateFeatures, featureIds, viewId) d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt index 209fec47..9a7167a9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/vulcan/Vulcan.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan +import com.google.gson.JsonObject import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410 import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback @@ -48,7 +49,8 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| __/ | |__*/ - override fun sync(featureIds: List, viewId: Int?) { + override fun sync(featureIds: List, viewId: Int?, arguments: JsonObject?) { + data.arguments = arguments data.prepare(vulcanLoginMethods, VulcanFeatures, featureIds, viewId) d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}") d(TAG, "Endpoint IDs: ${data.targetEndpointIds}") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/Lesson.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/Lesson.kt index abc75820..38834b30 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/Lesson.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/Lesson.kt @@ -17,6 +17,7 @@ import pl.szczodrzynski.edziennik.utils.models.Time ]) open class Lesson(val profileId: Int, @PrimaryKey val id: Long) { companion object { + const val TYPE_NO_LESSONS = -1 const val TYPE_NORMAL = 0 const val TYPE_CANCELLED = 1 const val TYPE_CHANGE = 2 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/TimetableDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/TimetableDao.kt index f9ab8b84..f5beeaf3 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/TimetableDao.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/modules/timetable/TimetableDao.kt @@ -17,6 +17,13 @@ interface TimetableDao { @Query("DELETE FROM timetable WHERE profileId = :profileId") fun clear(profileId: Int) + @Query("DELETE FROM timetable WHERE profileId = :profileId AND (type != 3 AND date >= :dateFrom) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom)") + fun clearFromDate(profileId: Int, dateFrom: Date) + @Query("DELETE FROM timetable WHERE profileId = :profileId AND (type != 3 AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate <= :dateTo)") + fun clearToDate(profileId: Int, dateTo: Date) + @Query("DELETE FROM timetable WHERE profileId = :profileId AND (type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo)") + fun clearBetweenDates(profileId: Int, dateFrom: Date, dateTo: Date) + @Query(""" SELECT timetable.*, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java b/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java index 6347b1e1..45630fe9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/sync/MyFirebaseMessagingService.java @@ -112,7 +112,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { app.notifier.postAll(profile); app.saveConfig("notifications");*/ d(TAG, "Syncing profile " + profile.getId()); - EdziennikTask.Companion.syncProfile(profile.getId(), null).enqueue(app); + EdziennikTask.Companion.syncProfile(profile.getId(), null, null).enqueue(app); } else { /*app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message")) .withProfileData(profile.id, profile.name) @@ -123,7 +123,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService { app.notifier.postAll(profile); app.saveConfig("notifications");*/ d(TAG, "Syncing profile " + profile.getId()); - EdziennikTask.Companion.syncProfile(profile.getId(), null).enqueue(app); + EdziennikTask.Companion.syncProfile(profile.getId(), null, null).enqueue(app); } } }); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt index b29a7e72..31b40e52 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt @@ -10,6 +10,7 @@ import com.mikepenz.iconics.typeface.library.community.material.CommunityMateria import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_LIBRUS import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2Binding import pl.szczodrzynski.edziennik.utils.Themes import pl.szczodrzynski.edziennik.utils.models.Date @@ -42,6 +43,14 @@ class TimetableFragment : Fragment() { if (app.profile == null || !isAdded) return + if (app.profile.loginStoreType == LOGIN_TYPE_LIBRUS && app.profile.getLoginData("timetableNotPublic", false)) { + b.timetableLayout.visibility = View.GONE + b.timetableNotPublicLayout.visibility = View.VISIBLE + return + } + b.timetableLayout.visibility = View.VISIBLE + b.timetableNotPublicLayout.visibility = View.GONE + val items = mutableListOf() val monthDayCount = listOf(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt index 817774cf..4642aa9e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt @@ -12,6 +12,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import com.linkedin.android.tachyon.DayView import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2DayBinding @@ -45,7 +46,6 @@ class TimetableDayFragment(val date: Date) : Fragment() { return Log.d(TAG, "onViewCreated, date=$date") - b.date.text = date.formattedString // Inflate a label view for each hour the day view will display val hourLabelViews = ArrayList() @@ -62,6 +62,31 @@ class TimetableDayFragment(val date: Date) : Fragment() { } private fun buildLessonViews(lessons: List) { + if (lessons.isEmpty()) { + b.dayScroll.visibility = View.GONE + b.noTimetableLayout.visibility = View.VISIBLE + b.noLessonsLayout.visibility = View.GONE + b.noTimetableSync.setOnClickListener { + EdziennikTask.syncProfile( + profileId = App.profileId, + arguments = JsonObject( + "weekStart" to date.clone().stepForward(0, 0, -date.weekDay).stringY_m_d + ) + ).enqueue(activity) + } + return + } + if (lessons.size == 1 && lessons[0].type == Lesson.TYPE_NO_LESSONS) { + b.dayScroll.visibility = View.GONE + b.noTimetableLayout.visibility = View.GONE + b.noLessonsLayout.visibility = View.VISIBLE + return + } + + b.dayScroll.visibility = View.VISIBLE + b.noTimetableLayout.visibility = View.GONE + b.noLessonsLayout.visibility = View.GONE + val eventViews = mutableListOf() val eventTimeRanges = mutableListOf() diff --git a/app/src/main/res/drawable/ic_no_timetable.xml b/app/src/main/res/drawable/ic_no_timetable.xml new file mode 100644 index 00000000..8c93d926 --- /dev/null +++ b/app/src/main/res/drawable/ic_no_timetable.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_sync.xml b/app/src/main/res/drawable/ic_sync.xml new file mode 100644 index 00000000..2374a3e5 --- /dev/null +++ b/app/src/main/res/drawable/ic_sync.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_timetable.xml b/app/src/main/res/drawable/ic_timetable.xml new file mode 100644 index 00000000..9bd05e1e --- /dev/null +++ b/app/src/main/res/drawable/ic_timetable.xml @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_timetable_v2.xml b/app/src/main/res/layout/fragment_timetable_v2.xml index 78893ddf..56644931 100644 --- a/app/src/main/res/layout/fragment_timetable_v2.xml +++ b/app/src/main/res/layout/fragment_timetable_v2.xml @@ -1,15 +1,16 @@ - + + android:layout_height="match_parent"> + android:layout_height="match_parent" + tools:visibility="gone"> - + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_timetable_v2_day.xml b/app/src/main/res/layout/fragment_timetable_v2_day.xml index 9912a5ef..2fa0f81a 100644 --- a/app/src/main/res/layout/fragment_timetable_v2_day.xml +++ b/app/src/main/res/layout/fragment_timetable_v2_day.xml @@ -1,24 +1,16 @@ - + - - - - + app:startHour="5" + tools:visibility="gone"/> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 0742a6a74c74cf96c087afc242a18f62d9bc8b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 11 Nov 2019 14:16:31 +0100 Subject: [PATCH 148/691] [Timetable] Fix removing all lessons when not needed. --- .../main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index a176ecd1..6ac629ad 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -289,7 +289,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) if (lessonChangeList.isNotEmpty()) db.lessonChangeDao().addAll(lessonChangeList) if (lessonNewList.isNotEmpty()) { - db.timetableDao().clear(profile.id) db.timetableDao() += lessonNewList } if (gradeList.isNotEmpty()) { From 1052b824dbdea246e4c16574e5bc9818b2a74ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 11 Nov 2019 14:52:09 +0100 Subject: [PATCH 149/691] [Timetable] Make it sync only timetable when getting a single week. --- .../modules/timetable/v2/day/TimetableDayFragment.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt index 4642aa9e..dc4f4d76 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt @@ -12,6 +12,8 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import com.linkedin.android.tachyon.DayView import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE +import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_LIBRUS import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull @@ -69,6 +71,9 @@ class TimetableDayFragment(val date: Date) : Fragment() { b.noTimetableSync.setOnClickListener { EdziennikTask.syncProfile( profileId = App.profileId, + viewIds = listOf( + DRAWER_ITEM_TIMETABLE to 0 + ), arguments = JsonObject( "weekStart" to date.clone().stepForward(0, 0, -date.weekDay).stringY_m_d ) @@ -83,6 +88,13 @@ class TimetableDayFragment(val date: Date) : Fragment() { return } + // reload the fragment when: no lessons, user wants to sync the week, the timetable is not public, pager gets removed + if (app.profile.loginStoreType == LOGIN_TYPE_LIBRUS && app.profile.getLoginData("timetableNotPublic", false)) { + activity.reloadTarget() + // TODO fix for (not really)possible infinite loops + return + } + b.dayScroll.visibility = View.VISIBLE b.noTimetableLayout.visibility = View.GONE b.noLessonsLayout.visibility = View.GONE From f42ec8435a50e5b60f3913e70498599bc2d0df46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 11 Nov 2019 15:46:04 +0100 Subject: [PATCH 150/691] [Timetable] Show user friendly day name in view pager. --- .../ui/modules/timetable/v2/TimetablePagerAdapter.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetablePagerAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetablePagerAdapter.kt index 84d7a004..63ecdd6d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetablePagerAdapter.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetablePagerAdapter.kt @@ -4,12 +4,17 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentStatePagerAdapter import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Week class TimetablePagerAdapter(val fragmentManager: FragmentManager, val items: List) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { companion object { private const val TAG = "TimetablePagerAdapter" } + private val today by lazy { Date.getToday() } + private val weekStart by lazy { today.clone().stepForward(0, 0, -today.weekDay) } + private val weekEnd by lazy { weekStart.clone().stepForward(0, 0, 6) } + override fun getItem(position: Int): Fragment { return pl.szczodrzynski.edziennik.ui.modules.timetable.v2.day.TimetableDayFragment(items[position]) /*return TimetableDayFragment().apply { @@ -24,6 +29,11 @@ class TimetablePagerAdapter(val fragmentManager: FragmentManager, val items: Lis } override fun getPageTitle(position: Int): CharSequence? { - return items[position].formattedStringShort + val date = items[position] + val pageTitle = StringBuilder(Week.getFullDayName(date.weekDay)) + if (date > weekEnd || date < weekStart) { + pageTitle.append(", ").append(date.stringDm) + } + return pageTitle } } \ No newline at end of file From 29d74e14bd8516a21c29fe571043aedb15c475ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 11 Nov 2019 18:13:37 +0100 Subject: [PATCH 151/691] [Timetable] Fix scrolling to first lesson. Update lesson ID generation. --- .../api/v2/librus/data/api/LibrusApiTimetables.kt | 7 +++++-- .../pl/szczodrzynski/edziennik/api/v2/models/Data.kt | 1 + .../ui/modules/timetable/v2/day/TimetableDayFragment.kt | 9 ++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt index 898e5ea7..88151096 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt @@ -14,6 +14,7 @@ import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson +import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Time @@ -58,6 +59,7 @@ class LibrusApiTimetables(override val data: DataLibrus, val weekStart = Date.fromY_m_d(getDate) val weekEnd = weekStart.clone().stepForward(0, 0, 6) + d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate") data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd)) data.setSyncNext(ENDPOINT_LIBRUS_API_TIMETABLES, SYNC_ALWAYS) @@ -66,8 +68,6 @@ class LibrusApiTimetables(override val data: DataLibrus, } private fun parseLesson(lessonDate: Date, lesson: JsonObject) { - val lessonObject = Lesson(profileId, lesson.hashCode().toLong()) - val isSubstitution = lesson.getBoolean("IsSubstitutionClass") ?: false val isCancelled = lesson.getBoolean("IsCanceled") ?: false @@ -80,6 +80,9 @@ class LibrusApiTimetables(override val data: DataLibrus, val virtualClassId = lesson.getJsonObject("VirtualClass")?.getLong("Id") val teamId = lesson.getJsonObject("Class")?.getLong("Id") ?: virtualClassId + val id = lessonDate.combineWith(startTime) / 6L * 10L + (lesson.hashCode() and 0xFFFF) + val lessonObject = Lesson(profileId, id) + if (isSubstitution && isCancelled) { // shifted lesson - source val newDate = lesson.getString("NewDate")?.let { Date.fromY_m_d(it) } ?: return diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index 6ac629ad..a89d5ff2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -179,6 +179,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) fun clear() { loginMethods.clear() + toRemove.clear() endpointTimers.clear() teacherList.clear() subjectList.clear() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt index dc4f4d76..07220a2e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt @@ -22,6 +22,7 @@ import pl.szczodrzynski.edziennik.databinding.TimetableLessonBinding import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.navlib.getColorFromAttr import java.util.* +import kotlin.math.min class TimetableDayFragment(val date: Date) : Fragment() { companion object { @@ -99,6 +100,8 @@ class TimetableDayFragment(val date: Date) : Fragment() { b.noTimetableLayout.visibility = View.GONE b.noLessonsLayout.visibility = View.GONE + var firstEventMinute = 24*60 + val eventViews = mutableListOf() val eventTimeRanges = mutableListOf() @@ -115,6 +118,8 @@ class TimetableDayFragment(val date: Date) : Fragment() { val startTime = lesson.displayStartTime ?: continue val endTime = lesson.displayEndTime ?: continue + firstEventMinute = min(firstEventMinute, startTime.hour*60 + startTime.minute) + // Try to recycle an existing event view if there are enough left, otherwise inflate // a new one val eventView = (if (remaining > 0) recycled?.get(--remaining) else layoutInflater.inflate(R.layout.timetable_lesson, b.day, false)) @@ -270,7 +275,9 @@ class TimetableDayFragment(val date: Date) : Fragment() { eventTimeRanges.add(DayView.EventTimeRange(startMinute, endMinute)) } + val minuteHeight = (b.day.getHourTop(1) - b.day.getHourTop(0)).toFloat() / 60f + val firstEventTop = (firstEventMinute - b.day.startHour * 60) * minuteHeight b.day.setEventViews(eventViews, eventTimeRanges) - b.dayScroll.scrollTo(0, b.day.firstEventTop) + b.dayScroll.scrollTo(0, firstEventTop.toInt()) } } From 69b512e3d15436e13c74273ee61bf5b811b29de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 11 Nov 2019 18:32:49 +0100 Subject: [PATCH 152/691] [Timetable] Extract string resources. Increase offscreen page limit. --- .../modules/timetable/v2/TimetableFragment.kt | 2 +- .../timetable/v2/day/TimetableDayFragment.kt | 4 ++- .../main/res/layout/fragment_timetable_v2.xml | 17 ++++++++++-- .../res/layout/fragment_timetable_v2_day.xml | 27 ++++++++++++------- app/src/main/res/values/strings.xml | 11 ++++++++ 5 files changed, 47 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt index 31b40e52..ac489ab6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt @@ -75,6 +75,7 @@ class TimetableFragment : Fragment() { } val pagerAdapter = TimetablePagerAdapter(fragmentManager ?: return, items) + b.viewPager.offscreenPageLimit = 2 b.viewPager.adapter = pagerAdapter b.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { override fun onPageScrollStateChanged(state: Int) { @@ -92,7 +93,6 @@ class TimetableFragment : Fragment() { fabShown = true } } - }) b.tabLayout.setUpWithViewPager(b.viewPager) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt index 07220a2e..3e09faad 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt @@ -69,6 +69,7 @@ class TimetableDayFragment(val date: Date) : Fragment() { b.dayScroll.visibility = View.GONE b.noTimetableLayout.visibility = View.VISIBLE b.noLessonsLayout.visibility = View.GONE + val weekStart = date.clone().stepForward(0, 0, -date.weekDay).stringY_m_d b.noTimetableSync.setOnClickListener { EdziennikTask.syncProfile( profileId = App.profileId, @@ -76,10 +77,11 @@ class TimetableDayFragment(val date: Date) : Fragment() { DRAWER_ITEM_TIMETABLE to 0 ), arguments = JsonObject( - "weekStart" to date.clone().stepForward(0, 0, -date.weekDay).stringY_m_d + "weekStart" to weekStart ) ).enqueue(activity) } + b.noTimetableWeek.setText(R.string.timetable_no_timetable_week, weekStart) return } if (lessons.size == 1 && lessons[0].type == Lesson.TYPE_NO_LESSONS) { diff --git a/app/src/main/res/layout/fragment_timetable_v2.xml b/app/src/main/res/layout/fragment_timetable_v2.xml index 56644931..72c22fdb 100644 --- a/app/src/main/res/layout/fragment_timetable_v2.xml +++ b/app/src/main/res/layout/fragment_timetable_v2.xml @@ -61,16 +61,29 @@ android:layout_height="wrap_content" android:layout_marginTop="16dp" android:fontFamily="sans-serif-light" - android:text="Brak planu lekcji" + android:text="@string/timetable_not_public_title" android:textSize="24sp" /> + + diff --git a/app/src/main/res/layout/fragment_timetable_v2_day.xml b/app/src/main/res/layout/fragment_timetable_v2_day.xml index 2fa0f81a..36ccd46e 100644 --- a/app/src/main/res/layout/fragment_timetable_v2_day.xml +++ b/app/src/main/res/layout/fragment_timetable_v2_day.xml @@ -47,7 +47,7 @@ android:layout_height="wrap_content" android:layout_marginTop="16dp" android:fontFamily="sans-serif-light" - android:text="Brak lekcji tego dnia" + android:text="@string/timetable_no_lessons_title" android:textSize="24sp" /> @@ -71,21 +71,24 @@ android:layout_height="wrap_content" android:layout_marginTop="16dp" android:fontFamily="sans-serif-light" - android:text="Dzień wolny" + android:text="@string/timetable_free_day_title" android:textSize="24sp" /> + android:text="@string/timetable_free_day_show" /> @@ -104,7 +107,7 @@ android:orientation="vertical" android:visibility="gone" android:gravity="center" - tools:visibility="gone"> + tools:visibility="visible"> + android:text="@string/timetable_no_timetable_sync" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bffcc7f6..d4ab4de1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -995,4 +995,15 @@ Lekcja przeniesiona z dnia %s, godz. %s Lekcja przeniesiona na inny termin Lekcja przeniesiona z innego terminu + Brak planu lekcji + Plan lekcji nie został opublikowany przez szkołę. + Skontaktuj się z wychowawcą w celu udostępnienia planu lekcji. + Dzień wolny + W tym dniu nie ma lekcji: + Pokaż plan lekcji + Brak lekcji tego dnia + Brak planu lekcji + Nie pobrano planu lekcji na ten tydzień. + Pobierz plan lekcji + na tydzień %s From 124437fd73145a6afb9d5dedf3ff3719b1f7d6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 11 Nov 2019 18:41:39 +0100 Subject: [PATCH 153/691] [Login] Allow fake login with DevMode. --- .../java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt | 7 +++++-- .../edziennik/ui/modules/login/LoginChooserFragment.java | 3 +-- .../edziennik/ui/modules/login/LoginProgressFragment.java | 3 +-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt index a89d5ff2..aedbc4ef 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/models/Data.kt @@ -5,7 +5,7 @@ import android.util.SparseArray import androidx.core.util.size import com.google.gson.JsonObject import im.wangchao.mhttp.Response -import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.api.v2.DataNotifications import pl.szczodrzynski.edziennik.api.v2.EXCEPTION_NOTIFY_AND_SYNC import pl.szczodrzynski.edziennik.api.v2.ServerSync @@ -38,8 +38,11 @@ import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsence import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceType import pl.szczodrzynski.edziennik.data.db.modules.teams.Team +import pl.szczodrzynski.edziennik.singleOrNull +import pl.szczodrzynski.edziennik.toSparseArray import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.values import java.io.InterruptedIOException import java.net.SocketTimeoutException import java.net.UnknownHostException @@ -162,7 +165,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore) val db: AppDb by lazy { app.db } init { - if (BuildConfig.DEBUG) { + if (App.devMode) { fakeLogin = loginStore.hasLoginData("fakeLogin") } clear() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.java index 2aff483c..95e53cc4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginChooserFragment.java @@ -14,7 +14,6 @@ import androidx.navigation.NavController; import androidx.navigation.Navigation; import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.databinding.FragmentLoginChooserBinding; import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackActivity; @@ -73,7 +72,7 @@ public class LoginChooserFragment extends Fragment { b.cancelButton.setVisibility(View.GONE); } - b.fakeLogin.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); + b.fakeLogin.setVisibility(App.devMode ? View.VISIBLE : View.GONE); b.fakeLogin.setChecked(fakeLogin); b.fakeLogin.setOnCheckedChangeListener((v, isChecked) -> fakeLogin = isChecked); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java index 08c7e919..bce2a0be 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/login/LoginProgressFragment.java @@ -19,7 +19,6 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.api.v2.events.ApiTaskErrorEvent; import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent; @@ -90,7 +89,7 @@ public class LoginProgressFragment extends Fragment { LoginStore loginStore = new LoginStore(-1, loginType, new JsonObject()); loginStore.copyFrom(args); - if (BuildConfig.DEBUG && LoginChooserFragment.fakeLogin) { + if (App.devMode && LoginChooserFragment.fakeLogin) { loginStore.putLoginData("fakeLogin", true); } From eb0540b5cbf0f12faf4ef501d5d64e276b74fa2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 11 Nov 2019 19:14:02 +0100 Subject: [PATCH 154/691] [Timetable] Fix adding NO_LESSONS in Mobidziennik. Change ID generation. --- .../v2/librus/data/api/LibrusApiTimetables.kt | 2 +- .../v2/mobidziennik/MobidziennikFeatures.kt | 4 +-- .../data/api/MobidziennikApiTimetable.kt | 26 ++++++++++++++++++- .../edziennik/utils/models/Date.java | 7 +++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt index 88151096..6fd669b5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/librus/data/api/LibrusApiTimetables.kt @@ -50,7 +50,7 @@ class LibrusApiTimetables(override val data: DataLibrus, } if (day.isNullOrEmpty() || !lessonsFound) { - data.lessonNewList += Lesson(profileId, lessonDate.inUnix).apply { + data.lessonNewList += Lesson(profileId, lessonDate.value.toLong()).apply { type = Lesson.TYPE_NO_LESSONS date = lessonDate } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt index 057ce2b9..234b41e7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/MobidziennikFeatures.kt @@ -21,10 +21,10 @@ const val ENDPOINT_MOBIDZIENNIK_API2_MAIN = 3000 val MobidziennikFeatures = listOf( // always synced - /*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ALWAYS_NEEDED, listOf( + Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ALWAYS_NEEDED, listOf( ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB, ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL to LOGIN_METHOD_MOBIDZIENNIK_WEB - ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)),*/ + ), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), // TODO divide features into separate view IDs (all with API_MAIN) // push config /*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_PUSH_CONFIG, listOf( diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTimetable.kt index 3f529a7c..fdc0f5b2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTimetable.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/api/v2/mobidziennik/data/api/MobidziennikApiTimetable.kt @@ -4,7 +4,9 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api +import pl.szczodrzynski.edziennik.App.profileId import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik +import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson import pl.szczodrzynski.edziennik.fixName @@ -16,11 +18,24 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List) { init { val lessons = rows.filterNot { it.isEmpty() }.map { it.split("|") } + val dataStart = Date.getToday() + val dataEnd = dataStart.clone().stepForward(0, 0, 7 + (6 - dataStart.weekDay)) + + data.toRemove.add(DataRemoveModel.Timetable.between(dataStart.clone(), dataEnd)) + + val dataDays = mutableListOf() + while (dataStart <= dataEnd) { + dataDays += dataStart.value + dataStart.stepForward(0, 0, 1) + } + for (lesson in lessons) { val date = Date.fromYmd(lesson[2]) val startTime = Time.fromYmdHm(lesson[3]) val endTime = Time.fromYmdHm(lesson[4]) - val id = date.combineWith(startTime) / 1000L + val id = date.combineWith(startTime) / 6L * 10L + (lesson.joinToString("|").hashCode() and 0xFFFF) + + dataDays.remove(date.value) val subjectId = data.subjectList.singleOrNull { it.longName == lesson[5] }?.id ?: -1 val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == (lesson[7]+" "+lesson[6]).fixName() }?.id ?: -1 @@ -75,6 +90,15 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List) { data.lessonNewList += it } } + + for (day in dataDays) { + val lessonDate = Date.fromValue(day) + data.lessonNewList += Lesson(profileId, lessonDate.value.toLong()).apply { + type = Lesson.TYPE_NO_LESSONS + date = lessonDate + } + } + /*for (lessonStr in rows) { if (lessonStr.isNotEmpty()) { val lesson = lessonStr.split("|") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Date.java b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Date.java index 7877e1f9..d0411da2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Date.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/models/Date.java @@ -132,6 +132,13 @@ public class Date implements Comparable { return year * 10000 + month * 100 + day; } + public static Date fromValue(int value) { + int year = value / 10000; + int month = (value-year*10000) / 100; + int day = (value-year*10000-month*100); + return new Date(year, month, day); + } + public String getStringValue() { return Integer.toString(getValue()); From 810976d97602946175098ec76bb0c33215eb421c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 11 Nov 2019 19:22:15 +0100 Subject: [PATCH 155/691] [3.9.4-dev] The Timetable Release --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 6be1df23..fba584b9 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ buildscript { kotlin_version = '1.3.50' release = [ - versionName: "3.9.3-dev", - versionCode: 3090300 + versionName: "3.9.4-dev", + versionCode: 3090400 ] setup = [ From 74db524db6047358790864f8dbfb59c01a16119a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 11 Nov 2019 23:59:45 +0100 Subject: [PATCH 156/691] [Timetable] Add lesson details dialog. --- .idea/misc.xml | 5 +- .../pl/szczodrzynski/edziennik/Binding.java | 21 ++ .../szczodrzynski/edziennik/MainActivity.kt | 7 +- .../dialogs/timetable/LessonDetailsDialog.kt | 139 ++++++++++ .../modules/timetable/v2/TimetableFragment.kt | 28 ++- .../timetable/v2/day/TimetableDayFragment.kt | 27 +- .../main/res/layout/dialog_lesson_details.xml | 237 ++++++++++++++++++ app/src/main/res/values/strings.xml | 7 + 8 files changed, 448 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/Binding.java create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/timetable/LessonDetailsDialog.kt create mode 100644 app/src/main/res/layout/dialog_lesson_details.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index 4ccf11f8..e4b2b0ee 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -6,8 +6,9 @@ - - + + + diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Binding.java b/app/src/main/java/pl/szczodrzynski/edziennik/Binding.java new file mode 100644 index 00000000..d9c97a16 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Binding.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-11-11. + */ + +package pl.szczodrzynski.edziennik; + +import android.graphics.Paint; +import android.widget.TextView; + +import androidx.databinding.BindingAdapter; + +public class Binding { + @BindingAdapter("strikeThrough") + public static void strikeThrough(TextView textView, Boolean strikeThrough) { + if (strikeThrough) { + textView.setPaintFlags(textView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + } else { + textView.setPaintFlags(textView.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt index e84f210f..7e2de44d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt @@ -345,7 +345,7 @@ class MainActivity : AppCompatActivity() { if (!profileListEmpty) { handleIntent(intent?.extras) } - app.db.profileDao().getAllFull().observe(this, Observer { profiles -> + app.db.profileDao().allFull.observe(this, Observer { profiles -> // TODO fix weird -1 profiles ??? profiles.removeAll { it.id < 0 } drawer.setProfileList(profiles) @@ -362,7 +362,7 @@ class MainActivity : AppCompatActivity() { if (app.profile != null) setDrawerItems() - app.db.metadataDao().getUnreadCounts().observe(this, Observer { unreadCounters -> + app.db.metadataDao().unreadCounts.observe(this, Observer { unreadCounters -> unreadCounters.map { it.type = it.thingType } @@ -701,7 +701,8 @@ class MainActivity : AppCompatActivity() { } intentTargetId != -1 -> { drawer.currentProfile = app.profile.id - loadTarget(intentTargetId, extras) + if (navTargetId != intentTargetId) + loadTarget(intentTargetId, extras) } else -> { drawer.currentProfile = app.profile.id diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/timetable/LessonDetailsDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/timetable/LessonDetailsDialog.kt new file mode 100644 index 00000000..06f924d0 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/timetable/LessonDetailsDialog.kt @@ -0,0 +1,139 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2019-11-11. + */ + +package pl.szczodrzynski.edziennik.ui.dialogs.timetable + +import android.app.Activity +import android.content.Intent +import android.view.View +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson +import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull +import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding +import pl.szczodrzynski.edziennik.setText +import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog +import pl.szczodrzynski.edziennik.ui.modules.timetable.v2.TimetableFragment +import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Week + +class LessonDetailsDialog( + val activity: Activity, + val lesson: LessonFull +) { + companion object { + private const val TAG = "LessonDetailsDialog" + } + + init { run { + val b = DialogLessonDetailsBinding.inflate(activity.layoutInflater) + val dialog = MaterialAlertDialogBuilder(activity) + .setView(b.root) + .setPositiveButton(R.string.close) { dialog, _ -> + dialog.dismiss() + } + .setNeutralButton(R.string.add) { dialog, _ -> + dialog.dismiss() + MaterialAlertDialogBuilder(activity) + .setItems(R.array.main_menu_add_options) { dialog2, which -> + dialog2.dismiss() + EventManualDialog(activity, lesson.profileId) + .show( + activity.application as App, + null, + lesson.displayDate, + lesson.displayStartTime, + when (which) { + 1 -> EventManualDialog.DIALOG_HOMEWORK + else -> EventManualDialog.DIALOG_EVENT + } + ) + + } + .setNegativeButton(R.string.cancel) { dialog2, _ -> dialog2.dismiss() } + .show() + } + .show() + b.lesson = lesson + val lessonDate = lesson.displayDate ?: return@run + b.lessonDate.text = Week.getFullDayName(lessonDate.weekDay) + ", " + lessonDate.formattedString + + if (lesson.type >= Lesson.TYPE_SHIFTED_SOURCE) { + b.shiftedLayout.visibility = View.VISIBLE + var otherLessonDate: Date? = null + when (lesson.type) { + Lesson.TYPE_SHIFTED_SOURCE -> { + otherLessonDate = lesson.date + when { + lesson.date != lesson.oldDate -> b.shiftedText.setText( + R.string.timetable_lesson_shifted_other_day, + lesson.date?.stringY_m_d ?: "?", + lesson.startTime?.stringHM ?: "?" + ) + lesson.startTime != lesson.oldStartTime -> b.shiftedText.setText( + R.string.timetable_lesson_shifted_same_day, + lesson.startTime?.stringHM ?: "?" + ) + else -> b.shiftedText.setText(R.string.timetable_lesson_shifted) + } + } + Lesson.TYPE_SHIFTED_TARGET -> { + otherLessonDate = lesson.oldDate + when { + lesson.date != lesson.oldDate -> b.shiftedText.setText( + R.string.timetable_lesson_shifted_from_other_day, + lesson.oldDate?.stringY_m_d ?: "?", + lesson.oldStartTime?.stringHM ?: "?" + ) + lesson.startTime != lesson.oldStartTime -> b.shiftedText.setText( + R.string.timetable_lesson_shifted_from_same_day, + lesson.oldStartTime?.stringHM ?: "?" + ) + else -> b.shiftedText.setText(R.string.timetable_lesson_shifted_from) + } + } + } + b.shiftedGoTo.setOnClickListener { + dialog.dismiss() + val dateStr = otherLessonDate?.stringY_m_d ?: return@setOnClickListener + val intent = Intent(TimetableFragment.ACTION_SCROLL_TO_DATE).apply { + putExtra("date", dateStr) + } + activity.sendBroadcast(intent) + } + } + else { + b.shiftedLayout.visibility = View.GONE + } + + if (lesson.type < Lesson.TYPE_SHIFTED_SOURCE && lesson.oldSubjectId != null && lesson.subjectId != lesson.oldSubjectId) { + b.oldSubjectName = lesson.oldSubjectName + } + if (lesson.type != Lesson.TYPE_CANCELLED && lesson.subjectId != null) { + b.subjectName = lesson.subjectName + } + + if (lesson.type < Lesson.TYPE_SHIFTED_SOURCE && lesson.oldTeacherId != null && lesson.teacherId != lesson.oldTeacherId) { + b.oldTeacherName = lesson.oldTeacherName + } + if (lesson.type != Lesson.TYPE_CANCELLED && lesson.teacherId != null) { + b.teacherName = lesson.teacherName + } + + if (lesson.oldClassroom != null && lesson.classroom != lesson.oldClassroom) { + b.oldClassroom = lesson.oldClassroom + } + if (lesson.type != Lesson.TYPE_CANCELLED && lesson.classroom != null) { + b.classroom = lesson.classroom + } + + if (lesson.type < Lesson.TYPE_SHIFTED_SOURCE && lesson.oldTeamId != null && lesson.teamId != lesson.oldTeamId) { + b.oldTeamName = lesson.oldTeamName + } + if (lesson.type != Lesson.TYPE_CANCELLED && lesson.teamId != null) { + b.teamName = lesson.teamName + } + }} +} \ No newline at end of file diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt index ac489ab6..56627dc0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/TimetableFragment.kt @@ -1,5 +1,9 @@ package pl.szczodrzynski.edziennik.ui.modules.timetable.v2 +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -18,12 +22,14 @@ import pl.szczodrzynski.edziennik.utils.models.Date class TimetableFragment : Fragment() { companion object { private const val TAG = "TimetableFragment" + const val ACTION_SCROLL_TO_DATE = "pl.szczodrzynski.edziennik.timetable.SCROLL_TO_DATE" } private lateinit var app: App private lateinit var activity: MainActivity private lateinit var b: FragmentTimetableV2Binding private var fabShown = false + private val items = mutableListOf() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { activity = (getActivity() as MainActivity?) ?: return null @@ -38,6 +44,24 @@ class TimetableFragment : Fragment() { return b.root } + private val broadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, i: Intent) { + if (!isAdded) + return + val dateStr = i.extras?.getString("date", null) ?: return + val date = Date.fromY_m_d(dateStr) + b.viewPager.setCurrentItem(items.indexOf(date), true) + } + } + override fun onResume() { + super.onResume() + activity.registerReceiver(broadcastReceiver, IntentFilter(ACTION_SCROLL_TO_DATE)) + } + override fun onPause() { + super.onPause() + activity.unregisterReceiver(broadcastReceiver) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // TODO check if app, activity, b can be null if (app.profile == null || !isAdded) @@ -51,7 +75,7 @@ class TimetableFragment : Fragment() { b.timetableLayout.visibility = View.VISIBLE b.timetableNotPublicLayout.visibility = View.GONE - val items = mutableListOf() + items.clear() val monthDayCount = listOf(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) @@ -99,7 +123,7 @@ class TimetableFragment : Fragment() { b.tabLayout.setCurrentItem(items.indexOfFirst { it.value == today }, false) //activity.navView.bottomBar.fabEnable = true - activity.navView.bottomBar.fabExtendedText = getString(R.string.timetable_today) + activity.navView.bottomBar.fabExtendedText = getString(pl.szczodrzynski.edziennik.R.string.timetable_today) activity.navView.bottomBar.fabIcon = CommunityMaterial.Icon.cmd_calendar_today activity.navView.setFabOnClickListener(View.OnClickListener { b.tabLayout.setCurrentItem(items.indexOfFirst { it.value == today }, true) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt index 3e09faad..885cbb9c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/v2/day/TimetableDayFragment.kt @@ -19,6 +19,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2DayBinding import pl.szczodrzynski.edziennik.databinding.TimetableLessonBinding +import pl.szczodrzynski.edziennik.ui.dialogs.timetable.LessonDetailsDialog import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.navlib.getColorFromAttr import java.util.* @@ -133,6 +134,8 @@ class TimetableDayFragment(val date: Date) : Fragment() { eventView.setOnClickListener { Log.d(TAG, "Clicked ${it.tag}") + if (isAdded && it.tag is LessonFull) + LessonDetailsDialog(activity, it.tag as LessonFull) } @@ -221,21 +224,17 @@ class TimetableDayFragment(val date: Date) : Fragment() { } Lesson.TYPE_SHIFTED_SOURCE -> { lb.annotationVisible = true - if (lesson.date != lesson.oldDate) { - lb.annotation.setText( + when { + lesson.date != lesson.oldDate -> lb.annotation.setText( R.string.timetable_lesson_shifted_other_day, lesson.date?.stringY_m_d ?: "?", lesson.startTime?.stringHM ?: "?" ) - } - else if (lesson.startTime != lesson.oldStartTime) { - lb.annotation.setText( + lesson.startTime != lesson.oldStartTime -> lb.annotation.setText( R.string.timetable_lesson_shifted_same_day, lesson.startTime?.stringHM ?: "?" ) - } - else { - lb.annotation.setText(R.string.timetable_lesson_shifted) + else -> lb.annotation.setText(R.string.timetable_lesson_shifted) } lb.annotation.background.colorFilter = PorterDuffColorFilter( @@ -245,21 +244,17 @@ class TimetableDayFragment(val date: Date) : Fragment() { } Lesson.TYPE_SHIFTED_TARGET -> { lb.annotationVisible = true - if (lesson.date != lesson.oldDate) { - lb.annotation.setText( + when { + lesson.date != lesson.oldDate -> lb.annotation.setText( R.string.timetable_lesson_shifted_from_other_day, lesson.oldDate?.stringY_m_d ?: "?", lesson.oldStartTime?.stringHM ?: "?" ) - } - else if (lesson.startTime != lesson.oldStartTime) { - lb.annotation.setText( + lesson.startTime != lesson.oldStartTime -> lb.annotation.setText( R.string.timetable_lesson_shifted_from_same_day, lesson.oldStartTime?.stringHM ?: "?" ) - } - else { - lb.annotation.setText(R.string.timetable_lesson_shifted_from) + else -> lb.annotation.setText(R.string.timetable_lesson_shifted_from) } lb.annotation.background.colorFilter = PorterDuffColorFilter( diff --git a/app/src/main/res/layout/dialog_lesson_details.xml b/app/src/main/res/layout/dialog_lesson_details.xml new file mode 100644 index 00000000..2e12a33e --- /dev/null +++ b/app/src/main/res/layout/dialog_lesson_details.xml @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +