forked from github/szkolny
Compare commits
14 Commits
v3.9.3-dev
...
v3.9.4-dev
Author | SHA1 | Date | |
---|---|---|---|
810976d976 | |||
eb0540b5cb | |||
124437fd73 | |||
69b512e3d1 | |||
29d74e14bd | |||
f42ec8435a | |||
1052b824db | |||
0742a6a74c | |||
d4e9e1730f | |||
4eeaa54a47 | |||
5fa7409317 | |||
0bcd190714 | |||
563f08b0ab | |||
1b75424604 |
@ -166,6 +166,10 @@ dependencies {
|
|||||||
implementation "androidx.work:work-runtime-ktx:${versions.work}"
|
implementation "androidx.work:work-runtime-ktx:${versions.work}"
|
||||||
|
|
||||||
implementation 'com.hypertrack:hyperlog:0.0.10'
|
implementation 'com.hypertrack:hyperlog:0.0.10'
|
||||||
|
|
||||||
|
implementation 'com.github.kuba2k2:RecyclerTabLayout:700f980584'
|
||||||
|
|
||||||
|
implementation 'com.linkedin.android.tachyon:tachyon:1.0.2'
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
9
app/sampledata/check/ic_check.xml
Normal file
9
app/sampledata/check/ic_check.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF4caf50"
|
||||||
|
android:pathData="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"/>
|
||||||
|
</vector>
|
@ -6,8 +6,13 @@ import android.content.Context
|
|||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.*
|
||||||
|
import android.text.style.ForegroundColorSpan
|
||||||
|
import android.text.style.StrikethroughSpan
|
||||||
import android.util.LongSparseArray
|
import android.util.LongSparseArray
|
||||||
import android.util.SparseArray
|
import android.util.SparseArray
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.util.forEach
|
import androidx.core.util.forEach
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
@ -326,4 +331,81 @@ fun String.crc32(): Long {
|
|||||||
return crc.value
|
return crc.value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Long.formatDate(format: String = "yyyy-MM-dd HH:mm:ss"): String = SimpleDateFormat(format).format(this)
|
fun Long.formatDate(format: String = "yyyy-MM-dd HH:mm:ss"): String = SimpleDateFormat(format).format(this)
|
||||||
|
|
||||||
|
fun CharSequence?.asColoredSpannable(colorInt: Int): Spannable {
|
||||||
|
val spannable = SpannableString(this)
|
||||||
|
spannable.setSpan(ForegroundColorSpan(colorInt), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
return spannable
|
||||||
|
}
|
||||||
|
fun CharSequence?.asStrikethroughSpannable(): Spannable {
|
||||||
|
val spannable = SpannableString(this)
|
||||||
|
spannable.setSpan(StrikethroughSpan(), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
return spannable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new read-only list only of those given elements, that are not empty.
|
||||||
|
* Applies for CharSequence and descendants.
|
||||||
|
*/
|
||||||
|
fun <T : CharSequence> listOfNotEmpty(vararg elements: T): List<T> = elements.filterNot { it.isEmpty() }
|
||||||
|
|
||||||
|
fun List<CharSequence>.concat(delimiter: String? = null): CharSequence {
|
||||||
|
if (this.isEmpty()) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.size == 1) {
|
||||||
|
return this[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
var spanned = false
|
||||||
|
for (piece in this) {
|
||||||
|
if (piece is Spanned) {
|
||||||
|
spanned = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var first = true
|
||||||
|
if (spanned) {
|
||||||
|
val ssb = SpannableStringBuilder()
|
||||||
|
for (piece in this) {
|
||||||
|
if (!first && delimiter != null)
|
||||||
|
ssb.append(delimiter)
|
||||||
|
first = false
|
||||||
|
ssb.append(piece)
|
||||||
|
}
|
||||||
|
return SpannedString(ssb)
|
||||||
|
} else {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
for (piece in this) {
|
||||||
|
if (!first && delimiter != null)
|
||||||
|
sb.append(delimiter)
|
||||||
|
first = false
|
||||||
|
sb.append(piece)
|
||||||
|
}
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TextView.setText(@StringRes resid: Int, vararg formatArgs: Any) {
|
||||||
|
text = context.getString(resid, *formatArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun JsonObject(vararg properties: Pair<String, Any>): 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
|
@ -62,7 +62,7 @@ import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
|
|||||||
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment
|
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
|
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
|
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
|
||||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
import pl.szczodrzynski.edziennik.ui.modules.timetable.v2.TimetableFragment
|
||||||
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
|
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
|
||||||
import pl.szczodrzynski.edziennik.utils.Themes
|
import pl.szczodrzynski.edziennik.utils.Themes
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils
|
import pl.szczodrzynski.edziennik.utils.Utils
|
||||||
|
@ -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"
|
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"
|
val LIBRUS_USER_AGENT = "$SYSTEM_USER_AGENT LibrusMobileApp"
|
||||||
const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0"
|
const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0"
|
||||||
const val LIBRUS_CLIENT_ID = "wmSyUMo8llDAs4y9tJVYY92oyZ6h4lAt7KCuy0Gv"
|
const val LIBRUS_CLIENT_ID = "wmSyUMo8llDAs4y9tJVYY92oyZ6h4lAt7KCuy0Gv"
|
||||||
|
@ -66,13 +66,13 @@ val librusLoginMethods = listOf(
|
|||||||
},
|
},
|
||||||
|
|
||||||
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, LibrusLoginSynergia::class.java)
|
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, LibrusLoginSynergia::class.java)
|
||||||
.withIsPossible { _, _ -> true }
|
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
|
||||||
.withRequiredLoginMethod { profile, _ ->
|
.withRequiredLoginMethod { profile, _ ->
|
||||||
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED
|
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)
|
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, LibrusLoginMessages::class.java)
|
||||||
.withIsPossible { _, _ -> true }
|
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
|
||||||
.withRequiredLoginMethod { profile, _ ->
|
.withRequiredLoginMethod { profile, _ ->
|
||||||
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED
|
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package pl.szczodrzynski.edziennik.api.v2.events.task
|
package pl.szczodrzynski.edziennik.api.v2.events.task
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.R
|
import pl.szczodrzynski.edziennik.R
|
||||||
import pl.szczodrzynski.edziennik.api.v2.*
|
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 firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore))
|
||||||
fun sync() = EdziennikTask(-1, SyncRequest())
|
fun sync() = EdziennikTask(-1, SyncRequest())
|
||||||
fun syncProfile(profileId: Int, viewIds: List<Pair<Int, Int>>? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds))
|
fun syncProfile(profileId: Int, viewIds: List<Pair<Int, Int>>? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds, arguments))
|
||||||
fun syncProfileList(profileList: List<Int>) = EdziennikTask(-1, SyncProfileListRequest(profileList))
|
fun syncProfileList(profileList: List<Int>) = EdziennikTask(-1, SyncProfileListRequest(profileList))
|
||||||
fun messageGet(profileId: Int, messageId: Int) = EdziennikTask(profileId, MessageGetRequest(messageId))
|
fun messageGet(profileId: Int, messageId: Int) = EdziennikTask(profileId, MessageGetRequest(messageId))
|
||||||
fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest())
|
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
|
private var edziennikInterface: EdziennikInterface? = null
|
||||||
|
|
||||||
fun run(app: App, taskCallback: EdziennikCallback) {
|
internal fun run(app: App, taskCallback: EdziennikCallback) {
|
||||||
edziennikInterface = when (loginStore.type) {
|
edziennikInterface = when (loginStore.type) {
|
||||||
LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback)
|
LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback)
|
||||||
LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(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) {
|
when (request) {
|
||||||
is SyncProfileRequest -> edziennikInterface?.sync(
|
is SyncProfileRequest -> edziennikInterface?.sync(
|
||||||
featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) } ?: Features.getAllIds(),
|
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 MessageGetRequest -> edziennikInterface?.getMessage(request.messageId)
|
||||||
is FirstLoginRequest -> edziennikInterface?.firstLogin()
|
is FirstLoginRequest -> edziennikInterface?.firstLogin()
|
||||||
is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead()
|
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)
|
data class FirstLoginRequest(val loginStore: LoginStore)
|
||||||
class SyncRequest
|
class SyncRequest
|
||||||
data class SyncProfileRequest(val viewIds: List<Pair<Int, Int>>? = null)
|
data class SyncProfileRequest(val viewIds: List<Pair<Int, Int>>? = null, val arguments: JsonObject? = null)
|
||||||
data class SyncProfileListRequest(val profileList: List<Int>)
|
data class SyncProfileListRequest(val profileList: List<Int>)
|
||||||
data class MessageGetRequest(val messageId: Int)
|
data class MessageGetRequest(val messageId: Int)
|
||||||
class AnnouncementsReadRequest
|
class AnnouncementsReadRequest
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.api.v2.idziennik
|
package pl.szczodrzynski.edziennik.api.v2.idziennik
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
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.data.IdziennikData
|
||||||
@ -48,7 +49,8 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
|||||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||||
__/ |
|
__/ |
|
||||||
|__*/
|
|__*/
|
||||||
override fun sync(featureIds: List<Int>, viewId: Int?) {
|
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||||
|
data.arguments = arguments
|
||||||
data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId)
|
data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId)
|
||||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||||
|
@ -4,8 +4,10 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.api.v2.interfaces
|
package pl.szczodrzynski.edziennik.api.v2.interfaces
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
|
||||||
interface EdziennikInterface {
|
interface EdziennikInterface {
|
||||||
fun sync(featureIds: List<Int>, viewId: Int? = null)
|
fun sync(featureIds: List<Int>, viewId: Int? = null, arguments: JsonObject? = null)
|
||||||
fun getMessage(messageId: Int)
|
fun getMessage(messageId: Int)
|
||||||
fun markAllAnnouncementsAsRead()
|
fun markAllAnnouncementsAsRead()
|
||||||
fun firstLogin()
|
fun firstLogin()
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.api.v2.librus
|
package pl.szczodrzynski.edziennik.api.v2.librus
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.api.v2.*
|
import pl.szczodrzynski.edziennik.api.v2.*
|
||||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
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<Int>, viewId: Int?) {
|
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||||
|
data.arguments = arguments
|
||||||
data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId)
|
data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId)
|
||||||
login()
|
login()
|
||||||
}
|
}
|
||||||
@ -173,7 +175,10 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
|||||||
login()
|
login()
|
||||||
}
|
}
|
||||||
// TODO PORTAL CAPTCHA
|
// 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_LUCKY_NUMBER_NOT_ACTIVE,
|
||||||
ERROR_LIBRUS_API_NOTES_NOT_ACTIVE -> {
|
ERROR_LIBRUS_API_NOTES_NOT_ACTIVE -> {
|
||||||
data()
|
data()
|
||||||
|
@ -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) {
|
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() {
|
val callback = object : JsonCallbackHandler() {
|
||||||
override fun onSuccess(json: JsonObject?, response: Response?) {
|
override fun onSuccess(json: JsonObject?, response: Response?) {
|
||||||
@ -90,7 +90,7 @@ open class LibrusApi(open val data: DataLibrus) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Request.builder()
|
Request.builder()
|
||||||
.url("$LIBRUS_API_URL/$endpoint")
|
.url("${if (data.fakeLogin) FAKE_LIBRUS_API else LIBRUS_API_URL}/$endpoint")
|
||||||
.userAgent(LIBRUS_USER_AGENT)
|
.userAgent(LIBRUS_USER_AGENT)
|
||||||
.addHeader("Authorization", "Bearer ${data.apiAccessToken}")
|
.addHeader("Authorization", "Bearer ${data.apiAccessToken}")
|
||||||
.apply {
|
.apply {
|
||||||
|
@ -76,7 +76,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
LibrusApiClassrooms(data, onSuccess)
|
LibrusApiClassrooms(data, onSuccess)
|
||||||
}
|
}
|
||||||
// TODO push config
|
// 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 -> {
|
ENDPOINT_LIBRUS_API_NORMAL_GRADES -> {
|
||||||
data.startProgress(R.string.edziennik_progress_endpoint_grades)
|
data.startProgress(R.string.edziennik_progress_endpoint_grades)
|
||||||
|
@ -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) {
|
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() {
|
val callback = object : JsonCallbackHandler() {
|
||||||
override fun onSuccess(json: JsonObject?, response: Response?) {
|
override fun onSuccess(json: JsonObject?, response: Response?) {
|
||||||
@ -81,7 +81,7 @@ open class LibrusPortal(open val data: DataLibrus) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Request.builder()
|
Request.builder()
|
||||||
.url(LIBRUS_PORTAL_URL + endpoint)
|
.url((if (data.fakeLogin) FAKE_LIBRUS_PORTAL else LIBRUS_PORTAL_URL) + endpoint)
|
||||||
.userAgent(LIBRUS_USER_AGENT)
|
.userAgent(LIBRUS_USER_AGENT)
|
||||||
.addHeader("Authorization", "Bearer ${data.portalAccessToken}")
|
.addHeader("Authorization", "Bearer ${data.portalAccessToken}")
|
||||||
.apply {
|
.apply {
|
||||||
|
@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Kuba Szczodrzyński 2019-11-10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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
|
||||||
|
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||||
|
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 {
|
||||||
|
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 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.value.toLong()).apply {
|
||||||
|
type = Lesson.TYPE_NO_LESSONS
|
||||||
|
date = lessonDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
onSuccess()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseLesson(lessonDate: Date, lesson: JsonObject) {
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ package pl.szczodrzynski.edziennik.api.v2.librus.firstlogin
|
|||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.api.v2.ERROR_NO_STUDENTS_IN_ACCOUNT
|
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.LIBRUS_ACCOUNTS_URL
|
||||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_EMAIL
|
import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_EMAIL
|
||||||
import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent
|
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) {
|
if (data.loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) {
|
||||||
// email login: use Portal for account list
|
// email login: use Portal for account list
|
||||||
LibrusLoginPortal(data) {
|
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")
|
val accounts = json.getJsonArray("accounts")
|
||||||
|
|
||||||
if (accounts == null || accounts.size() < 1) {
|
if (accounts == null || accounts.size() < 1) {
|
||||||
|
@ -7,14 +7,15 @@ import im.wangchao.mhttp.Response
|
|||||||
import im.wangchao.mhttp.body.MediaTypeUtils
|
import im.wangchao.mhttp.body.MediaTypeUtils
|
||||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||||
import pl.szczodrzynski.edziennik.*
|
|
||||||
import pl.szczodrzynski.edziennik.api.v2.*
|
import pl.szczodrzynski.edziennik.api.v2.*
|
||||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
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 pl.szczodrzynski.edziennik.utils.Utils.d
|
||||||
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
|
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
|
||||||
import java.util.ArrayList
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||||
@ -42,7 +43,7 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data.app.cookieJar.clearForDomain("portal.librus.pl")
|
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) {
|
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()
|
Request.builder()
|
||||||
.url(LIBRUS_LOGIN_URL)
|
.url(if (data.fakeLogin) FAKE_LIBRUS_LOGIN else LIBRUS_LOGIN_URL)
|
||||||
.userAgent(LIBRUS_USER_AGENT)
|
.userAgent(LIBRUS_USER_AGENT)
|
||||||
.addParameter("email", data.portalEmail)
|
.addParameter("email", data.portalEmail)
|
||||||
.addParameter("password", data.portalPassword)
|
.addParameter("password", data.portalPassword)
|
||||||
@ -135,7 +136,7 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
|
|
||||||
private var refreshTokenFailed = false
|
private var refreshTokenFailed = false
|
||||||
private fun accessToken(code: String?, refreshToken: String?) {
|
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? ->
|
val onSuccess = { json: JsonObject, response: Response? ->
|
||||||
data.portalAccessToken = json.getString("access_token")
|
data.portalAccessToken = json.getString("access_token")
|
||||||
@ -204,7 +205,7 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Request.builder()
|
Request.builder()
|
||||||
.url(LIBRUS_TOKEN_URL)
|
.url(if (data.fakeLogin) FAKE_LIBRUS_TOKEN else LIBRUS_TOKEN_URL)
|
||||||
.userAgent(LIBRUS_USER_AGENT)
|
.userAgent(LIBRUS_USER_AGENT)
|
||||||
.addParams(params)
|
.addParams(params)
|
||||||
.post()
|
.post()
|
||||||
|
@ -43,7 +43,7 @@ class SynergiaTokenExtractor(override val data: DataLibrus, val onSuccess: () ->
|
|||||||
val accountLogin = data.apiLogin ?: return false
|
val accountLogin = data.apiLogin ?: return false
|
||||||
data.portalAccessToken ?: 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? ->
|
val onSuccess = { json: JsonObject, response: Response? ->
|
||||||
// synergiaAccount is executed when a synergia token needs a refresh
|
// 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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.api.v2.mobidziennik
|
package pl.szczodrzynski.edziennik.api.v2.mobidziennik
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
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.EdziennikCallback
|
||||||
@ -48,7 +49,8 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
|
|||||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||||
__/ |
|
__/ |
|
||||||
|__*/
|
|__*/
|
||||||
override fun sync(featureIds: List<Int>, viewId: Int?) {
|
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||||
|
data.arguments = arguments
|
||||||
data.prepare(mobidziennikLoginMethods, MobidziennikFeatures, featureIds, viewId)
|
data.prepare(mobidziennikLoginMethods, MobidziennikFeatures, featureIds, viewId)
|
||||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||||
|
@ -21,10 +21,10 @@ const val ENDPOINT_MOBIDZIENNIK_API2_MAIN = 3000
|
|||||||
|
|
||||||
val MobidziennikFeatures = listOf(
|
val MobidziennikFeatures = listOf(
|
||||||
// always synced
|
// 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_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB,
|
||||||
ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL 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
|
// push config
|
||||||
/*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_PUSH_CONFIG, listOf(
|
/*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_PUSH_CONFIG, listOf(
|
||||||
|
@ -4,16 +4,102 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api
|
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.mobidziennik.DataMobidziennik
|
||||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.Lesson
|
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
|
||||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange
|
|
||||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||||
import pl.szczodrzynski.edziennik.fixName
|
import pl.szczodrzynski.edziennik.fixName
|
||||||
import pl.szczodrzynski.edziennik.singleOrNull
|
import pl.szczodrzynski.edziennik.singleOrNull
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
|
||||||
class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||||
init {
|
init {
|
||||||
for (lessonStr in rows) {
|
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<Int>()
|
||||||
|
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) / 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
|
||||||
|
val teamId = data.teamList.singleOrNull { it.name == lesson[8]+lesson[9] }?.id ?: -1
|
||||||
|
val classroom = lesson[11]
|
||||||
|
|
||||||
|
Lesson(data.profileId, id).also {
|
||||||
|
when (lesson[1]) {
|
||||||
|
"plan_lekcji", "lekcja" -> {
|
||||||
|
it.type = Lesson.TYPE_NORMAL
|
||||||
|
it.date = date
|
||||||
|
it.startTime = startTime
|
||||||
|
it.endTime = endTime
|
||||||
|
it.subjectId = subjectId
|
||||||
|
it.teacherId = teacherId
|
||||||
|
it.teamId = teamId
|
||||||
|
it.classroom = classroom
|
||||||
|
}
|
||||||
|
"lekcja_odwolana" -> {
|
||||||
|
it.type = Lesson.TYPE_CANCELLED
|
||||||
|
it.date = date
|
||||||
|
it.startTime = startTime
|
||||||
|
it.endTime = endTime
|
||||||
|
it.oldSubjectId = subjectId
|
||||||
|
//it.oldTeacherId = teacherId
|
||||||
|
it.oldTeamId = teamId
|
||||||
|
//it.oldClassroom = classroom
|
||||||
|
}
|
||||||
|
"zastepstwo" -> {
|
||||||
|
it.type = Lesson.TYPE_CHANGE
|
||||||
|
it.date = date
|
||||||
|
it.startTime = startTime
|
||||||
|
it.endTime = endTime
|
||||||
|
it.subjectId = subjectId
|
||||||
|
it.teacherId = teacherId
|
||||||
|
it.teamId = teamId
|
||||||
|
it.classroom = classroom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it.type != Lesson.TYPE_NORMAL) {
|
||||||
|
data.metadataList.add(
|
||||||
|
Metadata(
|
||||||
|
data.profileId,
|
||||||
|
Metadata.TYPE_LESSON_CHANGE,
|
||||||
|
it.id,
|
||||||
|
data.profile?.empty ?: false,
|
||||||
|
data.profile?.empty ?: false,
|
||||||
|
System.currentTimeMillis()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
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()) {
|
if (lessonStr.isNotEmpty()) {
|
||||||
val lesson = lessonStr.split("|")
|
val lesson = lessonStr.split("|")
|
||||||
|
|
||||||
@ -76,9 +162,9 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
|||||||
if (originalLesson == null) {
|
if (originalLesson == null) {
|
||||||
// original lesson doesn't exist, save a new addition
|
// original lesson doesn't exist, save a new addition
|
||||||
// TODO
|
// TODO
|
||||||
/*if (!RegisterLessonChange.existsAddition(app.profile, registerLessonChange)) {
|
*//*if (!RegisterLessonChange.existsAddition(app.profile, registerLessonChange)) {
|
||||||
app.profile.timetable.addLessonAddition(registerLessonChange);
|
app.profile.timetable.addLessonAddition(registerLessonChange);
|
||||||
}*/
|
}*//*
|
||||||
} else {
|
} else {
|
||||||
// original lesson exists, so we need to compare them
|
// original lesson exists, so we need to compare them
|
||||||
if (!lessonChange.matches(originalLesson)) {
|
if (!lessonChange.matches(originalLesson)) {
|
||||||
@ -108,6 +194,6 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -60,6 +60,8 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
|||||||
val profileId
|
val profileId
|
||||||
get() = profile?.id ?: -1
|
get() = profile?.id ?: -1
|
||||||
|
|
||||||
|
var arguments: JsonObject? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A callback passed to all [Feature]s and [LoginMethod]s
|
* A callback passed to all [Feature]s and [LoginMethod]s
|
||||||
*/
|
*/
|
||||||
@ -133,23 +135,20 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
|||||||
mTeamClass = value
|
mTeamClass = value
|
||||||
}
|
}
|
||||||
|
|
||||||
var lessonsToRemove: DataRemoveModel? = null
|
var toRemove = mutableListOf<DataRemoveModel>()
|
||||||
|
|
||||||
val lessonList = mutableListOf<Lesson>()
|
val lessonList = mutableListOf<Lesson>()
|
||||||
val lessonChangeList = mutableListOf<LessonChange>()
|
val lessonChangeList = mutableListOf<LessonChange>()
|
||||||
|
val lessonNewList = mutableListOf<pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson>()
|
||||||
|
|
||||||
var gradesToRemove: DataRemoveModel? = null
|
|
||||||
val gradeList = mutableListOf<Grade>()
|
val gradeList = mutableListOf<Grade>()
|
||||||
|
|
||||||
var eventsToRemove: DataRemoveModel? = null
|
|
||||||
val eventList = mutableListOf<Event>()
|
val eventList = mutableListOf<Event>()
|
||||||
|
|
||||||
var noticesToRemove: DataRemoveModel? = null
|
|
||||||
val noticeList = mutableListOf<Notice>()
|
val noticeList = mutableListOf<Notice>()
|
||||||
|
|
||||||
var attendancesToRemove: DataRemoveModel? = null
|
|
||||||
val attendanceList = mutableListOf<Attendance>()
|
val attendanceList = mutableListOf<Attendance>()
|
||||||
|
|
||||||
var announcementsToRemove: DataRemoveModel? = null
|
|
||||||
val announcementList = mutableListOf<Announcement>()
|
val announcementList = mutableListOf<Announcement>()
|
||||||
|
|
||||||
val luckyNumberList = mutableListOf<LuckyNumber>()
|
val luckyNumberList = mutableListOf<LuckyNumber>()
|
||||||
@ -166,6 +165,9 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
|||||||
val db: AppDb by lazy { app.db }
|
val db: AppDb by lazy { app.db }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
if (App.devMode) {
|
||||||
|
fakeLogin = loginStore.hasLoginData("fakeLogin")
|
||||||
|
}
|
||||||
clear()
|
clear()
|
||||||
if (profile != null) {
|
if (profile != null) {
|
||||||
endpointTimers = db.endpointTimerDao().getAllNow(profile.id).toMutableList()
|
endpointTimers = db.endpointTimerDao().getAllNow(profile.id).toMutableList()
|
||||||
@ -180,6 +182,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
|||||||
fun clear() {
|
fun clear() {
|
||||||
loginMethods.clear()
|
loginMethods.clear()
|
||||||
|
|
||||||
|
toRemove.clear()
|
||||||
endpointTimers.clear()
|
endpointTimers.clear()
|
||||||
teacherList.clear()
|
teacherList.clear()
|
||||||
subjectList.clear()
|
subjectList.clear()
|
||||||
@ -195,6 +198,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
|||||||
|
|
||||||
lessonList.clear()
|
lessonList.clear()
|
||||||
lessonChangeList.clear()
|
lessonChangeList.clear()
|
||||||
|
lessonNewList.clear()
|
||||||
gradeList.clear()
|
gradeList.clear()
|
||||||
noticeList.clear()
|
noticeList.clear()
|
||||||
attendanceList.clear()
|
attendanceList.clear()
|
||||||
@ -248,6 +252,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
|||||||
app.profile.loginStoreData = loginStore.data
|
app.profile.loginStoreData = loginStore.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// always present and not empty, during every sync
|
||||||
db.endpointTimerDao().addAll(endpointTimers)
|
db.endpointTimerDao().addAll(endpointTimers)
|
||||||
db.teacherDao().clear(profileId)
|
db.teacherDao().clear(profileId)
|
||||||
db.teacherDao().addAll(teacherList.values())
|
db.teacherDao().addAll(teacherList.values())
|
||||||
@ -260,6 +265,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
|||||||
db.gradeCategoryDao().clear(profileId)
|
db.gradeCategoryDao().clear(profileId)
|
||||||
db.gradeCategoryDao().addAll(gradeCategories.values())
|
db.gradeCategoryDao().addAll(gradeCategories.values())
|
||||||
|
|
||||||
|
// may be empty - extracted from DB on demand, by an endpoint
|
||||||
if (classrooms.size > 0)
|
if (classrooms.size > 0)
|
||||||
db.classroomDao().addAll(classrooms.values())
|
db.classroomDao().addAll(classrooms.values())
|
||||||
if (attendanceTypes.size > 0)
|
if (attendanceTypes.size > 0)
|
||||||
@ -271,17 +277,24 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
|||||||
if (teacherAbsenceTypes.size > 0)
|
if (teacherAbsenceTypes.size > 0)
|
||||||
db.teacherAbsenceTypeDao().addAll(teacherAbsenceTypes.values())
|
db.teacherAbsenceTypeDao().addAll(teacherAbsenceTypes.values())
|
||||||
|
|
||||||
gradesToRemove?.let { it ->
|
// clear DB with DataRemoveModels added by endpoints
|
||||||
it.removeAll?.let { _ -> db.gradeDao().clear(profileId) }
|
for (model in toRemove) {
|
||||||
it.removeSemester?.let { semester -> db.gradeDao().clearForSemester(profileId, semester) }
|
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()) {
|
if (lessonList.isNotEmpty()) {
|
||||||
db.lessonDao().clear(profile.id)
|
db.lessonDao().clear(profile.id)
|
||||||
db.lessonDao().addAll(lessonList)
|
db.lessonDao().addAll(lessonList)
|
||||||
}
|
}
|
||||||
if (lessonChangeList.isNotEmpty())
|
if (lessonChangeList.isNotEmpty())
|
||||||
db.lessonChangeDao().addAll(lessonChangeList)
|
db.lessonChangeDao().addAll(lessonChangeList)
|
||||||
|
if (lessonNewList.isNotEmpty()) {
|
||||||
|
db.timetableDao() += lessonNewList
|
||||||
|
}
|
||||||
if (gradeList.isNotEmpty()) {
|
if (gradeList.isNotEmpty()) {
|
||||||
db.gradeDao().addAll(gradeList)
|
db.gradeDao().addAll(gradeList)
|
||||||
}
|
}
|
||||||
|
@ -4,28 +4,37 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.api.v2.models
|
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
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
|
||||||
class DataRemoveModel {
|
open class DataRemoveModel {
|
||||||
var removeAll: Boolean? = null
|
class Timetable(private val dateFrom: Date?, private val dateTo: Date?) : DataRemoveModel() {
|
||||||
var removeSemester: Int? = null
|
companion object {
|
||||||
var removeDateFrom: Date? = null
|
fun from(dateFrom: Date) = Timetable(dateFrom, null)
|
||||||
var removeDateTo: Date? = null
|
fun to(dateTo: Date) = Timetable(null, dateTo)
|
||||||
|
fun between(dateFrom: Date, dateTo: Date) = Timetable(dateFrom, dateTo)
|
||||||
constructor() {
|
}
|
||||||
this.removeAll = true
|
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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
class Grades(val all: Boolean, val semester: Int?) : DataRemoveModel() {
|
||||||
constructor(semester: Int) {
|
companion object {
|
||||||
this.removeSemester = semester
|
fun all() = Grades(true, null)
|
||||||
}
|
fun semester(semester: Int) = Grades(false, semester)
|
||||||
|
}
|
||||||
constructor(dateFrom: Date?, dateTo: Date) {
|
fun commit(profileId: Int, dao: GradeDao) {
|
||||||
this.removeDateFrom = dateFrom
|
if (all) {
|
||||||
this.removeDateTo = dateTo
|
dao.clear(profileId)
|
||||||
}
|
}
|
||||||
|
semester?.let { dao.clearForSemester(profileId, it) }
|
||||||
constructor(dateFrom: Date) {
|
}
|
||||||
this.removeDateFrom = dateFrom
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.api.v2.template
|
package pl.szczodrzynski.edziennik.api.v2.template
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
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.EdziennikCallback
|
||||||
@ -48,7 +49,8 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
|||||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||||
__/ |
|
__/ |
|
||||||
|__*/
|
|__*/
|
||||||
override fun sync(featureIds: List<Int>, viewId: Int?) {
|
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||||
|
data.arguments = arguments
|
||||||
data.prepare(templateLoginMethods, TemplateFeatures, featureIds, viewId)
|
data.prepare(templateLoginMethods, TemplateFeatures, featureIds, viewId)
|
||||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.api.v2.vulcan
|
package pl.szczodrzynski.edziennik.api.v2.vulcan
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject
|
||||||
import pl.szczodrzynski.edziennik.App
|
import pl.szczodrzynski.edziennik.App
|
||||||
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
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.EdziennikCallback
|
||||||
@ -48,7 +49,8 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
|||||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||||
__/ |
|
__/ |
|
||||||
|__*/
|
|__*/
|
||||||
override fun sync(featureIds: List<Int>, viewId: Int?) {
|
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||||
|
data.arguments = arguments
|
||||||
data.prepare(vulcanLoginMethods, VulcanFeatures, featureIds, viewId)
|
data.prepare(vulcanLoginMethods, VulcanFeatures, featureIds, viewId)
|
||||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||||
|
@ -72,6 +72,7 @@ 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.teachers.TeacherDao;
|
||||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team;
|
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team;
|
||||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.TeamDao;
|
import pl.szczodrzynski.edziennik.data.db.modules.teams.TeamDao;
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.modules.timetable.TimetableDao;
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||||
|
|
||||||
@Database(entities = {
|
@Database(entities = {
|
||||||
@ -103,7 +104,8 @@ import pl.szczodrzynski.edziennik.utils.models.Date;
|
|||||||
Classroom.class,
|
Classroom.class,
|
||||||
NoticeType.class,
|
NoticeType.class,
|
||||||
AttendanceType.class,
|
AttendanceType.class,
|
||||||
Metadata.class}, version = 63)
|
pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson.class,
|
||||||
|
Metadata.class}, version = 64)
|
||||||
@TypeConverters({
|
@TypeConverters({
|
||||||
ConverterTime.class,
|
ConverterTime.class,
|
||||||
ConverterDate.class,
|
ConverterDate.class,
|
||||||
@ -141,6 +143,7 @@ public abstract class AppDb extends RoomDatabase {
|
|||||||
public abstract ClassroomDao classroomDao();
|
public abstract ClassroomDao classroomDao();
|
||||||
public abstract NoticeTypeDao noticeTypeDao();
|
public abstract NoticeTypeDao noticeTypeDao();
|
||||||
public abstract AttendanceTypeDao attendanceTypeDao();
|
public abstract AttendanceTypeDao attendanceTypeDao();
|
||||||
|
public abstract TimetableDao timetableDao();
|
||||||
public abstract MetadataDao metadataDao();
|
public abstract MetadataDao metadataDao();
|
||||||
|
|
||||||
private static volatile AppDb INSTANCE;
|
private static volatile AppDb INSTANCE;
|
||||||
@ -729,6 +732,37 @@ public abstract class AppDb extends RoomDatabase {
|
|||||||
database.execSQL("ALTER TABLE profiles ADD COLUMN studentSchoolYear TEXT DEFAULT NULL");
|
database.execSQL("ALTER TABLE profiles ADD COLUMN studentSchoolYear TEXT DEFAULT NULL");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private static final Migration MIGRATION_63_64 = new Migration(63, 64) {
|
||||||
|
@Override
|
||||||
|
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||||
|
//database.execSQL("ALTER TABLE lessons RENAME TO lessonsOld;");
|
||||||
|
database.execSQL("CREATE TABLE timetable (" +
|
||||||
|
"profileId INTEGER NOT NULL," +
|
||||||
|
"id INTEGER NOT NULL," +
|
||||||
|
"type INTEGER NOT NULL," +
|
||||||
|
|
||||||
|
"date TEXT DEFAULT NULL," +
|
||||||
|
"lessonNumber INTEGER DEFAULT NULL," +
|
||||||
|
"startTime TEXT DEFAULT NULL," +
|
||||||
|
"endTime TEXT DEFAULT NULL," +
|
||||||
|
"subjectId INTEGER DEFAULT NULL," +
|
||||||
|
"teacherId INTEGER DEFAULT NULL," +
|
||||||
|
"teamId INTEGER DEFAULT NULL," +
|
||||||
|
"classroom TEXT DEFAULT NULL," +
|
||||||
|
|
||||||
|
"oldDate TEXT DEFAULT NULL," +
|
||||||
|
"oldLessonNumber INTEGER DEFAULT NULL," +
|
||||||
|
"oldStartTime TEXT DEFAULT NULL," +
|
||||||
|
"oldEndTime TEXT DEFAULT NULL," +
|
||||||
|
"oldSubjectId INTEGER DEFAULT NULL," +
|
||||||
|
"oldTeacherId INTEGER DEFAULT NULL," +
|
||||||
|
"oldTeamId INTEGER DEFAULT NULL," +
|
||||||
|
"oldClassroom TEXT DEFAULT NULL," +
|
||||||
|
"PRIMARY KEY(id));");
|
||||||
|
database.execSQL("CREATE INDEX index_lessons_profileId_type_date ON timetable (profileId, type, date);");
|
||||||
|
database.execSQL("CREATE INDEX index_lessons_profileId_type_oldDate ON timetable (profileId, type, oldDate);");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
public static AppDb getDatabase(final Context context) {
|
public static AppDb getDatabase(final Context context) {
|
||||||
@ -789,7 +823,8 @@ public abstract class AppDb extends RoomDatabase {
|
|||||||
MIGRATION_59_60,
|
MIGRATION_59_60,
|
||||||
MIGRATION_60_61,
|
MIGRATION_60_61,
|
||||||
MIGRATION_61_62,
|
MIGRATION_61_62,
|
||||||
MIGRATION_62_63
|
MIGRATION_62_63,
|
||||||
|
MIGRATION_63_64
|
||||||
)
|
)
|
||||||
.allowMainThreadQueries()
|
.allowMainThreadQueries()
|
||||||
//.fallbackToDestructiveMigration()
|
//.fallbackToDestructiveMigration()
|
||||||
|
@ -4,12 +4,20 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.data.db.modules.timetable
|
package pl.szczodrzynski.edziennik.data.db.modules.timetable
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Index
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
|
||||||
open class Lesson(val profileId: Int) {
|
@Entity(tableName = "timetable",
|
||||||
|
indices = [
|
||||||
|
Index(value = ["profileId", "type", "date"]),
|
||||||
|
Index(value = ["profileId", "type", "oldDate"])
|
||||||
|
])
|
||||||
|
open class Lesson(val profileId: Int, @PrimaryKey val id: Long) {
|
||||||
companion object {
|
companion object {
|
||||||
|
const val TYPE_NO_LESSONS = -1
|
||||||
const val TYPE_NORMAL = 0
|
const val TYPE_NORMAL = 0
|
||||||
const val TYPE_CANCELLED = 1
|
const val TYPE_CANCELLED = 1
|
||||||
const val TYPE_CHANGE = 2
|
const val TYPE_CHANGE = 2
|
||||||
@ -17,15 +25,14 @@ open class Lesson(val profileId: Int) {
|
|||||||
const val TYPE_SHIFTED_TARGET = 4 /* target lesson */
|
const val TYPE_SHIFTED_TARGET = 4 /* target lesson */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ColumnInfo(name = "lessonType")
|
|
||||||
var type: Int = TYPE_NORMAL
|
var type: Int = TYPE_NORMAL
|
||||||
|
|
||||||
var date: Date? = null
|
var date: Date? = null
|
||||||
var lessonNumber: Int? = null
|
var lessonNumber: Int? = null
|
||||||
var startTime: Time? = null
|
var startTime: Time? = null
|
||||||
var endTime: Time? = null
|
var endTime: Time? = null
|
||||||
var teacherId: Long? = null
|
|
||||||
var subjectId: Long? = null
|
var subjectId: Long? = null
|
||||||
|
var teacherId: Long? = null
|
||||||
var teamId: Long? = null
|
var teamId: Long? = null
|
||||||
var classroom: String? = null
|
var classroom: String? = null
|
||||||
|
|
||||||
@ -33,8 +40,58 @@ open class Lesson(val profileId: Int) {
|
|||||||
var oldLessonNumber: Int? = null
|
var oldLessonNumber: Int? = null
|
||||||
var oldStartTime: Time? = null
|
var oldStartTime: Time? = null
|
||||||
var oldEndTime: Time? = null
|
var oldEndTime: Time? = null
|
||||||
var oldTeacherId: Long? = null
|
|
||||||
var oldSubjectId: Long? = null
|
var oldSubjectId: Long? = null
|
||||||
|
var oldTeacherId: Long? = null
|
||||||
var oldTeamId: Long? = null
|
var oldTeamId: Long? = null
|
||||||
var oldClassroom: String? = null
|
var oldClassroom: String? = null
|
||||||
}
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "Lesson(profileId=$profileId, " +
|
||||||
|
"id=$id, " +
|
||||||
|
"type=$type, " +
|
||||||
|
"date=$date, " +
|
||||||
|
"lessonNumber=$lessonNumber, " +
|
||||||
|
"startTime=$startTime, " +
|
||||||
|
"endTime=$endTime, " +
|
||||||
|
"subjectId=$subjectId, " +
|
||||||
|
"teacherId=$teacherId, " +
|
||||||
|
"teamId=$teamId, " +
|
||||||
|
"classroom=$classroom, " +
|
||||||
|
"oldDate=$oldDate, " +
|
||||||
|
"oldLessonNumber=$oldLessonNumber, " +
|
||||||
|
"oldStartTime=$oldStartTime, " +
|
||||||
|
"oldEndTime=$oldEndTime, " +
|
||||||
|
"oldSubjectId=$oldSubjectId, " +
|
||||||
|
"oldTeacherId=$oldTeacherId, " +
|
||||||
|
"oldTeamId=$oldTeamId, " +
|
||||||
|
"oldClassroom=$oldClassroom)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
DROP TABLE lessons;
|
||||||
|
DROP TABLE lessonChanges;
|
||||||
|
CREATE TABLE lessons (
|
||||||
|
profileId INTEGER NOT NULL,
|
||||||
|
type INTEGER NOT NULL,
|
||||||
|
|
||||||
|
date TEXT DEFAULT NULL,
|
||||||
|
lessonNumber INTEGER DEFAULT NULL,
|
||||||
|
startTime TEXT DEFAULT NULL,
|
||||||
|
endTime TEXT DEFAULT NULL,
|
||||||
|
teacherId INTEGER DEFAULT NULL,
|
||||||
|
subjectId INTEGER DEFAULT NULL,
|
||||||
|
teamId INTEGER DEFAULT NULL,
|
||||||
|
classroom TEXT DEFAULT NULL,
|
||||||
|
|
||||||
|
oldDate TEXT DEFAULT NULL,
|
||||||
|
oldLessonNumber INTEGER DEFAULT NULL,
|
||||||
|
oldStartTime TEXT DEFAULT NULL,
|
||||||
|
oldEndTime TEXT DEFAULT NULL,
|
||||||
|
oldTeacherId INTEGER DEFAULT NULL,
|
||||||
|
oldSubjectId INTEGER DEFAULT NULL,
|
||||||
|
oldTeamId INTEGER DEFAULT NULL,
|
||||||
|
oldClassroom TEXT DEFAULT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(profileId)
|
||||||
|
);
|
||||||
|
*/
|
@ -0,0 +1,69 @@
|
|||||||
|
package pl.szczodrzynski.edziennik.data.db.modules.timetable
|
||||||
|
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||||
|
|
||||||
|
class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
|
||||||
|
var subjectName: String? = null
|
||||||
|
var teacherName: String? = null
|
||||||
|
var teamName: String? = null
|
||||||
|
var oldSubjectName: String? = null
|
||||||
|
var oldTeacherName: String? = null
|
||||||
|
var oldTeamName: String? = null
|
||||||
|
|
||||||
|
val displayDate: Date?
|
||||||
|
get() {
|
||||||
|
if (type == TYPE_SHIFTED_SOURCE)
|
||||||
|
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)
|
||||||
|
return oldStartTime
|
||||||
|
return startTime ?: oldStartTime
|
||||||
|
}
|
||||||
|
val displayEndTime: Time?
|
||||||
|
get() {
|
||||||
|
if (type == TYPE_SHIFTED_SOURCE)
|
||||||
|
return oldEndTime
|
||||||
|
return endTime ?: oldEndTime
|
||||||
|
}
|
||||||
|
|
||||||
|
val displaySubjectName: String?
|
||||||
|
get() {
|
||||||
|
if (type == TYPE_SHIFTED_SOURCE)
|
||||||
|
return oldSubjectName
|
||||||
|
return subjectName ?: oldSubjectName
|
||||||
|
}
|
||||||
|
val displayTeacherName: String?
|
||||||
|
get() {
|
||||||
|
if (type == TYPE_SHIFTED_SOURCE)
|
||||||
|
return oldTeacherName
|
||||||
|
return teacherName ?: oldTeacherName
|
||||||
|
}
|
||||||
|
val displayTeamName: String?
|
||||||
|
get() {
|
||||||
|
if (type == TYPE_SHIFTED_SOURCE)
|
||||||
|
return oldTeamName
|
||||||
|
return teamName ?: oldTeamName
|
||||||
|
}
|
||||||
|
|
||||||
|
val displayClassroom: String?
|
||||||
|
get() {
|
||||||
|
if (type == TYPE_SHIFTED_SOURCE)
|
||||||
|
return oldClassroom
|
||||||
|
return classroom ?: oldClassroom
|
||||||
|
}
|
||||||
|
|
||||||
|
// metadata
|
||||||
|
var seen: Boolean = false
|
||||||
|
var notified: Boolean = false
|
||||||
|
var addedDate: Long = 0
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package pl.szczodrzynski.edziennik.data.db.modules.timetable
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||||
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface TimetableDao {
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
operator fun plusAssign(lessonList: List<Lesson>)
|
||||||
|
|
||||||
|
@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.*,
|
||||||
|
subjects.subjectLongName AS subjectName,
|
||||||
|
teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName,
|
||||||
|
teams.teamName AS teamName,
|
||||||
|
oldS.subjectLongName AS oldSubjectName,
|
||||||
|
oldT.teacherName ||" "|| oldT.teacherSurname AS oldTeacherName,
|
||||||
|
oldG.teamName AS oldTeamName,
|
||||||
|
metadata.seen, metadata.notified, metadata.addedDate
|
||||||
|
FROM timetable
|
||||||
|
LEFT JOIN subjects USING(profileId, subjectId)
|
||||||
|
LEFT JOIN teachers USING(profileId, teacherId)
|
||||||
|
LEFT JOIN teams USING(profileId, teamId)
|
||||||
|
LEFT JOIN subjects AS oldS ON timetable.profileId = oldS.profileId AND timetable.oldSubjectId = oldS.subjectId
|
||||||
|
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 OR type = 1) AND oldDate = :date)
|
||||||
|
ORDER BY type
|
||||||
|
""")
|
||||||
|
fun getForDate(profileId: Int, date: Date) : LiveData<List<LessonFull>>
|
||||||
|
}
|
@ -112,7 +112,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
|||||||
app.notifier.postAll(profile);
|
app.notifier.postAll(profile);
|
||||||
app.saveConfig("notifications");*/
|
app.saveConfig("notifications");*/
|
||||||
d(TAG, "Syncing profile " + profile.getId());
|
d(TAG, "Syncing profile " + profile.getId());
|
||||||
EdziennikTask.Companion.syncProfile(profile.getId(), null).enqueue(app);
|
EdziennikTask.Companion.syncProfile(profile.getId(), null, null).enqueue(app);
|
||||||
} else {
|
} else {
|
||||||
/*app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message"))
|
/*app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message"))
|
||||||
.withProfileData(profile.id, profile.name)
|
.withProfileData(profile.id, profile.name)
|
||||||
@ -123,7 +123,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
|||||||
app.notifier.postAll(profile);
|
app.notifier.postAll(profile);
|
||||||
app.saveConfig("notifications");*/
|
app.saveConfig("notifications");*/
|
||||||
d(TAG, "Syncing profile " + profile.getId());
|
d(TAG, "Syncing profile " + profile.getId());
|
||||||
EdziennikTask.Companion.syncProfile(profile.getId(), null).enqueue(app);
|
EdziennikTask.Companion.syncProfile(profile.getId(), null, null).enqueue(app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -26,6 +26,7 @@ public class LoginChooserFragment extends Fragment {
|
|||||||
private NavController nav;
|
private NavController nav;
|
||||||
private FragmentLoginChooserBinding b;
|
private FragmentLoginChooserBinding b;
|
||||||
private static final String TAG = "LoginTemplate";
|
private static final String TAG = "LoginTemplate";
|
||||||
|
public static boolean fakeLogin = false;
|
||||||
|
|
||||||
public LoginChooserFragment() { }
|
public LoginChooserFragment() { }
|
||||||
|
|
||||||
@ -71,6 +72,10 @@ public class LoginChooserFragment extends Fragment {
|
|||||||
b.cancelButton.setVisibility(View.GONE);
|
b.cancelButton.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.fakeLogin.setVisibility(App.devMode ? View.VISIBLE : View.GONE);
|
||||||
|
b.fakeLogin.setChecked(fakeLogin);
|
||||||
|
b.fakeLogin.setOnCheckedChangeListener((v, isChecked) -> fakeLogin = isChecked);
|
||||||
|
|
||||||
b.helpButton.setOnClickListener((v -> {
|
b.helpButton.setOnClickListener((v -> {
|
||||||
startActivity(new Intent(getActivity(), FeedbackActivity.class));
|
startActivity(new Intent(getActivity(), FeedbackActivity.class));
|
||||||
}));
|
}));
|
||||||
|
@ -89,6 +89,10 @@ public class LoginProgressFragment extends Fragment {
|
|||||||
LoginStore loginStore = new LoginStore(-1, loginType, new JsonObject());
|
LoginStore loginStore = new LoginStore(-1, loginType, new JsonObject());
|
||||||
loginStore.copyFrom(args);
|
loginStore.copyFrom(args);
|
||||||
|
|
||||||
|
if (App.devMode && LoginChooserFragment.fakeLogin) {
|
||||||
|
loginStore.putLoginData("fakeLogin", true);
|
||||||
|
}
|
||||||
|
|
||||||
EdziennikTask.Companion.firstLogin(loginStore).enqueue(getContext());
|
EdziennikTask.Companion.firstLogin(loginStore).enqueue(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
package pl.szczodrzynski.edziennik.ui.modules.timetable.v2
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.viewpager.widget.ViewPager
|
||||||
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||||
|
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
|
||||||
|
|
||||||
|
class TimetableFragment : Fragment() {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "TimetableFragment"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
private lateinit var activity: MainActivity
|
||||||
|
private lateinit var b: FragmentTimetableV2Binding
|
||||||
|
private var fabShown = false
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
activity = (getActivity() as MainActivity?) ?: return null
|
||||||
|
if (context == null)
|
||||||
|
return null
|
||||||
|
app = activity.application as App
|
||||||
|
context!!.theme.applyStyle(Themes.appTheme, true)
|
||||||
|
if (app.profile == null)
|
||||||
|
return inflater.inflate(R.layout.fragment_loading, container, false)
|
||||||
|
// activity, context and profile is valid
|
||||||
|
b = FragmentTimetableV2Binding.inflate(inflater)
|
||||||
|
return b.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
// TODO check if app, activity, b can be null
|
||||||
|
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<Date>()
|
||||||
|
|
||||||
|
val monthDayCount = listOf(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
||||||
|
|
||||||
|
val today = Date.getToday().value
|
||||||
|
val yearStart = app.profile.dateSemester1Start?.clone() ?: return
|
||||||
|
val yearEnd = app.profile.dateYearEnd ?: return
|
||||||
|
while (yearStart.value <= yearEnd.value) {
|
||||||
|
items += yearStart.clone()
|
||||||
|
var maxDays = monthDayCount[yearStart.month-1]
|
||||||
|
if (yearStart.month == 2 && yearStart.isLeap)
|
||||||
|
maxDays++
|
||||||
|
yearStart.day++
|
||||||
|
if (yearStart.day > maxDays) {
|
||||||
|
yearStart.day = 1
|
||||||
|
yearStart.month++
|
||||||
|
}
|
||||||
|
if (yearStart.month > 12) {
|
||||||
|
yearStart.month = 1
|
||||||
|
yearStart.year++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPageSelected(position: Int) {
|
||||||
|
activity.navView.bottomBar.fabEnable = items[position].value != today
|
||||||
|
if (activity.navView.bottomBar.fabEnable && !fabShown) {
|
||||||
|
activity.gainAttentionFAB()
|
||||||
|
fabShown = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.tabLayout.setUpWithViewPager(b.viewPager)
|
||||||
|
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.fabIcon = CommunityMaterial.Icon.cmd_calendar_today
|
||||||
|
activity.navView.setFabOnClickListener(View.OnClickListener {
|
||||||
|
b.tabLayout.setCurrentItem(items.indexOfFirst { it.value == today }, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package pl.szczodrzynski.edziennik.ui.modules.timetable.v2
|
||||||
|
|
||||||
|
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<Date>) : 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 {
|
||||||
|
arguments = Bundle().also {
|
||||||
|
it.putLong("date", items[position].value.toLong())
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCount(): Int {
|
||||||
|
return items.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPageTitle(position: Int): CharSequence? {
|
||||||
|
val date = items[position]
|
||||||
|
val pageTitle = StringBuilder(Week.getFullDayName(date.weekDay))
|
||||||
|
if (date > weekEnd || date < weekStart) {
|
||||||
|
pageTitle.append(", ").append(date.stringDm)
|
||||||
|
}
|
||||||
|
return pageTitle
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,285 @@
|
|||||||
|
package pl.szczodrzynski.edziennik.ui.modules.timetable.v2.day
|
||||||
|
|
||||||
|
import android.graphics.PorterDuff
|
||||||
|
import android.graphics.PorterDuffColorFilter
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
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
|
||||||
|
import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2DayBinding
|
||||||
|
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 {
|
||||||
|
private const val TAG = "TimetableDayFragment"
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
private lateinit var activity: MainActivity
|
||||||
|
private lateinit var b: FragmentTimetableV2DayBinding
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
activity = (getActivity() as MainActivity?) ?: return null
|
||||||
|
if (context == null)
|
||||||
|
return null
|
||||||
|
app = activity.application as App
|
||||||
|
b = FragmentTimetableV2DayBinding.inflate(inflater)
|
||||||
|
Log.d(TAG, "onCreateView, date=$date")
|
||||||
|
return b.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
// TODO check if app, activity, b can be null
|
||||||
|
if (app.profile == null || !isAdded)
|
||||||
|
return
|
||||||
|
|
||||||
|
Log.d(TAG, "onViewCreated, date=$date")
|
||||||
|
|
||||||
|
// Inflate a label view for each hour the day view will display
|
||||||
|
val hourLabelViews = ArrayList<View>()
|
||||||
|
for (i in b.day.startHour..b.day.endHour) {
|
||||||
|
val hourLabelView = layoutInflater.inflate(R.layout.timetable_hour_label, b.day, false) as TextView
|
||||||
|
hourLabelView.text = "$i:00"
|
||||||
|
hourLabelViews.add(hourLabelView)
|
||||||
|
}
|
||||||
|
b.day.setHourLabelViews(hourLabelViews)
|
||||||
|
|
||||||
|
app.db.timetableDao().getForDate(App.profileId, date).observe(this, Observer<List<LessonFull>> { lessons ->
|
||||||
|
buildLessonViews(lessons)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildLessonViews(lessons: List<LessonFull>) {
|
||||||
|
if (lessons.isEmpty()) {
|
||||||
|
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,
|
||||||
|
viewIds = listOf(
|
||||||
|
DRAWER_ITEM_TIMETABLE to 0
|
||||||
|
),
|
||||||
|
arguments = JsonObject(
|
||||||
|
"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) {
|
||||||
|
b.dayScroll.visibility = View.GONE
|
||||||
|
b.noTimetableLayout.visibility = View.GONE
|
||||||
|
b.noLessonsLayout.visibility = View.VISIBLE
|
||||||
|
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
|
||||||
|
|
||||||
|
var firstEventMinute = 24*60
|
||||||
|
|
||||||
|
val eventViews = mutableListOf<View>()
|
||||||
|
val eventTimeRanges = mutableListOf<DayView.EventTimeRange>()
|
||||||
|
|
||||||
|
// Reclaim all of the existing event views so we can reuse them if needed, this process
|
||||||
|
// can be useful if your day view is hosted in a recycler view for example
|
||||||
|
val recycled = b.day.removeEventViews()
|
||||||
|
var remaining = recycled?.size ?: 0
|
||||||
|
|
||||||
|
val arrowRight = " → "
|
||||||
|
val bullet = " • "
|
||||||
|
val colorSecondary = getColorFromAttr(activity, android.R.attr.textColorSecondary)
|
||||||
|
|
||||||
|
for (lesson in lessons) {
|
||||||
|
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))
|
||||||
|
?: continue
|
||||||
|
val lb = TimetableLessonBinding.bind(eventView)
|
||||||
|
eventViews += eventView
|
||||||
|
|
||||||
|
eventView.tag = lesson
|
||||||
|
|
||||||
|
eventView.setOnClickListener {
|
||||||
|
Log.d(TAG, "Clicked ${it.tag}")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val timeRange = "${startTime.stringHM} - ${endTime.stringHM}".asColoredSpannable(colorSecondary)
|
||||||
|
|
||||||
|
// teacher
|
||||||
|
val teacherInfo = if (lesson.teacherId != null && lesson.teacherId == lesson.oldTeacherId)
|
||||||
|
lesson.teacherName ?: "?"
|
||||||
|
else
|
||||||
|
mutableListOf<CharSequence>().apply {
|
||||||
|
lesson.oldTeacherName?.let { add(it.asStrikethroughSpannable()) }
|
||||||
|
lesson.teacherName?.let { add(it) }
|
||||||
|
}.concat(arrowRight)
|
||||||
|
|
||||||
|
// team
|
||||||
|
val teamInfo = if (lesson.teamId != null && lesson.teamId == lesson.oldTeamId)
|
||||||
|
lesson.teamName ?: "?"
|
||||||
|
else
|
||||||
|
mutableListOf<CharSequence>().apply {
|
||||||
|
lesson.oldTeamName?.let { add(it.asStrikethroughSpannable()) }
|
||||||
|
lesson.teamName?.let { add(it) }
|
||||||
|
}.concat(arrowRight)
|
||||||
|
|
||||||
|
// classroom
|
||||||
|
val classroomInfo = if (lesson.classroom != null && lesson.classroom == lesson.oldClassroom)
|
||||||
|
lesson.classroom ?: "?"
|
||||||
|
else
|
||||||
|
mutableListOf<CharSequence>().apply {
|
||||||
|
lesson.oldClassroom?.let { add(it.asStrikethroughSpannable()) }
|
||||||
|
lesson.classroom?.let { add(it) }
|
||||||
|
}.concat(arrowRight)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
//lb.subjectName.typeface = Typeface.create("sans-serif-light", Typeface.BOLD)
|
||||||
|
when (lesson.type) {
|
||||||
|
Lesson.TYPE_NORMAL -> {
|
||||||
|
lb.annotationVisible = false
|
||||||
|
}
|
||||||
|
Lesson.TYPE_CANCELLED -> {
|
||||||
|
lb.annotationVisible = true
|
||||||
|
lb.annotation.setText(R.string.timetable_lesson_cancelled)
|
||||||
|
lb.annotation.background.colorFilter = PorterDuffColorFilter(
|
||||||
|
getColorFromAttr(activity, R.attr.timetable_lesson_cancelled_color),
|
||||||
|
PorterDuff.Mode.SRC_ATOP
|
||||||
|
)
|
||||||
|
//lb.subjectName.typeface = Typeface.DEFAULT
|
||||||
|
}
|
||||||
|
Lesson.TYPE_CHANGE -> {
|
||||||
|
lb.annotationVisible = true
|
||||||
|
if (lesson.subjectId != lesson.oldSubjectId && lesson.teacherId != lesson.oldTeacherId) {
|
||||||
|
lb.annotation.setText(
|
||||||
|
R.string.timetable_lesson_change_format,
|
||||||
|
"${lesson.oldSubjectName ?: "?"}, ${lesson.oldTeacherName ?: "?"}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else if (lesson.subjectId != lesson.oldSubjectId) {
|
||||||
|
lb.annotation.setText(
|
||||||
|
R.string.timetable_lesson_change_format,
|
||||||
|
lesson.oldSubjectName ?: "?"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else if (lesson.teacherId != lesson.oldTeacherId) {
|
||||||
|
lb.annotation.setText(
|
||||||
|
R.string.timetable_lesson_change_format,
|
||||||
|
lesson.oldTeacherName ?: "?"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lb.annotation.setText(R.string.timetable_lesson_change)
|
||||||
|
}
|
||||||
|
|
||||||
|
lb.annotation.background.colorFilter = PorterDuffColorFilter(
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The day view needs the event time ranges in the start minute/end minute format,
|
||||||
|
// so calculate those here
|
||||||
|
val startMinute = 60 * (lesson.displayStartTime?.hour ?: 0) + (lesson.displayStartTime?.minute ?: 0)
|
||||||
|
val endMinute = startMinute + 45
|
||||||
|
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, firstEventTop.toInt())
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package pl.szczodrzynski.edziennik.utils.models;
|
package pl.szczodrzynski.edziennik.utils.models;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
@ -131,6 +132,13 @@ public class Date implements Comparable<Date> {
|
|||||||
return year * 10000 + month * 100 + day;
|
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()
|
public String getStringValue()
|
||||||
{
|
{
|
||||||
return Integer.toString(getValue());
|
return Integer.toString(getValue());
|
||||||
@ -182,6 +190,10 @@ public class Date implements Comparable<Date> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLeap() {
|
||||||
|
return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
public static Date getToday()
|
public static Date getToday()
|
||||||
{
|
{
|
||||||
Calendar cal = Calendar.getInstance();
|
Calendar cal = Calendar.getInstance();
|
||||||
@ -214,6 +226,11 @@ public class Date implements Comparable<Date> {
|
|||||||
return this.getValue() - o.getValue();
|
return this.getValue() - o.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object obj) {
|
||||||
|
return obj instanceof Date && this.getValue() == ((Date) obj).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Date{" +
|
return "Date{" +
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package pl.szczodrzynski.edziennik.utils.models;
|
package pl.szczodrzynski.edziennik.utils.models;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
||||||
public class Time {
|
public class Time {
|
||||||
@ -173,6 +175,11 @@ public class Time {
|
|||||||
return (currentTime.getValue() >= startTime.getValue() && currentTime.getValue() <= endTime.getValue());
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Time{" +
|
return "Time{" +
|
||||||
|
10
app/src/main/res/drawable-v21/bg_rounded_ripple_4dp.xml
Normal file
10
app/src/main/res/drawable-v21/bg_rounded_ripple_4dp.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="?android:attr/colorControlHighlight">
|
||||||
|
<item android:id="@android:id/mask">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<solid android:color="#000000" />
|
||||||
|
<corners android:radius="4dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
6
app/src/main/res/drawable/bg_rounded_ripple_4dp.xml
Normal file
6
app/src/main/res/drawable/bg_rounded_ripple_4dp.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_pressed="true" android:drawable="@drawable/bg_rounded_edittext_pressed" />
|
||||||
|
<item android:state_focused="true" android:drawable="@drawable/bg_rounded_edittext_pressed" />
|
||||||
|
<item android:state_selected="true" android:drawable="@drawable/bg_rounded_edittext_pressed" />
|
||||||
|
</selector>
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<shape
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="4dp" />
|
||||||
|
<stroke android:color="@color/dividerColor" android:width="1dp" />
|
||||||
|
<solid android:color="#DCDCDC" />
|
||||||
|
</shape>
|
61
app/src/main/res/drawable/ic_no_timetable.xml
Normal file
61
app/src/main/res/drawable/ic_no_timetable.xml
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2019-11-11.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="128dp"
|
||||||
|
android:height="128dp"
|
||||||
|
android:viewportWidth="128"
|
||||||
|
android:viewportHeight="128">
|
||||||
|
<path
|
||||||
|
android:pathData="m117,76h4c3.852,0 7,-3.148 7,-7 0,-3.852 -3.148,-7 -7,-7h-13c-2.199,0 -4,-1.801 -4,-4s1.801,-4 4,-4h11c3.852,0 7,-3.148 7,-7 0,-3.852 -3.148,-7 -7,-7h-1c-2.211,0 -4,-1.789 -4,-4 0,-2.211 1.789,-4 4,-4h4c3.695,0 6.637,-3.387 5.879,-7.211 -0.566,-2.844 -3.238,-4.789 -6.141,-4.789h-15.738c-1.656,0 -3,-1.344 -3,-3s1.344,-3 3,-3h6.824c2.277,0 4.402,-1.441 4.992,-3.641 0.895,-3.328 -1.625,-6.359 -4.816,-6.359h-96c-3.852,0 -7,3.148 -7,7 0,3.852 3.148,7 7,7h3c2.211,0 4,1.789 4,4 0,2.211 -1.789,4 -4,4h-10.77c-3.34,0 -6.391,2.242 -7.074,5.516 -0.941,4.488 2.508,8.484 6.844,8.484h15l-6,24h-10.77c-3.34,0 -6.391,2.242 -7.074,5.516 -0.941,4.488 2.508,8.484 6.844,8.484h1c2.211,0 4,1.789 4,4 0,2.211 -1.789,4 -4,4h-1c-4.336,0 -7.785,3.996 -6.844,8.484 0.684,3.273 3.734,5.516 7.074,5.516h10.77c2.211,0 4,1.789 4,4s-1.789,4 -4,4h-2.77c-3.34,0 -6.391,2.242 -7.074,5.516 -0.941,4.488 2.508,8.484 6.844,8.484h97c3.313,0 6,-2.688 6,-6s-2.688,-6 -6,-6h-1c-2.762,0 -5,-2.238 -5,-5s2.238,-5 5,-5h6c3.852,0 7,-3.148 7,-7 0,-3.852 -3.148,-7 -7,-7 -2.75,0 -5,-2.25 -5,-5s2.25,-5 5,-5z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:gradientRadius="100.522"
|
||||||
|
android:centerX="63.746"
|
||||||
|
android:centerY="71.138"
|
||||||
|
android:type="radial"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#1F0FCCFF"/>
|
||||||
|
<item android:offset="0.1927" android:color="#1F0FCEFF"/>
|
||||||
|
<item android:offset="0.7025" android:color="#1F0FD5FF"/>
|
||||||
|
<item android:offset="1" android:color="#1F0FD7FF"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="m103.24,15.168 l-7.883,7.871c-8.746,-7.063 -19.719,-11.039 -31.355,-11.039 -27.57,0 -50,22.43 -50,50 0,27.57 22.43,50 50,50 17.758,0 34.348,-9.367 43.301,-24.449 1.41,-2.375 0.625,-5.441 -1.75,-6.852 -2.367,-1.41 -5.438,-0.629 -6.852,1.746 -7.156,12.062 -20.453,19.555 -34.699,19.555 -22.055,0 -40,-17.945 -40,-40 0,-22.055 17.945,-40 40,-40 8.934,0 17.371,2.938 24.223,8.16l-9.063,9.047c-2.48,2.5 -0.719,6.762 2.801,6.762h24.039c2.199,0 4,-1.801 4,-4v-24c0,-3.52 -4.262,-5.301 -6.762,-2.801z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="125.452"
|
||||||
|
android:startX="62"
|
||||||
|
android:endY="6.4762"
|
||||||
|
android:endX="62"
|
||||||
|
android:type="linear"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#FFFEAA53"/>
|
||||||
|
<item android:offset="0.6124" android:color="#FFFFCD49"/>
|
||||||
|
<item android:offset="1" android:color="#FFFFDE44"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="m68,85c0,2.762 -2.238,5 -5,5s-5,-2.238 -5,-5 2.238,-5 5,-5 5,2.238 5,5zM70,41c0,-3.867 -3.133,-7 -7,-7 -3.867,0 -7,3.133 -7,7 0,0.047 0.016,0.094 0.016,0.141h-0.016l2.438,28.59c0.203,2.414 2.188,4.269 4.563,4.269s4.359,-1.855 4.563,-4.269l2.438,-28.59h-0.016c0,-0.047 0.016,-0.094 0.016,-0.141z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="90"
|
||||||
|
android:startX="63"
|
||||||
|
android:endY="11.7176"
|
||||||
|
android:endX="63"
|
||||||
|
android:type="linear"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#FFFF634D"/>
|
||||||
|
<item android:offset="0.2043" android:color="#FFFE6464"/>
|
||||||
|
<item android:offset="0.5209" android:color="#FFFC6581"/>
|
||||||
|
<item android:offset="0.7936" android:color="#FFFA6694"/>
|
||||||
|
<item android:offset="0.9892" android:color="#FFFA669A"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
</vector>
|
45
app/src/main/res/drawable/ic_sync.xml
Normal file
45
app/src/main/res/drawable/ic_sync.xml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2019-11-11.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector android:height="128dp" android:viewportHeight="64"
|
||||||
|
android:viewportWidth="64" android:width="128dp"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:pathData="m59,16h2c1.848,0 3.319,-1.693 2.94,-3.605 -0.283,-1.423 -1.62,-2.395 -3.071,-2.395h-10.369c-0.828,0 -1.5,-0.672 -1.5,-1.5s0.672,-1.5 1.5,-1.5h5.912c1.139,0 2.202,-0.721 2.497,-1.821 0.446,-1.663 -0.813,-3.179 -2.409,-3.179h-48c-1.925,0 -3.5,1.575 -3.5,3.5s1.575,3.5 3.5,3.5h1.5c1.105,0 2,0.895 2,2s-0.895,2 -2,2h-5.385c-1.67,0 -3.195,1.122 -3.537,2.757 -0.47,2.245 1.254,4.243 3.422,4.243h7.5l-3,12h-5.385c-1.67,0 -3.195,1.122 -3.537,2.757 -0.47,2.245 1.254,4.243 3.422,4.243h0.5c1.105,0 2,0.895 2,2s-0.895,2 -2,2h-0.5c-2.168,0 -3.892,1.998 -3.422,4.243 0.342,1.635 1.867,2.757 3.537,2.757h5.385c1.105,0 2,0.895 2,2s-0.895,2 -2,2h-1.385c-1.67,0 -3.195,1.122 -3.537,2.757 -0.47,2.245 1.254,4.243 3.422,4.243h48.5c1.657,0 3,-1.343 3,-3s-1.343,-3 -3,-3h-0.5c-1.381,0 -2.5,-1.119 -2.5,-2.5s1.119,-2.5 2.5,-2.5h4.5c1.657,0 3,-1.343 3,-3s-1.343,-3 -3,-3h-8.377c2.141,-3.494 3.377,-7.602 3.377,-12 0,-2.441 -0.384,-4.792 -1.088,-7h6.501c1.139,0 2.202,-0.721 2.497,-1.821 0.445,-1.663 -0.814,-3.179 -2.41,-3.179h-1.5c-1.105,0 -2,-0.895 -2,-2s0.895,-2 2,-2z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient android:centerX="32" android:centerY="31.500021"
|
||||||
|
android:gradientRadius="32" android:tileMode="mirror" android:type="radial">
|
||||||
|
<item android:color="#1F0FCCFF" android:offset="0"/>
|
||||||
|
<item android:color="#1F0FCEFF" android:offset="0.193"/>
|
||||||
|
<item android:color="#1F0FD5FF" android:offset="0.703"/>
|
||||||
|
<item android:color="#1F0ECEFF" android:offset="1"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path android:pathData="M51.483,15.326l3.937,-3.93c1.24,-1.25 0.36,-3.38 -1.4,-3.38H42c-1.1,0 -2,0.9 -2,2v12c0,1.76 2.13,2.65 3.38,1.4l4.537,-4.529C50.532,22.313 52,26.533 52,31c0,11.215 -8.565,20 -19.5,20c-1.381,0 -2.5,1.119 -2.5,2.5s1.119,2.5 2.5,2.5C46.238,56 57,45.019 57,31C57,25.183 55.012,19.699 51.483,15.326z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient android:endX="43.5" android:endY="-21.15"
|
||||||
|
android:startX="43.5" android:startY="59.034"
|
||||||
|
android:tileMode="mirror" android:type="linear">
|
||||||
|
<item android:color="#FFFF634D" android:offset="0"/>
|
||||||
|
<item android:color="#FFFE6464" android:offset="0.204"/>
|
||||||
|
<item android:color="#FFFC6581" android:offset="0.521"/>
|
||||||
|
<item android:color="#FFFA6694" android:offset="0.794"/>
|
||||||
|
<item android:color="#FFFA669A" android:offset="0.989"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path android:pathData="M20.604,38.58l-4.523,4.53C13.468,39.683 12,35.463 12,31c0,-11.215 8.565,-20 19.5,-20c1.381,0 2.5,-1.119 2.5,-2.5S32.881,6 31.5,6C17.762,6 7,16.981 7,31c0,5.815 1.989,11.303 5.52,15.677L8.584,50.62c-1.25,1.25 -0.36,3.38 1.4,3.38h12c1.1,0 2,-0.9 2,-2V39.98C23.984,38.22 21.854,37.34 20.604,38.58z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient android:endX="20.5" android:endY="-24.844"
|
||||||
|
android:startX="20.5" android:startY="55.227"
|
||||||
|
android:tileMode="mirror" android:type="linear">
|
||||||
|
<item android:color="#FFFF634D" android:offset="0"/>
|
||||||
|
<item android:color="#FFFE6464" android:offset="0.204"/>
|
||||||
|
<item android:color="#FFFC6581" android:offset="0.521"/>
|
||||||
|
<item android:color="#FFFA6694" android:offset="0.794"/>
|
||||||
|
<item android:color="#FFFA669A" android:offset="0.989"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
</vector>
|
202
app/src/main/res/drawable/ic_timetable.xml
Normal file
202
app/src/main/res/drawable/ic_timetable.xml
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2019-11-11.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="128dp"
|
||||||
|
android:height="128dp"
|
||||||
|
android:viewportWidth="128"
|
||||||
|
android:viewportHeight="128">
|
||||||
|
<path
|
||||||
|
android:pathData="m118,62.129h2.605c3.789,0 7.188,-2.84 7.383,-6.625 0.211,-4.035 -3,-7.375 -6.988,-7.375h-4.805c-1.965,0 -3.785,-1.328 -4.129,-3.262 -0.043,-0.25 -0.066,-0.5 -0.066,-0.746 0.004,-2.168 1.734,-3.93 3.887,-3.988 3.023,-0.082 5.731,-2.293 6.07,-5.297 0.027,-0.242 0.039,-0.477 0.039,-0.711 0,-3.313 -2.688,-5.996 -6,-5.996h-4.602c-0.434,0 -4.863,-0.035 -5.281,-0.105 -0.035,-0.008 -0.074,-0.016 -0.113,-0.023v18h-42v-18h25.715c0.438,-1.688 0.539,-3.512 -0.945,-5.719 -1.836,-2.742 -5.023,-4.281 -8.324,-4.281h-9.445c-1.656,0 -3,-1.344 -3,-3s1.344,-3 3,-3h0.66c3.25,0 6.16,-2.434 6.332,-5.68 0.18,-3.461 -2.57,-6.32 -5.992,-6.32h-57.66c-3.25,0 -6.16,2.434 -6.332,5.68 -0.18,3.457 2.57,6.32 5.992,6.32h15c1.656,0 3,1.344 3,3s-1.344,3 -3,3h-17c-4.418,0 -8,3.582 -8,8s3.582,8 8,8h26v12h-14l2,21.109c-1.07,0.801 -1.836,1.98 -1.973,3.375 -0.184,1.84 0.633,3.5 1.973,4.504v5.047c-0.027,0.148 -0.043,0.297 -0.027,0.449 0.301,2.992 -2.039,5.516 -4.973,5.516h-14.66c-2.984,0 -5.762,2.023 -6.25,4.965 -0.246,1.465 0.043,2.848 0.684,4.012 1.074,1.938 3.234,3.023 5.449,3.023h3.164c2.375,0 4.207,1.328 4.551,3.27 0.039,0.246 0.063,0.488 0.063,0.723 0.004,2.215 -1.789,4.008 -3.996,4.008h-0.004c-2.27,0 -4.473,1.203 -5.398,3.277 -1.98,4.426 1.207,8.723 5.398,8.723h62c3.313,0 6,-2.688 6,-6s-2.688,-6 -6,-6h-10v-20l46,0.129h9.66c3.141,0 6.168,-2.539 6.328,-5.676 0.184,-3.461 -2.57,-6.324 -5.992,-6.324h-0.023,-0.035c-1.555,0 -3.078,-0.508 -4.156,-1.535 -0.34,-0.324 -0.637,-0.699 -0.875,-1.129 -2.574,-4.637 0.711,-9.336 5.094,-9.336z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:gradientRadius="80.322"
|
||||||
|
android:centerX="60.334"
|
||||||
|
android:centerY="65.146"
|
||||||
|
android:type="radial"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#1F0FCCFF"/>
|
||||||
|
<item android:offset="0.1927" android:color="#1F0FCEFF"/>
|
||||||
|
<item android:offset="0.7025" android:color="#1F0FD5FF"/>
|
||||||
|
<item android:offset="1" android:color="#1F0FD7FF"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="m18,100v-60h92v60c0,5.523 -4.477,10 -10,10h-72c-5.523,0 -10,-4.477 -10,-10z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="110"
|
||||||
|
android:startX="64"
|
||||||
|
android:endY="40"
|
||||||
|
android:endX="64"
|
||||||
|
android:type="linear"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#FFFFC662"/>
|
||||||
|
<item android:offset="0.0036" android:color="#FFFFC662"/>
|
||||||
|
<item android:offset="0.6085" android:color="#FFFFC582"/>
|
||||||
|
<item android:offset="1" android:color="#FFFFC491"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="m110,29.602v16.398h-92v-16.398c0,-5.309 4.332,-9.602 9.684,-9.602h72.633c5.352,0 9.684,4.293 9.684,9.602">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="46"
|
||||||
|
android:startX="64"
|
||||||
|
android:endY="20"
|
||||||
|
android:endX="64"
|
||||||
|
android:type="linear"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#FFFF634D"/>
|
||||||
|
<item android:offset="0.2083" android:color="#FFFD6464"/>
|
||||||
|
<item android:offset="0.5223" android:color="#FFFC6582"/>
|
||||||
|
<item android:offset="0.7935" android:color="#FFFA6694"/>
|
||||||
|
<item android:offset="0.9892" android:color="#FFFA669A"/>
|
||||||
|
<item android:offset="1" android:color="#FFFA669A"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="m49.309,20h-12.621c-2.832,1.988 -4.688,5.277 -4.688,9 0,6.07 4.93,11 11,11 6.07,0 11,-4.93 11,-11 0,-3.723 -1.855,-7.012 -4.691,-9z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="40"
|
||||||
|
android:startX="43"
|
||||||
|
android:endY="20"
|
||||||
|
android:endX="43"
|
||||||
|
android:type="linear"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#FFFF5840"/>
|
||||||
|
<item android:offset="0.0072" android:color="#FFFF5840"/>
|
||||||
|
<item android:offset="0.9892" android:color="#FFFA528C"/>
|
||||||
|
<item android:offset="1" android:color="#FFFA528C"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="m43,34c-2.75,0 -5,-2.25 -5,-5v-12c0,-2.75 2.25,-5 5,-5s5,2.25 5,5v12c0,2.75 -2.25,5 -5,5z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="12"
|
||||||
|
android:startX="43"
|
||||||
|
android:endY="34"
|
||||||
|
android:endX="43"
|
||||||
|
android:type="linear"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#FFA4A4A4"/>
|
||||||
|
<item android:offset="0.6301" android:color="#FF7F7F7F"/>
|
||||||
|
<item android:offset="1" android:color="#FF6F6F6F"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="m91.309,20h-12.621c-2.832,1.988 -4.688,5.277 -4.688,9 0,6.07 4.93,11 11,11 6.07,0 11,-4.93 11,-11 0,-3.723 -1.855,-7.012 -4.691,-9z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="40"
|
||||||
|
android:startX="85"
|
||||||
|
android:endY="20"
|
||||||
|
android:endX="85"
|
||||||
|
android:type="linear"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#FFFF5840"/>
|
||||||
|
<item android:offset="0.0072" android:color="#FFFF5840"/>
|
||||||
|
<item android:offset="0.9892" android:color="#FFFA528C"/>
|
||||||
|
<item android:offset="1" android:color="#FFFA528C"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="m85,34c-2.75,0 -5,-2.25 -5,-5v-12c0,-2.75 2.25,-5 5,-5s5,2.25 5,5v12c0,2.75 -2.25,5 -5,5z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="12"
|
||||||
|
android:startX="85"
|
||||||
|
android:endY="34"
|
||||||
|
android:endX="85"
|
||||||
|
android:type="linear"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#FFA4A4A4"/>
|
||||||
|
<item android:offset="0.6301" android:color="#FF7F7F7F"/>
|
||||||
|
<item android:offset="1" android:color="#FF6F6F6F"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="m78,81.008c0,2.762 2.238,5 5,4.996h2.992c3.316,-0.008 6.008,2.684 6.008,5.996s-2.688,6 -6,6h-8c-3.313,0 -6,2.688 -6,6s2.688,6 6,6h22c5.523,0 10,-4.477 10,-10v-24l-27.008,0.012c-2.758,-0 -4.992,2.234 -4.992,4.996z"
|
||||||
|
android:fillColor="#ffb86a"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m18,68h17.785c1.992,0 3.84,-1.363 4.16,-3.328 0.406,-2.508 -1.516,-4.672 -3.945,-4.672h-3c-1.656,0 -3,-1.344 -3,-3s1.344,-3 3,-3h14.785c1.992,0 3.84,-1.363 4.16,-3.328 0.406,-2.508 -1.516,-4.672 -3.945,-4.672h-30z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="108.25"
|
||||||
|
android:startX="35"
|
||||||
|
android:endY="43.742"
|
||||||
|
android:endX="35"
|
||||||
|
android:type="linear"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#FFFFCE76"/>
|
||||||
|
<item android:offset="0.0036" android:color="#FFFFCE76"/>
|
||||||
|
<item android:offset="0.6054" android:color="#FFFFCD92"/>
|
||||||
|
<item android:offset="1" android:color="#FFFFCCA0"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="m98,62c0,3.313 -2.688,6 -6,6s-6,-2.688 -6,-6 2.688,-6 6,-6 6,2.688 6,6z"
|
||||||
|
android:fillColor="#ffb977"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m48,74h-8c-2.211,0 -4,-1.789 -4,-4v-8c0,-2.211 1.789,-4 4,-4h8c2.211,0 4,1.789 4,4v8c0,2.211 -1.789,4 -4,4zM72,70v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4zM92,70v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4zM52,90v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4zM72,90v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4zM92,90v-8c0,-2.211 -1.789,-4 -4,-4h-8c-2.211,0 -4,1.789 -4,4v8c0,2.211 1.789,4 4,4h8c2.211,0 4,-1.789 4,-4z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="94"
|
||||||
|
android:startX="64"
|
||||||
|
android:endY="58"
|
||||||
|
android:endX="64"
|
||||||
|
android:type="linear"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#FFFFE79F"/>
|
||||||
|
<item android:offset="0.1186" android:color="#FFFFE9A6"/>
|
||||||
|
<item android:offset="1" android:color="#FFFFF5D5"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="m124,104c0,11.047 -8.953,20 -20,20 -11.047,0 -20,-8.953 -20,-20 0,-11.047 8.953,-20 20,-20 11.047,0 20,8.953 20,20z"
|
||||||
|
android:fillColor="#fff"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m104,80c-13.254,0 -24,10.746 -24,24 0,13.254 10.746,24 24,24 13.254,0 24,-10.746 24,-24 0,-13.254 -10.746,-24 -24,-24zM104,120c-8.836,0 -16,-7.164 -16,-16 0,-8.836 7.164,-16 16,-16 8.836,0 16,7.164 16,16 0,8.836 -7.164,16 -16,16z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="128"
|
||||||
|
android:startX="104"
|
||||||
|
android:endY="80"
|
||||||
|
android:endX="104"
|
||||||
|
android:type="linear"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#FF155CDE"/>
|
||||||
|
<item android:offset="0.6248" android:color="#FF2289E7"/>
|
||||||
|
<item android:offset="1" android:color="#FF289FEC"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="m113.41,110.59 l-5.563,-5.563c0.086,-0.328 0.148,-0.668 0.148,-1.023 0,-1.477 -0.809,-2.754 -2,-3.445v-6.555c0,-1.105 -0.895,-2 -2,-2s-2,0.895 -2,2v6.555c-1.191,0.691 -2,1.969 -2,3.445 0,2.211 1.789,4 4,4 0.355,0 0.695,-0.063 1.023,-0.148l5.563,5.563c0.391,0.391 0.902,0.586 1.414,0.586s1.023,-0.195 1.414,-0.586c0.781,-0.781 0.781,-2.047 0,-2.828z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="92"
|
||||||
|
android:startX="107"
|
||||||
|
android:endY="114"
|
||||||
|
android:endX="107"
|
||||||
|
android:type="linear"
|
||||||
|
android:tileMode="mirror">
|
||||||
|
<item android:offset="0" android:color="#FF919191"/>
|
||||||
|
<item android:offset="1" android:color="#FF6F6F6F"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
</vector>
|
11
app/src/main/res/drawable/timetable_lesson_annotation.xml
Normal file
11
app/src/main/res/drawable/timetable_lesson_annotation.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2019-11-10.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:topLeftRadius="4dp" android:topRightRadius="4dp" />
|
||||||
|
<solid android:color="#2196f3" tools:color="?timetable_lesson_change_color" />
|
||||||
|
</shape>
|
7
app/src/main/res/drawable/timetable_lesson_bg_dark.xml
Normal file
7
app/src/main/res/drawable/timetable_lesson_bg_dark.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<shape android:shape="rectangle"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<corners android:radius="4dp" />
|
||||||
|
<solid android:color="@color/colorSurface_4dp" />
|
||||||
|
</shape>
|
7
app/src/main/res/drawable/timetable_lesson_bg_light.xml
Normal file
7
app/src/main/res/drawable/timetable_lesson_bg_light.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<shape android:shape="rectangle"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<corners android:radius="4dp" />
|
||||||
|
<stroke android:width="1dp" android:color="#1e000000" />
|
||||||
|
</shape>
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) Kuba Szczodrzyński 2019-11-10.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:topLeftRadius="4dp" android:topRightRadius="4dp" />
|
||||||
|
<solid android:color="#e91e63" />
|
||||||
|
</shape>
|
@ -151,6 +151,16 @@
|
|||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/fakeLogin"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="24dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginRight="24dp"
|
||||||
|
android:text="Fake login" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
90
app/src/main/res/layout/fragment_timetable_v2.xml
Normal file
90
app/src/main/res/layout/fragment_timetable_v2.xml
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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">
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:id="@+id/timetableLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:visibility="gone">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?colorSurface"
|
||||||
|
style="@style/Widget.MaterialComponents.AppBarLayout.Surface">
|
||||||
|
|
||||||
|
<com.nshmura.recyclertablayout.RecyclerTabLayout
|
||||||
|
android:id="@+id/tabLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="@color/colorSurface_6dp"
|
||||||
|
app:rtl_tabTextAppearance="@style/rtl_RecyclerTabLayout.Tab"
|
||||||
|
app:rtl_tabIndicatorColor="?colorPrimary"
|
||||||
|
app:rtl_tabMinWidth="90dp"
|
||||||
|
app:rtl_tabMaxWidth="300dp"
|
||||||
|
app:rtl_tabSelectedTextColor="?colorPrimary"
|
||||||
|
app:rtl_tabPaddingStart="16dp"
|
||||||
|
app:rtl_tabPaddingEnd="16dp"
|
||||||
|
app:rtl_tabPaddingTop="12dp"
|
||||||
|
app:rtl_tabPaddingBottom="12dp"/>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.viewpager.widget.ViewPager
|
||||||
|
android:id="@+id/viewPager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/timetableNotPublicLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:gravity="center"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:srcCompat="@drawable/ic_no_timetable" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:fontFamily="sans-serif-light"
|
||||||
|
android:text="@string/timetable_not_public_title"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/timetable_not_public_text"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/timetable_not_public_hint"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
</layout>
|
153
app/src/main/res/layout/fragment_timetable_v2_day.xml
Normal file
153
app/src/main/res/layout/fragment_timetable_v2_day.xml
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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">
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/dayScroll"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<com.linkedin.android.tachyon.DayView
|
||||||
|
android:id="@+id/day"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="10dp"
|
||||||
|
app:dividerHeight="1dp"
|
||||||
|
app:endHour="18"
|
||||||
|
app:eventMargin="2dp"
|
||||||
|
app:halfHourDividerColor="#e0e0e0"
|
||||||
|
app:halfHourHeight="60dp"
|
||||||
|
app:hourDividerColor="#b0b0b0"
|
||||||
|
app:hourLabelMarginEnd="10dp"
|
||||||
|
app:hourLabelWidth="40dp"
|
||||||
|
app:startHour="5"
|
||||||
|
tools:visibility="gone"/>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/noLessonsLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:gravity="center"
|
||||||
|
tools:visibility="gone">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:srcCompat="@drawable/ic_timetable" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:fontFamily="sans-serif-light"
|
||||||
|
android:text="@string/timetable_no_lessons_title"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/freeDayLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:gravity="center"
|
||||||
|
tools:visibility="gone">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:srcCompat="@drawable/ic_sunbed" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:fontFamily="sans-serif-light"
|
||||||
|
android:text="@string/timetable_free_day_title"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/timetable_free_day_text"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/freeDayText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:gravity="center"
|
||||||
|
tools:text="Dzień wolny dla szkoły z puli dyrektorskiej z okazji obchodów Światowego Dnia Wtorku w mieście Poznań i na przedmieśiach"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/freeDayShowTimetable"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/timetable_free_day_show" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/noTimetableLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:gravity="center"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:srcCompat="@drawable/ic_sync" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:fontFamily="sans-serif-light"
|
||||||
|
android:text="@string/timetable_no_timetable_title"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/timetable_no_timetable_text"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/noTimetableSync"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/timetable_no_timetable_sync" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/noTimetableWeek"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="@string/timetable_no_timetable_week"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="italic"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
</layout>
|
16
app/src/main/res/layout/timetable_hour_label.xml
Normal file
16
app/src/main/res/layout/timetable_hour_label.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright 2019 LinkedIn Corporation -->
|
||||||
|
<!-- All Rights Reserved. -->
|
||||||
|
<!-- -->
|
||||||
|
<!-- Licensed under the BSD 2-Clause License (the "License"). See License in the project root -->
|
||||||
|
<!-- for license information. -->
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="@style/Base.TextAppearance.AppCompat.Small"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textAlignment="viewEnd"
|
||||||
|
tools:text="1 PM" />
|
139
app/src/main/res/layout/timetable_lesson.xml
Normal file
139
app/src/main/res/layout/timetable_lesson.xml
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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">
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<import type="android.view.View" />
|
||||||
|
<variable
|
||||||
|
name="annotationVisible"
|
||||||
|
type="boolean"/>
|
||||||
|
<variable
|
||||||
|
name="lessonNumber"
|
||||||
|
type="Integer" />
|
||||||
|
</data>
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?android:colorBackground"
|
||||||
|
android:foreground="@drawable/bg_rounded_ripple_4dp"
|
||||||
|
tools:padding="32dp">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:layout_height="90dp"
|
||||||
|
android:background="?timetable_lesson_bg"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="4dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/annotation"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/timetable_lesson_annotation"
|
||||||
|
android:fontFamily="sans-serif-condensed"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:text="@string/timetable_lesson_cancelled"
|
||||||
|
android:textColor="#000"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="italic"
|
||||||
|
android:visibility="@{annotationVisible ? View.VISIBLE : View.GONE}"
|
||||||
|
tools:text="Zastępstwo: zamiast lekcji język polski z Adam Dodatkowy"
|
||||||
|
tools:visibility="gone" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="top"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:baselineAligned="false">
|
||||||
|
<!--tools:background="@drawable/timetable_subject_color_rounded"-->
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/subjectName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:fontFamily="sans-serif-light"
|
||||||
|
android:maxLines="@{annotationVisible ? 1 : 2}"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:autoSizeMaxTextSize="16sp"
|
||||||
|
app:autoSizeMinTextSize="12sp"
|
||||||
|
app:autoSizeTextType="uniform"
|
||||||
|
tools:maxLines="2"
|
||||||
|
tools:text="pracownia urządzeń techniki komputerowej" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/attendanceIcon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:srcCompat="@sample/check"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imageView4"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_weight="0"
|
||||||
|
app:srcCompat="@drawable/bg_circle"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView6"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:fontFamily="sans-serif-condensed-light"
|
||||||
|
android:includeFontPadding="false"
|
||||||
|
android:layout_marginBottom="-4dp"
|
||||||
|
android:paddingStart="4dp"
|
||||||
|
android:paddingEnd="4dp"
|
||||||
|
android:text="@{Integer.toString(lessonNumber)}"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:visibility="@{lessonNumber != null ? View.VISIBLE : View.GONE}"
|
||||||
|
tools:text="3"/>
|
||||||
|
<!--android:layout_marginTop="@{annotationVisible ? `-4dp` : `4dp`}"
|
||||||
|
android:layout_marginBottom="@{annotationVisible ? `-4dp` : `0dp`}"-->
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="bottom"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detailsFirst"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
tools:text="8:10 - 8:55 • 015 językowa → 016 językowa" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detailsSecond"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="@style/NavView.TextView.Helper"
|
||||||
|
android:textSize="12sp"
|
||||||
|
tools:text="Paweł Informatyczny • 2b3T n1" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
</layout>
|
@ -3,4 +3,9 @@
|
|||||||
<attr name="colorSection" format="color" />
|
<attr name="colorSection" format="color" />
|
||||||
<attr name="cardBackgroundDimmed" format="color" />
|
<attr name="cardBackgroundDimmed" format="color" />
|
||||||
<attr name="cardBackgroundHighlight" format="color" />
|
<attr name="cardBackgroundHighlight" format="color" />
|
||||||
|
<attr name="timetable_lesson_bg" format="reference" />
|
||||||
|
<attr name="timetable_lesson_cancelled_color" format="color" />
|
||||||
|
<attr name="timetable_lesson_change_color" format="color" />
|
||||||
|
<attr name="timetable_lesson_shifted_source_color" format="color" />
|
||||||
|
<attr name="timetable_lesson_shifted_target_color" format="color" />
|
||||||
</resources>
|
</resources>
|
@ -985,4 +985,25 @@
|
|||||||
<string name="app_manager_open_failed">Nie udało się otworzyć ustawień</string>
|
<string name="app_manager_open_failed">Nie udało się otworzyć ustawień</string>
|
||||||
<string name="edziennik_notification_api_notify_title">Tworzenie powiadomień</string>
|
<string name="edziennik_notification_api_notify_title">Tworzenie powiadomień</string>
|
||||||
<string name="login_librus_captcha_title">Librus - logowanie</string>
|
<string name="login_librus_captcha_title">Librus - logowanie</string>
|
||||||
|
<string name="timetable_today">Dzisiaj</string>
|
||||||
|
<string name="timetable_lesson_cancelled">Lekcja odwołana</string>
|
||||||
|
<string name="timetable_lesson_change">Zastępstwo</string>
|
||||||
|
<string name="timetable_lesson_change_format">Zastępstwo: zamiast %s</string>
|
||||||
|
<string name="timetable_lesson_shifted_same_day">Lekcja przeniesiona na godz. %s</string>
|
||||||
|
<string name="timetable_lesson_shifted_other_day">Lekcja przeniesiona na %s, godz. %s</string>
|
||||||
|
<string name="timetable_lesson_shifted_from_same_day">Lekcja przeniesiona z godz. %s</string>
|
||||||
|
<string name="timetable_lesson_shifted_from_other_day">Lekcja przeniesiona z dnia %s, godz. %s</string>
|
||||||
|
<string name="timetable_lesson_shifted">Lekcja przeniesiona na inny termin</string>
|
||||||
|
<string name="timetable_lesson_shifted_from">Lekcja przeniesiona z innego terminu</string>
|
||||||
|
<string name="timetable_not_public_title">Brak planu lekcji</string>
|
||||||
|
<string name="timetable_not_public_text">Plan lekcji nie został opublikowany przez szkołę.</string>
|
||||||
|
<string name="timetable_not_public_hint">Skontaktuj się z wychowawcą w celu udostępnienia planu lekcji.</string>
|
||||||
|
<string name="timetable_free_day_title">Dzień wolny</string>
|
||||||
|
<string name="timetable_free_day_text">W tym dniu nie ma lekcji:</string>
|
||||||
|
<string name="timetable_free_day_show">Pokaż plan lekcji</string>
|
||||||
|
<string name="timetable_no_lessons_title">Brak lekcji tego dnia</string>
|
||||||
|
<string name="timetable_no_timetable_title">Brak planu lekcji</string>
|
||||||
|
<string name="timetable_no_timetable_text">Nie pobrano planu lekcji na ten tydzień.</string>
|
||||||
|
<string name="timetable_no_timetable_sync">Pobierz plan lekcji</string>
|
||||||
|
<string name="timetable_no_timetable_week">na tydzień %s</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources>
|
||||||
|
|
||||||
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
|
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
|
||||||
<item name="colorAccent">@color/colorAccent</item>
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
@ -95,6 +95,12 @@
|
|||||||
<item name="mal_color_secondary">?android:textColorSecondary</item>
|
<item name="mal_color_secondary">?android:textColorSecondary</item>
|
||||||
<item name="mal_card_background">?colorSurface</item>
|
<item name="mal_card_background">?colorSurface</item>
|
||||||
<item name="mal_divider_color">@color/dividerColor</item>
|
<item name="mal_divider_color">@color/dividerColor</item>
|
||||||
|
|
||||||
|
<item name="timetable_lesson_bg">@drawable/timetable_lesson_bg_light</item>
|
||||||
|
<item name="timetable_lesson_cancelled_color">#9f9f9f</item>
|
||||||
|
<item name="timetable_lesson_change_color">#ffb300</item>
|
||||||
|
<item name="timetable_lesson_shifted_source_color">#A1887F</item>
|
||||||
|
<item name="timetable_lesson_shifted_target_color">#4caf50</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="AppTheme.Dark" parent="NavView.Dark">
|
<style name="AppTheme.Dark" parent="NavView.Dark">
|
||||||
<item name="colorPrimary">#64b5f6</item>
|
<item name="colorPrimary">#64b5f6</item>
|
||||||
@ -119,6 +125,12 @@
|
|||||||
<item name="mal_color_secondary">@color/secondaryTextDark</item>
|
<item name="mal_color_secondary">@color/secondaryTextDark</item>
|
||||||
<item name="mal_card_background">?colorSurface</item>
|
<item name="mal_card_background">?colorSurface</item>
|
||||||
<item name="mal_divider_color">@color/dividerColor</item>
|
<item name="mal_divider_color">@color/dividerColor</item>
|
||||||
|
|
||||||
|
<item name="timetable_lesson_bg">@drawable/timetable_lesson_bg_dark</item>
|
||||||
|
<item name="timetable_lesson_cancelled_color">#838383</item>
|
||||||
|
<item name="timetable_lesson_change_color">#ffb300</item>
|
||||||
|
<item name="timetable_lesson_shifted_source_color">#A1887F</item>
|
||||||
|
<item name="timetable_lesson_shifted_target_color">#4caf50</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@ buildscript {
|
|||||||
kotlin_version = '1.3.50'
|
kotlin_version = '1.3.50'
|
||||||
|
|
||||||
release = [
|
release = [
|
||||||
versionName: "3.9.3-dev",
|
versionName: "3.9.4-dev",
|
||||||
versionCode: 3090300
|
versionCode: 3090400
|
||||||
]
|
]
|
||||||
|
|
||||||
setup = [
|
setup = [
|
||||||
|
Reference in New Issue
Block a user