mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-06-20 17:18:02 +02:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
8e3d404352 | |||
810cfd8092 | |||
bd2a9524c6 | |||
d780d5118d | |||
f1570b8eb9 | |||
de0f29a09e | |||
c0d11c91e3 | |||
22c540a3d4 | |||
b7e35d0322 | |||
7bcd6bf038 |
@ -64,6 +64,6 @@
|
|||||||
|
|
||||||
-keep class pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing { public final byte[] pleaseStopRightNow(java.lang.String, long); }
|
-keep class pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing { public final byte[] pleaseStopRightNow(java.lang.String, long); }
|
||||||
|
|
||||||
-keepclassmembernames class pl.szczodrzynski.edziennik.data.api.szkolny.request.** { *; }
|
-keepclassmembers class pl.szczodrzynski.edziennik.data.api.szkolny.request.** { *; }
|
||||||
-keepclassmembernames class pl.szczodrzynski.edziennik.data.api.szkolny.response.** { *; }
|
-keepclassmembers class pl.szczodrzynski.edziennik.data.api.szkolny.response.** { *; }
|
||||||
-keepclassmembernames class pl.szczodrzynski.edziennik.ui.modules.login.LoginInfo.Platform { *; }
|
-keepclassmembernames class pl.szczodrzynski.edziennik.ui.modules.login.LoginInfo.Platform { *; }
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
<h3>Wersja 4.3.1, 2020-08-27</h3>
|
<h3>Wersja 4.4.2, 2020-09-05</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Dodana opcja automatycznej archiwizacji profilu na nowy rok szkolny.</li>
|
<li>Poprawione komunikaty o aktualizacjach aplikacji.</li>
|
||||||
<li>Poprawione problemy z synchronizacją oraz mieszaniem się danych.</li>
|
<li>Mobidziennik: poprawione wyświetlanie przedmiotu w planie lekcji.</li>
|
||||||
<li>Przywrócona synchronizacja w wakacje.</li>
|
<li>Mobidziennik: naprawiony moduł frekwencji.</li>
|
||||||
|
<li>Naprawione zatrzymanie aplikacji na ekranie logowania.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
/*secret password - removed for source code publication*/
|
/*secret password - removed for source code publication*/
|
||||||
static toys AES_IV[16] = {
|
static toys AES_IV[16] = {
|
||||||
0x24, 0x85, 0x31, 0x5b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
0x1a, 0xcd, 0xf0, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||||
|
|
||||||
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
|
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
|
||||||
|
|
||||||
|
@ -41,6 +41,8 @@ import pl.droidsonroids.gif.GifDrawable
|
|||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.*
|
import pl.szczodrzynski.edziennik.data.api.events.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.*
|
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.*
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||||
@ -48,7 +50,9 @@ import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding
|
|||||||
import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent
|
import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent
|
||||||
import pl.szczodrzynski.edziennik.sync.SyncWorker
|
import pl.szczodrzynski.edziennik.sync.SyncWorker
|
||||||
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
||||||
|
import pl.szczodrzynski.edziennik.ui.dialogs.RegisterUnavailableDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.ServerMessageDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.ServerMessageDialog
|
||||||
|
import pl.szczodrzynski.edziennik.ui.dialogs.UpdateAvailableDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog
|
||||||
@ -438,7 +442,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
})
|
})
|
||||||
|
|
||||||
b.swipeRefreshLayout.isEnabled = true
|
b.swipeRefreshLayout.isEnabled = true
|
||||||
b.swipeRefreshLayout.setOnRefreshListener { this.syncCurrentFeature() }
|
b.swipeRefreshLayout.setOnRefreshListener { launch { syncCurrentFeature() } }
|
||||||
b.swipeRefreshLayout.setColorSchemeResources(
|
b.swipeRefreshLayout.setColorSchemeResources(
|
||||||
R.color.md_blue_500,
|
R.color.md_blue_500,
|
||||||
R.color.md_amber_500,
|
R.color.md_amber_500,
|
||||||
@ -604,7 +608,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
|_____/ \__, |_| |_|\___|
|
|_____/ \__, |_| |_|\___|
|
||||||
__/ |
|
__/ |
|
||||||
|__*/
|
|__*/
|
||||||
fun syncCurrentFeature() {
|
suspend fun syncCurrentFeature() {
|
||||||
if (app.profile.archived) {
|
if (app.profile.archived) {
|
||||||
MaterialAlertDialogBuilder(this)
|
MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.profile_archived_title)
|
.setTitle(R.string.profile_archived_title)
|
||||||
@ -640,6 +644,30 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
swipeRefreshLayout.isRefreshing = false
|
swipeRefreshLayout.isRefreshing = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.profile.registerName?.let { registerName ->
|
||||||
|
var status = app.config.sync.registerAvailability[registerName]
|
||||||
|
if (status == null || status.nextCheckAt < currentTimeUnix()) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val api = SzkolnyApi(app)
|
||||||
|
api.runCatching(this@MainActivity) {
|
||||||
|
val availability = getRegisterAvailability()
|
||||||
|
app.config.sync.registerAvailability = availability
|
||||||
|
status = availability[registerName]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status?.available != true
|
||||||
|
|| status?.minVersionCode ?: BuildConfig.VERSION_CODE > BuildConfig.VERSION_CODE) {
|
||||||
|
swipeRefreshLayout.isRefreshing = false
|
||||||
|
loadTarget(DRAWER_ITEM_HOME)
|
||||||
|
if (status != null)
|
||||||
|
RegisterUnavailableDialog(this, status!!)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
swipeRefreshLayout.isRefreshing = true
|
swipeRefreshLayout.isRefreshing = true
|
||||||
Toast.makeText(this, fragmentToSyncName(navTargetId), Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, fragmentToSyncName(navTargetId), Toast.LENGTH_SHORT).show()
|
||||||
val fragmentParam = when (navTargetId) {
|
val fragmentParam = when (navTargetId) {
|
||||||
@ -656,6 +684,20 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
arguments = arguments
|
arguments = arguments
|
||||||
).enqueue(this)
|
).enqueue(this)
|
||||||
}
|
}
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||||
|
fun onUpdateEvent(event: Update) {
|
||||||
|
EventBus.getDefault().removeStickyEvent(event)
|
||||||
|
UpdateAvailableDialog(this, event)
|
||||||
|
}
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||||
|
fun onRegisterAvailabilityEvent(event: RegisterAvailabilityEvent) {
|
||||||
|
EventBus.getDefault().removeStickyEvent(event)
|
||||||
|
app.profile.registerName?.let { registerName ->
|
||||||
|
event.data[registerName]?.let {
|
||||||
|
RegisterUnavailableDialog(this, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onApiTaskStartedEvent(event: ApiTaskStartedEvent) {
|
fun onApiTaskStartedEvent(event: ApiTaskStartedEvent) {
|
||||||
swipeRefreshLayout.isRefreshing = true
|
swipeRefreshLayout.isRefreshing = true
|
||||||
|
@ -4,12 +4,18 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.config
|
package pl.szczodrzynski.edziennik.config
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
import pl.szczodrzynski.edziennik.config.utils.get
|
import pl.szczodrzynski.edziennik.config.utils.get
|
||||||
import pl.szczodrzynski.edziennik.config.utils.getIntList
|
import pl.szczodrzynski.edziennik.config.utils.getIntList
|
||||||
import pl.szczodrzynski.edziennik.config.utils.set
|
import pl.szczodrzynski.edziennik.config.utils.set
|
||||||
|
import pl.szczodrzynski.edziennik.config.utils.setMap
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
|
||||||
class ConfigSync(private val config: Config) {
|
class ConfigSync(private val config: Config) {
|
||||||
|
private val gson = Gson()
|
||||||
|
|
||||||
private var mDontShowAppManagerDialog: Boolean? = null
|
private var mDontShowAppManagerDialog: Boolean? = null
|
||||||
var dontShowAppManagerDialog: Boolean
|
var dontShowAppManagerDialog: Boolean
|
||||||
get() { mDontShowAppManagerDialog = mDontShowAppManagerDialog ?: config.values.get("dontShowAppManagerDialog", false); return mDontShowAppManagerDialog ?: false }
|
get() { mDontShowAppManagerDialog = mDontShowAppManagerDialog ?: config.values.get("dontShowAppManagerDialog", false); return mDontShowAppManagerDialog ?: false }
|
||||||
@ -106,4 +112,9 @@ class ConfigSync(private val config: Config) {
|
|||||||
var tokenVulcanList: List<Int>
|
var tokenVulcanList: List<Int>
|
||||||
get() { mTokenVulcanList = mTokenVulcanList ?: config.values.getIntList("tokenVulcanList", listOf()); return mTokenVulcanList ?: listOf() }
|
get() { mTokenVulcanList = mTokenVulcanList ?: config.values.getIntList("tokenVulcanList", listOf()); return mTokenVulcanList ?: listOf() }
|
||||||
set(value) { config.set("tokenVulcanList", value); mTokenVulcanList = value }
|
set(value) { config.set("tokenVulcanList", value); mTokenVulcanList = value }
|
||||||
|
|
||||||
|
private var mRegisterAvailability: Map<String, RegisterAvailabilityStatus>? = null
|
||||||
|
var registerAvailability: Map<String, RegisterAvailabilityStatus>
|
||||||
|
get() { mRegisterAvailability = mRegisterAvailability ?: config.values.get("registerAvailability", null as String?)?.let { it -> gson.fromJson<Map<String, RegisterAvailabilityStatus>>(it, object: TypeToken<Map<String, RegisterAvailabilityStatus>>(){}.type) }; return mRegisterAvailability ?: mapOf() }
|
||||||
|
set(value) { config.setMap("registerAvailability", value); mRegisterAvailability = value }
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,9 @@ fun AbstractConfig.setIntList(key: String, value: List<Int>?) {
|
|||||||
fun AbstractConfig.setLongList(key: String, value: List<Long>?) {
|
fun AbstractConfig.setLongList(key: String, value: List<Long>?) {
|
||||||
set(key, value?.let { gson.toJson(it) })
|
set(key, value?.let { gson.toJson(it) })
|
||||||
}
|
}
|
||||||
|
fun <K, V> AbstractConfig.setMap(key: String, value: Map<K, V>?) {
|
||||||
|
set(key, value?.let { gson.toJson(it) })
|
||||||
|
}
|
||||||
|
|
||||||
fun HashMap<String, String?>.get(key: String, default: String?): String? {
|
fun HashMap<String, String?>.get(key: String, default: String?): String? {
|
||||||
return this[key] ?: default
|
return this[key] ?: default
|
||||||
|
@ -80,6 +80,15 @@ object Regexes {
|
|||||||
val MOBIDZIENNIK_ATTENDANCE_ENTRIES by lazy {
|
val MOBIDZIENNIK_ATTENDANCE_ENTRIES by lazy {
|
||||||
"""font-size:.+?class=".*?">(.*?)</td>""".toRegex(DOT_MATCHES_ALL)
|
"""font-size:.+?class=".*?">(.*?)</td>""".toRegex(DOT_MATCHES_ALL)
|
||||||
}
|
}
|
||||||
|
val MOBIDZIENNIK_ATTENDANCE_COLUMNS by lazy {
|
||||||
|
"""<tr><td class="border-right1".+?/td>(.+?)</tr>""".toRegex(DOT_MATCHES_ALL)
|
||||||
|
}
|
||||||
|
val MOBIDZIENNIK_ATTENDANCE_COLUMN by lazy {
|
||||||
|
"""(<td.+?>)(.*?)</td>""".toRegex(DOT_MATCHES_ALL)
|
||||||
|
}
|
||||||
|
val MOBIDZIENNIK_ATTENDANCE_COLUMN_SPAN by lazy {
|
||||||
|
"""colspan="(\d+)"""".toRegex()
|
||||||
|
}
|
||||||
val MOBIDZIENNIK_ATTENDANCE_RANGE by lazy {
|
val MOBIDZIENNIK_ATTENDANCE_RANGE by lazy {
|
||||||
"""<span>([0-9:]+) - .+? (.+?)</span></a>""".toRegex(DOT_MATCHES_ALL)
|
"""<span>([0-9:]+) - .+? (.+?)</span></a>""".toRegex(DOT_MATCHES_ALL)
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
package pl.szczodrzynski.edziennik.data.api.edziennik
|
package pl.szczodrzynski.edziennik.data.api.edziennik
|
||||||
|
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import pl.szczodrzynski.edziennik.App
|
import org.greenrobot.eventbus.EventBus
|
||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.*
|
import pl.szczodrzynski.edziennik.data.api.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.Edudziennik
|
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.Edudziennik
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.Idziennik
|
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.Idziennik
|
||||||
@ -15,9 +15,11 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.Mobidziennik
|
|||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.Podlasie
|
import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.Podlasie
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.template.Template
|
import pl.szczodrzynski.edziennik.data.api.edziennik.template.Template
|
||||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan
|
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.events.RegisterAvailabilityEvent
|
||||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||||
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||||
@ -88,6 +90,33 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
|||||||
taskCallback.onCompleted()
|
taskCallback.onCompleted()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
profile.registerName?.let { registerName ->
|
||||||
|
var status = app.config.sync.registerAvailability[registerName]
|
||||||
|
if (status == null || status.nextCheckAt < currentTimeUnix()) {
|
||||||
|
val api = SzkolnyApi(app)
|
||||||
|
api.runCatching({
|
||||||
|
val availability = getRegisterAvailability()
|
||||||
|
app.config.sync.registerAvailability = availability
|
||||||
|
status = availability[registerName]
|
||||||
|
}, onError = {
|
||||||
|
taskCallback.onError(it.toApiError(TAG))
|
||||||
|
return
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status?.available != true
|
||||||
|
|| status?.minVersionCode ?: BuildConfig.VERSION_CODE > BuildConfig.VERSION_CODE) {
|
||||||
|
if (EventBus.getDefault().hasSubscriberForEvent(RegisterAvailabilityEvent::class.java)) {
|
||||||
|
EventBus.getDefault().postSticky(
|
||||||
|
RegisterAvailabilityEvent(app.config.sync.registerAvailability)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
taskCallback.onCompleted()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
edziennikInterface = when (loginStore.type) {
|
edziennikInterface = when (loginStore.type) {
|
||||||
|
@ -44,7 +44,7 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
|||||||
|
|
||||||
dataDays.remove(date.value)
|
dataDays.remove(date.value)
|
||||||
|
|
||||||
val subjectId = data.subjectList.singleOrNull { it.longName == lesson[5] }?.id ?: -1
|
val subjectId = data.subjectList.singleOrNull { it.longName == lesson[5].trim() }?.id ?: -1
|
||||||
val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == (lesson[7]+" "+lesson[6]).fixName() }?.id ?: -1
|
val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == (lesson[7]+" "+lesson[6]).fixName() }?.id ?: -1
|
||||||
val teamId = data.teamList.singleOrNull { it.name == lesson[8]+lesson[9] }?.id ?: -1
|
val teamId = data.teamList.singleOrNull { it.name == lesson[8]+lesson[9] }?.id ?: -1
|
||||||
val classroom = lesson[11]
|
val classroom = lesson[11]
|
||||||
|
@ -91,8 +91,11 @@ class MobidziennikWebAttendance(override val data: DataMobidziennik,
|
|||||||
|
|
||||||
Regexes.MOBIDZIENNIK_ATTENDANCE_TABLE.findAll(text).forEach { tableResult ->
|
Regexes.MOBIDZIENNIK_ATTENDANCE_TABLE.findAll(text).forEach { tableResult ->
|
||||||
val table = tableResult[1]
|
val table = tableResult[1]
|
||||||
|
|
||||||
val lessonDates = mutableListOf<Date>()
|
val lessonDates = mutableListOf<Date>()
|
||||||
val entries = mutableListOf<String>()
|
val entries = mutableListOf<String>()
|
||||||
|
val ranges = mutableListOf<MatchResult?>()
|
||||||
|
|
||||||
Regexes.MOBIDZIENNIK_ATTENDANCE_LESSON_COUNT.findAll(table).forEach {
|
Regexes.MOBIDZIENNIK_ATTENDANCE_LESSON_COUNT.findAll(table).forEach {
|
||||||
val date = Date.fromY_m_d(it[1])
|
val date = Date.fromY_m_d(it[1])
|
||||||
for (i in 0 until (it[2].toIntOrNull() ?: 0)) {
|
for (i in 0 until (it[2].toIntOrNull() ?: 0)) {
|
||||||
@ -101,102 +104,52 @@ class MobidziennikWebAttendance(override val data: DataMobidziennik,
|
|||||||
}
|
}
|
||||||
Regexes.MOBIDZIENNIK_ATTENDANCE_ENTRIES.findAll(table).mapTo(entries) { it[1] }
|
Regexes.MOBIDZIENNIK_ATTENDANCE_ENTRIES.findAll(table).mapTo(entries) { it[1] }
|
||||||
|
|
||||||
|
Regexes.MOBIDZIENNIK_ATTENDANCE_COLUMNS.findAll(table).forEach { columns ->
|
||||||
|
var index = 0
|
||||||
|
Regexes.MOBIDZIENNIK_ATTENDANCE_COLUMN.findAll(columns[1]).forEach { column ->
|
||||||
|
if (column[1].contains("colspan")) {
|
||||||
|
val colspan =
|
||||||
|
Regexes.MOBIDZIENNIK_ATTENDANCE_COLUMN_SPAN.find(column[1])
|
||||||
|
?.get(1)
|
||||||
|
?.toIntOrNull() ?: 0
|
||||||
|
entries.addAll(index, List(colspan) { "" })
|
||||||
|
ranges.addAll(List(colspan) { null })
|
||||||
|
index += colspan
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val range = Regexes.MOBIDZIENNIK_ATTENDANCE_RANGE.find(column[2])
|
||||||
|
ranges.add(range)
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val dateIterator = lessonDates.iterator()
|
val dateIterator = lessonDates.iterator()
|
||||||
val entriesIterator = entries.iterator()
|
val entriesIterator = entries.iterator()
|
||||||
Regexes.MOBIDZIENNIK_ATTENDANCE_RANGE.findAll(table).let { ranges ->
|
|
||||||
val count = ranges.count()
|
val count = ranges.count()
|
||||||
// verify the lesson count is the same as dates & entries
|
// verify the lesson count is the same as dates & entries
|
||||||
if (count != lessonDates.count() || count != entries.count())
|
if (count != lessonDates.count() || count != entries.count())
|
||||||
|
return@forEach
|
||||||
|
ranges.forEach { range ->
|
||||||
|
val lessonDate = dateIterator.next()
|
||||||
|
val entry = entriesIterator.next()
|
||||||
|
if (range == null || entry.isBlank())
|
||||||
return@forEach
|
return@forEach
|
||||||
ranges.forEach { range ->
|
val startTime = Time.fromH_m(range[1])
|
||||||
val lessonDate = dateIterator.next()
|
|
||||||
var entry = entriesIterator.next()
|
|
||||||
if (entry.isBlank())
|
|
||||||
return@forEach
|
|
||||||
val startTime = Time.fromH_m(range[1])
|
|
||||||
|
|
||||||
range[2].split(" / ").mapNotNull { Regexes.MOBIDZIENNIK_ATTENDANCE_LESSON.find(it) }.forEachIndexed { index, lesson ->
|
range[2].split(" / ").mapNotNull {
|
||||||
val topic = lesson[1].substringAfter(" - ", missingDelimiterValue = "").takeIf { it.isNotBlank() }
|
Regexes.MOBIDZIENNIK_ATTENDANCE_LESSON.find(it)
|
||||||
if (topic?.startsWith("Lekcja odwołana: ") == true || entry.isEmpty())
|
}.forEachIndexed { index, lesson ->
|
||||||
return@forEachIndexed
|
processEntry(
|
||||||
val subjectName = lesson[1].substringBefore(" - ")
|
index,
|
||||||
//val team = lesson[3]
|
lesson,
|
||||||
val teacherName = lesson[3].fixName()
|
lessonDate,
|
||||||
|
startTime,
|
||||||
val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == teacherName }?.id ?: -1
|
entry,
|
||||||
val subjectId = data.subjectList.singleOrNull { it.longName == subjectName }?.id ?: -1
|
types,
|
||||||
|
typeSymbols
|
||||||
var typeSymbol = ""
|
)
|
||||||
for (symbol in typeSymbols) {
|
|
||||||
if (entry.startsWith(symbol) && symbol.length > typeSymbol.length)
|
|
||||||
typeSymbol = symbol
|
|
||||||
}
|
|
||||||
entry = entry.removePrefix(typeSymbol)
|
|
||||||
|
|
||||||
var isCounted = true
|
|
||||||
val baseType = when (typeSymbol) {
|
|
||||||
"." -> TYPE_PRESENT
|
|
||||||
"|" -> TYPE_ABSENT
|
|
||||||
"+" -> TYPE_ABSENT_EXCUSED
|
|
||||||
"s" -> TYPE_BELATED
|
|
||||||
"z" -> TYPE_RELEASED
|
|
||||||
else -> {
|
|
||||||
isCounted = false
|
|
||||||
when (typeSymbol) {
|
|
||||||
"e" -> TYPE_PRESENT_CUSTOM
|
|
||||||
"en" -> TYPE_ABSENT
|
|
||||||
"ep" -> TYPE_PRESENT_CUSTOM
|
|
||||||
else -> TYPE_UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val typeName = types?.get(typeSymbol) ?: ""
|
|
||||||
val typeColor = when (typeSymbol) {
|
|
||||||
"e" -> 0xff673ab7
|
|
||||||
"en" -> 0xffec407a
|
|
||||||
"ep" -> 0xff4caf50
|
|
||||||
else -> null
|
|
||||||
}?.toInt()
|
|
||||||
|
|
||||||
val typeShort = if (isCounted)
|
|
||||||
data.app.attendanceManager.getTypeShort(baseType)
|
|
||||||
else
|
|
||||||
typeSymbol
|
|
||||||
|
|
||||||
val semester = data.profile?.dateToSemester(lessonDate) ?: 1
|
|
||||||
|
|
||||||
val id = lessonDate.combineWith(startTime) / 6L * 10L + (lesson[0].hashCode() and 0xFFFF) + index
|
|
||||||
|
|
||||||
val attendanceObject = Attendance(
|
|
||||||
profileId = profileId,
|
|
||||||
id = id,
|
|
||||||
baseType = baseType,
|
|
||||||
typeName = typeName,
|
|
||||||
typeShort = typeShort,
|
|
||||||
typeSymbol = typeSymbol,
|
|
||||||
typeColor = typeColor,
|
|
||||||
date = lessonDate,
|
|
||||||
startTime = startTime,
|
|
||||||
semester = semester,
|
|
||||||
teacherId = teacherId,
|
|
||||||
subjectId = subjectId
|
|
||||||
).also {
|
|
||||||
it.lessonTopic = topic
|
|
||||||
it.isCounted = isCounted
|
|
||||||
}
|
|
||||||
|
|
||||||
data.attendanceList.add(attendanceObject)
|
|
||||||
if (baseType != TYPE_PRESENT) {
|
|
||||||
data.metadataList.add(
|
|
||||||
Metadata(
|
|
||||||
data.profileId,
|
|
||||||
Metadata.TYPE_ATTENDANCE,
|
|
||||||
id,
|
|
||||||
data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN,
|
|
||||||
data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,4 +159,97 @@ class MobidziennikWebAttendance(override val data: DataMobidziennik,
|
|||||||
onSuccess()
|
onSuccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun processEntry(
|
||||||
|
index: Int,
|
||||||
|
lesson: MatchResult,
|
||||||
|
lessonDate: Date,
|
||||||
|
startTime: Time,
|
||||||
|
entry: String,
|
||||||
|
types: Map<String?, String?>?,
|
||||||
|
typeSymbols: List<String>
|
||||||
|
) {
|
||||||
|
var entry = entry
|
||||||
|
|
||||||
|
val topic = lesson[1].substringAfter(" - ", missingDelimiterValue = "").takeIf { it.isNotBlank() }
|
||||||
|
if (topic?.startsWith("Lekcja odwołana: ") == true || entry.isEmpty())
|
||||||
|
return
|
||||||
|
val subjectName = lesson[1].substringBefore(" - ")
|
||||||
|
//val team = lesson[3]
|
||||||
|
val teacherName = lesson[3].fixName()
|
||||||
|
|
||||||
|
val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == teacherName }?.id ?: -1
|
||||||
|
val subjectId = data.subjectList.singleOrNull { it.longName == subjectName }?.id ?: -1
|
||||||
|
|
||||||
|
var typeSymbol = ""
|
||||||
|
for (symbol in typeSymbols) {
|
||||||
|
if (entry.startsWith(symbol) && symbol.length > typeSymbol.length)
|
||||||
|
typeSymbol = symbol
|
||||||
|
}
|
||||||
|
entry = entry.removePrefix(typeSymbol)
|
||||||
|
|
||||||
|
var isCounted = true
|
||||||
|
val baseType = when (typeSymbol) {
|
||||||
|
"." -> TYPE_PRESENT
|
||||||
|
"|" -> TYPE_ABSENT
|
||||||
|
"+" -> TYPE_ABSENT_EXCUSED
|
||||||
|
"s" -> TYPE_BELATED
|
||||||
|
"z" -> TYPE_RELEASED
|
||||||
|
else -> {
|
||||||
|
isCounted = false
|
||||||
|
when (typeSymbol) {
|
||||||
|
"e" -> TYPE_PRESENT_CUSTOM
|
||||||
|
"en" -> TYPE_ABSENT
|
||||||
|
"ep" -> TYPE_PRESENT_CUSTOM
|
||||||
|
else -> TYPE_UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val typeName = types?.get(typeSymbol) ?: ""
|
||||||
|
val typeColor = when (typeSymbol) {
|
||||||
|
"e" -> 0xff673ab7
|
||||||
|
"en" -> 0xffec407a
|
||||||
|
"ep" -> 0xff4caf50
|
||||||
|
else -> null
|
||||||
|
}?.toInt()
|
||||||
|
|
||||||
|
val typeShort = if (isCounted)
|
||||||
|
data.app.attendanceManager.getTypeShort(baseType)
|
||||||
|
else
|
||||||
|
typeSymbol
|
||||||
|
|
||||||
|
val semester = data.profile?.dateToSemester(lessonDate) ?: 1
|
||||||
|
|
||||||
|
val id = lessonDate.combineWith(startTime) / 6L * 10L + (lesson[0].hashCode() and 0xFFFF) + index
|
||||||
|
|
||||||
|
val attendanceObject = Attendance(
|
||||||
|
profileId = profileId,
|
||||||
|
id = id,
|
||||||
|
baseType = baseType,
|
||||||
|
typeName = typeName,
|
||||||
|
typeShort = typeShort,
|
||||||
|
typeSymbol = typeSymbol,
|
||||||
|
typeColor = typeColor,
|
||||||
|
date = lessonDate,
|
||||||
|
startTime = startTime,
|
||||||
|
semester = semester,
|
||||||
|
teacherId = teacherId,
|
||||||
|
subjectId = subjectId
|
||||||
|
).also {
|
||||||
|
it.lessonTopic = topic
|
||||||
|
it.isCounted = isCounted
|
||||||
|
}
|
||||||
|
|
||||||
|
data.attendanceList.add(attendanceObject)
|
||||||
|
if (baseType != TYPE_PRESENT) {
|
||||||
|
data.metadataList.add(
|
||||||
|
Metadata(
|
||||||
|
data.profileId,
|
||||||
|
Metadata.TYPE_ATTENDANCE,
|
||||||
|
id,
|
||||||
|
data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN,
|
||||||
|
data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-9-3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.data.api.events
|
||||||
|
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus
|
||||||
|
|
||||||
|
data class RegisterAvailabilityEvent(
|
||||||
|
val data: Map< String, RegisterAvailabilityStatus>
|
||||||
|
)
|
@ -12,12 +12,14 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.DateAdapter
|
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.DateAdapter
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.TimeAdapter
|
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.TimeAdapter
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.SignatureInterceptor
|
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.SignatureInterceptor
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.*
|
import pl.szczodrzynski.edziennik.data.api.szkolny.request.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.ApiResponse
|
import pl.szczodrzynski.edziennik.data.api.szkolny.response.ApiResponse
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
|
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.WebPushResponse
|
import pl.szczodrzynski.edziennik.data.api.szkolny.response.WebPushResponse
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||||
@ -112,6 +114,22 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
|||||||
*/
|
*/
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
private inline fun <reified T> parseResponse(response: Response<ApiResponse<T>>): T {
|
private inline fun <reified T> parseResponse(response: Response<ApiResponse<T>>): T {
|
||||||
|
app.config.update = response.body()?.update?.let { update ->
|
||||||
|
if (update.versionCode > BuildConfig.VERSION_CODE) {
|
||||||
|
if (update.updateMandatory
|
||||||
|
&& EventBus.getDefault().hasSubscriberForEvent(update::class.java)) {
|
||||||
|
EventBus.getDefault().postSticky(update)
|
||||||
|
}
|
||||||
|
update
|
||||||
|
}
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
response.body()?.registerAvailability?.let { registerAvailability ->
|
||||||
|
app.config.sync.registerAvailability = registerAvailability
|
||||||
|
}
|
||||||
|
|
||||||
if (response.isSuccessful && response.body()?.success == true) {
|
if (response.isSuccessful && response.body()?.success == true) {
|
||||||
if (Unit is T) {
|
if (Unit is T) {
|
||||||
return Unit
|
return Unit
|
||||||
@ -341,4 +359,10 @@ class SzkolnyApi(val app: App) : CoroutineScope {
|
|||||||
val response = api.firebaseToken(registerName).execute()
|
val response = api.firebaseToken(registerName).execute()
|
||||||
return parseResponse(response)
|
return parseResponse(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun getRegisterAvailability(): Map<String, RegisterAvailabilityStatus> {
|
||||||
|
val response = api.registerAvailability().execute()
|
||||||
|
return parseResponse(response)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,4 +38,7 @@ interface SzkolnyService {
|
|||||||
|
|
||||||
@GET("firebase/token/{registerName}")
|
@GET("firebase/token/{registerName}")
|
||||||
fun firebaseToken(@Path("registerName") registerName: String): Call<ApiResponse<String>>
|
fun firebaseToken(@Path("registerName") registerName: String): Call<ApiResponse<String>>
|
||||||
|
|
||||||
|
@GET("registerAvailability")
|
||||||
|
fun registerAvailability(): Call<ApiResponse<Map<String, RegisterAvailabilityStatus>>>
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,6 @@ object Signing {
|
|||||||
|
|
||||||
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
||||||
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
||||||
return "$param1.MTIzNDU2Nzg5MDiVEOFGvG===.$param2".sha256()
|
return "$param1.MTIzNDU2Nzg5MDx45DzIF8===.$param2".sha256()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,10 @@ data class ApiResponse<T> (
|
|||||||
|
|
||||||
val errors: List<Error>? = null,
|
val errors: List<Error>? = null,
|
||||||
|
|
||||||
val data: T? = null
|
val data: T? = null,
|
||||||
|
|
||||||
|
val update: Update? = null,
|
||||||
|
val registerAvailability: Map<String, RegisterAvailabilityStatus>? = null
|
||||||
) {
|
) {
|
||||||
data class Error (val code: String, val reason: String)
|
data class Error (val code: String, val reason: String)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-9-2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.data.api.szkolny.response
|
||||||
|
|
||||||
|
import pl.szczodrzynski.edziennik.BuildConfig
|
||||||
|
import pl.szczodrzynski.edziennik.DAY
|
||||||
|
import pl.szczodrzynski.edziennik.currentTimeUnix
|
||||||
|
|
||||||
|
data class RegisterAvailabilityStatus(
|
||||||
|
val available: Boolean,
|
||||||
|
val name: String?,
|
||||||
|
val userMessage: Message?,
|
||||||
|
val nextCheckAt: Long = currentTimeUnix() + 7 * DAY,
|
||||||
|
val minVersionCode: Int = BuildConfig.VERSION_CODE
|
||||||
|
) {
|
||||||
|
data class Message(
|
||||||
|
val title: String,
|
||||||
|
val contentShort: String,
|
||||||
|
val contentLong: String,
|
||||||
|
val icon: String?,
|
||||||
|
val image: String?,
|
||||||
|
val url: String?
|
||||||
|
)
|
||||||
|
}
|
@ -11,5 +11,6 @@ data class Update(
|
|||||||
val releaseNotes: String?,
|
val releaseNotes: String?,
|
||||||
val releaseType: String,
|
val releaseType: String,
|
||||||
val isOnGooglePlay: Boolean,
|
val isOnGooglePlay: Boolean,
|
||||||
val downloadUrl: String?
|
val downloadUrl: String?,
|
||||||
)
|
val updateMandatory: Boolean
|
||||||
|
)
|
||||||
|
@ -16,8 +16,7 @@ import androidx.room.Ignore
|
|||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import pl.droidsonroids.gif.GifDrawable
|
import pl.droidsonroids.gif.GifDrawable
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_EDUDZIENNIK
|
import pl.szczodrzynski.edziennik.data.api.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_PODLASIE
|
|
||||||
import pl.szczodrzynski.edziennik.utils.ProfileImageHolder
|
import pl.szczodrzynski.edziennik.utils.ProfileImageHolder
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import pl.szczodrzynski.navlib.ImageHolder
|
import pl.szczodrzynski.navlib.ImageHolder
|
||||||
@ -128,6 +127,17 @@ open class Profile(
|
|||||||
val isParent
|
val isParent
|
||||||
get() = accountName != null
|
get() = accountName != null
|
||||||
|
|
||||||
|
val registerName
|
||||||
|
get() = when (loginStoreType) {
|
||||||
|
LOGIN_TYPE_LIBRUS -> "librus"
|
||||||
|
LOGIN_TYPE_VULCAN -> "vulcan"
|
||||||
|
LOGIN_TYPE_IDZIENNIK -> "idziennik"
|
||||||
|
LOGIN_TYPE_MOBIDZIENNIK -> "mobidziennik"
|
||||||
|
LOGIN_TYPE_PODLASIE -> "podlasie"
|
||||||
|
LOGIN_TYPE_EDUDZIENNIK -> "edudziennik"
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
override fun getImageDrawable(context: Context): Drawable {
|
override fun getImageDrawable(context: Context): Drawable {
|
||||||
if (archived) {
|
if (archived) {
|
||||||
return context.getDrawableFromRes(pl.szczodrzynski.edziennik.R.drawable.profile_archived).also {
|
return context.getDrawableFromRes(pl.szczodrzynski.edziennik.R.drawable.profile_archived).also {
|
||||||
|
@ -5,10 +5,13 @@
|
|||||||
package pl.szczodrzynski.edziennik.data.firebase
|
package pl.szczodrzynski.edziennik.data.firebase
|
||||||
|
|
||||||
import com.google.gson.JsonParser
|
import com.google.gson.JsonParser
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.events.FeedbackMessageEvent
|
import pl.szczodrzynski.edziennik.data.api.events.FeedbackMessageEvent
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.events.RegisterAvailabilityEvent
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
|
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
|
||||||
import pl.szczodrzynski.edziennik.data.api.task.PostNotifications
|
import pl.szczodrzynski.edziennik.data.api.task.PostNotifications
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.*
|
import pl.szczodrzynski.edziennik.data.db.entity.*
|
||||||
@ -50,6 +53,16 @@ class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message:
|
|||||||
val message = app.gson.fromJson(message.data.getString("message"), FeedbackMessage::class.java) ?: return@launch
|
val message = app.gson.fromJson(message.data.getString("message"), FeedbackMessage::class.java) ?: return@launch
|
||||||
feedbackMessage(message)
|
feedbackMessage(message)
|
||||||
}
|
}
|
||||||
|
"registerAvailability" -> launch {
|
||||||
|
val data = app.gson.fromJson<Map<String, RegisterAvailabilityStatus>>(
|
||||||
|
message.data.getString("registerAvailability"),
|
||||||
|
object: TypeToken<Map<String, RegisterAvailabilityStatus>>(){}.type
|
||||||
|
) ?: return@launch
|
||||||
|
app.config.sync.registerAvailability = data
|
||||||
|
if (EventBus.getDefault().hasSubscriberForEvent(RegisterAvailabilityEvent::class.java)) {
|
||||||
|
EventBus.getDefault().postSticky(RegisterAvailabilityEvent(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import android.widget.Toast
|
|||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||||
@ -76,7 +77,7 @@ class UpdateWorker(val context: Context, val params: WorkerParameters) : Worker(
|
|||||||
try {
|
try {
|
||||||
val update = overrideUpdate
|
val update = overrideUpdate
|
||||||
?: run {
|
?: run {
|
||||||
val updates = withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
SzkolnyApi(app).runCatching({
|
SzkolnyApi(app).runCatching({
|
||||||
getUpdate("beta")
|
getUpdate("beta")
|
||||||
}, {
|
}, {
|
||||||
@ -84,15 +85,25 @@ class UpdateWorker(val context: Context, val params: WorkerParameters) : Worker(
|
|||||||
})
|
})
|
||||||
} ?: return@run null
|
} ?: return@run null
|
||||||
|
|
||||||
if (updates.isEmpty()) {
|
if (app.config.update == null
|
||||||
|
|| app.config.update?.versionCode ?: BuildConfig.VERSION_CODE <= BuildConfig.VERSION_CODE) {
|
||||||
app.config.update = null
|
app.config.update = null
|
||||||
Toast.makeText(app, app.getString(R.string.notification_no_update), Toast.LENGTH_SHORT).show()
|
Toast.makeText(app, app.getString(R.string.notification_no_update), Toast.LENGTH_SHORT).show()
|
||||||
return@run null
|
return@run null
|
||||||
}
|
}
|
||||||
updates[0]
|
app.config.update
|
||||||
} ?: return
|
} ?: return
|
||||||
|
|
||||||
app.config.update = update
|
if (update.versionCode <= BuildConfig.VERSION_CODE) {
|
||||||
|
app.config.update = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EventBus.getDefault().hasSubscriberForEvent(update::class.java)) {
|
||||||
|
if (!update.updateMandatory) // mandatory updates are posted by the SzkolnyApi
|
||||||
|
EventBus.getDefault().postSticky(update)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val notificationIntent = Intent(app, UpdateDownloaderService::class.java)
|
val notificationIntent = Intent(app, UpdateDownloaderService::class.java)
|
||||||
val pendingIntent = PendingIntent.getService(app, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
val pendingIntent = PendingIntent.getService(app, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-9-3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.dialogs
|
||||||
|
|
||||||
|
import android.text.method.LinkMovementMethod
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import coil.api.load
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import pl.szczodrzynski.edziennik.App
|
||||||
|
import pl.szczodrzynski.edziennik.BuildConfig
|
||||||
|
import pl.szczodrzynski.edziennik.R
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus
|
||||||
|
import pl.szczodrzynski.edziennik.databinding.DialogRegisterUnavailableBinding
|
||||||
|
import pl.szczodrzynski.edziennik.onClick
|
||||||
|
import pl.szczodrzynski.edziennik.utils.Utils
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class RegisterUnavailableDialog(
|
||||||
|
val activity: AppCompatActivity,
|
||||||
|
val status: RegisterAvailabilityStatus,
|
||||||
|
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||||
|
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||||
|
) : CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "RegisterUnavailableDialog"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
private lateinit var dialog: AlertDialog
|
||||||
|
|
||||||
|
private val job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
init { run {
|
||||||
|
if (activity.isFinishing)
|
||||||
|
return@run
|
||||||
|
if (status.available && status.minVersionCode <= BuildConfig.VERSION_CODE)
|
||||||
|
return@run
|
||||||
|
onShowListener?.invoke(TAG)
|
||||||
|
app = activity.applicationContext as App
|
||||||
|
|
||||||
|
if (!status.available && status.userMessage != null) {
|
||||||
|
val b = DialogRegisterUnavailableBinding.inflate(LayoutInflater.from(activity), null, false)
|
||||||
|
b.message = status.userMessage
|
||||||
|
if (status.userMessage.image != null)
|
||||||
|
b.image.load(status.userMessage.image)
|
||||||
|
if (status.userMessage.url != null) {
|
||||||
|
b.readMore.onClick {
|
||||||
|
Utils.openUrl(activity, status.userMessage.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.text.movementMethod = LinkMovementMethod.getInstance()
|
||||||
|
dialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setView(b.root)
|
||||||
|
.setPositiveButton(R.string.close) { dialog, _ ->
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.setOnDismissListener {
|
||||||
|
onDismissListener?.invoke(TAG)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
return@run
|
||||||
|
}
|
||||||
|
|
||||||
|
val update = app.config.update
|
||||||
|
if (status.minVersionCode > BuildConfig.VERSION_CODE) {
|
||||||
|
if (update != null && update.versionCode >= status.minVersionCode) {
|
||||||
|
UpdateAvailableDialog(activity, update, true, onShowListener, onDismissListener)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// this *should* never happen
|
||||||
|
dialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.update_available_title)
|
||||||
|
.setMessage(R.string.update_available_fallback)
|
||||||
|
.setPositiveButton(R.string.update_available_button) { dialog, _ ->
|
||||||
|
Utils.openGooglePlay(activity)
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.setCancelable(false)
|
||||||
|
.setOnDismissListener {
|
||||||
|
onDismissListener?.invoke(TAG)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
return@run
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
}
|
@ -17,7 +17,7 @@ import kotlin.coroutines.CoroutineContext
|
|||||||
class ServerMessageDialog(
|
class ServerMessageDialog(
|
||||||
val activity: AppCompatActivity,
|
val activity: AppCompatActivity,
|
||||||
val title: String,
|
val title: String,
|
||||||
val message: String,
|
val message: CharSequence,
|
||||||
val onShowListener: ((tag: String) -> Unit)? = null,
|
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||||
val onDismissListener: ((tag: String) -> Unit)? = null
|
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||||
) : CoroutineScope {
|
) : CoroutineScope {
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-9-3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.dialogs
|
||||||
|
|
||||||
|
import android.text.Html
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import pl.szczodrzynski.edziennik.*
|
||||||
|
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
|
||||||
|
import pl.szczodrzynski.edziennik.sync.UpdateDownloaderService
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class UpdateAvailableDialog(
|
||||||
|
val activity: AppCompatActivity,
|
||||||
|
val update: Update,
|
||||||
|
val mandatory: Boolean = update.updateMandatory,
|
||||||
|
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||||
|
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||||
|
) : CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "UpdateAvailableDialog"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
private lateinit var dialog: AlertDialog
|
||||||
|
|
||||||
|
private val job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
init { run {
|
||||||
|
if (activity.isFinishing)
|
||||||
|
return@run
|
||||||
|
if (update.versionCode <= BuildConfig.VERSION_CODE)
|
||||||
|
return@run
|
||||||
|
onShowListener?.invoke(TAG)
|
||||||
|
app = activity.applicationContext as App
|
||||||
|
|
||||||
|
dialog = MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.update_available_title)
|
||||||
|
.setMessage(
|
||||||
|
R.string.update_available_format,
|
||||||
|
BuildConfig.VERSION_NAME,
|
||||||
|
update.versionName,
|
||||||
|
update.releaseNotes?.let { Html.fromHtml(it) } ?: "---"
|
||||||
|
)
|
||||||
|
.setPositiveButton(R.string.update_available_button) { dialog, _ ->
|
||||||
|
activity.startService(Intent(app, UpdateDownloaderService::class.java))
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
.also {
|
||||||
|
if (!mandatory)
|
||||||
|
it.setNeutralButton(R.string.update_available_later, null)
|
||||||
|
}
|
||||||
|
.setCancelable(!mandatory)
|
||||||
|
.setOnDismissListener {
|
||||||
|
onDismissListener?.invoke(TAG)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}}
|
||||||
|
}
|
@ -33,9 +33,10 @@ class CardItemTouchHelperCallback(private val cardAdapter: HomeCardAdapter, priv
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||||
removeCard(viewHolder.adapterPosition, cardAdapter)
|
val position = viewHolder.adapterPosition
|
||||||
cardAdapter.items.removeAt(viewHolder.adapterPosition)
|
removeCard(position, cardAdapter)
|
||||||
cardAdapter.notifyItemRemoved(viewHolder.adapterPosition)
|
cardAdapter.items.removeAt(position)
|
||||||
|
cardAdapter.notifyItemRemoved(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
|
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
|
||||||
|
@ -21,12 +21,9 @@ import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate
|
|||||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial.Icon
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial.Icon
|
||||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.MainActivity
|
|
||||||
import pl.szczodrzynski.edziennik.R
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||||
import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding
|
import pl.szczodrzynski.edziennik.databinding.FragmentHomeBinding
|
||||||
import pl.szczodrzynski.edziennik.onClick
|
|
||||||
import pl.szczodrzynski.edziennik.ui.dialogs.home.StudentNumberDialog
|
import pl.szczodrzynski.edziennik.ui.dialogs.home.StudentNumberDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.home.cards.*
|
import pl.szczodrzynski.edziennik.ui.modules.home.cards.*
|
||||||
import pl.szczodrzynski.edziennik.utils.Themes
|
import pl.szczodrzynski.edziennik.utils.Themes
|
||||||
@ -50,9 +47,11 @@ class HomeFragment : Fragment(), CoroutineScope {
|
|||||||
cardAdapter.notifyItemMoved(fromPosition, toPosition)
|
cardAdapter.notifyItemMoved(fromPosition, toPosition)
|
||||||
|
|
||||||
val homeCards = App.config.forProfile().ui.homeCards.toMutableList()
|
val homeCards = App.config.forProfile().ui.homeCards.toMutableList()
|
||||||
val fromPair = homeCards[fromPosition]
|
val fromIndex = homeCards.indexOfFirst { it.cardId == fromCard.id }
|
||||||
homeCards[fromPosition] = homeCards[toPosition]
|
val toIndex = homeCards.indexOfFirst { it.cardId == toCard.id }
|
||||||
homeCards[toPosition] = fromPair
|
val fromPair = homeCards[fromIndex]
|
||||||
|
homeCards[fromIndex] = homeCards[toIndex]
|
||||||
|
homeCards[toIndex] = fromPair
|
||||||
App.config.forProfile().ui.homeCards = homeCards
|
App.config.forProfile().ui.homeCards = homeCards
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -64,10 +63,10 @@ class HomeFragment : Fragment(), CoroutineScope {
|
|||||||
val card = cardAdapter.items[position]
|
val card = cardAdapter.items[position]
|
||||||
if (card.id >= 100) {
|
if (card.id >= 100) {
|
||||||
// debug & archive cards are not removable
|
// debug & archive cards are not removable
|
||||||
cardAdapter.notifyDataSetChanged()
|
//cardAdapter.notifyDataSetChanged()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
homeCards.removeAt(position)
|
homeCards.removeAll { it.cardId == card.id }
|
||||||
App.config.forProfile().ui.homeCards = homeCards
|
App.config.forProfile().ui.homeCards = homeCards
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,6 +165,13 @@ class HomeFragment : Fragment(), CoroutineScope {
|
|||||||
if (app.profile.archived)
|
if (app.profile.archived)
|
||||||
items.add(0, HomeArchiveCard(101, app, activity, this, app.profile))
|
items.add(0, HomeArchiveCard(101, app, activity, this, app.profile))
|
||||||
|
|
||||||
|
val status = app.config.sync.registerAvailability[app.profile.registerName]
|
||||||
|
val update = app.config.update
|
||||||
|
if (update != null && update.versionCode > BuildConfig.VERSION_CODE
|
||||||
|
|| status != null && (!status.available || status.minVersionCode > BuildConfig.VERSION_CODE)) {
|
||||||
|
items.add(0, HomeAvailabilityCard(102, app, activity, this, app.profile))
|
||||||
|
}
|
||||||
|
|
||||||
val adapter = HomeCardAdapter(items)
|
val adapter = HomeCardAdapter(items)
|
||||||
val itemTouchHelper = ItemTouchHelper(CardItemTouchHelperCallback(adapter, b.refreshLayout))
|
val itemTouchHelper = ItemTouchHelper(CardItemTouchHelperCallback(adapter, b.refreshLayout))
|
||||||
adapter.itemTouchHelper = itemTouchHelper
|
adapter.itemTouchHelper = itemTouchHelper
|
||||||
|
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2020-9-3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.ui.modules.home.cards
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.core.text.HtmlCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.view.plusAssign
|
||||||
|
import androidx.core.view.setMargins
|
||||||
|
import coil.api.load
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import pl.szczodrzynski.edziennik.*
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||||
|
import pl.szczodrzynski.edziennik.databinding.CardHomeAvailabilityBinding
|
||||||
|
import pl.szczodrzynski.edziennik.sync.UpdateDownloaderService
|
||||||
|
import pl.szczodrzynski.edziennik.ui.dialogs.RegisterUnavailableDialog
|
||||||
|
import pl.szczodrzynski.edziennik.ui.dialogs.UpdateAvailableDialog
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
|
||||||
|
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class HomeAvailabilityCard(
|
||||||
|
override val id: Int,
|
||||||
|
val app: App,
|
||||||
|
val activity: MainActivity,
|
||||||
|
val fragment: HomeFragment,
|
||||||
|
val profile: Profile
|
||||||
|
) : HomeCard, CoroutineScope {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "HomeAvailabilityCard"
|
||||||
|
}
|
||||||
|
|
||||||
|
private var job: Job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = job + Dispatchers.Main
|
||||||
|
|
||||||
|
override fun bind(position: Int, holder: HomeCardAdapter.ViewHolder) {
|
||||||
|
holder.root.removeAllViews()
|
||||||
|
val b = CardHomeAvailabilityBinding.inflate(LayoutInflater.from(holder.root.context))
|
||||||
|
b.root.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
|
||||||
|
setMargins(8.dp)
|
||||||
|
}
|
||||||
|
holder.root += b.root
|
||||||
|
|
||||||
|
val status = app.config.sync.registerAvailability[profile.registerName]
|
||||||
|
val update = app.config.update
|
||||||
|
|
||||||
|
if (update == null && status == null)
|
||||||
|
return
|
||||||
|
|
||||||
|
var onInfoClick = { _: View -> }
|
||||||
|
|
||||||
|
if (status != null && !status.available && status.userMessage != null) {
|
||||||
|
b.homeAvailabilityTitle.text = HtmlCompat.fromHtml(status.userMessage.title, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||||
|
b.homeAvailabilityText.text = HtmlCompat.fromHtml(status.userMessage.contentShort, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||||
|
b.homeAvailabilityUpdate.isVisible = false
|
||||||
|
b.homeAvailabilityIcon.setImageResource(R.drawable.ic_sync)
|
||||||
|
if (status.userMessage.icon != null)
|
||||||
|
b.homeAvailabilityIcon.load(status.userMessage.icon)
|
||||||
|
onInfoClick = {
|
||||||
|
RegisterUnavailableDialog(activity, status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (update != null && update.versionCode > BuildConfig.VERSION_CODE) {
|
||||||
|
b.homeAvailabilityTitle.setText(R.string.home_availability_title)
|
||||||
|
b.homeAvailabilityText.setText(R.string.home_availability_text, update.versionName)
|
||||||
|
b.homeAvailabilityUpdate.isVisible = true
|
||||||
|
b.homeAvailabilityIcon.setImageResource(R.drawable.ic_update)
|
||||||
|
onInfoClick = {
|
||||||
|
UpdateAvailableDialog(activity, update)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.homeAvailabilityUpdate.onClick {
|
||||||
|
if (update == null)
|
||||||
|
return@onClick
|
||||||
|
activity.startService(Intent(app, UpdateDownloaderService::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.homeAvailabilityInfo.onClick(onInfoClick)
|
||||||
|
holder.root.onClick(onInfoClick)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unbind(position: Int, holder: HomeCardAdapter.ViewHolder) = Unit
|
||||||
|
}
|
@ -13,14 +13,12 @@ import android.view.ViewGroup
|
|||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import pl.szczodrzynski.edziennik.*
|
||||||
import kotlinx.coroutines.Job
|
import pl.szczodrzynski.edziennik.data.api.*
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||||
import pl.szczodrzynski.edziennik.Bundle
|
|
||||||
import pl.szczodrzynski.edziennik.R
|
|
||||||
import pl.szczodrzynski.edziennik.databinding.LoginChooserFragmentBinding
|
import pl.szczodrzynski.edziennik.databinding.LoginChooserFragmentBinding
|
||||||
import pl.szczodrzynski.edziennik.onClick
|
import pl.szczodrzynski.edziennik.ui.dialogs.RegisterUnavailableDialog
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackActivity
|
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackActivity
|
||||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
@ -53,18 +51,23 @@ class LoginChooserFragment : Fragment(), CoroutineScope {
|
|||||||
if (!isAdded) return
|
if (!isAdded) return
|
||||||
|
|
||||||
val adapter = LoginChooserAdapter(activity) { loginType, loginMode ->
|
val adapter = LoginChooserAdapter(activity) { loginType, loginMode ->
|
||||||
if (loginMode.isPlatformSelection) {
|
launch {
|
||||||
nav.navigate(R.id.loginPlatformListFragment, Bundle(
|
if (!checkAvailability(loginType.loginType))
|
||||||
|
return@launch
|
||||||
|
|
||||||
|
if (loginMode.isPlatformSelection) {
|
||||||
|
nav.navigate(R.id.loginPlatformListFragment, Bundle(
|
||||||
|
"loginType" to loginType.loginType,
|
||||||
|
"loginMode" to loginMode.loginMode
|
||||||
|
), activity.navOptions)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.navigate(R.id.loginFormFragment, Bundle(
|
||||||
"loginType" to loginType.loginType,
|
"loginType" to loginType.loginType,
|
||||||
"loginMode" to loginMode.loginMode
|
"loginMode" to loginMode.loginMode
|
||||||
), activity.navOptions)
|
), activity.navOptions)
|
||||||
return@LoginChooserAdapter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav.navigate(R.id.loginFormFragment, Bundle(
|
|
||||||
"loginType" to loginType.loginType,
|
|
||||||
"loginMode" to loginMode.loginMode
|
|
||||||
), activity.navOptions)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LoginInfo.chooserList = LoginInfo.chooserList
|
LoginInfo.chooserList = LoginInfo.chooserList
|
||||||
@ -102,4 +105,35 @@ class LoginChooserFragment : Fragment(), CoroutineScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun checkAvailability(loginType: Int): Boolean {
|
||||||
|
when (loginType) {
|
||||||
|
LOGIN_TYPE_LIBRUS -> "librus"
|
||||||
|
LOGIN_TYPE_VULCAN -> "vulcan"
|
||||||
|
LOGIN_TYPE_IDZIENNIK -> "idziennik"
|
||||||
|
LOGIN_TYPE_MOBIDZIENNIK -> "mobidziennik"
|
||||||
|
LOGIN_TYPE_PODLASIE -> "podlasie"
|
||||||
|
LOGIN_TYPE_EDUDZIENNIK -> "edudziennik"
|
||||||
|
else -> null
|
||||||
|
}?.let { registerName ->
|
||||||
|
var status = app.config.sync.registerAvailability[registerName]
|
||||||
|
if (status == null || status.nextCheckAt < currentTimeUnix()) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val api = SzkolnyApi(app)
|
||||||
|
api.runCatching(activity) {
|
||||||
|
val availability = getRegisterAvailability()
|
||||||
|
app.config.sync.registerAvailability = availability
|
||||||
|
status = availability[registerName]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status?.available != true) {
|
||||||
|
if (status != null)
|
||||||
|
RegisterUnavailableDialog(activity, status!!)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
11
app/src/main/res/drawable/ic_update.xml
Normal file
11
app/src/main/res/drawable/ic_update.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-9-3.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector android:height="128dp" android:viewportHeight="64"
|
||||||
|
android:viewportWidth="64" android:width="128dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#1a6ee2" android:pathData="m29.583,48.966c1.329,1.347 3.505,1.347 4.834,0l10.917,-11.06c1.466,-1.484 0.353,-3.906 -1.795,-3.906h-5.539v-8.5c0,-1.375 -1.125,-2.5 -2.5,-2.5h-7c-1.375,0 -2.5,1.125 -2.5,2.5v8.5h-5.539c-2.147,0 -3.26,2.422 -1.795,3.906z"/>
|
||||||
|
<path android:fillColor="#2aa7ed" android:pathData="m28.5,12h7c1.375,0 2.5,-1.125 2.5,-2.5s-1.125,-2.5 -2.5,-2.5h-7c-1.375,0 -2.5,1.125 -2.5,2.5s1.125,2.5 2.5,2.5z"/>
|
||||||
|
<path android:fillColor="#2082e6" android:pathData="m28.5,20h7c1.375,0 2.5,-1.125 2.5,-2.5s-1.125,-2.5 -2.5,-2.5h-7c-1.375,0 -2.5,1.125 -2.5,2.5s1.125,2.5 2.5,2.5z"/>
|
||||||
|
<path android:fillColor="#1762df" android:pathData="m54,55.5c0,1.375 -1.125,2.5 -2.5,2.5h-39c-1.375,0 -2.5,-1.125 -2.5,-2.5s1.125,-2.5 2.5,-2.5h39c1.375,0 2.5,1.125 2.5,2.5z"/>
|
||||||
|
</vector>
|
65
app/src/main/res/layout/card_home_availability.xml
Normal file
65
app/src/main/res/layout/card_home_availability.xml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-9-3.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
tools:layout_margin="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/homeAvailabilityTitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/home_availability_title"
|
||||||
|
android:textAppearance="@style/NavView.TextView.Title" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/homeAvailabilityText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:fontFamily="sans-serif-light"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:text="@string/home_availability_text"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="Zaktualizuj aplikację do najnowszej wersji 4.3.1." />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/homeAvailabilityUpdate"
|
||||||
|
style="@style/Widget.MaterialComponents.Button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/home_availability_update" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/homeAvailabilityInfo"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/home_availability_info" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/homeAvailabilityIcon"
|
||||||
|
android:layout_width="64dp"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
app:srcCompat="@drawable/ic_update" />
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
72
app/src/main/res/layout/dialog_register_unavailable.xml
Normal file
72
app/src/main/res/layout/dialog_register_unavailable.xml
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2020-9-3.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
<import type="android.view.View" />
|
||||||
|
<import type="androidx.core.text.HtmlCompat" />
|
||||||
|
<variable
|
||||||
|
name="message"
|
||||||
|
type="pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus.Message" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:scaleType="fitXY"
|
||||||
|
android:visibility="@{message.image != null ? View.VISIBLE : View.GONE}"
|
||||||
|
tools:src="@tools:sample/backgrounds/scenic" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="24dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingRight="24dp"
|
||||||
|
android:paddingBottom="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{HtmlCompat.fromHtml(message.title, HtmlCompat.FROM_HTML_MODE_LEGACY)}"
|
||||||
|
android:textAppearance="@style/NavView.TextView.Title"
|
||||||
|
tools:text="Dziennik nie działa" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@{HtmlCompat.fromHtml(message.contentLong, HtmlCompat.FROM_HTML_MODE_LEGACY)}"
|
||||||
|
tools:text="Dziennik się zepsuł i nie działa, szkoda\n\n\nwiele linijek ma ten tekst" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/readMore"
|
||||||
|
style="@style/Widget.MaterialComponents.Button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/register_unavailable_read_more"
|
||||||
|
android:visibility="@{message.url != null ? View.VISIBLE : View.GONE}" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</layout>
|
@ -854,7 +854,7 @@
|
|||||||
<string name="settings_about_licenses_text">Open-Source-Lizenzen</string>
|
<string name="settings_about_licenses_text">Open-Source-Lizenzen</string>
|
||||||
<string name="settings_about_privacy_policy_text">Datenschutzrichtlinie</string>
|
<string name="settings_about_privacy_policy_text">Datenschutzrichtlinie</string>
|
||||||
<string name="settings_about_register_title_text">E-Klassenbuch</string>
|
<string name="settings_about_register_title_text">E-Klassenbuch</string>
|
||||||
<string name="settings_about_title_subtext">© Kuba Szczodrzyński && Kacper Ziubryniewicz\nSeptember 2018 - August 2020</string>
|
<string name="settings_about_title_subtext">© Kuba Szczodrzyński && Kacper Ziubryniewicz\nSeptember 2018 - 2020</string>
|
||||||
<string name="settings_about_update_subtext">Klicken Sie hier, um nach Aktualisierungen zu suchen</string>
|
<string name="settings_about_update_subtext">Klicken Sie hier, um nach Aktualisierungen zu suchen</string>
|
||||||
<string name="settings_about_update_text">Aktualisierung</string>
|
<string name="settings_about_update_text">Aktualisierung</string>
|
||||||
<string name="settings_about_version_text">Version</string>
|
<string name="settings_about_version_text">Version</string>
|
||||||
|
@ -856,7 +856,7 @@
|
|||||||
<string name="settings_about_licenses_text">Open-source licenses</string>
|
<string name="settings_about_licenses_text">Open-source licenses</string>
|
||||||
<string name="settings_about_privacy_policy_text">Privacy policy</string>
|
<string name="settings_about_privacy_policy_text">Privacy policy</string>
|
||||||
<string name="settings_about_register_title_text">E-register</string>
|
<string name="settings_about_register_title_text">E-register</string>
|
||||||
<string name="settings_about_title_subtext">© Kuba Szczodrzyński && Kacper Ziubryniewicz\nSeptember 2018 - August 2020</string>
|
<string name="settings_about_title_subtext">© Kuba Szczodrzyński && Kacper Ziubryniewicz\nSeptember 2018 - 2020</string>
|
||||||
<string name="settings_about_update_subtext">Click to check for updates</string>
|
<string name="settings_about_update_subtext">Click to check for updates</string>
|
||||||
<string name="settings_about_update_text">Update</string>
|
<string name="settings_about_update_text">Update</string>
|
||||||
<string name="settings_about_version_text">Version</string>
|
<string name="settings_about_version_text">Version</string>
|
||||||
|
@ -919,7 +919,7 @@
|
|||||||
<string name="settings_about_licenses_text">Licencje open-source</string>
|
<string name="settings_about_licenses_text">Licencje open-source</string>
|
||||||
<string name="settings_about_privacy_policy_text">Polityka prywatności</string>
|
<string name="settings_about_privacy_policy_text">Polityka prywatności</string>
|
||||||
<string name="settings_about_register_title_text">E-dziennik</string>
|
<string name="settings_about_register_title_text">E-dziennik</string>
|
||||||
<string name="settings_about_title_subtext">© Kuba Szczodrzyński && Kacper Ziubryniewicz\nwrzesień 2018 - sierpień 2020</string>
|
<string name="settings_about_title_subtext">© Kuba Szczodrzyński && Kacper Ziubryniewicz\nwrzesień 2018 - 2020</string>
|
||||||
<string name="settings_about_update_subtext">Kliknij, aby sprawdzić aktualizacje</string>
|
<string name="settings_about_update_subtext">Kliknij, aby sprawdzić aktualizacje</string>
|
||||||
<string name="settings_about_update_text">Aktualizacja</string>
|
<string name="settings_about_update_text">Aktualizacja</string>
|
||||||
<string name="settings_about_version_text">Wersja</string>
|
<string name="settings_about_version_text">Wersja</string>
|
||||||
@ -1368,4 +1368,14 @@
|
|||||||
<string name="home_archive_close_no_target_title">Brak aktualnego profilu</string>
|
<string name="home_archive_close_no_target_title">Brak aktualnego profilu</string>
|
||||||
<string name="home_archive_close_no_target_text">Uczeń %s nie posiada profilu na tym koncie w aktualnym roku szkolnym. Prawdopodobnie ten profil został usunięty lub uczeń nie uczęszcza już do tej klasy.\n\nAby przejść do aktualnego profilu, wybierz ucznia z listy lub zaloguj się na jego konto przyciskiem Dodaj ucznia.</string>
|
<string name="home_archive_close_no_target_text">Uczeń %s nie posiada profilu na tym koncie w aktualnym roku szkolnym. Prawdopodobnie ten profil został usunięty lub uczeń nie uczęszcza już do tej klasy.\n\nAby przejść do aktualnego profilu, wybierz ucznia z listy lub zaloguj się na jego konto przyciskiem Dodaj ucznia.</string>
|
||||||
<string name="login_copyright_notice">Znaki towarowe zamieszczone w tej aplikacji należą do ich prawowitych właścicieli i są używane wyłącznie w celach informacyjnych.</string>
|
<string name="login_copyright_notice">Znaki towarowe zamieszczone w tej aplikacji należą do ich prawowitych właścicieli i są używane wyłącznie w celach informacyjnych.</string>
|
||||||
|
<string name="update_available_title">Dostępna aktualizacja aplikacji</string>
|
||||||
|
<string name="update_available_format">Używasz starej wersji aplikacji Szkolny.eu (%s). Aby móc korzystać z aplikacji oraz zapewnić najlepsze działanie, zaktualizuj aplikację do wersji %s.\n\nDziennik zmian:\n%s</string>
|
||||||
|
<string name="update_available_fallback">Posiadasz nieaktualną wersję aplikacji Szkolny.eu. Aby móc dalej synchronizować dane, musisz zaktualizować aplikację.</string>
|
||||||
|
<string name="update_available_button">Aktualizuj</string>
|
||||||
|
<string name="update_available_later">Nie teraz</string>
|
||||||
|
<string name="home_availability_title">Dostępna aktualizacja</string>
|
||||||
|
<string name="home_availability_text">Zaktualizuj aplikację do najnowszej wersji %s.</string>
|
||||||
|
<string name="home_availability_info">Zobacz więcej</string>
|
||||||
|
<string name="home_availability_update">Aktualizuj</string>
|
||||||
|
<string name="register_unavailable_read_more">Dowiedz się więcej</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -5,8 +5,8 @@ buildscript {
|
|||||||
kotlin_version = '1.3.61'
|
kotlin_version = '1.3.61'
|
||||||
|
|
||||||
release = [
|
release = [
|
||||||
versionName: "4.3.1",
|
versionName: "4.4.2",
|
||||||
versionCode: 4030199
|
versionCode: 4040299
|
||||||
]
|
]
|
||||||
|
|
||||||
setup = [
|
setup = [
|
||||||
|
Reference in New Issue
Block a user