mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-06-21 09:33:06 +02:00
Compare commits
13 Commits
v3.9.10-de
...
v3.9.11-de
Author | SHA1 | Date | |
---|---|---|---|
f79b7eaf83 | |||
ae13bf946f | |||
f116c4f1f4 | |||
867c8920a8 | |||
6e6dd34872 | |||
0759468fa7 | |||
1b1fb09211 | |||
de414c912c | |||
d274a2fed1 | |||
285b7e9b9e | |||
875efcff7e | |||
07ae37167d | |||
f689f4d427 |
@ -29,9 +29,13 @@ import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Response
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.navlib.R
|
||||
import pl.szczodrzynski.navlib.getColorFromRes
|
||||
import java.text.SimpleDateFormat
|
||||
@ -418,15 +422,15 @@ fun TextView.setText(@StringRes resid: Int, vararg formatArgs: Any) {
|
||||
text = context.getString(resid, *formatArgs)
|
||||
}
|
||||
|
||||
fun JsonObject(vararg properties: Pair<String, Any>): JsonObject {
|
||||
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)
|
||||
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?)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -503,3 +507,29 @@ fun View.findParentById(targetId: Int): View? {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun CoroutineScope.startCoroutineTimer(delayMillis: Long = 0, repeatMillis: Long = 0, action: () -> Unit) = launch {
|
||||
delay(delayMillis)
|
||||
if (repeatMillis > 0) {
|
||||
while (true) {
|
||||
action()
|
||||
delay(repeatMillis)
|
||||
}
|
||||
} else {
|
||||
action()
|
||||
}
|
||||
}
|
||||
|
||||
operator fun Time?.compareTo(other: Time?): Int {
|
||||
if (this == null && other == null)
|
||||
return 0
|
||||
if (this == null)
|
||||
return -1
|
||||
if (other == null)
|
||||
return 1
|
||||
return this.compareTo(other)
|
||||
}
|
||||
|
||||
operator fun StringBuilder.plusAssign(str: String?) {
|
||||
this.append(str)
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragmentV2
|
||||
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment
|
||||
@ -122,7 +122,7 @@ class MainActivity : AppCompatActivity() {
|
||||
val list: MutableList<NavTarget> = mutableListOf()
|
||||
|
||||
// home item
|
||||
list += NavTarget(DRAWER_ITEM_HOME, R.string.menu_home_page, HomeFragment::class)
|
||||
list += NavTarget(DRAWER_ITEM_HOME, R.string.menu_home_page, HomeFragmentV2::class)
|
||||
.withTitle(R.string.app_name)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_home_outline)
|
||||
.isInDrawer(true)
|
||||
@ -538,9 +538,7 @@ class MainActivity : AppCompatActivity() {
|
||||
else -> 0
|
||||
}
|
||||
val arguments = when (navTargetId) {
|
||||
DRAWER_ITEM_TIMETABLE -> JsonObject().apply {
|
||||
addProperty("weekStart", TimetableFragment.pageSelection?.weekStart?.stringY_m_d)
|
||||
}
|
||||
DRAWER_ITEM_TIMETABLE -> JsonObject("weekStart" to TimetableFragment.pageSelection?.weekStart?.stringY_m_d)
|
||||
else -> null
|
||||
}
|
||||
EdziennikTask.syncProfile(
|
||||
@ -704,7 +702,10 @@ class MainActivity : AppCompatActivity() {
|
||||
loadProfile(intentProfileId, intentTargetId, extras)
|
||||
}
|
||||
intentProfileId != -1 -> {
|
||||
if (app.profile.id != intentProfileId)
|
||||
loadProfile(intentProfileId, intentTargetId, extras)
|
||||
else
|
||||
loadTarget(intentTargetId, extras)
|
||||
}
|
||||
intentTargetId != -1 -> {
|
||||
drawer.currentProfile = app.profile.id
|
||||
|
@ -45,8 +45,8 @@ class DataNotifications(val data: Data) {
|
||||
return@run
|
||||
}
|
||||
|
||||
for (change in app.db.lessonChangeDao().getNotNotifiedNow(profileId)) {
|
||||
val text = app.getString(R.string.notification_lesson_change_format, change.changeTypeStr(app), if (change.lessonDate == null) "" else change.lessonDate!!.formattedString, change.subjectLongName)
|
||||
for (lesson in app.db.timetableDao().getNotNotifiedNow(profileId)) {
|
||||
val text = app.getString(R.string.notification_lesson_change_format, lesson.getDisplayChangeType(app), if (lesson.displayDate == null) "" else lesson.displayDate!!.formattedString, lesson.changeSubjectName)
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_TIMETABLE_LESSON_CHANGE),
|
||||
text = text,
|
||||
@ -54,8 +54,8 @@ class DataNotifications(val data: Data) {
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_TIMETABLE,
|
||||
addedDate = change.addedDate
|
||||
).addExtra("timetableDate", change.lessonDate?.value?.toLong())
|
||||
addedDate = lesson.addedDate
|
||||
).addExtra("timetableDate", lesson.displayDate?.stringY_m_d ?: "")
|
||||
}
|
||||
|
||||
for (event in app.db.eventDao().getNotNotifiedNow(profileId)) {
|
||||
@ -186,10 +186,10 @@ class DataNotifications(val data: Data) {
|
||||
val luckyNumbers = app.db.luckyNumberDao().getNotNotifiedNow(profileId)
|
||||
luckyNumbers?.removeAll { it.date < today }
|
||||
luckyNumbers?.forEach { luckyNumber ->
|
||||
val text = when {
|
||||
luckyNumber.date.value == todayValue -> // LN for today
|
||||
val text = when (luckyNumber.date.value) {
|
||||
todayValue -> // LN for today
|
||||
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_format else R.string.notification_lucky_number_format, luckyNumber.number)
|
||||
luckyNumber.date.value == todayValue + 1 -> // LN for tomorrow
|
||||
todayValue + 1 -> // LN for tomorrow
|
||||
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_tomorrow_format else R.string.notification_lucky_number_tomorrow_format, luckyNumber.number)
|
||||
else -> // LN for later
|
||||
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_later_format else R.string.notification_lucky_number_later_format, luckyNumber.date.formattedString, luckyNumber.number)
|
||||
|
@ -48,6 +48,7 @@ const val ERROR_PROFILE_MISSING = 105
|
||||
const val ERROR_INVALID_LOGIN_MODE = 110
|
||||
const val ERROR_LOGIN_METHOD_NOT_SATISFIED = 111
|
||||
const val ERROR_NOT_IMPLEMENTED = 112
|
||||
const val ERROR_FILE_DOWNLOAD = 113
|
||||
|
||||
const val ERROR_NO_STUDENTS_IN_ACCOUNT = 115
|
||||
|
||||
|
@ -76,4 +76,10 @@ object Regexes {
|
||||
val VULCAN_SHITFT_ANNOTATION by lazy {
|
||||
"""\(przeniesiona (z|na) lekcj[ię] ([0-9]+), (.+)\)""".toRegex()
|
||||
}
|
||||
|
||||
|
||||
|
||||
val LIBRUS_ATTACHMENT_KEY by lazy {
|
||||
"""singleUseKey=([0-9A-f_]+)""".toRegex()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-24
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.events
|
||||
|
||||
data class AttachmentGetEvent(val profileId: Int, val messageId: Long, val attachmentId: Long,
|
||||
var eventType: Int = TYPE_PROGRESS, val fileName: String? = null,
|
||||
val bytesWritten: Long = 0) {
|
||||
companion object {
|
||||
const val TYPE_PROGRESS = 0
|
||||
const val TYPE_FINISHED = 1
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
fun syncProfileList(profileList: List<Int>) = EdziennikTask(-1, SyncProfileListRequest(profileList))
|
||||
fun messageGet(profileId: Int, message: MessageFull) = EdziennikTask(profileId, MessageGetRequest(message))
|
||||
fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest())
|
||||
fun attachmentGet(profileId: Int, messageId: Long, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(messageId, attachmentId, attachmentName))
|
||||
}
|
||||
|
||||
private lateinit var loginStore: LoginStore
|
||||
@ -35,8 +36,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
loginStore = request.loginStore
|
||||
// save the profile ID and name as the current task's
|
||||
taskName = app.getString(R.string.edziennik_notification_api_first_login_title)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// get the requested profile and login store
|
||||
val profile = app.db.profileDao().getByIdNow(profileId)
|
||||
this.profile = profile
|
||||
@ -67,12 +67,14 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
|
||||
when (request) {
|
||||
is SyncProfileRequest -> edziennikInterface?.sync(
|
||||
featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) } ?: Features.getAllIds(),
|
||||
featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) }
|
||||
?: Features.getAllIds(),
|
||||
viewId = request.viewIds?.get(0)?.first,
|
||||
arguments = request.arguments)
|
||||
is MessageGetRequest -> edziennikInterface?.getMessage(request.message)
|
||||
is FirstLoginRequest -> edziennikInterface?.firstLogin()
|
||||
is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead()
|
||||
is AttachmentGetRequest -> edziennikInterface?.getAttachment(request.messageId, request.attachmentId, request.attachmentName)
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,4 +92,5 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
data class SyncProfileListRequest(val profileList: List<Int>)
|
||||
data class MessageGetRequest(val message: MessageFull)
|
||||
class AnnouncementsReadRequest
|
||||
data class AttachmentGetRequest(val messageId: Long, val attachmentId: Long, val attachmentName: String)
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.Notifier.ID_NOTIFICATIONS
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.getNotificationTitle
|
||||
import pl.szczodrzynski.edziennik.utils.models.Notification
|
||||
import kotlin.math.min
|
||||
|
||||
@ -33,9 +34,9 @@ class NotifyTask : IApiTask(-1) {
|
||||
val pendingIntent = PendingIntent.getActivity(app, notification.id, intent, 0)
|
||||
val notificationBuilder = NotificationCompat.Builder(app, app.notifier.notificationGroup)
|
||||
// title, text, type, date
|
||||
.setContentTitle(notification.title)
|
||||
.setContentTitle(notification.profileName)
|
||||
.setContentText(notification.text)
|
||||
.setSubText(Notification.stringType(app, notification.type))
|
||||
.setSubText(app.getNotificationTitle(notification.type))
|
||||
.setWhen(notification.addedDate)
|
||||
.setTicker(app.getString(R.string.notification_ticker_format, Notification.stringType(app, notification.type)))
|
||||
// icon, color, lights, priority
|
||||
|
@ -70,6 +70,10 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
||||
|
||||
}
|
||||
|
||||
override fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
IdziennikFirstLogin(data) {
|
||||
completed()
|
||||
|
@ -28,7 +28,7 @@ class IdziennikWebTimetable(override val data: DataIdziennik,
|
||||
private const val TAG = "IdziennikWebTimetable"
|
||||
}
|
||||
|
||||
init {
|
||||
init { data.profile?.also { profile ->
|
||||
val currentWeekStart = Week.getWeekStart()
|
||||
|
||||
if (Date.getToday().weekDay > 4) {
|
||||
@ -91,10 +91,9 @@ class IdziennikWebTimetable(override val data: DataIdziennik,
|
||||
val lessonDate = weekStart.clone().stepForward(0, 0, weekDay)
|
||||
val classroom = lesson.getString("NazwaSali")
|
||||
|
||||
val id = lessonDate.combineWith(lessonRange.startTime) / 6L * 10L + (lesson.hashCode() and 0xFFFF)
|
||||
val type = lesson.getInt("TypZastepstwa") ?: -1
|
||||
|
||||
val lessonObject = Lesson(profileId, id)
|
||||
val lessonObject = Lesson(profileId, -1)
|
||||
|
||||
when (type) {
|
||||
1, 2, 3, 4, 5 -> {
|
||||
@ -150,16 +149,20 @@ class IdziennikWebTimetable(override val data: DataIdziennik,
|
||||
}
|
||||
}
|
||||
|
||||
lessonObject.id = lessonObject.buildId()
|
||||
|
||||
dates.add(lessonDate.value)
|
||||
lessons.add(lessonObject)
|
||||
|
||||
val seen = profile.empty || lessonDate < Date.getToday()
|
||||
|
||||
if (lessonObject.type != Lesson.TYPE_NORMAL && lessonDate >= Date.getToday()) {
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
lessonObject.id,
|
||||
profile?.empty ?: false,
|
||||
profile?.empty ?: false,
|
||||
seen,
|
||||
seen,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
@ -185,5 +188,5 @@ class IdziennikWebTimetable(override val data: DataIdziennik,
|
||||
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_TIMETABLE, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ interface EdziennikInterface {
|
||||
fun sync(featureIds: List<Int>, viewId: Int? = null, arguments: JsonObject? = null)
|
||||
fun getMessage(message: MessageFull)
|
||||
fun markAllAnnouncementsAsRead()
|
||||
fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String)
|
||||
fun firstLogin()
|
||||
fun cancel()
|
||||
}
|
||||
|
@ -10,13 +10,11 @@ import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.messages.LibrusMessagesGetAttachment
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.messages.LibrusMessagesGetMessage
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.synergia.LibrusSynergiaMarkAllAnnouncementsAsRead
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.LibrusFirstLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginMessages
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.login.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
@ -82,6 +80,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
}
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
LibrusLoginPortal(data) {
|
||||
LibrusLoginApi(data) {
|
||||
LibrusLoginSynergia(data) {
|
||||
LibrusLoginMessages(data) {
|
||||
@ -92,8 +91,10 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
LibrusLoginPortal(data) {
|
||||
LibrusLoginApi(data) {
|
||||
LibrusLoginSynergia(data) {
|
||||
LibrusSynergiaMarkAllAnnouncementsAsRead(data) {
|
||||
@ -102,6 +103,21 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String) {
|
||||
LibrusLoginPortal(data) {
|
||||
LibrusLoginApi(data) {
|
||||
LibrusLoginSynergia(data) {
|
||||
LibrusLoginMessages(data) {
|
||||
LibrusMessagesGetAttachment(data, messageId, attachmentId, attachmentName) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
LibrusFirstLogin(data) {
|
||||
|
@ -4,9 +4,12 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.data
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.body.MediaTypeUtils
|
||||
import im.wangchao.mhttp.callback.FileCallbackHandler
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import okhttp3.Cookie
|
||||
import org.jsoup.Jsoup
|
||||
@ -16,6 +19,7 @@ import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import java.io.File
|
||||
import java.io.StringWriter
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import javax.xml.transform.OutputKeys
|
||||
@ -131,4 +135,95 @@ open class LibrusMessages(open val data: DataLibrus) {
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
|
||||
fun sandboxGet(tag: String, action: String, parameters: Map<String, Any>? = null,
|
||||
onSuccess: (json: JsonObject) -> Unit) {
|
||||
|
||||
d(tag, "Request: Librus/Messages - $LIBRUS_SANDBOX_URL$action")
|
||||
|
||||
val callback = object : JsonCallbackHandler() {
|
||||
override fun onSuccess(json: JsonObject?, response: Response?) {
|
||||
if (json == null) {
|
||||
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
onSuccess(json)
|
||||
} catch (e: Exception) {
|
||||
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
|
||||
.withResponse(response)
|
||||
.withThrowable(e)
|
||||
.withApiResponse(json))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response?, throwable: Throwable?) {
|
||||
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
.withThrowable(throwable))
|
||||
}
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url("$LIBRUS_SANDBOX_URL$action")
|
||||
.userAgent(SYNERGIA_USER_AGENT)
|
||||
.apply {
|
||||
parameters?.forEach { (k, v) ->
|
||||
addParameter(k, v)
|
||||
}
|
||||
}
|
||||
.post()
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
|
||||
fun sandboxGetFile(tag: String, action: String, targetFile: File, onSuccess: (file: File) -> Unit,
|
||||
onProgress: (written: Long, total: Long) -> Unit) {
|
||||
|
||||
d(tag, "Request: Librus/Messages - $LIBRUS_SANDBOX_URL$action")
|
||||
|
||||
val callback = object : FileCallbackHandler(targetFile) {
|
||||
override fun onSuccess(file: File?, response: Response?) {
|
||||
if (file == null) {
|
||||
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
onSuccess(file)
|
||||
} catch (e: Exception) {
|
||||
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
|
||||
.withResponse(response)
|
||||
.withThrowable(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onProgress(bytesWritten: Long, bytesTotal: Long) {
|
||||
try {
|
||||
onProgress(bytesWritten, bytesTotal)
|
||||
} catch (e: Exception) {
|
||||
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
|
||||
.withThrowable(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response?, throwable: Throwable?) {
|
||||
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
.withThrowable(throwable))
|
||||
}
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url("$LIBRUS_SANDBOX_URL$action")
|
||||
.userAgent(SYNERGIA_USER_AGENT)
|
||||
.post()
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ class LibrusApiTimetables(override val data: DataLibrus,
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseLesson(lessonDate: Date, lesson: JsonObject) {
|
||||
private fun parseLesson(lessonDate: Date, lesson: JsonObject) { data.profile?.also { profile ->
|
||||
val isSubstitution = lesson.getBoolean("IsSubstitutionClass") ?: false
|
||||
val isCancelled = lesson.getBoolean("IsCanceled") ?: false
|
||||
|
||||
@ -88,8 +88,7 @@ class LibrusApiTimetables(override val data: DataLibrus,
|
||||
val virtualClassId = lesson.getJsonObject("VirtualClass")?.getLong("Id")
|
||||
val teamId = lesson.getJsonObject("Class")?.getLong("Id") ?: virtualClassId
|
||||
|
||||
val id = lessonDate.combineWith(startTime) / 6L * 10L + (lesson.hashCode() and 0xFFFF)
|
||||
val lessonObject = Lesson(profileId, id)
|
||||
val lessonObject = Lesson(profileId, -1)
|
||||
|
||||
if (isSubstitution && isCancelled) {
|
||||
// shifted lesson - source
|
||||
@ -184,17 +183,21 @@ class LibrusApiTimetables(override val data: DataLibrus,
|
||||
}
|
||||
}
|
||||
|
||||
if (lessonObject.type != Lesson.TYPE_NORMAL && lessonDate >= Date.getToday()) {
|
||||
lessonObject.id = lessonObject.buildId()
|
||||
|
||||
val seen = profile.empty || lessonDate < Date.getToday()
|
||||
|
||||
if (lessonObject.type != Lesson.TYPE_NORMAL) {
|
||||
data.metadataList.add(
|
||||
Metadata(
|
||||
data.profileId,
|
||||
profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
lessonObject.id,
|
||||
data.profile?.empty ?: false,
|
||||
data.profile?.empty ?: false,
|
||||
seen,
|
||||
seen,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
data.lessonNewList.add(lessonObject)
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-24
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.data.messages
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.api.v2.ERROR_FILE_DOWNLOAD
|
||||
import pl.szczodrzynski.edziennik.api.v2.EXCEPTION_LIBRUS_MESSAGES_REQUEST
|
||||
import pl.szczodrzynski.edziennik.api.v2.Regexes
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.AttachmentGetEvent
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.AttachmentGetEvent.Companion.TYPE_FINISHED
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.AttachmentGetEvent.Companion.TYPE_PROGRESS
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusMessages
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import java.io.File
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
|
||||
class LibrusMessagesGetAttachment(override val data: DataLibrus, val messageId: Long, val attachmentId: Long,
|
||||
val attachmentName: String, val onSuccess: () -> Unit) : LibrusMessages(data), CoroutineScope {
|
||||
companion object {
|
||||
const val TAG = "LibrusMessagesGetAttachment"
|
||||
}
|
||||
|
||||
private var job = Job()
|
||||
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Default
|
||||
|
||||
private var getAttachmentCheckKeyTries = 0
|
||||
|
||||
init {
|
||||
messagesGet(TAG, "GetFileDownloadLink", parameters = mapOf(
|
||||
"fileId" to attachmentId,
|
||||
"msgId" to messageId,
|
||||
"archive" to 0
|
||||
)) { doc ->
|
||||
val downloadLink = doc.select("response GetFileDownloadLink downloadLink").text()
|
||||
val keyMatcher = Regexes.LIBRUS_ATTACHMENT_KEY.find(downloadLink)
|
||||
|
||||
if (keyMatcher != null) {
|
||||
getAttachmentCheckKeyTries = 0
|
||||
|
||||
val attachmentKey = keyMatcher[1]
|
||||
getAttachmentCheckKey(attachmentKey) {
|
||||
downloadAttachment(attachmentKey)
|
||||
}
|
||||
} else {
|
||||
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
|
||||
.withApiResponse(doc.toString()))
|
||||
}
|
||||
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAttachmentCheckKey(attachmentKey: String, callback: () -> Unit) {
|
||||
sandboxGet(TAG, "CSCheckKey",
|
||||
parameters = mapOf("singleUseKey" to attachmentKey)) { json ->
|
||||
|
||||
when (json.getString("status")) {
|
||||
"not_downloaded_yet" -> {
|
||||
if (getAttachmentCheckKeyTries++ > 5) {
|
||||
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
|
||||
.withApiResponse(json))
|
||||
return@sandboxGet
|
||||
}
|
||||
launch {
|
||||
delay(2000)
|
||||
getAttachmentCheckKey(attachmentKey, callback)
|
||||
}
|
||||
}
|
||||
|
||||
"ready" -> {
|
||||
launch { callback() }
|
||||
}
|
||||
|
||||
else -> {
|
||||
data.error(ApiError(TAG, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
|
||||
.withApiResponse(json))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadAttachment(attachmentKey: String) {
|
||||
val targetFile = File(Utils.getStorageDir(), attachmentName)
|
||||
|
||||
sandboxGetFile(TAG, "CSDownload&singleUseKey=$attachmentKey",
|
||||
targetFile, { file ->
|
||||
|
||||
val event = AttachmentGetEvent(
|
||||
profileId,
|
||||
messageId,
|
||||
attachmentId,
|
||||
TYPE_FINISHED,
|
||||
file.absolutePath
|
||||
)
|
||||
|
||||
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.messageId}_${event.attachmentId}")
|
||||
Utils.writeStringToFile(attachmentDataFile, event.fileName)
|
||||
|
||||
EventBus.getDefault().post(event)
|
||||
|
||||
}) { written, _ ->
|
||||
val event = AttachmentGetEvent(
|
||||
profileId,
|
||||
messageId,
|
||||
attachmentId,
|
||||
TYPE_PROGRESS,
|
||||
bytesWritten = written
|
||||
)
|
||||
|
||||
EventBus.getDefault().post(event)
|
||||
}
|
||||
}
|
||||
}
|
@ -76,6 +76,10 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
|
||||
|
||||
}
|
||||
|
||||
override fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
MobidziennikFirstLogin(data) {
|
||||
completed()
|
||||
|
@ -14,7 +14,7 @@ import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
init {
|
||||
init { data.profile?.also { profile ->
|
||||
val lessons = rows.filterNot { it.isEmpty() }.map { it.split("|") }
|
||||
|
||||
val dataStart = Date.getToday()
|
||||
@ -32,7 +32,6 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
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)
|
||||
|
||||
@ -41,7 +40,7 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
val teamId = data.teamList.singleOrNull { it.name == lesson[8]+lesson[9] }?.id ?: -1
|
||||
val classroom = lesson[11]
|
||||
|
||||
Lesson(data.profileId, id).also {
|
||||
Lesson(data.profileId, -1).also {
|
||||
when (lesson[1]) {
|
||||
"plan_lekcji", "lekcja" -> {
|
||||
it.type = Lesson.TYPE_NORMAL
|
||||
@ -75,14 +74,18 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
if (it.type != Lesson.TYPE_NORMAL && date >= Date.getToday()) {
|
||||
it.id = it.buildId()
|
||||
|
||||
val seen = profile.empty || date < Date.getToday()
|
||||
|
||||
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,
|
||||
seen,
|
||||
seen,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
@ -194,5 +197,5 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -375,7 +375,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
}
|
||||
|
||||
fun shouldSyncLuckyNumber(): Boolean {
|
||||
return (db.luckyNumberDao().getNearestFutureNow(profileId, Date.getToday()) ?: -1) == -1
|
||||
return (db.luckyNumberDao().getNearestFutureNow(profileId, Date.getToday().value) ?: -1) == -1
|
||||
}
|
||||
|
||||
fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) {
|
||||
|
@ -70,6 +70,10 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
||||
|
||||
}
|
||||
|
||||
override fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
TemplateFirstLogin(data) {
|
||||
completed()
|
||||
|
@ -76,6 +76,10 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
|
||||
}
|
||||
|
||||
override fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
VulcanFirstLogin(data) {
|
||||
completed()
|
||||
|
@ -29,14 +29,14 @@ class VulcanApiTimetable(override val data: DataVulcan, val onSuccess: () -> Uni
|
||||
|
||||
init { data.profile?.also { profile ->
|
||||
val currentWeekStart = Week.getWeekStart()
|
||||
|
||||
if (Date.getToday().weekDay > 4) {
|
||||
currentWeekStart.stepForward(0, 0, 7)
|
||||
}
|
||||
|
||||
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
|
||||
|
||||
val weekStart = Date.fromY_m_d(getDate)
|
||||
|
||||
if (Date.getToday().weekDay > 4 && weekStart == currentWeekStart) {
|
||||
weekStart.stepForward(0, 0, 7)
|
||||
}
|
||||
|
||||
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
|
||||
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_TIMETABLE, parameters = mapOf(
|
||||
@ -114,9 +114,7 @@ class VulcanApiTimetable(override val data: DataVulcan, val onSuccess: () -> Uni
|
||||
}
|
||||
}
|
||||
|
||||
val id = lessonDate.combineWith(startTime) / 6L * 10L + (lesson.hashCode() and 0xFFFF)
|
||||
|
||||
val lessonObject = Lesson(profileId, id).apply {
|
||||
val lessonObject = Lesson(profileId, -1).apply {
|
||||
this.type = type
|
||||
|
||||
when (type) {
|
||||
@ -170,15 +168,19 @@ class VulcanApiTimetable(override val data: DataVulcan, val onSuccess: () -> Uni
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.id = buildId()
|
||||
}
|
||||
|
||||
if (type != Lesson.TYPE_NORMAL && lessonDate >= Date.getToday()) {
|
||||
val seen = profile.empty || lessonDate < Date.getToday()
|
||||
|
||||
if (type != Lesson.TYPE_NORMAL) {
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
lessonObject.id,
|
||||
seen,
|
||||
seen,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
|
@ -37,7 +37,10 @@ public abstract class LuckyNumberDao {
|
||||
|
||||
@Nullable
|
||||
@Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate >= :date ORDER BY luckyNumberDate DESC LIMIT 1")
|
||||
public abstract LuckyNumber getNearestFutureNow(int profileId, Date date);
|
||||
public abstract LuckyNumber getNearestFutureNow(int profileId, int date);
|
||||
|
||||
@Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate >= :date ORDER BY luckyNumberDate DESC LIMIT 1")
|
||||
public abstract LiveData<LuckyNumber> getNearestFuture(int profileId, int date);
|
||||
|
||||
@RawQuery(observedEntities = {LuckyNumber.class})
|
||||
abstract LiveData<List<LuckyNumberFull>> getAll(SupportSQLiteQuery query);
|
||||
|
@ -15,7 +15,7 @@ import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
Index(value = ["profileId", "type", "date"]),
|
||||
Index(value = ["profileId", "type", "oldDate"])
|
||||
])
|
||||
open class Lesson(val profileId: Int, @PrimaryKey val id: Long) {
|
||||
open class Lesson(val profileId: Int, @PrimaryKey var id: Long) {
|
||||
companion object {
|
||||
const val TYPE_NO_LESSONS = -1
|
||||
const val TYPE_NORMAL = 0
|
||||
@ -45,6 +45,22 @@ open class Lesson(val profileId: Int, @PrimaryKey val id: Long) {
|
||||
var oldTeamId: Long? = null
|
||||
var oldClassroom: String? = null
|
||||
|
||||
val displayDate: Date?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldDate
|
||||
return date ?: oldDate
|
||||
}
|
||||
|
||||
val displayStartTime: Time?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldStartTime
|
||||
return startTime ?: oldStartTime
|
||||
}
|
||||
|
||||
fun buildId(): Long = (displayDate?.combineWith(displayStartTime) ?: 0L) / 6L * 10L + (hashCode() and 0xFFFF)
|
||||
|
||||
override fun toString(): String {
|
||||
return "Lesson(profileId=$profileId, " +
|
||||
"id=$id, " +
|
||||
@ -66,6 +82,55 @@ open class Lesson(val profileId: Int, @PrimaryKey val id: Long) {
|
||||
"oldTeamId=$oldTeamId, " +
|
||||
"oldClassroom=$oldClassroom)"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Lesson) return false
|
||||
|
||||
if (profileId != other.profileId) return false
|
||||
if (id != other.id) return false
|
||||
if (type != other.type) return false
|
||||
if (date != other.date) return false
|
||||
if (lessonNumber != other.lessonNumber) return false
|
||||
if (startTime != other.startTime) return false
|
||||
if (endTime != other.endTime) return false
|
||||
if (subjectId != other.subjectId) return false
|
||||
if (teacherId != other.teacherId) return false
|
||||
if (teamId != other.teamId) return false
|
||||
if (classroom != other.classroom) return false
|
||||
if (oldDate != other.oldDate) return false
|
||||
if (oldLessonNumber != other.oldLessonNumber) return false
|
||||
if (oldStartTime != other.oldStartTime) return false
|
||||
if (oldEndTime != other.oldEndTime) return false
|
||||
if (oldSubjectId != other.oldSubjectId) return false
|
||||
if (oldTeacherId != other.oldTeacherId) return false
|
||||
if (oldTeamId != other.oldTeamId) return false
|
||||
if (oldClassroom != other.oldClassroom) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = profileId
|
||||
result = 31 * result + type
|
||||
result = 31 * result + (date?.hashCode() ?: 0)
|
||||
result = 31 * result + (lessonNumber ?: 0)
|
||||
result = 31 * result + (startTime?.hashCode() ?: 0)
|
||||
result = 31 * result + (endTime?.hashCode() ?: 0)
|
||||
result = 31 * result + (subjectId?.hashCode() ?: 0)
|
||||
result = 31 * result + (teacherId?.hashCode() ?: 0)
|
||||
result = 31 * result + (teamId?.hashCode() ?: 0)
|
||||
result = 31 * result + (classroom?.hashCode() ?: 0)
|
||||
result = 31 * result + (oldDate?.hashCode() ?: 0)
|
||||
result = 31 * result + (oldLessonNumber ?: 0)
|
||||
result = 31 * result + (oldStartTime?.hashCode() ?: 0)
|
||||
result = 31 * result + (oldEndTime?.hashCode() ?: 0)
|
||||
result = 31 * result + (oldSubjectId?.hashCode() ?: 0)
|
||||
result = 31 * result + (oldTeacherId?.hashCode() ?: 0)
|
||||
result = 31 * result + (oldTeamId?.hashCode() ?: 0)
|
||||
result = 31 * result + (oldClassroom?.hashCode() ?: 0)
|
||||
return result
|
||||
}
|
||||
}
|
||||
/*
|
||||
DROP TABLE lessons;
|
||||
|
@ -1,6 +1,7 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.timetable
|
||||
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import android.content.Context
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
|
||||
@ -11,24 +12,13 @@ class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
|
||||
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)
|
||||
@ -42,12 +32,14 @@ class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
|
||||
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)
|
||||
@ -68,12 +60,14 @@ class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
|
||||
return oldTeamId
|
||||
return teamId ?: oldTeamId
|
||||
}
|
||||
|
||||
val displaySubjectId: Long?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
return oldSubjectId
|
||||
return subjectId ?: oldSubjectId
|
||||
}
|
||||
|
||||
val displayTeacherId: Long?
|
||||
get() {
|
||||
if (type == TYPE_SHIFTED_SOURCE)
|
||||
@ -81,6 +75,33 @@ class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
|
||||
return teacherId ?: oldTeacherId
|
||||
}
|
||||
|
||||
fun getDisplayChangeType(context: Context): String {
|
||||
return context.getString(when (type) {
|
||||
TYPE_CHANGE -> R.string.lesson_change
|
||||
TYPE_CANCELLED -> R.string.lesson_cancelled
|
||||
TYPE_SHIFTED_TARGET, TYPE_SHIFTED_SOURCE -> R.string.lesson_shifted
|
||||
else -> R.string.lesson_timetable_change
|
||||
})
|
||||
}
|
||||
|
||||
val changeSubjectName: String
|
||||
get() {
|
||||
val first = when (type) {
|
||||
TYPE_CHANGE, TYPE_CANCELLED, TYPE_SHIFTED_SOURCE -> oldSubjectName
|
||||
else -> subjectName
|
||||
}
|
||||
|
||||
val second = when (type) {
|
||||
TYPE_CHANGE -> subjectName
|
||||
else -> null
|
||||
}
|
||||
|
||||
return when (second) {
|
||||
null -> first ?: ""
|
||||
else -> "$first -> $second"
|
||||
}
|
||||
}
|
||||
|
||||
// metadata
|
||||
var seen: Boolean = false
|
||||
var notified: Boolean = false
|
||||
|
@ -75,10 +75,23 @@ interface TimetableDao {
|
||||
""")
|
||||
fun getBetweenDatesNow(dateFrom: Date, dateTo: Date) : List<LessonFull>
|
||||
|
||||
@Query("""
|
||||
$QUERY
|
||||
WHERE (type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo)
|
||||
ORDER BY profileId, id, type
|
||||
""")
|
||||
fun getBetweenDates(dateFrom: Date, dateTo: Date) : LiveData<List<LessonFull>>
|
||||
|
||||
@Query("""
|
||||
$QUERY
|
||||
WHERE timetable.profileId = :profileId AND timetable.id = :lessonId
|
||||
ORDER BY id, type
|
||||
""")
|
||||
fun getByIdNow(profileId: Int, lessonId: Long) : LessonFull?
|
||||
|
||||
@Query("""
|
||||
$QUERY
|
||||
WHERE timetable.profileId = :profileId AND timetable.type NOT IN (${Lesson.TYPE_NORMAL}, ${Lesson.TYPE_NO_LESSONS}, ${Lesson.TYPE_SHIFTED_SOURCE}) AND metadata.notified = 0
|
||||
""")
|
||||
fun getNotNotifiedNow(profileId: Int): List<LessonFull>
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-24.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.dialogs.home
|
||||
|
||||
import android.text.InputType
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
|
||||
class StudentNumberDialog(
|
||||
val activity: AppCompatActivity,
|
||||
val profile: Profile,
|
||||
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||
) {
|
||||
companion object {
|
||||
private const val TAG = "StudentNumberDialog"
|
||||
}
|
||||
|
||||
private lateinit var dialog: AlertDialog
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
return@run
|
||||
onShowListener?.invoke(TAG)
|
||||
MaterialDialog.Builder(activity)
|
||||
.title(R.string.card_lucky_number_set_title)
|
||||
.content(R.string.card_lucky_number_set_text)
|
||||
.inputType(InputType.TYPE_CLASS_NUMBER)
|
||||
.input(null, if (profile.studentNumber == -1) "" else profile.studentNumber.toString()) { _: MaterialDialog?, input: CharSequence ->
|
||||
try {
|
||||
profile.studentNumber = input.toString().toInt()
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(activity, R.string.incorrect_format, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
.dismissListener {
|
||||
onDismissListener?.invoke(TAG)
|
||||
}.show()
|
||||
}}
|
||||
}
|
@ -94,12 +94,20 @@ class SyncViewListDialog(
|
||||
listOfNotNull(*it.toTypedArray())
|
||||
}
|
||||
|
||||
if (selectedViewIds.isNotEmpty()) {
|
||||
activity.swipeRefreshLayout.isRefreshing = true
|
||||
EdziennikTask.syncProfile(
|
||||
App.profileId,
|
||||
selectedViewIds
|
||||
).enqueue(activity)
|
||||
}
|
||||
}
|
||||
.setNeutralButton(R.string.sync_feature_all) { _, _ ->
|
||||
dialog.dismiss()
|
||||
|
||||
activity.swipeRefreshLayout.isRefreshing = true
|
||||
EdziennikTask.syncProfile(App.profileId).enqueue(activity)
|
||||
}
|
||||
.setNegativeButton(R.string.cancel) { _, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ class LessonDetailsDialog(
|
||||
dialog.dismiss()
|
||||
val dateStr = otherLessonDate?.stringY_m_d ?: return@setOnClickListener
|
||||
val intent = Intent(TimetableFragment.ACTION_SCROLL_TO_DATE).apply {
|
||||
putExtra("date", dateStr)
|
||||
putExtra("timetableDate", dateStr)
|
||||
}
|
||||
activity.sendBroadcast(intent)
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-23.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.home
|
||||
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragmentV2.Companion.swapCards
|
||||
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
|
||||
|
||||
class CardItemTouchHelperCallback(private val cardAdapter: HomeCardAdapter, private val refreshLayout: SwipeRefreshLayoutNoIndicator?) : ItemTouchHelper.Callback() {
|
||||
companion object {
|
||||
private const val TAG = "CardItemTouchHelperCallback"
|
||||
private const val DRAG_FLAGS = ItemTouchHelper.UP or ItemTouchHelper.DOWN
|
||||
private const val SWIPE_FLAGS = 0
|
||||
}
|
||||
|
||||
private var dragCardView: MaterialCardView? = null
|
||||
|
||||
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
|
||||
return makeMovementFlags(DRAG_FLAGS, SWIPE_FLAGS)
|
||||
}
|
||||
|
||||
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
||||
val fromPosition = viewHolder.adapterPosition
|
||||
val toPosition = target.adapterPosition
|
||||
|
||||
swapCards(fromPosition, toPosition, cardAdapter)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit
|
||||
|
||||
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
|
||||
super.onSelectedChanged(viewHolder, actionState)
|
||||
|
||||
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG && viewHolder != null) {
|
||||
dragCardView = viewHolder.itemView as MaterialCardView
|
||||
dragCardView?.isDragged = true
|
||||
refreshLayout?.isEnabled = false
|
||||
} else if (actionState == ItemTouchHelper.ACTION_STATE_IDLE && dragCardView != null) {
|
||||
refreshLayout?.isEnabled = true
|
||||
dragCardView?.isDragged = false
|
||||
dragCardView = null
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-23.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.home
|
||||
|
||||
interface HomeCard {
|
||||
fun bind(position: Int, holder: HomeCardAdapter.ViewHolder)
|
||||
fun unbind(position: Int, holder: HomeCardAdapter.ViewHolder)
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-23.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.home
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
|
||||
class HomeCardAdapter(var items: MutableList<HomeCard>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
companion object {
|
||||
private const val TAG = "HomeCardAdapter"
|
||||
}
|
||||
|
||||
var itemTouchHelper: ItemTouchHelper? = null
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
(holder as ViewHolder).bind(itemTouchHelper)
|
||||
items[position].bind(position, holder)
|
||||
}
|
||||
|
||||
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
|
||||
items.getOrNull(holder.adapterPosition)?.unbind(holder.adapterPosition, holder as ViewHolder)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return ViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(R.layout.card_home, parent, false) as MaterialCardView
|
||||
)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
|
||||
class ViewHolder(val root: MaterialCardView) : RecyclerView.ViewHolder(root) {
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
fun bind(itemTouchHelper: ItemTouchHelper?) {
|
||||
/*root.setOnTouchListener { _: View?, event: MotionEvent ->
|
||||
if (event.action == MotionEvent.ACTION_DOWN) {
|
||||
itemTouchHelper?.startDrag(this)
|
||||
return@setOnTouchListener true
|
||||
}
|
||||
false
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-23.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.home
|
||||
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.plusAssign
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.startCoroutineTimer
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class HomeDummyCard(val id: Int) : HomeCard, CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "HomeDummyCard"
|
||||
}
|
||||
|
||||
private lateinit var app: App
|
||||
private lateinit var activity: MainActivity
|
||||
|
||||
private var job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
var timer: Job? = null
|
||||
var time = 0
|
||||
|
||||
override fun bind(position: Int, holder: HomeCardAdapter.ViewHolder) { launch {
|
||||
holder.root.removeAllViews()
|
||||
//holder.setIsRecyclable(false)
|
||||
|
||||
val text = TextView(holder.root.context).apply {
|
||||
text = "This is a card #$id"
|
||||
}
|
||||
holder.root += text
|
||||
|
||||
timer = startCoroutineTimer(repeatMillis = 1000) {
|
||||
time++
|
||||
text.text = "Coroutine timer at #$id! $time seconds"
|
||||
}
|
||||
|
||||
/*val button = MaterialButton(holder.root.context).apply {
|
||||
setText("Cancel")
|
||||
onClick {
|
||||
timer.cancel()
|
||||
}
|
||||
}
|
||||
holder.root += button*/
|
||||
}}
|
||||
|
||||
override fun unbind(position: Int, holder: HomeCardAdapter.ViewHolder) {
|
||||
timer?.cancel()
|
||||
timer = null
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-23.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.home
|
||||
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.View.OnClickListener
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.AccessibilityDelegateCompat
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial.Icon
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentHomeV2Binding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.home.StudentNumberDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.cards.HomeLuckyNumberCard
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.cards.HomeTimetableCard
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class HomeFragmentV2 : Fragment(), CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "HomeFragment"
|
||||
|
||||
fun swapCards(fromPosition: Int, toPosition: Int, cardAdapter: HomeCardAdapter) {
|
||||
val fromCard = cardAdapter.items[fromPosition]
|
||||
cardAdapter.items[fromPosition] = cardAdapter.items[toPosition]
|
||||
cardAdapter.items[toPosition] = fromCard
|
||||
cardAdapter.notifyItemMoved(fromPosition, toPosition)
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var app: App
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var b: FragmentHomeV2Binding
|
||||
|
||||
private lateinit var job: Job
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
activity = (getActivity() as MainActivity?) ?: return null
|
||||
context ?: return null
|
||||
app = activity.application as App
|
||||
context!!.theme.applyStyle(Themes.appTheme, true)
|
||||
b = FragmentHomeV2Binding.inflate(inflater)
|
||||
b.refreshLayout.setParent(activity.swipeRefreshLayout)
|
||||
job = Job()
|
||||
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
|
||||
|
||||
activity.bottomSheet.prependItems(
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_set_student_number)
|
||||
.withIcon(SzkolnyFont.Icon.szf_clipboard_list_outline)
|
||||
.withOnClickListener(OnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
StudentNumberDialog(activity, app.profile) {
|
||||
app.profileSaveAsync()
|
||||
}
|
||||
}),
|
||||
BottomSheetSeparatorItem(true),
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_everything_as_read)
|
||||
.withIcon(Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(OnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
AsyncTask.execute { app.db.metadataDao().setAllSeen(App.profileId, true) }
|
||||
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
|
||||
})
|
||||
)
|
||||
|
||||
val items = mutableListOf<HomeCard>(
|
||||
HomeLuckyNumberCard(0, app, activity, this, app.profile),
|
||||
HomeTimetableCard(1, app, activity, this, app.profile)
|
||||
)
|
||||
|
||||
val adapter = HomeCardAdapter(items)
|
||||
val itemTouchHelper = ItemTouchHelper(CardItemTouchHelperCallback(adapter, b.refreshLayout))
|
||||
adapter.itemTouchHelper = itemTouchHelper
|
||||
b.list.layoutManager = LinearLayoutManager(activity)
|
||||
b.list.adapter = adapter
|
||||
b.list.setAccessibilityDelegateCompat(object : RecyclerViewAccessibilityDelegate(b.list) {
|
||||
override fun getItemDelegate(): AccessibilityDelegateCompat {
|
||||
return object : ItemDelegate(this) {
|
||||
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
|
||||
super.onInitializeAccessibilityNodeInfo(host, info)
|
||||
val position: Int = b.list.getChildLayoutPosition(host)
|
||||
if (position != 0) {
|
||||
info.addAction(AccessibilityActionCompat(
|
||||
R.id.move_card_up_action,
|
||||
host.resources.getString(R.string.card_action_move_up)
|
||||
))
|
||||
}
|
||||
if (position != adapter.itemCount - 1) {
|
||||
info.addAction(AccessibilityActionCompat(
|
||||
R.id.move_card_down_action,
|
||||
host.resources.getString(R.string.card_action_move_down)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
override fun performAccessibilityAction(host: View, action: Int, args: Bundle): Boolean {
|
||||
val fromPosition: Int = b.list.getChildLayoutPosition(host)
|
||||
if (action == R.id.move_card_down_action) {
|
||||
swapCards(fromPosition, fromPosition + 1, adapter)
|
||||
return true
|
||||
} else if (action == R.id.move_card_up_action) {
|
||||
swapCards(fromPosition, fromPosition - 1, adapter)
|
||||
return true
|
||||
}
|
||||
return super.performAccessibilityAction(host, action, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
itemTouchHelper.attachToRecyclerView(b.list)
|
||||
}
|
||||
}
|
@ -33,17 +33,17 @@ class HomeTimetableCard(
|
||||
private val layoutInflater: LayoutInflater,
|
||||
private val insertPoint: ViewGroup
|
||||
) {
|
||||
companion object {
|
||||
private const val TAG = "HomeTimetableCard"
|
||||
const val TIME_TILL = 0
|
||||
const val TIME_LEFT = 1
|
||||
}
|
||||
|
||||
private lateinit var timetableTimer: Timer
|
||||
private lateinit var b: CardTimetableBinding
|
||||
|
||||
private var bellSyncTime: Time? = null
|
||||
|
||||
private companion object {
|
||||
const val TIME_TILL = 0
|
||||
const val TIME_LEFT = 1
|
||||
}
|
||||
|
||||
private var counterType = TIME_TILL
|
||||
private val counterTarget = Time(0, 0, 0)
|
||||
|
||||
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-24.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.home.cards
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.plusAssign
|
||||
import androidx.core.view.setMargins
|
||||
import androidx.lifecycle.Observer
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.databinding.CardHomeLuckyNumberBinding
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.home.StudentNumberDialog
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragmentV2
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class HomeLuckyNumberCard(
|
||||
val id: Int,
|
||||
val app: App,
|
||||
val activity: MainActivity,
|
||||
val fragment: HomeFragmentV2,
|
||||
val profile: Profile
|
||||
) : HomeCard, CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "HomeLuckyNumberCard"
|
||||
}
|
||||
|
||||
private var job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
override fun bind(position: Int, holder: HomeCardAdapter.ViewHolder) { launch {
|
||||
holder.root.removeAllViews()
|
||||
val b = CardHomeLuckyNumberBinding.inflate(LayoutInflater.from(holder.root.context))
|
||||
b.root.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT).apply {
|
||||
setMargins(8.dp)
|
||||
}
|
||||
holder.root += b.root
|
||||
|
||||
val today = Date.getToday()
|
||||
val todayValue = today.value
|
||||
|
||||
val subTextRes = if (profile.studentNumber == -1)
|
||||
R.string.home_lucky_number_details_click_to_set
|
||||
else
|
||||
R.string.home_lucky_number_details
|
||||
b.subText.setText(subTextRes, profile.name ?: "", profile.studentNumber)
|
||||
|
||||
app.db.luckyNumberDao().getNearestFuture(App.profileId, todayValue).observe(fragment, Observer { luckyNumber ->
|
||||
val isYours = luckyNumber?.number == profile.studentNumber
|
||||
val titleRes = when {
|
||||
luckyNumber == null -> R.string.home_lucky_number_no_info
|
||||
luckyNumber.number == -1 -> R.string.home_lucky_number_no_number
|
||||
else -> when (isYours) {
|
||||
true -> when (luckyNumber.date.value) {
|
||||
todayValue -> R.string.home_lucky_number_yours_today
|
||||
todayValue + 1 -> R.string.home_lucky_number_yours_tomorrow
|
||||
else -> R.string.home_lucky_number_yours_later
|
||||
}
|
||||
false -> when (luckyNumber.date.value) {
|
||||
todayValue -> R.string.home_lucky_number_today
|
||||
todayValue + 1 -> R.string.home_lucky_number_tomorrow
|
||||
else -> R.string.home_lucky_number_later
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b.title.setText(
|
||||
titleRes,
|
||||
luckyNumber?.number ?: 0,
|
||||
luckyNumber?.date?.formattedString ?: ""
|
||||
)
|
||||
|
||||
val drawableRes = when {
|
||||
luckyNumber == null || luckyNumber.number == -1 -> R.drawable.emoji_sad
|
||||
isYours -> R.drawable.emoji_glasses
|
||||
!isYours -> R.drawable.emoji_smiling
|
||||
else -> R.drawable.emoji_no_face
|
||||
}
|
||||
b.image.setImageResource(drawableRes)
|
||||
})
|
||||
|
||||
holder.root.onClick {
|
||||
StudentNumberDialog(activity, profile, onDismissListener = {
|
||||
app.profileSaveAsync(profile)
|
||||
val newSubTextRes = if (profile.studentNumber == -1)
|
||||
R.string.home_lucky_number_details_click_to_set
|
||||
else
|
||||
R.string.home_lucky_number_details
|
||||
b.subText.setText(newSubTextRes, profile.name ?: "", profile.studentNumber)
|
||||
})
|
||||
}
|
||||
}}
|
||||
|
||||
override fun unbind(position: Int, holder: HomeCardAdapter.ViewHolder) = Unit
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-24.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.home.cards
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.plusAssign
|
||||
import androidx.core.view.setMargins
|
||||
import androidx.lifecycle.Observer
|
||||
import kotlinx.coroutines.*
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.CardHomeTimetableBinding
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragmentV2
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class HomeTimetableCard(
|
||||
val id: Int,
|
||||
val app: App,
|
||||
val activity: MainActivity,
|
||||
val fragment: HomeFragmentV2,
|
||||
val profile: Profile
|
||||
) : HomeCard, CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "HomeTimetableCard"
|
||||
}
|
||||
|
||||
private var job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
private lateinit var b: CardHomeTimetableBinding
|
||||
|
||||
private val today = Date.getToday()
|
||||
private val searchEnd = today.clone().stepForward(0, 0, 7)
|
||||
|
||||
private var allLessons = listOf<LessonFull>()
|
||||
private var lessons = listOf<LessonFull>()
|
||||
private var events = listOf<Event>()
|
||||
|
||||
override fun bind(position: Int, holder: HomeCardAdapter.ViewHolder) {
|
||||
holder.root.removeAllViews()
|
||||
b = CardHomeTimetableBinding.inflate(LayoutInflater.from(holder.root.context))
|
||||
b.root.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
|
||||
setMargins(8.dp)
|
||||
}
|
||||
holder.root += b.root
|
||||
|
||||
// get all lessons within the search bounds
|
||||
app.db.timetableDao().getBetweenDates(today, searchEnd).observe(fragment, Observer {
|
||||
allLessons = it
|
||||
update()
|
||||
})
|
||||
}
|
||||
|
||||
private fun update() { launch {
|
||||
val deferred = async(Dispatchers.Default) {
|
||||
// get current bell-sync params
|
||||
var bellSyncDiffMillis: Long = 0
|
||||
if (app.appConfig.bellSyncDiff != null) {
|
||||
bellSyncDiffMillis = (app.appConfig.bellSyncDiff.hour * 60 * 60 * 1000 + app.appConfig.bellSyncDiff.minute * 60 * 1000 + app.appConfig.bellSyncDiff.second * 1000).toLong()
|
||||
bellSyncDiffMillis *= app.appConfig.bellSyncMultiplier.toLong()
|
||||
bellSyncDiffMillis *= -1
|
||||
}
|
||||
// get the current bell-synced time
|
||||
val now = Time.fromMillis(Time.getNow().inMillis + bellSyncDiffMillis)
|
||||
|
||||
// search for lessons to display
|
||||
val timetableDate = Date.getToday()
|
||||
var checkedDays = 0
|
||||
lessons = allLessons.filter { it.profileId == profile.id && it.displayDate == timetableDate && it.displayEndTime > now && it.type != Lesson.TYPE_NO_LESSONS }
|
||||
while ((lessons.isEmpty() || lessons.none {
|
||||
it.displayDate != today || (it.displayDate == today && it.displayEndTime != null && it.displayEndTime!! >= now)
|
||||
}) && checkedDays < 7) {
|
||||
timetableDate.stepForward(0, 0, 1)
|
||||
lessons = allLessons.filter { it.profileId == profile.id && it.displayDate == timetableDate && it.type != Lesson.TYPE_NO_LESSONS }
|
||||
checkedDays++
|
||||
}
|
||||
}
|
||||
deferred.await()
|
||||
|
||||
val text = StringBuilder()
|
||||
for (lesson in lessons) {
|
||||
text += lesson.displayStartTime?.stringHM+" "+lesson.displaySubjectName+"\n"
|
||||
}
|
||||
b.text.text = text.toString()
|
||||
}}
|
||||
|
||||
override fun unbind(position: Int, holder: HomeCardAdapter.ViewHolder) = Unit
|
||||
}
|
@ -31,6 +31,9 @@ import org.greenrobot.eventbus.ThreadMode
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.AttachmentGetEvent
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.AttachmentGetEvent.Companion.TYPE_FINISHED
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.AttachmentGetEvent.Companion.TYPE_PROGRESS
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.MessageGetEvent
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT
|
||||
@ -225,18 +228,17 @@ class MessageFragment : Fragment(), CoroutineScope {
|
||||
attachmentChip.ellipsize = TextUtils.TruncateAt.MIDDLE
|
||||
|
||||
// create an icon for the attachment
|
||||
var icon: IIcon = CommunityMaterial.Icon.cmd_file_outline
|
||||
when (Utils.getExtensionFromFileName(name)) {
|
||||
"txt" -> icon = CommunityMaterial.Icon.cmd_file_document_outline
|
||||
"doc", "docx", "odt", "rtf" -> icon = SzkolnyFont.Icon.szf_file_word_outline
|
||||
"xls", "xlsx", "ods" -> icon = SzkolnyFont.Icon.szf_file_excel_outline
|
||||
"ppt", "pptx", "odp" -> icon = SzkolnyFont.Icon.szf_file_powerpoint_outline
|
||||
"pdf" -> icon = SzkolnyFont.Icon.szf_file_pdf_outline
|
||||
"mp3", "wav", "aac" -> icon = SzkolnyFont.Icon.szf_file_music_outline
|
||||
"mp4", "avi", "3gp", "mkv", "flv" -> icon = SzkolnyFont.Icon.szf_file_video_outline
|
||||
"jpg", "jpeg", "png", "bmp", "gif" -> icon = SzkolnyFont.Icon.szf_file_image_outline
|
||||
"zip", "rar", "tar", "7z" -> icon = SzkolnyFont.Icon.szf_zip_box_outline
|
||||
"html", "cpp", "c", "h", "css", "java", "py" -> icon = SzkolnyFont.Icon.szf_file_code_outline
|
||||
val icon: IIcon = when (Utils.getExtensionFromFileName(name)) {
|
||||
"doc", "docx", "odt", "rtf" -> SzkolnyFont.Icon.szf_file_word_outline
|
||||
"xls", "xlsx", "ods" -> SzkolnyFont.Icon.szf_file_excel_outline
|
||||
"ppt", "pptx", "odp" -> SzkolnyFont.Icon.szf_file_powerpoint_outline
|
||||
"pdf" -> SzkolnyFont.Icon.szf_file_pdf_outline
|
||||
"mp3", "wav", "aac" -> SzkolnyFont.Icon.szf_file_music_outline
|
||||
"mp4", "avi", "3gp", "mkv", "flv" -> SzkolnyFont.Icon.szf_file_video_outline
|
||||
"jpg", "jpeg", "png", "bmp", "gif" -> SzkolnyFont.Icon.szf_file_image_outline
|
||||
"zip", "rar", "tar", "7z" -> SzkolnyFont.Icon.szf_zip_box_outline
|
||||
"html", "cpp", "c", "h", "css", "java", "py" -> SzkolnyFont.Icon.szf_file_code_outline
|
||||
else -> CommunityMaterial.Icon.cmd_file_document_outline
|
||||
}
|
||||
attachmentChip.chipIcon = IconicsDrawable(activity).color(IconicsColor.colorRes(R.color.colorPrimary)).icon(icon).size(IconicsSize.dp(26))
|
||||
attachmentChip.closeIcon = IconicsDrawable(activity).icon(CommunityMaterial.Icon.cmd_check).size(IconicsSize.dp(18)).color(IconicsColor.colorInt(Utils.getAttr(activity, android.R.attr.textColorPrimary)))
|
||||
@ -245,7 +247,7 @@ class MessageFragment : Fragment(), CoroutineScope {
|
||||
attachmentChip.tag = index
|
||||
attachmentChip.setOnClickListener { v ->
|
||||
if (v.tag is Int) {
|
||||
// TODO downloadAttachment(v.tag as Int)
|
||||
downloadAttachment(v.tag as Int)
|
||||
}
|
||||
}
|
||||
attachmentLayout.addView(attachmentChip)
|
||||
@ -269,6 +271,60 @@ class MessageFragment : Fragment(), CoroutineScope {
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadAttachment(index: Int) {
|
||||
val attachment = attachmentList[index]
|
||||
|
||||
if (attachment.downloaded != null) {
|
||||
Utils.openFile(activity, File(attachment.downloaded))
|
||||
return
|
||||
}
|
||||
|
||||
attachment.chip.isEnabled = false
|
||||
attachment.chip.setTextColor(Themes.getSecondaryTextColor(activity))
|
||||
attachment.progressBar.visibility = View.VISIBLE
|
||||
|
||||
EdziennikTask.attachmentGet(
|
||||
App.profileId,
|
||||
attachment.messageId,
|
||||
attachment.attachmentId,
|
||||
attachment.attachmentName
|
||||
).enqueue(activity)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onAttachmentGetEvent(event: AttachmentGetEvent) {
|
||||
attachmentList.firstOrNull { it.profileId == event.profileId
|
||||
&& it.messageId == event.messageId
|
||||
&& it.attachmentId == event.attachmentId }?.let { attachment ->
|
||||
|
||||
when (event.eventType) {
|
||||
TYPE_FINISHED -> {
|
||||
// save the downloaded file name
|
||||
attachment.downloaded = event.fileName
|
||||
|
||||
// set the correct name (and size)
|
||||
if (attachment.attachmentSize == -1L)
|
||||
attachment.chip.text = getString(R.string.messages_attachment_no_size_format, attachment.attachmentName)
|
||||
else
|
||||
attachment.chip.text = getString(R.string.messages_attachment_format, attachment.attachmentName, readableFileSize(attachment.attachmentSize))
|
||||
|
||||
// hide the progress bar and show a tick icon
|
||||
attachment.progressBar.visibility = View.GONE
|
||||
attachment.chip.isEnabled = true
|
||||
attachment.chip.setTextColor(Themes.getPrimaryTextColor(activity))
|
||||
attachment.chip.isCloseIconVisible = true
|
||||
|
||||
// open the file
|
||||
Utils.openFile(activity, File(attachment.downloaded))
|
||||
}
|
||||
|
||||
TYPE_PROGRESS -> {
|
||||
attachment.chip.text = getString(R.string.messages_attachment_downloading_format, attachment.attachmentName, event.bytesWritten.toFloat() / 1000000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAttachment(attachment: Attachment) {
|
||||
val storageDir = Environment.getExternalStoragePublicDirectory("Szkolny.eu")
|
||||
storageDir.mkdirs()
|
||||
@ -286,7 +342,6 @@ class MessageFragment : Fragment(), CoroutineScope {
|
||||
e.printStackTrace()
|
||||
//app.apiEdziennik.guiReportException(activity, 355, e)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ class TimetableFragment : Fragment(), CoroutineScope {
|
||||
override fun onReceive(context: Context, i: Intent) {
|
||||
if (!isAdded)
|
||||
return
|
||||
val dateStr = i.extras?.getString("date", null) ?: return
|
||||
val dateStr = i.extras?.getString("timetableDate", null) ?: return
|
||||
val date = Date.fromY_m_d(dateStr)
|
||||
b.viewPager.setCurrentItem(items.indexOf(date), true)
|
||||
}
|
||||
@ -155,8 +155,10 @@ class TimetableFragment : Fragment(), CoroutineScope {
|
||||
}
|
||||
})
|
||||
|
||||
val selectedDate = arguments?.getString("timetableDate", "")?.let { if (it.isBlank()) null else Date.fromY_m_d(it) }
|
||||
|
||||
b.tabLayout.setUpWithViewPager(b.viewPager)
|
||||
b.tabLayout.setCurrentItem(items.indexOfFirst { it.value == today }, false)
|
||||
b.tabLayout.setCurrentItem(items.indexOfFirst { it.value == selectedDate?.value ?: today }, false)
|
||||
|
||||
activity.navView.bottomSheet.prependItems(
|
||||
BottomSheetPrimaryItem(true)
|
||||
|
@ -272,4 +272,12 @@ public class Date implements Comparable<Date> {
|
||||
", day=" + day +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = year;
|
||||
result = 31 * result + month;
|
||||
result = 31 * result + day;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ public class Time implements Comparable<Time> {
|
||||
|
||||
public String getStringValue()
|
||||
{
|
||||
return (hour < 10 ? "0" : "")+Integer.toString(hour)+(minute < 10 ? "0" : "")+Integer.toString(minute)+(second < 10 ? "0" : "")+Integer.toString(second);
|
||||
return (hour < 10 ? "0" : "")+ hour +(minute < 10 ? "0" : "")+ minute +(second < 10 ? "0" : "")+ second;
|
||||
}
|
||||
|
||||
public String getStringHM()
|
||||
@ -129,18 +129,18 @@ public class Time implements Comparable<Time> {
|
||||
if (hour < 0) {
|
||||
return "";
|
||||
}
|
||||
return Integer.toString(hour)+":"+(minute < 10 ? "0" : "")+Integer.toString(minute);
|
||||
return hour +":"+(minute < 10 ? "0" : "")+ minute;
|
||||
}
|
||||
public String getStringH_M()
|
||||
{
|
||||
if (hour < 0) {
|
||||
return "";
|
||||
}
|
||||
return Integer.toString(hour)+"-"+(minute < 10 ? "0" : "")+Integer.toString(minute);
|
||||
return hour +"-"+(minute < 10 ? "0" : "")+ minute;
|
||||
}
|
||||
public String getStringHMS()
|
||||
{
|
||||
return Integer.toString(hour)+":"+(minute < 10 ? "0" : "")+Integer.toString(minute)+":"+(second < 10 ? "0" : "")+Integer.toString(second);
|
||||
return hour +":"+(minute < 10 ? "0" : "")+ minute +":"+(second < 10 ? "0" : "")+ second;
|
||||
}
|
||||
|
||||
public static Time getNow()
|
||||
@ -194,4 +194,12 @@ public class Time implements Comparable<Time> {
|
||||
", second=" + second +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = hour;
|
||||
result = 31 * result + minute;
|
||||
result = 31 * result + second;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
28
app/src/main/res/drawable/emoji_glasses.xml
Normal file
28
app/src/main/res/drawable/emoji_glasses.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-24.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:pathData="M63.79,8.64C1.48,8.64 0,78.5 0,92.33c0,13.83 28.56,25.03 63.79,25.03c35.24,0 63.79,-11.21 63.79,-25.03C127.58,78.5 126.11,8.64 63.79,8.64z"
|
||||
android:fillColor="#FCC21B"/>
|
||||
<path
|
||||
android:pathData="M63.91,104.82c-3.43,0 -6.87,-0.43 -10.25,-1.31c-1.6,-0.42 -2.56,-2.06 -2.15,-3.66c0.42,-1.6 2.06,-2.56 3.66,-2.14c11.65,3.04 24.21,-0.21 32.78,-8.48c1.19,-1.15 3.09,-1.12 4.24,0.08c1.15,1.19 1.12,3.09 -0.08,4.24C84.54,100.85 74.32,104.82 63.91,104.82z"
|
||||
android:fillColor="#2F2F2F"/>
|
||||
<path
|
||||
android:pathData="M55.53,67.26c-0.01,0.01 -0.02,0.02 -0.02,0.02C55.51,67.27 55.52,67.26 55.53,67.26z"
|
||||
android:fillColor="#2F2F2F"/>
|
||||
<path
|
||||
android:pathData="M98.21,41.34c-13.36,0 -15.15,2.03 -21.4,3.36C70.56,46.02 64,46.02 64,46.02s-6.56,0 -12.81,-1.33c-6.25,-1.33 -8.05,-3.36 -21.4,-3.36c-13.36,0 -29.37,2.89 -29.37,2.89v8.51c0,0 3.59,0.47 3.91,3.75c0.16,1.33 -3.12,28.35 23.51,28.35c18.9,0 26.87,-11.33 29.45,-20.54c1.17,-4.37 2.19,-9.37 6.72,-9.37c4.53,0 5.55,5 6.72,9.37c2.58,9.22 10.54,20.54 29.45,20.54c26.63,0 23.35,-27.03 23.51,-28.35c0.31,-3.28 3.91,-3.75 3.91,-3.75v-8.51C127.58,44.23 111.57,41.34 98.21,41.34z"
|
||||
android:fillColor="#2F2F2F"/>
|
||||
<path
|
||||
android:pathData="M95.94,45.05c-6.62,0.23 -11.65,1.31 -11.65,1.31c-9.84,2.06 -10.55,8.14 -9.93,12.97c0.8,6.07 3.29,13.75 10.04,18.49c0.53,0.38 1.76,0.79 2.35,-0.77c0,0 -0.02,0.11 0,0c2.22,-10.48 5.52,-20.14 10.78,-29.89l0,0C98.14,45.37 96.71,45.02 95.94,45.05z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M31.06,45.02c-4.27,-0.09 -9.11,0.19 -13.65,1.34c-5.1,1.28 -7.07,3.85 -7.6,9.39c-0.53,5.43 -1.13,19.27 8.73,24.46c0.57,0.3 1.83,0.5 2.44,-0.91l0,0C24,66.21 25.61,60.13 32.54,47.22l0,0C33.11,45.49 31.83,45.03 31.06,45.02z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
22
app/src/main/res/drawable/emoji_neutral.xml
Normal file
22
app/src/main/res/drawable/emoji_neutral.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-24.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:pathData="M64,9.56c-62.41,0 -63.88,69.96 -63.88,83.8c0,13.86 28.59,25.08 63.88,25.08c35.28,0 63.88,-11.22 63.88,-25.08C127.88,79.52 126.4,9.56 64,9.56z"
|
||||
android:fillColor="#FCC21B"/>
|
||||
<path
|
||||
android:pathData="M42.21,62.3c-4.49,0.04 -8.17,-4.27 -8.22,-9.62c-0.05,-5.37 3.55,-9.75 8.04,-9.79c4.48,-0.04 8.17,4.27 8.22,9.64C50.3,57.88 46.7,62.25 42.21,62.3z"
|
||||
android:fillColor="#2F2F2F"/>
|
||||
<path
|
||||
android:pathData="M86.32,62.3c4.48,-0.01 8.11,-4.36 8.1,-9.71c-0.01,-5.37 -3.66,-9.7 -8.14,-9.69c-4.49,0.01 -8.13,4.36 -8.12,9.73C78.18,57.98 81.83,62.31 86.32,62.3z"
|
||||
android:fillColor="#2F2F2F"/>
|
||||
<path
|
||||
android:pathData="M89.69,84.75H38.31c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3h51.39c1.66,0 3,1.34 3,3S91.35,84.75 89.69,84.75z"
|
||||
android:fillColor="#2F2F2F"/>
|
||||
</vector>
|
19
app/src/main/res/drawable/emoji_no_face.xml
Normal file
19
app/src/main/res/drawable/emoji_no_face.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-24.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:pathData="M64,9.56c-62.41,0 -63.88,69.96 -63.88,83.8c0,13.86 28.59,25.08 63.88,25.08c35.28,0 63.88,-11.22 63.88,-25.08C127.88,79.52 126.4,9.56 64,9.56z"
|
||||
android:fillColor="#FCC21B"/>
|
||||
<path
|
||||
android:pathData="M42.21,65.3c-4.49,0.04 -8.17,-4.27 -8.22,-9.62c-0.05,-5.37 3.55,-9.75 8.04,-9.79c4.48,-0.04 8.17,4.27 8.22,9.64C50.3,60.88 46.7,65.25 42.21,65.3z"
|
||||
android:fillColor="#2F2F2F"/>
|
||||
<path
|
||||
android:pathData="M86.32,65.3c4.48,-0.01 8.11,-4.36 8.1,-9.71c-0.01,-5.37 -3.66,-9.7 -8.14,-9.69c-4.49,0.01 -8.13,4.36 -8.12,9.73C78.18,60.98 81.83,65.31 86.32,65.3z"
|
||||
android:fillColor="#2F2F2F"/>
|
||||
</vector>
|
22
app/src/main/res/drawable/emoji_sad.xml
Normal file
22
app/src/main/res/drawable/emoji_sad.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-24.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:pathData="M64,9.62c-62.41,0 -63.88,69.96 -63.88,83.8c0,13.86 28.59,25.08 63.88,25.08c35.28,0 63.88,-11.22 63.88,-25.08C127.88,79.58 126.4,9.62 64,9.62z"
|
||||
android:fillColor="#FCC21B"/>
|
||||
<path
|
||||
android:pathData="M41.99,65.5c-4.49,0.04 -8.17,-4.27 -8.22,-9.62c-0.05,-5.37 3.55,-9.75 8.04,-9.79c4.48,-0.04 8.17,4.27 8.22,9.64C50.08,61.09 46.47,65.46 41.99,65.5z"
|
||||
android:fillColor="#2F2F2F"/>
|
||||
<path
|
||||
android:pathData="M86.1,65.5c4.48,-0.01 8.11,-4.36 8.1,-9.71c-0.01,-5.37 -3.66,-9.7 -8.14,-9.69c-4.49,0.01 -8.13,4.36 -8.12,9.73C77.95,61.18 81.61,65.51 86.1,65.5z"
|
||||
android:fillColor="#2F2F2F"/>
|
||||
<path
|
||||
android:pathData="M43.08,97.67c1.99,1.34 4.5,0.46 6.71,0c6.18,-1.28 11.6,-1.33 14.2,-1.33s8.03,0.05 14.2,1.33c2.21,0.46 4.72,1.34 6.71,0c2.52,-1.71 0.66,-7.83 -3.31,-11.97c-2.4,-2.5 -8.13,-7.35 -17.61,-7.35c-9.48,0 -15.2,4.85 -17.61,7.35C42.42,89.85 40.56,95.97 43.08,97.67z"
|
||||
android:fillColor="#ED6C30"/>
|
||||
</vector>
|
22
app/src/main/res/drawable/emoji_smiling.xml
Normal file
22
app/src/main/res/drawable/emoji_smiling.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-24.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:pathData="M127.94,93.75c0,14.02 -28.61,25.39 -63.93,25.39S0.06,107.77 0.06,93.75c0,-14.03 1.48,-84.89 63.95,-84.89C126.47,8.86 127.94,79.72 127.94,93.75"
|
||||
android:fillColor="#FCC21B"/>
|
||||
<path
|
||||
android:pathData="M48.14,57.33c0,5.47 -3.66,9.9 -8.19,9.9c-4.53,0 -8.21,-4.43 -8.21,-9.9c0,-5.48 3.68,-9.91 8.21,-9.91C44.48,47.42 48.14,51.85 48.14,57.33"
|
||||
android:fillColor="#2F2F2F"/>
|
||||
<path
|
||||
android:pathData="M80.14,57.33c0,5.47 3.66,9.9 8.21,9.9c4.53,0 8.21,-4.43 8.21,-9.9c0,-5.48 -3.68,-9.91 -8.21,-9.91C83.8,47.42 80.14,51.85 80.14,57.33"
|
||||
android:fillColor="#2F2F2F"/>
|
||||
<path
|
||||
android:pathData="M66.8,93.74c-0.72,0 -1.48,-0.03 -2.25,-0.07C46.53,92.59 39.69,82.82 39.41,82.4c-1,-1.48 -0.62,-3.48 0.85,-4.48c1.46,-1 3.45,-0.62 4.46,0.83c0.25,0.37 5.61,7.61 20.22,8.47c14.57,0.84 20.91,-8.67 20.99,-8.77c0.95,-1.49 2.97,-1.92 4.45,-0.96c1.49,0.97 1.93,2.96 0.95,4.45C91.01,82.46 83.52,93.74 66.8,93.74z"
|
||||
android:fillColor="#2F2F2F"/>
|
||||
</vector>
|
14
app/src/main/res/layout/card_home.xml
Normal file
14
app/src/main/res/layout/card_home.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-23.
|
||||
-->
|
||||
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true" />
|
47
app/src/main/res/layout/card_home_lucky_number.xml
Normal file
47
app/src/main/res/layout/card_home_lucky_number.xml
Normal file
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-24.
|
||||
-->
|
||||
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
tools:layout_margin="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
app:srcCompat="@drawable/emoji_sad" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:text="@string/home_lucky_number_no_info"
|
||||
android:textAppearance="@style/NavView.TextView.Title" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/NavView.TextView.Helper"
|
||||
tools:text="Oranż Metylowy • Numer w dzienniku to 23" />
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
21
app/src/main/res/layout/card_home_timetable.xml
Normal file
21
app/src/main/res/layout/card_home_timetable.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-24.
|
||||
-->
|
||||
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
tools:layout_margin="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
20
app/src/main/res/layout/fragment_home_v2.xml
Normal file
20
app/src/main/res/layout/fragment_home_v2.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-23.
|
||||
-->
|
||||
|
||||
<layout xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
|
||||
android:id="@+id/refreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:listitem="@layout/card_home" />
|
||||
|
||||
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
|
||||
</layout>
|
@ -295,6 +295,7 @@
|
||||
<string name="lesson_break">Break</string>
|
||||
<string name="lesson_cancelled">Lesson cancelled</string>
|
||||
<string name="lesson_change">Lesson change</string>
|
||||
<string name="lesson_shifted">Shifted lesson</string>
|
||||
<string name="lesson_timetable_change">Timetable change</string>
|
||||
<string name="loading">Loading…</string>
|
||||
<string name="login_allow_registration">Allow registration</string>
|
||||
|
9
app/src/main/res/values/ids.xml
Normal file
9
app/src/main/res/values/ids.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-24.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<item name="move_card_down_action" type="id"/>
|
||||
<item name="move_card_up_action" type="id"/>
|
||||
</resources>
|
@ -318,6 +318,7 @@
|
||||
<string name="lesson_break">Przerwa</string>
|
||||
<string name="lesson_cancelled">Lekcja odwołana</string>
|
||||
<string name="lesson_change">Zastępstwo</string>
|
||||
<string name="lesson_shifted">Lekcja przeniesiona</string>
|
||||
<string name="lesson_timetable_change">Zmiana planu</string>
|
||||
<string name="loading">Ładowanie…</string>
|
||||
<string name="login_allow_registration">Zezwól na rejestrację</string>
|
||||
@ -448,7 +449,7 @@
|
||||
<string name="menu_timetable">Plan lekcji</string>
|
||||
<string name="messages_attachment_cannot_download">Nie można pobrać załącznika</string>
|
||||
<string name="messages_attachment_cannot_download_text">Wystąpił błąd wewnętrzny pobierania załącznika. Może to być spowodowane słabym połączeniem internetowym.</string>
|
||||
<string name="messages_attachment_downloading_format" translatable="false">%s (%d%%)</string>
|
||||
<string name="messages_attachment_downloading_format" translatable="false">%s (%.2fMB)</string>
|
||||
<string name="messages_attachment_format" translatable="false">%s (%s)</string>
|
||||
<string name="messages_attachment_no_size_format" translatable="false">%s</string>
|
||||
<string name="messages_compose_menu_attachment">Dodaj załącznik</string>
|
||||
@ -1031,4 +1032,16 @@
|
||||
<string name="menu_remove_notifications">Usuń wszystkie</string>
|
||||
<string name="menu_remove_notifications_success">Wyczyszczono powiadomienia</string>
|
||||
<string name="timetable_select_day">Wybierz dzień</string>
|
||||
<string name="card_action_move_up">Przesuń w górę</string>
|
||||
<string name="card_action_move_down">Przesuń w dół</string>
|
||||
<string name="home_lucky_number_details_click_to_set">%s • Kliknij, aby ustawić swój numerek.</string>
|
||||
<string name="home_lucky_number_details">%s • Numer w dzienniku to %d</string>
|
||||
<string name="home_lucky_number_no_info">Brak informacji o szczęśliwym numerku.</string>
|
||||
<string name="home_lucky_number_yours_today">Dzisiaj to Ty masz szczęśliwy numerek!</string>
|
||||
<string name="home_lucky_number_yours_tomorrow">Jutro to Ty masz szczęśliwy numerek!</string>
|
||||
<string name="home_lucky_number_yours_later">Dnia %s Twój numerek jest szczęśliwy.</string>
|
||||
<string name="home_lucky_number_later">Dnia %s szczęśliwy numerek to %d.</string>
|
||||
<string name="home_lucky_number_today">%d to dzisiejszy szczęśliwy numerek.</string>
|
||||
<string name="home_lucky_number_tomorrow">%d to szczęśliwy numerek na jutro.</string>
|
||||
<string name="home_lucky_number_no_number">Nie ma dzisiaj szczęśliwego numerka.</string>
|
||||
</resources>
|
||||
|
@ -5,8 +5,8 @@ buildscript {
|
||||
kotlin_version = '1.3.50'
|
||||
|
||||
release = [
|
||||
versionName: "3.9.10-dev",
|
||||
versionCode: 3091000
|
||||
versionName: "3.9.11-dev",
|
||||
versionCode: 3091100
|
||||
]
|
||||
|
||||
setup = [
|
||||
|
Reference in New Issue
Block a user