forked from github/szkolny
Compare commits
47 Commits
v3.9.8-dev
...
v3.9.12-de
Author | SHA1 | Date | |
---|---|---|---|
462b1df767 | |||
d17d2c8417 | |||
6892832fff | |||
66d54c7c45 | |||
d432685aa8 | |||
37f3d76fb8 | |||
7961a74995 | |||
9d590508ad | |||
f79b7eaf83 | |||
ae13bf946f | |||
f116c4f1f4 | |||
867c8920a8 | |||
6e6dd34872 | |||
0759468fa7 | |||
1b1fb09211 | |||
de414c912c | |||
d274a2fed1 | |||
285b7e9b9e | |||
875efcff7e | |||
07ae37167d | |||
f689f4d427 | |||
19bc2b8b37 | |||
673116e27e | |||
59fcb0a050 | |||
cd76f99bbf | |||
6a4994b9c2 | |||
63960c5e05 | |||
540afb6a28 | |||
ae10b8abbd | |||
db2ebab879 | |||
6ec3d062df | |||
86b6060a09 | |||
83d123e341 | |||
34061695f9 | |||
de68476442 | |||
678a81a44b | |||
cfb3096d53 | |||
9b7aca745a | |||
82852389fa | |||
ce06084e6f | |||
3ca051983f | |||
cd379e4175 | |||
62fdfa2b6f | |||
9866017f7e | |||
67f98b08c6 | |||
fdb5f7ec02 | |||
04c3c7ca6e |
2
.gitignore
vendored
2
.gitignore
vendored
@ -81,3 +81,5 @@ lint/generated/
|
||||
lint/outputs/
|
||||
lint/tmp/
|
||||
# lint/reports/
|
||||
|
||||
app/schemas/
|
||||
|
13
app/sampledata/settings/ic_settings.xml
Normal file
13
app/sampledata/settings/ic_settings.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-11-25.
|
||||
-->
|
||||
|
||||
<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="#FF000000"
|
||||
android:pathData="M12,8A4,4 0,0 1,16 12A4,4 0,0 1,12 16A4,4 0,0 1,8 12A4,4 0,0 1,12 8M12,10A2,2 0,0 0,10 12A2,2 0,0 0,12 14A2,2 0,0 0,14 12A2,2 0,0 0,12 10M10,22C9.75,22 9.54,21.82 9.5,21.58L9.13,18.93C8.5,18.68 7.96,18.34 7.44,17.94L4.95,18.95C4.73,19.03 4.46,18.95 4.34,18.73L2.34,15.27C2.21,15.05 2.27,14.78 2.46,14.63L4.57,12.97L4.5,12L4.57,11L2.46,9.37C2.27,9.22 2.21,8.95 2.34,8.73L4.34,5.27C4.46,5.05 4.73,4.96 4.95,5.05L7.44,6.05C7.96,5.66 8.5,5.32 9.13,5.07L9.5,2.42C9.54,2.18 9.75,2 10,2H14C14.25,2 14.46,2.18 14.5,2.42L14.87,5.07C15.5,5.32 16.04,5.66 16.56,6.05L19.05,5.05C19.27,4.96 19.54,5.05 19.66,5.27L21.66,8.73C21.79,8.95 21.73,9.22 21.54,9.37L19.43,11L19.5,12L19.43,13L21.54,14.63C21.73,14.78 21.79,15.05 21.66,15.27L19.66,18.73C19.54,18.95 19.27,19.04 19.05,18.95L16.56,17.95C16.04,18.34 15.5,18.68 14.87,18.93L14.5,21.58C14.46,21.82 14.25,22 14,22H10M11.25,4L10.88,6.61C9.68,6.86 8.62,7.5 7.85,8.39L5.44,7.35L4.69,8.65L6.8,10.2C6.4,11.37 6.4,12.64 6.8,13.8L4.68,15.36L5.43,16.66L7.86,15.62C8.63,16.5 9.68,17.14 10.87,17.38L11.24,20H12.76L13.13,17.39C14.32,17.14 15.37,16.5 16.14,15.62L18.57,16.66L19.32,15.36L17.2,13.81C17.6,12.64 17.6,11.37 17.2,10.2L19.31,8.65L18.56,7.35L16.15,8.39C15.38,7.5 14.32,6.86 13.12,6.62L12.75,4H11.25Z"/>
|
||||
</vector>
|
@ -17,6 +17,7 @@ import android.util.LongSparseArray
|
||||
import android.util.SparseArray
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.*
|
||||
import androidx.core.app.ActivityCompat
|
||||
@ -28,10 +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.navlib.R
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.navlib.getColorFromRes
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
@ -374,13 +378,13 @@ fun CharSequence?.asItalicSpannable(): Spannable {
|
||||
*/
|
||||
fun <T : CharSequence> listOfNotEmpty(vararg elements: T): List<T> = elements.filterNot { it.isEmpty() }
|
||||
|
||||
fun List<CharSequence>.concat(delimiter: String? = null): CharSequence {
|
||||
fun List<CharSequence?>.concat(delimiter: String? = null): CharSequence {
|
||||
if (this.isEmpty()) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if (this.size == 1) {
|
||||
return this[0]
|
||||
return this[0] ?: ""
|
||||
}
|
||||
|
||||
var spanned = false
|
||||
@ -395,6 +399,8 @@ fun List<CharSequence>.concat(delimiter: String? = null): CharSequence {
|
||||
if (spanned) {
|
||||
val ssb = SpannableStringBuilder()
|
||||
for (piece in this) {
|
||||
if (piece == null)
|
||||
continue
|
||||
if (!first && delimiter != null)
|
||||
ssb.append(delimiter)
|
||||
first = false
|
||||
@ -404,6 +410,8 @@ fun List<CharSequence>.concat(delimiter: String? = null): CharSequence {
|
||||
} else {
|
||||
val sb = StringBuilder()
|
||||
for (piece in this) {
|
||||
if (piece == null)
|
||||
continue
|
||||
if (!first && delimiter != null)
|
||||
sb.append(delimiter)
|
||||
first = false
|
||||
@ -417,15 +425,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?)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -441,6 +449,13 @@ inline fun <T : View> T.onClick(crossinline onClickListener: (v: T) -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <T : CompoundButton> T.onChange(crossinline onChangeListener: (v: T, isChecked: Boolean) -> Unit) {
|
||||
setOnCheckedChangeListener { buttonView, isChecked ->
|
||||
onChangeListener(buttonView as T, isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
|
||||
observe(lifecycleOwner, object : Observer<T> {
|
||||
override fun onChanged(t: T?) {
|
||||
@ -494,4 +509,81 @@ fun View.findParentById(targetId: Int): View? {
|
||||
return viewParent.findParentById(targetId)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
fun Context.timeTill(time: Int, delimiter: String = " "): String {
|
||||
val parts = mutableListOf<Pair<Int, Int>>()
|
||||
|
||||
val hours = time / 3600
|
||||
val minutes = (time - hours*3600) / 60
|
||||
val seconds = time - minutes*60 - hours*3600
|
||||
|
||||
var prefixAdded = false
|
||||
if (hours > 0) {
|
||||
if (!prefixAdded) parts += R.plurals.time_till_text to hours; prefixAdded = true
|
||||
parts += R.plurals.time_till_hours to hours
|
||||
}
|
||||
if (minutes > 0) {
|
||||
if (!prefixAdded) parts += R.plurals.time_till_text to minutes; prefixAdded = true
|
||||
parts += R.plurals.time_till_minutes to minutes
|
||||
}
|
||||
if (hours == 0 && minutes < 10) {
|
||||
if (!prefixAdded) parts += R.plurals.time_till_text to seconds; prefixAdded = true
|
||||
parts += R.plurals.time_till_seconds to seconds
|
||||
}
|
||||
|
||||
return parts.joinToString(delimiter) { resources.getQuantityString(it.first, it.second, it.second) }
|
||||
}
|
||||
|
||||
fun Context.timeLeft(time: Int, delimiter: String = " "): String {
|
||||
val parts = mutableListOf<Pair<Int, Int>>()
|
||||
|
||||
val hours = time / 3600
|
||||
val minutes = (time - hours*3600) / 60
|
||||
val seconds = time - minutes*60 - hours*3600
|
||||
|
||||
var prefixAdded = false
|
||||
if (hours > 0) {
|
||||
if (!prefixAdded) parts += R.plurals.time_left_text to hours
|
||||
prefixAdded = true
|
||||
parts += R.plurals.time_left_hours to hours
|
||||
}
|
||||
if (minutes > 0) {
|
||||
if (!prefixAdded) parts += R.plurals.time_left_text to minutes
|
||||
prefixAdded = true
|
||||
parts += R.plurals.time_left_minutes to minutes
|
||||
}
|
||||
if (hours == 0 && minutes < 10) {
|
||||
if (!prefixAdded) parts += R.plurals.time_left_text to seconds
|
||||
prefixAdded = true
|
||||
parts += R.plurals.time_left_seconds to seconds
|
||||
}
|
||||
|
||||
return parts.joinToString(delimiter) { resources.getQuantityString(it.first, it.second, it.second) }
|
||||
}
|
||||
|
@ -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,9 +122,9 @@ 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)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_home_outline)
|
||||
.isInDrawer(true)
|
||||
.isStatic(true)
|
||||
.withPopToHome(false)
|
||||
@ -135,50 +135,50 @@ class MainActivity : AppCompatActivity() {
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_AGENDA, R.string.menu_agenda, AgendaFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar_outline)
|
||||
.withBadgeTypeId(TYPE_EVENT)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box_outline)
|
||||
.withBadgeTypeId(TYPE_GRADE)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_MESSAGES, R.string.menu_messages, MessagesFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_email)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_email_outline)
|
||||
.withBadgeTypeId(TYPE_MESSAGE)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_HOMEWORK, R.string.menu_homework, HomeworkFragment::class)
|
||||
.withIcon(SzkolnyFont.Icon.szf_file_document_edit)
|
||||
.withIcon(SzkolnyFont.Icon.szf_notebook_outline)
|
||||
.withBadgeTypeId(TYPE_HOMEWORK)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_BEHAVIOUR, R.string.menu_notices, BehaviourFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_message_alert)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_emoticon_outline)
|
||||
.withBadgeTypeId(TYPE_NOTICE)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_ATTENDANCE, R.string.menu_attendance, AttendanceFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar_remove)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar_remove_outline)
|
||||
.withBadgeTypeId(TYPE_ATTENDANCE)
|
||||
.isInDrawer(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_ANNOUNCEMENTS, R.string.menu_announcements, AnnouncementsFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_bulletin_board)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_bullhorn_outline)
|
||||
.withBadgeTypeId(TYPE_ANNOUNCEMENT)
|
||||
.isInDrawer(true)
|
||||
|
||||
|
||||
// static drawer items
|
||||
list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_bell_ring)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_bell_ring_outline)
|
||||
.isInDrawer(true)
|
||||
.isStatic(true)
|
||||
.isBelowSeparator(true)
|
||||
|
||||
list += NavTarget(DRAWER_ITEM_SETTINGS, R.string.menu_settings, SettingsNewFragment::class)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_settings)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_settings_outline)
|
||||
.isInDrawer(true)
|
||||
.isStatic(true)
|
||||
.isBelowSeparator(true)
|
||||
@ -197,7 +197,7 @@ class MainActivity : AppCompatActivity() {
|
||||
.isInProfileList(false)
|
||||
|
||||
list += NavTarget(DRAWER_PROFILE_SYNC_ALL, R.string.menu_sync_all, null)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_sync)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_download_outline)
|
||||
.isInProfileList(true)
|
||||
|
||||
|
||||
@ -434,7 +434,7 @@ class MainActivity : AppCompatActivity() {
|
||||
navView.coordinator.postDelayed({
|
||||
CafeBar.builder(this)
|
||||
.content(R.string.rate_snackbar_text)
|
||||
.icon(IconicsDrawable(this).icon(CommunityMaterial.Icon2.cmd_star).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.getPrimaryTextColor(this))))
|
||||
.icon(IconicsDrawable(this).icon(CommunityMaterial.Icon2.cmd_star_outline).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.getPrimaryTextColor(this))))
|
||||
.positiveText(R.string.rate_snackbar_positive)
|
||||
.positiveColor(-0xb350b0)
|
||||
.negativeText(R.string.rate_snackbar_negative)
|
||||
@ -471,7 +471,7 @@ class MainActivity : AppCompatActivity() {
|
||||
bottomSheet.appendItems(
|
||||
BottomSheetPrimaryItem(false)
|
||||
.withTitle(R.string.menu_sync)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_sync)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_download_outline)
|
||||
.withOnClickListener(View.OnClickListener {
|
||||
bottomSheet.close()
|
||||
SyncViewListDialog(this, navTargetId)
|
||||
@ -479,17 +479,17 @@ class MainActivity : AppCompatActivity() {
|
||||
BottomSheetSeparatorItem(false),
|
||||
BottomSheetPrimaryItem(false)
|
||||
.withTitle(R.string.menu_settings)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_settings)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_settings_outline)
|
||||
.withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_SETTINGS) }),
|
||||
BottomSheetPrimaryItem(false)
|
||||
.withTitle(R.string.menu_feedback)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_help_circle)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_help_circle_outline)
|
||||
.withOnClickListener(View.OnClickListener { loadTarget(TARGET_FEEDBACK) })
|
||||
)
|
||||
if (App.devMode) {
|
||||
bottomSheet += BottomSheetPrimaryItem(false)
|
||||
.withTitle(R.string.menu_debug)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_android_debug_bridge)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_android_studio)
|
||||
.withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_DEBUG) })
|
||||
}
|
||||
|
||||
@ -537,9 +537,14 @@ class MainActivity : AppCompatActivity() {
|
||||
DRAWER_ITEM_MESSAGES -> MessagesFragment.pageSelection
|
||||
else -> 0
|
||||
}
|
||||
val arguments = when (navTargetId) {
|
||||
DRAWER_ITEM_TIMETABLE -> JsonObject("weekStart" to TimetableFragment.pageSelection?.weekStart?.stringY_m_d)
|
||||
else -> null
|
||||
}
|
||||
EdziennikTask.syncProfile(
|
||||
App.profileId,
|
||||
listOf(navTargetId to fragmentParam)
|
||||
listOf(navTargetId to fragmentParam),
|
||||
arguments
|
||||
).enqueue(this)
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
@ -694,10 +699,13 @@ class MainActivity : AppCompatActivity() {
|
||||
app.profile == null -> {
|
||||
if (intentProfileId == -1)
|
||||
intentProfileId = app.appSharedPrefs.getInt("current_profile_id", 1)
|
||||
loadProfile(intentProfileId, intentTargetId)
|
||||
loadProfile(intentProfileId, intentTargetId, extras)
|
||||
}
|
||||
intentProfileId != -1 -> {
|
||||
loadProfile(intentProfileId, intentTargetId)
|
||||
if (app.profile.id != intentProfileId)
|
||||
loadProfile(intentProfileId, intentTargetId, extras)
|
||||
else
|
||||
loadTarget(intentTargetId, extras)
|
||||
}
|
||||
intentTargetId != -1 -> {
|
||||
drawer.currentProfile = app.profile.id
|
||||
|
@ -311,13 +311,14 @@ public class Notifier {
|
||||
\____/| .__/ \__,_|\__,_|\__\___||___/
|
||||
| |
|
||||
|*/
|
||||
public void notificationUpdatesShow(String updateVersion, String updateUrl, String updateFilename) {
|
||||
public void notificationUpdatesShow(String updateVersion, String updateUrl, String updateFilename, boolean updateDirect) {
|
||||
if (!app.appConfig.notifyAboutUpdates)
|
||||
return;
|
||||
Intent notificationIntent = new Intent(app.getContext(), BootReceiver.NotificationActionService.class)
|
||||
.putExtra("update_version", updateVersion)
|
||||
.putExtra("update_url", updateUrl)
|
||||
.putExtra("update_filename", updateFilename);
|
||||
.putExtra("update_filename", updateFilename)
|
||||
.putExtra("update_direct", updateDirect);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
|
||||
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
@ -87,7 +87,7 @@ class WidgetTimetable : AppWidgetProvider() {
|
||||
.colorInt(Color.WHITE)
|
||||
.sizeDp(if (widgetConfig.bigStyle) 24 else 16).toBitmap())
|
||||
|
||||
views.setImageViewBitmap(R.id.widgetTimetableSync, IconicsDrawable(context, CommunityMaterial.Icon2.cmd_sync)
|
||||
views.setImageViewBitmap(R.id.widgetTimetableSync, IconicsDrawable(context, CommunityMaterial.Icon.cmd_download_outline)
|
||||
.colorInt(Color.WHITE)
|
||||
.sizeDp(if (widgetConfig.bigStyle) 24 else 16).toBitmap())
|
||||
|
||||
|
@ -60,6 +60,9 @@ class ApiService : Service() {
|
||||
|
||||
private val notification by lazy { EdziennikNotification(this) }
|
||||
|
||||
private var lastEventTime = System.currentTimeMillis()
|
||||
private var taskCancelTries = 0
|
||||
|
||||
/* ______ _ _ _ _ _____ _ _ _ _
|
||||
| ____| | | (_) (_) | / ____| | | | | | |
|
||||
| |__ __| |_____ ___ _ __ _ __ _| | __ | | __ _| | | |__ __ _ ___| | __
|
||||
@ -68,22 +71,17 @@ class ApiService : Service() {
|
||||
|______\__,_/___|_|\___|_| |_|_| |_|_|_|\_\ \_____\__,_|_|_|_.__/ \__,_|\___|_|\*/
|
||||
private val taskCallback = object : EdziennikCallback {
|
||||
override fun onCompleted() {
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
d(TAG, "Task $taskRunningId (profile $taskProfileId) - $taskProgressText - finished")
|
||||
//if (!taskCancelled) {
|
||||
EventBus.getDefault().post(ApiTaskFinishedEvent(taskProfileId))
|
||||
//}
|
||||
taskIsRunning = false
|
||||
taskRunningId = -1
|
||||
taskRunning = null
|
||||
taskProfileId = -1
|
||||
taskProgress = -1f
|
||||
taskProgressText = null
|
||||
EventBus.getDefault().post(ApiTaskFinishedEvent(taskProfileId))
|
||||
clearTask()
|
||||
|
||||
notification.setIdle().post()
|
||||
runTask()
|
||||
}
|
||||
|
||||
override fun onError(apiError: ApiError) {
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
d(TAG, "Task $taskRunningId threw an error - $apiError")
|
||||
apiError.profileId = taskProfileId
|
||||
EventBus.getDefault().post(ApiTaskErrorEvent(apiError))
|
||||
@ -92,9 +90,7 @@ class ApiService : Service() {
|
||||
if (apiError.isCritical) {
|
||||
taskRunning?.cancel()
|
||||
notification.setCriticalError().post()
|
||||
taskRunning = null
|
||||
taskIsRunning = false
|
||||
taskRunningId = -1
|
||||
clearTask()
|
||||
runTask()
|
||||
}
|
||||
else {
|
||||
@ -103,6 +99,7 @@ class ApiService : Service() {
|
||||
}
|
||||
|
||||
override fun onProgress(step: Float) {
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
if (step <= 0)
|
||||
return
|
||||
if (taskProgress < 0)
|
||||
@ -115,6 +112,7 @@ class ApiService : Service() {
|
||||
}
|
||||
|
||||
override fun onStartProgress(stringRes: Int) {
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
taskProgressText = getString(stringRes)
|
||||
d(TAG, "Task $taskRunningId progress: $taskProgressText")
|
||||
EventBus.getDefault().post(ApiTaskProgressEvent(taskProfileId, taskProgress, taskProgressText))
|
||||
@ -129,6 +127,7 @@ class ApiService : Service() {
|
||||
| | (_| \__ \ < | __/> < __/ (__| |_| | |_| | (_) | | | |
|
||||
|_|\__,_|___/_|\_\ \___/_/\_\___|\___|\__,_|\__|_|\___/|_| |*/
|
||||
private fun runTask() {
|
||||
checkIfTaskFrozen()
|
||||
if (taskIsRunning)
|
||||
return
|
||||
if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && finishingTaskQueue.isEmpty())) {
|
||||
@ -137,6 +136,8 @@ class ApiService : Service() {
|
||||
return
|
||||
}
|
||||
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
|
||||
val task = if (taskQueue.isEmpty()) finishingTaskQueue.removeAt(0) else taskQueue.removeAt(0)
|
||||
task.taskId = ++taskMaximumId
|
||||
task.prepare(app)
|
||||
@ -166,6 +167,48 @@ class ApiService : Service() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a task is inactive for more than 30 seconds.
|
||||
* If the user tries to cancel a task with no success at least three times,
|
||||
* consider it frozen as well.
|
||||
*
|
||||
* This usually means it is broken and won't become active again.
|
||||
* This method cancels the task and removes any pointers to it.
|
||||
*/
|
||||
private fun checkIfTaskFrozen(): Boolean {
|
||||
if (System.currentTimeMillis() - lastEventTime > 30*1000
|
||||
|| taskCancelTries >= 3) {
|
||||
val time = System.currentTimeMillis() - lastEventTime
|
||||
d(TAG, "!!! Task $taskRunningId froze for $time ms. $taskRunning")
|
||||
clearTask()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the service if the current task is frozen/broken.
|
||||
*/
|
||||
private fun stopIfTaskFrozen() {
|
||||
if (checkIfTaskFrozen()) {
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any task descriptors or pointers from the service.
|
||||
*/
|
||||
private fun clearTask() {
|
||||
taskIsRunning = false
|
||||
taskRunningId = -1
|
||||
taskRunning = null
|
||||
taskProfileId = -1
|
||||
taskProgress = -1f
|
||||
taskProgressText = null
|
||||
taskCancelled = false
|
||||
taskCancelTries = 0
|
||||
}
|
||||
|
||||
private fun allCompleted() {
|
||||
EventBus.getDefault().post(ApiTaskAllFinishedEvent())
|
||||
stopSelf()
|
||||
@ -211,8 +254,10 @@ class ApiService : Service() {
|
||||
EventBus.getDefault().removeStickyEvent(request)
|
||||
d(TAG, request.toString())
|
||||
|
||||
taskCancelTries++
|
||||
taskCancelled = true
|
||||
taskRunning?.cancel()
|
||||
stopIfTaskFrozen()
|
||||
}
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.ASYNC)
|
||||
fun onServiceCloseRequest(request: ServiceCloseRequest) {
|
||||
|
@ -64,6 +64,7 @@ const val IDZIENNIK_WEB_TIMETABLE = "mod_panelRodzica/plan/WS_Plan.asmx/pobierzP
|
||||
const val IDZIENNIK_WEB_GRADES = "mod_panelRodzica/oceny/WS_ocenyUcznia.asmx/pobierzOcenyUcznia"
|
||||
const val IDZIENNIK_WEB_MISSING_GRADES = "mod_panelRodzica/brak_ocen/WS_BrakOcenUcznia.asmx/pobierzBrakujaceOcenyUcznia"
|
||||
const val IDZIENNIK_WEB_EXAMS = "mod_panelRodzica/sprawdziany/mod_sprawdzianyPanel.asmx/pobierzListe"
|
||||
const val IDZIENNIK_WEB_HOMEWORK = "mod_panelRodzica/pracaDomowa/WS_pracaDomowa.asmx/pobierzPraceDomowe"
|
||||
const val IDZIENNIK_WEB_NOTICES = "mod_panelRodzica/uwagi/WS_uwagiUcznia.asmx/pobierzUwagiUcznia"
|
||||
const val IDZIENNIK_WEB_ATTENDANCE = "mod_panelRodzica/obecnosci/WS_obecnosciUcznia.asmx/pobierzObecnosciUcznia"
|
||||
const val IDZIENNIK_WEB_ANNOUNCEMENTS = "mod_panelRodzica/tabOgl/WS_tablicaOgloszen.asmx/GetOgloszenia"
|
||||
|
@ -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)
|
||||
@ -207,4 +207,4 @@ class DataNotifications(val data: Data) {
|
||||
|
||||
data.db.metadataDao().setAllNotified(profileId, true)
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -39,12 +39,16 @@ const val ERROR_REQUEST_HTTP_404 = 54
|
||||
const val ERROR_REQUEST_HTTP_405 = 55
|
||||
const val ERROR_REQUEST_HTTP_410 = 56
|
||||
const val ERROR_REQUEST_HTTP_500 = 57
|
||||
const val ERROR_REQUEST_FAILURE_HOSTNAME_NOT_FOUND = 60
|
||||
const val ERROR_REQUEST_FAILURE_TIMEOUT = 61
|
||||
const val ERROR_REQUEST_FAILURE_NO_INTERNET = 62
|
||||
const val ERROR_RESPONSE_EMPTY = 100
|
||||
const val ERROR_LOGIN_DATA_MISSING = 101
|
||||
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
|
||||
|
||||
@ -104,6 +108,7 @@ const val ERROR_LIBRUS_MESSAGES_OTHER = 178
|
||||
const val ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN = 179
|
||||
const val ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN = 180
|
||||
const val ERROR_LIBRUS_API_MAINTENANCE = 181
|
||||
const val ERROR_LIBRUS_PORTAL_MAINTENANCE = 182
|
||||
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202
|
||||
|
@ -37,6 +37,16 @@ object Regexes {
|
||||
"""events: (.+),$""".toRegex(RegexOption.MULTILINE)
|
||||
}
|
||||
|
||||
val MOBIDZIENNIK_MESSAGE_READ_DATE by lazy {
|
||||
"""czas przeczytania:.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_MESSAGE_SENT_READ_DATE by lazy {
|
||||
""".+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_MESSAGE_ATTACHMENT by lazy {
|
||||
"""href="https://.+?\.mobidziennik.pl/.+?&(?:amp;)?zalacznik=([0-9]+)"(?:.+?<small.+?\(([0-9.]+)\s(M|K|G|)B\))*""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
|
||||
|
||||
|
||||
val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy {
|
||||
@ -61,7 +71,15 @@ object Regexes {
|
||||
"""<option.*?value="([0-9]+)"\sdata-id-ucznia="([A-z0-9]+?)".*?>(.+?)\s(.+?)\s*\((.+?),\s*(.+?)\)</option>""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
@ -138,10 +138,12 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(
|
||||
val teacher = teacherList.singleOrNull { it.fullName == "$firstName $lastName" }
|
||||
return validateTeacher(teacher, firstName, lastName)
|
||||
}
|
||||
|
||||
fun getTeacher(firstNameChar: Char, lastName: String): Teacher {
|
||||
val teacher = teacherList.singleOrNull { it.shortName == "$firstNameChar.$lastName" }
|
||||
return validateTeacher(teacher, firstNameChar.toString(), lastName)
|
||||
}
|
||||
|
||||
fun getTeacherByLastFirst(nameLastFirst: String): Teacher {
|
||||
val nameParts = nameLastFirst.split(" ")
|
||||
return if (nameParts.size == 1) getTeacher(nameParts[0], "") else getTeacher(nameParts[1], nameParts[0])
|
||||
|
@ -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()
|
||||
|
@ -11,6 +11,7 @@ const val ENDPOINT_IDZIENNIK_WEB_TIMETABLE = 1030
|
||||
const val ENDPOINT_IDZIENNIK_WEB_GRADES = 1040
|
||||
const val ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES = 1050
|
||||
const val ENDPOINT_IDZIENNIK_WEB_EXAMS = 1060
|
||||
const val ENDPOINT_IDZIENNIK_WEB_HOMEWORK = 1061
|
||||
const val ENDPOINT_IDZIENNIK_WEB_NOTICES = 1070
|
||||
const val ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS = 1080
|
||||
const val ENDPOINT_IDZIENNIK_WEB_ATTENDANCE = 1090
|
||||
@ -34,6 +35,10 @@ val IdziennikFeatures = listOf(
|
||||
ENDPOINT_IDZIENNIK_WEB_EXAMS to LOGIN_METHOD_IDZIENNIK_WEB
|
||||
), listOf(LOGIN_METHOD_IDZIENNIK_WEB)),
|
||||
|
||||
Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_HOMEWORK, listOf(
|
||||
ENDPOINT_IDZIENNIK_WEB_HOMEWORK to LOGIN_METHOD_IDZIENNIK_WEB
|
||||
), listOf(LOGIN_METHOD_IDZIENNIK_WEB)),
|
||||
|
||||
Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_BEHAVIOUR, listOf(
|
||||
ENDPOINT_IDZIENNIK_WEB_NOTICES to LOGIN_METHOD_IDZIENNIK_WEB
|
||||
), listOf(LOGIN_METHOD_IDZIENNIK_WEB)),
|
||||
|
@ -55,6 +55,10 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_exams)
|
||||
IdziennikWebExams(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_IDZIENNIK_WEB_HOMEWORK -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_homework)
|
||||
IdziennikWebHomework(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_IDZIENNIK_WEB_NOTICES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_notices)
|
||||
IdziennikWebNotices(data, onSuccess)
|
||||
|
@ -94,6 +94,7 @@ open class IdziennikWeb(open val data: DataIdziennik) {
|
||||
is Long -> json.addProperty(name, value)
|
||||
is Float -> json.addProperty(name, value)
|
||||
is Char -> json.addProperty(name, value)
|
||||
is Boolean -> json.addProperty(name, value)
|
||||
}
|
||||
}
|
||||
setJsonBody(json)
|
||||
|
@ -5,21 +5,21 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
|
||||
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_EXAMS
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_EXAMS
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.getJsonObject
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class IdziennikWebExams(override val data: DataIdziennik,
|
||||
val onSuccess: () -> Unit) : IdziennikWeb(data) {
|
||||
val onSuccess: () -> Unit) : IdziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "IdziennikWebExams"
|
||||
}
|
||||
@ -34,14 +34,15 @@ class IdziennikWebExams(override val data: DataIdziennik,
|
||||
}
|
||||
|
||||
private fun getExams() {
|
||||
val param = JsonObject()
|
||||
param.addProperty("strona", 1)
|
||||
param.addProperty("iloscNaStrone", "99")
|
||||
param.addProperty("iloscRekordow", -1)
|
||||
param.addProperty("kolumnaSort", "ss.Nazwa,sp.Data_sprawdzianu")
|
||||
param.addProperty("kierunekSort", 0)
|
||||
param.addProperty("maxIloscZaznaczonych", 0)
|
||||
param.addProperty("panelFiltrow", 0)
|
||||
val param = JsonObject().apply {
|
||||
addProperty("strona", 1)
|
||||
addProperty("iloscNaStrone", "99")
|
||||
addProperty("iloscRekordow", -1)
|
||||
addProperty("kolumnaSort", "ss.Nazwa,sp.Data_sprawdzianu")
|
||||
addProperty("kierunekSort", 0)
|
||||
addProperty("maxIloscZaznaczonych", 0)
|
||||
addProperty("panelFiltrow", 0)
|
||||
}
|
||||
|
||||
webApiGet(TAG, IDZIENNIK_WEB_EXAMS, mapOf(
|
||||
"idP" to data.registerId,
|
||||
@ -55,28 +56,33 @@ class IdziennikWebExams(override val data: DataIdziennik,
|
||||
return@webApiGet
|
||||
}
|
||||
|
||||
for (jExamEl in json.getAsJsonArray("ListK")) {
|
||||
val jExam = jExamEl.asJsonObject
|
||||
// jExam
|
||||
val eventId = jExam.get("_recordId").asLong
|
||||
val rSubject = data.getSubject(jExam.get("przedmiot").asString, -1, "")
|
||||
val rTeacher = data.getTeacherByLastFirst(jExam.get("wpisal").asString)
|
||||
val examDate = Date.fromY_m_d(jExam.get("data").asString)
|
||||
val lessonObject = Lesson.getByWeekDayAndSubject(data.lessonList, examDate.weekDay, rSubject.id)
|
||||
val examTime = lessonObject?.startTime
|
||||
json.getJsonArray("ListK")?.asJsonObjectList()?.forEach { exam ->
|
||||
val id = exam.getLong("_recordId") ?: return@forEach
|
||||
val examDate = Date.fromY_m_d(exam.getString("data") ?: return@forEach)
|
||||
val subjectId = data.getSubject(exam.getString("przedmiot") ?: return@forEach,
|
||||
-1, "").id
|
||||
val teacherId = data.getTeacherByLastFirst(exam.getString("wpisal")
|
||||
?: return@forEach).id
|
||||
val lessonList = data.db.timetableDao().getForDateNow(profileId, examDate)
|
||||
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime
|
||||
val topic = exam.getString("zakres") ?: ""
|
||||
|
||||
val eventType = when (exam.getString("rodzaj")) {
|
||||
"sprawdzian/praca klasowa" -> Event.TYPE_EXAM
|
||||
else -> Event.TYPE_SHORT_QUIZ
|
||||
}
|
||||
|
||||
val eventType = if (jExam.get("rodzaj").asString == "sprawdzian/praca klasowa") Event.TYPE_EXAM else Event.TYPE_SHORT_QUIZ
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
eventId,
|
||||
id,
|
||||
examDate,
|
||||
examTime,
|
||||
jExam.get("zakres").asString,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
eventType,
|
||||
false,
|
||||
rTeacher.id,
|
||||
rSubject.id,
|
||||
teacherId,
|
||||
subjectId,
|
||||
data.teamClass?.id ?: -1
|
||||
)
|
||||
|
||||
@ -106,9 +112,11 @@ class IdziennikWebExams(override val data: DataIdziennik,
|
||||
examsNextMonthChecked = true
|
||||
getExams()
|
||||
} else {
|
||||
data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
|
||||
|
||||
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_EXAMS, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-25
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
|
||||
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
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.events.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class IdziennikWebHomework(override val data: DataIdziennik,
|
||||
val onSuccess: () -> Unit) : IdziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "IdziennikWebHomework"
|
||||
}
|
||||
|
||||
init {
|
||||
val param = JsonObject().apply {
|
||||
addProperty("strona", 1)
|
||||
addProperty("iloscNaStrone", 997)
|
||||
addProperty("iloscRekordow", -1)
|
||||
addProperty("kolumnaSort", "DataZadania")
|
||||
addProperty("kierunekSort", 0)
|
||||
addProperty("maxIloscZaznaczonych", 0)
|
||||
addProperty("panelFiltrow", 0)
|
||||
}
|
||||
|
||||
webApiGet(TAG, IDZIENNIK_WEB_HOMEWORK, mapOf(
|
||||
"idP" to data.registerId,
|
||||
"data" to Date.getToday().stringY_m_d,
|
||||
"wszystkie" to true,
|
||||
"param" to param
|
||||
)) { result ->
|
||||
val json = result.getJsonObject("d") ?: run {
|
||||
data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA)
|
||||
.withApiResponse(result))
|
||||
return@webApiGet
|
||||
}
|
||||
|
||||
json.getJsonArray("ListK")?.asJsonObjectList()?.forEach { homework ->
|
||||
val id = homework.getLong("_recordId") ?: return@forEach
|
||||
val eventDate = Date.fromY_m_d(homework.getString("dataO") ?: return@forEach)
|
||||
val subjectId = data.getSubject(homework.getString("przed") ?: return@forEach,
|
||||
-1, "").id
|
||||
val teacherId = data.getTeacherByLastFirst(homework.getString("usr")
|
||||
?: return@forEach).id
|
||||
val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate)
|
||||
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.displayStartTime
|
||||
val topic = homework.getString("tytul") ?: ""
|
||||
|
||||
val seen = when (profile?.empty) {
|
||||
true -> true
|
||||
else -> eventDate < Date.getToday()
|
||||
}
|
||||
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
id,
|
||||
eventDate,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
Event.TYPE_HOMEWORK,
|
||||
false,
|
||||
teacherId,
|
||||
subjectId,
|
||||
data.teamClass?.id ?: -1
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_HOMEWORK,
|
||||
eventObject.id,
|
||||
seen,
|
||||
seen,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
|
||||
|
||||
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_HOMEWORK, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-27.
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-22
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web
|
||||
@ -12,33 +12,38 @@ import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_TIMETABLE
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange.TYPE_CANCELLED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange.TYPE_CHANGE
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonRange
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.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
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
class IdziennikWebTimetable(override val data: DataIdziennik,
|
||||
val onSuccess: () -> Unit) : IdziennikWeb(data) {
|
||||
val onSuccess: () -> Unit) : IdziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "IdziennikWebTimetable"
|
||||
}
|
||||
|
||||
init {
|
||||
val weekStart = Week.getWeekStart()
|
||||
init { data.profile?.also { profile ->
|
||||
val currentWeekStart = Week.getWeekStart()
|
||||
|
||||
if (Date.getToday().weekDay > 4) {
|
||||
weekStart.stepForward(0, 0, 7)
|
||||
currentWeekStart.stepForward(0, 0, 7)
|
||||
}
|
||||
|
||||
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
|
||||
|
||||
val weekStart = Date.fromY_m_d(getDate)
|
||||
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
|
||||
|
||||
webApiGet(TAG, IDZIENNIK_WEB_TIMETABLE, mapOf(
|
||||
"idPozDziennika" to data.registerId,
|
||||
"pidRokSzkolny" to data.schoolYearId,
|
||||
"data" to weekStart.stringY_m_d+"T10:00:00.000Z"
|
||||
"data" to "${weekStart.stringY_m_d}T10:00:00.000Z"
|
||||
)) { result ->
|
||||
val json = result.getJsonObject("d") ?: run {
|
||||
data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA)
|
||||
@ -56,73 +61,132 @@ class IdziennikWebTimetable(override val data: DataIdziennik,
|
||||
data.lessonRanges[lessonRange.lessonNumber] = lessonRange
|
||||
}
|
||||
|
||||
val dates = mutableSetOf<Int>()
|
||||
val lessons = mutableListOf<Lesson>()
|
||||
|
||||
json.getJsonArray("Przedmioty")?.asJsonObjectList()?.forEach { lesson ->
|
||||
val subject = data.getSubject(
|
||||
lesson.getString("Nazwa") ?: return@forEach,
|
||||
lesson.getLong("Id"),
|
||||
lesson.getString("Skrot") ?: ""
|
||||
)
|
||||
val teacher = data.getTeacherByFDotLast(lesson.getString("Nauczyciel") ?: return@forEach)
|
||||
val weekDay = lesson.getInt("DzienTygodnia")?.minus(1) ?: return@forEach
|
||||
val lessonRange = data.lessonRanges[lesson.getInt("Godzina")?.plus(1) ?: return@forEach]
|
||||
val teacher = data.getTeacherByFDotLast(lesson.getString("Nauczyciel")
|
||||
?: return@forEach)
|
||||
|
||||
val lessonObject = Lesson(
|
||||
profileId,
|
||||
weekDay,
|
||||
lessonRange.startTime,
|
||||
lessonRange.endTime
|
||||
).apply {
|
||||
subjectId = subject.id
|
||||
teacherId = teacher.id
|
||||
teamId = data.teamClass?.id ?: -1
|
||||
classroomName = lesson.getString("NazwaSali") ?: ""
|
||||
val newSubjectName = lesson.getString("PrzedmiotZastepujacy")
|
||||
val newSubject = when (newSubjectName.isNullOrBlank()) {
|
||||
true -> null
|
||||
else -> data.getSubject(newSubjectName, null, newSubjectName)
|
||||
}
|
||||
|
||||
data.lessonList.add(lessonObject)
|
||||
val newTeacherName = lesson.getString("NauZastepujacy")
|
||||
val newTeacher = when (newTeacherName.isNullOrBlank()) {
|
||||
true -> null
|
||||
else -> data.getTeacherByFDotLast(newTeacherName)
|
||||
}
|
||||
|
||||
val weekDay = lesson.getInt("DzienTygodnia")?.minus(1) ?: return@forEach
|
||||
val lessonRange = data.lessonRanges[lesson.getInt("Godzina")?.plus(1)
|
||||
?: return@forEach]
|
||||
val lessonDate = weekStart.clone().stepForward(0, 0, weekDay)
|
||||
val classroom = lesson.getString("NazwaSali")
|
||||
|
||||
val type = lesson.getInt("TypZastepstwa") ?: -1
|
||||
if (type != -1) {
|
||||
// we have a lesson change to process
|
||||
val lessonChangeObject = LessonChange(
|
||||
profileId,
|
||||
weekStart.clone().stepForward(0, 0, weekDay),
|
||||
lessonObject.startTime,
|
||||
lessonObject.endTime
|
||||
)
|
||||
|
||||
lessonChangeObject.teamId = lessonObject.teamId
|
||||
lessonChangeObject.teacherId = lessonObject.teacherId
|
||||
lessonChangeObject.subjectId = lessonObject.subjectId
|
||||
lessonChangeObject.classroomName = lessonObject.classroomName
|
||||
when (type) {
|
||||
0 -> lessonChangeObject.type = TYPE_CANCELLED
|
||||
1, 2, 3, 4, 5 -> {
|
||||
lessonChangeObject.type = TYPE_CHANGE
|
||||
val newTeacher = lesson.getString("NauZastepujacy")
|
||||
val newSubject = lesson.getString("PrzedmiotZastepujacy")
|
||||
if (newTeacher != null) {
|
||||
lessonChangeObject.teacherId = data.getTeacherByFDotLast(newTeacher).id
|
||||
}
|
||||
if (newSubject != null) {
|
||||
lessonChangeObject.subjectId = data.getSubject(newSubject, null, "").id
|
||||
}
|
||||
val lessonObject = Lesson(profileId, -1)
|
||||
|
||||
when (type) {
|
||||
1, 2, 3, 4, 5 -> {
|
||||
lessonObject.apply {
|
||||
this.type = Lesson.TYPE_CHANGE
|
||||
|
||||
this.date = lessonDate
|
||||
this.lessonNumber = lessonRange.lessonNumber
|
||||
this.startTime = lessonRange.startTime
|
||||
this.endTime = lessonRange.endTime
|
||||
this.subjectId = newSubject?.id
|
||||
this.teacherId = newTeacher?.id
|
||||
this.teamId = data.teamClass?.id
|
||||
this.classroom = classroom
|
||||
|
||||
this.oldDate = lessonDate
|
||||
this.oldLessonNumber = lessonRange.lessonNumber
|
||||
this.oldStartTime = lessonRange.startTime
|
||||
this.oldEndTime = lessonRange.endTime
|
||||
this.oldSubjectId = subject.id
|
||||
this.oldTeacherId = teacher.id
|
||||
this.oldTeamId = data.teamClass?.id
|
||||
this.oldClassroom = classroom
|
||||
}
|
||||
}
|
||||
0 -> {
|
||||
lessonObject.apply {
|
||||
this.type = Lesson.TYPE_CANCELLED
|
||||
|
||||
data.lessonChangeList.add(lessonChangeObject)
|
||||
this.oldDate = lessonDate
|
||||
this.oldLessonNumber = lessonRange.lessonNumber
|
||||
this.oldStartTime = lessonRange.startTime
|
||||
this.oldEndTime = lessonRange.endTime
|
||||
this.oldSubjectId = subject.id
|
||||
this.oldTeacherId = teacher.id
|
||||
this.oldTeamId = data.teamClass?.id
|
||||
this.oldClassroom = classroom
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
lessonObject.apply {
|
||||
this.type = Lesson.TYPE_NORMAL
|
||||
|
||||
this.date = lessonDate
|
||||
this.lessonNumber = lessonRange.lessonNumber
|
||||
this.startTime = lessonRange.startTime
|
||||
this.endTime = lessonRange.endTime
|
||||
this.subjectId = subject.id
|
||||
this.teacherId = teacher.id
|
||||
this.teamId = data.teamClass?.id
|
||||
this.classroom = classroom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
lessonChangeObject.id,
|
||||
profile?.empty ?: false,
|
||||
profile?.empty ?: false,
|
||||
lessonObject.id,
|
||||
seen,
|
||||
seen,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
val date: Date = weekStart.clone()
|
||||
while (date <= weekEnd) {
|
||||
if (!dates.contains(date.value)) {
|
||||
lessons.add(Lesson(profileId, date.value.toLong()).apply {
|
||||
this.type = Lesson.TYPE_NO_LESSONS
|
||||
this.date = date.clone()
|
||||
})
|
||||
}
|
||||
|
||||
date.stepForward(0, 0, 1)
|
||||
}
|
||||
|
||||
d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate")
|
||||
|
||||
data.lessonNewList.addAll(lessons)
|
||||
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
|
||||
|
||||
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,11 +80,13 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
}
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
LibrusLoginApi(data) {
|
||||
LibrusLoginSynergia(data) {
|
||||
LibrusLoginMessages(data) {
|
||||
LibrusMessagesGetMessage(data, message) {
|
||||
completed()
|
||||
LibrusLoginPortal(data) {
|
||||
LibrusLoginApi(data) {
|
||||
LibrusLoginSynergia(data) {
|
||||
LibrusLoginMessages(data) {
|
||||
LibrusMessagesGetMessage(data, message) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,10 +94,26 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
LibrusLoginApi(data) {
|
||||
LibrusLoginSynergia(data) {
|
||||
LibrusSynergiaMarkAllAnnouncementsAsRead(data) {
|
||||
completed()
|
||||
LibrusLoginPortal(data) {
|
||||
LibrusLoginApi(data) {
|
||||
LibrusLoginSynergia(data) {
|
||||
LibrusSynergiaMarkAllAnnouncementsAsRead(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String) {
|
||||
LibrusLoginPortal(data) {
|
||||
LibrusLoginApi(data) {
|
||||
LibrusLoginSynergia(data) {
|
||||
LibrusLoginMessages(data) {
|
||||
LibrusMessagesGetAttachment(data, messageId, attachmentId, attachmentName) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC = 1023
|
||||
const val ENDPOINT_LIBRUS_API_TEXT_GC = 1024
|
||||
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC = 1025
|
||||
const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GC = 1026
|
||||
const val ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS = 1030
|
||||
const val ENDPOINT_LIBRUS_API_NORMAL_GRADES = 1031
|
||||
const val ENDPOINT_LIBRUS_API_POINT_GRADES = 1032
|
||||
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES = 1033
|
||||
@ -97,6 +98,7 @@ val LibrusFeatures = listOf(
|
||||
ENDPOINT_LIBRUS_API_TEXT_GC to LOGIN_METHOD_LIBRUS_API,
|
||||
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC to LOGIN_METHOD_LIBRUS_API,
|
||||
ENDPOINT_LIBRUS_API_BEHAVIOUR_GC to LOGIN_METHOD_LIBRUS_API,
|
||||
ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS to LOGIN_METHOD_LIBRUS_API,
|
||||
ENDPOINT_LIBRUS_API_NORMAL_GRADES to LOGIN_METHOD_LIBRUS_API,
|
||||
ENDPOINT_LIBRUS_API_POINT_GRADES to LOGIN_METHOD_LIBRUS_API,
|
||||
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES to LOGIN_METHOD_LIBRUS_API,
|
||||
|
@ -85,6 +85,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_grades)
|
||||
LibrusApiGrades(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_grade_comments)
|
||||
LibrusApiGradeComments(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_LIBRUS_API_NORMAL_GC -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
|
||||
LibrusApiGradeCategories(data, onSuccess)
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -29,15 +29,6 @@ open class LibrusSynergia(open val data: DataLibrus) {
|
||||
|
||||
val callback = object : TextCallbackHandler() {
|
||||
override fun onSuccess(text: String?, response: Response?) {
|
||||
val location = response?.headers()?.get("Location")
|
||||
if (location?.endsWith("przerwa_techniczna") == true) {
|
||||
// double checking for maintenance?
|
||||
data.error(ApiError(TAG, ERROR_LIBRUS_SYNERGIA_MAINTENANCE)
|
||||
.withApiResponse(text)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
if (text.isNullOrEmpty()) {
|
||||
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
|
||||
.withResponse(response))
|
||||
|
@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_EVENTS
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
@ -69,6 +70,8 @@ class LibrusApiEvents(override val data: DataLibrus,
|
||||
))
|
||||
}
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
|
||||
|
||||
data.setSyncNext(ENDPOINT_LIBRUS_API_EVENTS, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-20
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.asJsonObjectList
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
|
||||
import pl.szczodrzynski.edziennik.getJsonArray
|
||||
import pl.szczodrzynski.edziennik.getLong
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
|
||||
class LibrusApiGradeComments(override val data: DataLibrus,
|
||||
val onSuccess: () -> Unit) : LibrusApi(data) {
|
||||
companion object {
|
||||
const val TAG = "LibrusApiGradeComments"
|
||||
}
|
||||
|
||||
init {
|
||||
apiGet(TAG, "Grades/Comments") { json ->
|
||||
|
||||
json.getJsonArray("Comments")?.asJsonObjectList()?.forEach { comment ->
|
||||
val id = comment.getLong("Id") ?: return@forEach
|
||||
val text = comment.getString("Text")
|
||||
|
||||
val gradeCategoryObject = GradeCategory(
|
||||
profileId,
|
||||
id,
|
||||
-1f,
|
||||
-1,
|
||||
text
|
||||
).apply {
|
||||
type = GradeCategory.TYPE_COMMENT
|
||||
}
|
||||
|
||||
data.gradeCategories.put(id, gradeCategoryObject)
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
@ -42,12 +43,21 @@ class LibrusApiGrades(override val data: DataLibrus,
|
||||
weight = 0f
|
||||
}
|
||||
|
||||
val description = grade.getJsonArray("Comments")?.asJsonObjectList()?.let { comments ->
|
||||
if (comments.isNotEmpty()) {
|
||||
data.gradeCategories.singleOrNull {
|
||||
it.type == GradeCategory.TYPE_COMMENT
|
||||
&& it.categoryId == comments[0].asJsonObject.getLong("Id")
|
||||
}?.text
|
||||
} else null
|
||||
} ?: ""
|
||||
|
||||
val gradeObject = Grade(
|
||||
profileId,
|
||||
id,
|
||||
categoryName,
|
||||
color,
|
||||
"",
|
||||
description,
|
||||
name,
|
||||
value,
|
||||
weight,
|
||||
|
@ -8,6 +8,7 @@ import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
@ -55,6 +56,8 @@ class LibrusApiHomework(override val data: DataLibrus,
|
||||
))
|
||||
}
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
|
||||
|
||||
data.setSyncNext(ENDPOINT_LIBRUS_API_HOMEWORK, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ 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
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
class LibrusApiTimetables(override val data: DataLibrus,
|
||||
val onSuccess: () -> Unit) : LibrusApi(data) {
|
||||
@ -29,9 +30,18 @@ class LibrusApiTimetables(override val data: DataLibrus,
|
||||
data.db.classroomDao().getAllNow(profileId).toSparseArray(data.classrooms) { it.id }
|
||||
}
|
||||
|
||||
val currentWeekStart = Date.getToday().let { it.stepForward(0, 0, -it.weekDay) }
|
||||
val currentWeekStart = Week.getWeekStart()
|
||||
|
||||
if (Date.getToday().weekDay > 4) {
|
||||
currentWeekStart.stepForward(0, 0, 7)
|
||||
}
|
||||
|
||||
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
|
||||
apiGet(TAG, "Timetables?weekStart=$getDate") { json ->
|
||||
|
||||
val weekStart = Date.fromY_m_d(getDate)
|
||||
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
|
||||
|
||||
apiGet(TAG, "Timetables?weekStart=${weekStart.stringY_m_d}") { json ->
|
||||
val days = json.getJsonObject("Timetable")
|
||||
|
||||
days?.entrySet()?.forEach { (dateString, dayEl) ->
|
||||
@ -57,8 +67,6 @@ class LibrusApiTimetables(override val data: DataLibrus,
|
||||
}
|
||||
}
|
||||
|
||||
val weekStart = Date.fromY_m_d(getDate)
|
||||
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
|
||||
d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate")
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
|
||||
@ -67,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
|
||||
|
||||
@ -80,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
|
||||
@ -176,17 +183,21 @@ class LibrusApiTimetables(override val data: DataLibrus,
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import pl.szczodrzynski.edziennik.api.v2.POST
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergia
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
@ -55,19 +56,18 @@ class LibrusSynergiaHomework(override val data: DataLibrus, val onSuccess: () ->
|
||||
val id = "/podglad/([0-9]+)'".toRegex().find(
|
||||
elements[9].select("input").attr("onclick")
|
||||
)?.get(1)?.toLong() ?: return@forEachIndexed
|
||||
val startTime = data.lessonList.singleOrNull {
|
||||
it.weekDay == eventDate.weekDay && it.subjectId == subjectId
|
||||
}?.startTime
|
||||
|
||||
val lessons = data.db.timetableDao().getForDateNow(profileId, eventDate)
|
||||
val startTime = lessons.firstOrNull { it.subjectId == subjectId }?.startTime
|
||||
|
||||
val moreInfo = graphElements[2 * i + 1].select("td[title]")
|
||||
.attr("title").trim()
|
||||
val description = "Treść: (.*)".toRegex(RegexOption.DOT_MATCHES_ALL).find(moreInfo)
|
||||
?.get(1)?.replace("<br.*/>".toRegex(), "\n")?.trim()
|
||||
|
||||
val notified = when (profile?.empty) {
|
||||
val seen = when (profile?.empty) {
|
||||
true -> true
|
||||
false -> Date.getToday() < eventDate
|
||||
else -> false
|
||||
else -> eventDate < Date.getToday()
|
||||
}
|
||||
|
||||
val eventObject = Event(
|
||||
@ -89,13 +89,15 @@ class LibrusSynergiaHomework(override val data: DataLibrus, val onSuccess: () ->
|
||||
profileId,
|
||||
Metadata.TYPE_HOMEWORK,
|
||||
id,
|
||||
notified,
|
||||
notified,
|
||||
seen,
|
||||
seen,
|
||||
addedDate
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
|
||||
|
||||
// because this requires a synergia login (2 more requests) sync this every two hours or if explicit :D
|
||||
data.setSyncNext(ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK, 2 * HOUR, DRAWER_ITEM_HOMEWORK)
|
||||
onSuccess()
|
||||
|
@ -101,7 +101,7 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
val loginElement = doc.createElement("login")
|
||||
loginElement.appendChild(doc.createTextNode(data.apiLogin))
|
||||
dataElement.appendChild(loginElement)
|
||||
val passwordElement = doc.createElement("login")
|
||||
val passwordElement = doc.createElement("password")
|
||||
passwordElement.appendChild(doc.createTextNode(data.apiPassword))
|
||||
dataElement.appendChild(passwordElement)
|
||||
val keyStrokeElement = doc.createElement("KeyStroke")
|
||||
@ -150,6 +150,7 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
private fun saveSessionId(response: Response?, text: String?) {
|
||||
var sessionId = data.app.cookieJar.getCookie("wiadomosci.librus.pl", "DZIENNIKSID")
|
||||
sessionId = sessionId?.replace("-MAINT", "") // dunno what's this
|
||||
sessionId = sessionId?.replace("MAINT", "") // dunno what's this
|
||||
if (sessionId == null) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID)
|
||||
.withResponse(response)
|
||||
|
@ -99,6 +99,14 @@ class LibrusLoginPortal(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
.post()
|
||||
.callback(object : JsonCallbackHandler() {
|
||||
override fun onSuccess(json: JsonObject?, response: Response) {
|
||||
val location = response.headers()?.get("Location")
|
||||
if (location == "http://localhost/bar?command=close") {
|
||||
data.error(ApiError(TAG, ERROR_LIBRUS_PORTAL_MAINTENANCE)
|
||||
.withApiResponse(json)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
if (json == null) {
|
||||
if (response.parserErrorBody?.contains("wciąż nieaktywne") == true) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_PORTAL_NOT_ACTIVATED)
|
||||
|
@ -10,8 +10,10 @@ import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikData
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebGetMessage
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.firstlogin.MobidziennikFirstLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLoginWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennikLoginMethods
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.api.v2.prepare
|
||||
@ -63,13 +65,21 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
|
||||
}
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
|
||||
MobidziennikLoginWeb(data) {
|
||||
MobidziennikWebGetMessage(data, message) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
|
||||
}
|
||||
|
||||
override fun getAttachment(messageId: Long, attachmentId: Long, attachmentName: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
MobidziennikFirstLogin(data) {
|
||||
completed()
|
||||
|
@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api
|
||||
import androidx.core.util.contains
|
||||
import pl.szczodrzynski.edziennik.api.v2.Regexes
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
@ -74,5 +75,7 @@ class MobidziennikApiEvents(val data: DataMobidziennik, rows: List<String>) {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api
|
||||
|
||||
import androidx.core.util.contains
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
@ -53,5 +54,7 @@ class MobidziennikApiHomework(val data: DataMobidziennik, rows: List<String>) {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>) {
|
||||
}
|
||||
}
|
||||
|
||||
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>) {
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-18.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.jsoup.Jsoup
|
||||
import pl.szczodrzynski.edziennik.api.v2.Regexes
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.MessageGetEvent
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipientFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.monthFromName
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class MobidziennikWebGetMessage(
|
||||
override val data: DataMobidziennik,
|
||||
private val message: MessageFull,
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikWebGetMessage"
|
||||
}
|
||||
|
||||
init {
|
||||
val typeUrl = if (message.type == Message.TYPE_SENT)
|
||||
"wiadwyslana"
|
||||
else
|
||||
"wiadodebrana"
|
||||
webGet(TAG, "/dziennik/$typeUrl/?id=${message.id}") { text ->
|
||||
MobidziennikLuckyNumberExtractor(data, text)
|
||||
|
||||
val messageRecipientList = mutableListOf<MessageRecipientFull>()
|
||||
|
||||
val doc = Jsoup.parse(text)
|
||||
|
||||
val content = doc.select("#content").first()
|
||||
|
||||
val body = content.select(".wiadomosc_tresc").first()
|
||||
|
||||
if (message.type == TYPE_RECEIVED) {
|
||||
var readDate = System.currentTimeMillis()
|
||||
Regexes.MOBIDZIENNIK_MESSAGE_READ_DATE.find(body.html())?.let {
|
||||
val date = Date(
|
||||
it[3].toIntOrNull() ?: 2019,
|
||||
monthFromName(it[2]),
|
||||
it[1].toIntOrNull() ?: 1
|
||||
)
|
||||
val time = Time.fromH_m_s(
|
||||
it[4] // TODO blank string safety
|
||||
)
|
||||
readDate = date.combineWith(time)
|
||||
}
|
||||
|
||||
val recipient = MessageRecipientFull(
|
||||
profileId,
|
||||
-1,
|
||||
-1,
|
||||
readDate,
|
||||
message.id
|
||||
)
|
||||
|
||||
recipient.fullName = profile?.accountNameLong ?: profile?.studentNameLong
|
||||
|
||||
messageRecipientList.add(recipient)
|
||||
} else {
|
||||
message.senderId = -1
|
||||
message.senderReplyId = -1
|
||||
|
||||
content.select("table.spis tr:has(td)")?.forEach { recipientEl ->
|
||||
val senderEl = recipientEl.select("td:eq(0)").first()
|
||||
val senderName = senderEl.text()
|
||||
|
||||
val teacher = data.teacherList.singleOrNull { it.fullNameLastFirst == senderName }
|
||||
val receiverId = teacher?.id ?: -1
|
||||
|
||||
var readDate = 0L
|
||||
val isReadEl = recipientEl.select("td:eq(2)").first()
|
||||
if (isReadEl.ownText() != "NIE") {
|
||||
val readDateEl = recipientEl.select("td:eq(3) small").first()
|
||||
Regexes.MOBIDZIENNIK_MESSAGE_SENT_READ_DATE.find(readDateEl.ownText())?.let {
|
||||
val date = Date(
|
||||
it[3].toIntOrNull() ?: 2019,
|
||||
monthFromName(it[2]),
|
||||
it[1].toIntOrNull() ?: 1
|
||||
)
|
||||
val time = Time.fromH_m_s(
|
||||
it[4] // TODO blank string safety
|
||||
)
|
||||
readDate = date.combineWith(time)
|
||||
}
|
||||
}
|
||||
|
||||
val recipient = MessageRecipientFull(
|
||||
profileId,
|
||||
receiverId,
|
||||
-1,
|
||||
readDate,
|
||||
message.id
|
||||
)
|
||||
|
||||
recipient.fullName = teacher?.fullName ?: "?"
|
||||
|
||||
messageRecipientList.add(recipient)
|
||||
}
|
||||
}
|
||||
|
||||
// this line removes the sender and read date details
|
||||
body.select("div").remove()
|
||||
|
||||
// this needs to be at the end
|
||||
message.apply {
|
||||
this.body = body.html()
|
||||
|
||||
clearAttachments()
|
||||
content.select("ul li").map { it.select("a").first() }.forEach {
|
||||
val attachmentName = it.ownText()
|
||||
Regexes.MOBIDZIENNIK_MESSAGE_ATTACHMENT.find(it.outerHtml())?.let { match ->
|
||||
val attachmentId = match[1].toLong()
|
||||
var size = match[2].toFloatOrNull() ?: 0f
|
||||
when (match[3]) {
|
||||
"K" -> size *= 1024f
|
||||
"M" -> size *= 1024f * 1024f
|
||||
"G" -> size *= 1024f * 1024f * 1024f
|
||||
}
|
||||
message.addAttachment(attachmentId, attachmentName, size.toLong())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!message.seen) { // TODO discover why this monstrosity instead of MetadataDao.setSeen
|
||||
data.messageMetadataList.add(Metadata(
|
||||
message.profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
message.id,
|
||||
true,
|
||||
true,
|
||||
message.addedDate
|
||||
))
|
||||
}
|
||||
|
||||
message.recipients = messageRecipientList
|
||||
data.messageRecipientList.addAll(messageRecipientList)
|
||||
data.messageList.add(message)
|
||||
|
||||
EventBus.getDefault().postSticky(MessageGetEvent(message))
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
@ -283,6 +283,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
when (model) {
|
||||
is DataRemoveModel.Timetable -> model.commit(profileId, db.timetableDao())
|
||||
is DataRemoveModel.Grades -> model.commit(profileId, db.gradeDao())
|
||||
is DataRemoveModel.Events -> model.commit(profileId, db.eventDao())
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,7 +306,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
|
||||
db.gradeDao().addAll(gradeList)
|
||||
}
|
||||
if (eventList.isNotEmpty()) {
|
||||
db.eventDao().removeFuture(profile.id, Date.getToday())
|
||||
db.eventDao().addAll(eventList)
|
||||
}
|
||||
if (noticeList.isNotEmpty()) {
|
||||
@ -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) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.models
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventDao
|
||||
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
|
||||
@ -15,21 +16,23 @@ open class DataRemoveModel {
|
||||
fun to(dateTo: Date) = Timetable(null, dateTo)
|
||||
fun between(dateFrom: Date, dateTo: Date) = Timetable(dateFrom, dateTo)
|
||||
}
|
||||
|
||||
fun commit(profileId: Int, dao: TimetableDao) {
|
||||
if (dateFrom != null && dateTo != null) {
|
||||
dao.clearBetweenDates(profileId, dateFrom, dateTo)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
dateFrom?.let { dateFrom -> dao.clearFromDate(profileId, dateFrom) }
|
||||
dateTo?.let { dateTo -> dao.clearToDate(profileId, dateTo) }
|
||||
}
|
||||
}
|
||||
}
|
||||
class Grades(val all: Boolean, val semester: Int?) : DataRemoveModel() {
|
||||
|
||||
class Grades(private val all: Boolean, private val semester: Int?) : DataRemoveModel() {
|
||||
companion object {
|
||||
fun all() = Grades(true, null)
|
||||
fun semester(semester: Int) = Grades(false, semester)
|
||||
}
|
||||
|
||||
fun commit(profileId: Int, dao: GradeDao) {
|
||||
if (all) {
|
||||
dao.clear(profileId)
|
||||
@ -37,4 +40,16 @@ open class DataRemoveModel {
|
||||
semester?.let { dao.clearForSemester(profileId, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Events(private val type: Int?, private val exceptType: Int?) : DataRemoveModel() {
|
||||
companion object {
|
||||
fun futureExceptType(exceptType: Int) = Events(null, exceptType)
|
||||
fun futureWithType(type: Int) = Events(type, null)
|
||||
}
|
||||
|
||||
fun commit(profileId: Int, dao: EventDao) {
|
||||
type?.let { dao.removeFutureWithType(profileId, Date.getToday(), it) }
|
||||
exceptType?.let { dao.removeFutureExceptType(profileId, Date.getToday(), it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -7,15 +7,14 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_VULCAN_API
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.Data
|
||||
import pl.szczodrzynski.edziennik.currentTimeUnix
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
|
||||
|
||||
class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
|
||||
|
||||
fun isApiLoginValid() = apiCertificateExpiryTime-30 > currentTimeUnix()
|
||||
&& apiCertificateKey.isNotNullNorEmpty()
|
||||
fun isApiLoginValid() = /*apiCertificateExpiryTime-30 > currentTimeUnix()
|
||||
&&*/ apiCertificateKey.isNotNullNorEmpty()
|
||||
&& apiCertificatePrivate.isNotNullNorEmpty()
|
||||
&& symbol.isNotNullNorEmpty()
|
||||
|
||||
@ -165,6 +164,8 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
"GD1" -> "https://uonetplus-komunikacja.edu.gdansk.pl"
|
||||
"KA1" -> "https://uonetplus-komunikacja.mcuw.katowice.eu"
|
||||
"KA2" -> "https://uonetplus-komunikacja-test.mcuw.katowice.eu"
|
||||
"LU1" -> "https://uonetplus-komunikacja.edu.lublin.eu"
|
||||
"LU2" -> "https://test-uonetplus-komunikacja.edu.lublin.eu"
|
||||
"P03" -> "https://efeb-komunikacja-pro-efebmobile.pro.vulcan.pl"
|
||||
"P01" -> "http://efeb-komunikacja.pro-hudson.win.vulcan.pl"
|
||||
"P02" -> "http://efeb-komunikacja.pro-hudsonrc.win.vulcan.pl"
|
||||
@ -173,7 +174,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
||||
"SZ9" -> "http://vulcan.szkolny.eu"
|
||||
else -> null
|
||||
}
|
||||
return if (url != null) "$url/$symbol" else null
|
||||
return if (url != null) "$url/$symbol" else loginStore.getLoginData("apiUrl", null)
|
||||
}
|
||||
|
||||
val fullApiUrl: String?
|
||||
|
@ -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()
|
||||
|
@ -6,6 +6,7 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_EVENTS
|
||||
import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_EVENTS
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_HOMEWORK
|
||||
@ -91,8 +92,14 @@ class VulcanApiEvents(override val data: DataVulcan, private val isHomework: Boo
|
||||
}
|
||||
|
||||
when (isHomework) {
|
||||
true -> data.setSyncNext(ENDPOINT_VULCAN_API_HOMEWORK, SYNC_ALWAYS)
|
||||
false -> data.setSyncNext(ENDPOINT_VULCAN_API_EVENTS, SYNC_ALWAYS)
|
||||
true -> {
|
||||
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
|
||||
data.setSyncNext(ENDPOINT_VULCAN_API_HOMEWORK, SYNC_ALWAYS)
|
||||
}
|
||||
false -> {
|
||||
data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
|
||||
data.setSyncNext(ENDPOINT_VULCAN_API_EVENTS, SYNC_ALWAYS)
|
||||
}
|
||||
}
|
||||
onSuccess()
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api
|
||||
|
||||
import androidx.core.util.set
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.Regexes
|
||||
import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_TIMETABLE
|
||||
@ -14,173 +15,199 @@ import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.crc16
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week
|
||||
|
||||
class VulcanApiTimetable(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) {
|
||||
companion object {
|
||||
const val TAG = "VulcanApiTimetable"
|
||||
}
|
||||
|
||||
init {
|
||||
data.profile?.also { profile ->
|
||||
val currentWeekStart = Date.getToday().let { it.stepForward(0, 0, -it.weekDay) }
|
||||
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
|
||||
init { data.profile?.also { profile ->
|
||||
val currentWeekStart = Week.getWeekStart()
|
||||
|
||||
val weekStart = Date.fromY_m_d(getDate)
|
||||
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
|
||||
if (Date.getToday().weekDay > 4) {
|
||||
currentWeekStart.stepForward(0, 0, 7)
|
||||
}
|
||||
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_TIMETABLE, parameters = mapOf(
|
||||
"DataPoczatkowa" to weekStart.stringY_m_d,
|
||||
"DataKoncowa" to weekEnd.stringY_m_d,
|
||||
"IdUczen" to data.studentId,
|
||||
"IdOddzial" to data.studentClassId,
|
||||
"IdOkresKlasyfikacyjny" to data.studentSemesterId
|
||||
)) { json, _ ->
|
||||
val dates: MutableSet<Int> = mutableSetOf()
|
||||
val lessons: MutableList<Lesson> = mutableListOf()
|
||||
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
|
||||
|
||||
json.getJsonArray("Data")?.asJsonObjectList()?.forEach { lesson ->
|
||||
val lessonDate = Date.fromY_m_d(lesson.getString("DzienTekst"))
|
||||
val lessonNumber = lesson.getInt("NumerLekcji")
|
||||
val lessonRange = data.lessonRanges.singleOrNull { it.lessonNumber == lessonNumber }
|
||||
val startTime = lessonRange?.startTime
|
||||
val endTime = lessonRange?.endTime
|
||||
val teacherId = lesson.getLong("IdPracownik")
|
||||
val teamId = data.studentClassId.toLong()
|
||||
val classroom = lesson.getString("Classroom")
|
||||
val weekStart = Date.fromY_m_d(getDate)
|
||||
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
|
||||
|
||||
val oldTeacherId = lesson.getLong("IdPracownikOld")
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_TIMETABLE, parameters = mapOf(
|
||||
"DataPoczatkowa" to weekStart.stringY_m_d,
|
||||
"DataKoncowa" to weekEnd.stringY_m_d,
|
||||
"IdUczen" to data.studentId,
|
||||
"IdOddzial" to data.studentClassId,
|
||||
"IdOkresKlasyfikacyjny" to data.studentSemesterId
|
||||
)) { json, _ ->
|
||||
val dates = mutableSetOf<Int>()
|
||||
val lessons = mutableListOf<Lesson>()
|
||||
|
||||
val changeAnnotation = lesson.getString("AdnotacjaOZmianie") ?: ""
|
||||
val type = when {
|
||||
changeAnnotation.startsWith("(przeniesiona z") -> Lesson.TYPE_SHIFTED_TARGET
|
||||
changeAnnotation.startsWith("(przeniesiona na") -> Lesson.TYPE_SHIFTED_SOURCE
|
||||
changeAnnotation.startsWith("(zastępstwo") -> Lesson.TYPE_CHANGE
|
||||
lesson.getBoolean("PrzekreslonaNazwa") == true -> Lesson.TYPE_CANCELLED
|
||||
else -> Lesson.TYPE_NORMAL
|
||||
json.getJsonArray("Data")?.asJsonObjectList()?.forEach { lesson ->
|
||||
if (lesson.getBoolean("PlanUcznia") != true)
|
||||
return@forEach
|
||||
val lessonDate = Date.fromY_m_d(lesson.getString("DzienTekst"))
|
||||
val lessonNumber = lesson.getInt("NumerLekcji")
|
||||
val lessonRange = data.lessonRanges.singleOrNull { it.lessonNumber == lessonNumber }
|
||||
val startTime = lessonRange?.startTime
|
||||
val endTime = lessonRange?.endTime
|
||||
val teacherId = lesson.getLong("IdPracownik")
|
||||
val classroom = lesson.getString("Sala")
|
||||
|
||||
val oldTeacherId = lesson.getLong("IdPracownikOld")
|
||||
|
||||
val changeAnnotation = lesson.getString("AdnotacjaOZmianie") ?: ""
|
||||
val type = when {
|
||||
changeAnnotation.startsWith("(przeniesiona z") -> Lesson.TYPE_SHIFTED_TARGET
|
||||
changeAnnotation.startsWith("(przeniesiona na") -> Lesson.TYPE_SHIFTED_SOURCE
|
||||
changeAnnotation.startsWith("(zastępstwo") -> Lesson.TYPE_CHANGE
|
||||
lesson.getBoolean("PrzekreslonaNazwa") == true -> Lesson.TYPE_CANCELLED
|
||||
else -> Lesson.TYPE_NORMAL
|
||||
}
|
||||
|
||||
val teamId = lesson.getString("PodzialSkrot")?.let { teamName ->
|
||||
val name = "${data.teamClass?.name} $teamName"
|
||||
val id = name.crc16().toLong()
|
||||
var team = data.teamList.singleOrNull { it.name == name }
|
||||
if (team == null) {
|
||||
team = Team(
|
||||
profileId,
|
||||
id,
|
||||
name,
|
||||
Team.TYPE_VIRTUAL,
|
||||
"${data.schoolName}:$name",
|
||||
teacherId ?: oldTeacherId ?: -1
|
||||
)
|
||||
data.teamList[id] = team
|
||||
}
|
||||
team.id
|
||||
} ?: data.studentClassId.toLong()
|
||||
|
||||
val subjectId = lesson.getLong("IdPrzedmiot")?.let {
|
||||
when (it) {
|
||||
0L -> {
|
||||
val subjectName = lesson.getString("PrzedmiotNazwa") ?: ""
|
||||
val subjectId = lesson.getLong("IdPrzedmiot")?.let {
|
||||
when (it) {
|
||||
0L -> {
|
||||
val subjectName = lesson.getString("PrzedmiotNazwa") ?: ""
|
||||
|
||||
data.subjectList.singleOrNull { subject -> subject.longName == subjectName }?.id
|
||||
?: {
|
||||
/**
|
||||
* CREATE A NEW SUBJECT IF IT DOESN'T EXIST
|
||||
*/
|
||||
data.subjectList.singleOrNull { subject -> subject.longName == subjectName }?.id
|
||||
?: {
|
||||
/**
|
||||
* CREATE A NEW SUBJECT IF IT DOESN'T EXIST
|
||||
*/
|
||||
|
||||
val subjectObject = Subject(
|
||||
profileId,
|
||||
-1 * crc16(subjectName.toByteArray()).toLong(),
|
||||
subjectName,
|
||||
subjectName
|
||||
)
|
||||
data.subjectList.put(subjectObject.id, subjectObject)
|
||||
subjectObject.id
|
||||
}.invoke()
|
||||
}
|
||||
else -> it
|
||||
val subjectObject = Subject(
|
||||
profileId,
|
||||
-1 * crc16(subjectName.toByteArray()).toLong(),
|
||||
subjectName,
|
||||
subjectName
|
||||
)
|
||||
data.subjectList.put(subjectObject.id, subjectObject)
|
||||
subjectObject.id
|
||||
}.invoke()
|
||||
}
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
|
||||
val lessonObject = Lesson(profileId, -1).apply {
|
||||
this.type = type
|
||||
|
||||
when (type) {
|
||||
Lesson.TYPE_NORMAL, Lesson.TYPE_CHANGE, Lesson.TYPE_SHIFTED_TARGET -> {
|
||||
this.date = lessonDate
|
||||
this.lessonNumber = lessonNumber
|
||||
this.startTime = startTime
|
||||
this.endTime = endTime
|
||||
this.subjectId = subjectId
|
||||
this.teacherId = teacherId
|
||||
this.teamId = teamId
|
||||
this.classroom = classroom
|
||||
|
||||
this.oldTeacherId = oldTeacherId
|
||||
}
|
||||
|
||||
Lesson.TYPE_CANCELLED, Lesson.TYPE_SHIFTED_SOURCE -> {
|
||||
this.oldDate = lessonDate
|
||||
this.oldLessonNumber = lessonNumber
|
||||
this.oldStartTime = startTime
|
||||
this.oldEndTime = endTime
|
||||
this.oldSubjectId = subjectId
|
||||
this.oldTeacherId = teacherId
|
||||
this.oldTeamId = teamId
|
||||
this.oldClassroom = classroom
|
||||
}
|
||||
}
|
||||
|
||||
val id = lessonDate.combineWith(startTime) / 6L * 10L + (lesson.hashCode() and 0xFFFF)
|
||||
if (type == Lesson.TYPE_SHIFTED_SOURCE || type == Lesson.TYPE_SHIFTED_TARGET) {
|
||||
val shift = Regexes.VULCAN_SHITFT_ANNOTATION.find(changeAnnotation)
|
||||
val oldLessonNumber = shift?.get(2)?.toInt()
|
||||
val oldLessonDate = shift?.get(3)?.let { Date.fromd_m_Y(it) }
|
||||
|
||||
val lessonObject = Lesson(profileId, id).apply {
|
||||
this.type = type
|
||||
val oldLessonRange = data.lessonRanges.singleOrNull { it.lessonNumber == oldLessonNumber }
|
||||
val oldStartTime = oldLessonRange?.startTime
|
||||
val oldEndTime = oldLessonRange?.endTime
|
||||
|
||||
when (type) {
|
||||
Lesson.TYPE_NORMAL, Lesson.TYPE_CHANGE, Lesson.TYPE_SHIFTED_TARGET -> {
|
||||
this.date = lessonDate
|
||||
this.lessonNumber = lessonNumber
|
||||
this.startTime = startTime
|
||||
this.endTime = endTime
|
||||
this.subjectId = subjectId
|
||||
this.teacherId = teacherId
|
||||
this.teamId = teamId
|
||||
this.classroom = classroom
|
||||
|
||||
this.oldTeacherId = oldTeacherId
|
||||
Lesson.TYPE_SHIFTED_SOURCE -> {
|
||||
this.lessonNumber = oldLessonNumber
|
||||
this.date = oldLessonDate
|
||||
this.startTime = oldStartTime
|
||||
this.endTime = oldEndTime
|
||||
}
|
||||
|
||||
Lesson.TYPE_CANCELLED, Lesson.TYPE_SHIFTED_SOURCE -> {
|
||||
this.oldDate = lessonDate
|
||||
this.oldLessonNumber = lessonNumber
|
||||
this.oldStartTime = startTime
|
||||
this.oldEndTime = endTime
|
||||
this.oldSubjectId = subjectId
|
||||
this.oldTeacherId = teacherId
|
||||
this.oldTeamId = teamId
|
||||
this.oldClassroom = classroom
|
||||
}
|
||||
}
|
||||
|
||||
if (type == Lesson.TYPE_SHIFTED_SOURCE || type == Lesson.TYPE_SHIFTED_TARGET) {
|
||||
val shift = Regexes.VULCAN_SHITFT_ANNOTATION.find(changeAnnotation)
|
||||
val oldLessonNumber = shift?.get(2)?.toInt()
|
||||
val oldLessonDate = shift?.get(3)?.let { Date.fromd_m_Y(it) }
|
||||
|
||||
val oldLessonRange = data.lessonRanges.singleOrNull { it.lessonNumber == oldLessonNumber }
|
||||
val oldStartTime = oldLessonRange?.startTime
|
||||
val oldEndTime = oldLessonRange?.endTime
|
||||
|
||||
when (type) {
|
||||
Lesson.TYPE_SHIFTED_SOURCE -> {
|
||||
this.lessonNumber = oldLessonNumber
|
||||
this.date = oldLessonDate
|
||||
this.startTime = oldStartTime
|
||||
this.endTime = oldEndTime
|
||||
}
|
||||
|
||||
Lesson.TYPE_SHIFTED_TARGET -> {
|
||||
this.oldLessonNumber = oldLessonNumber
|
||||
this.oldDate = oldLessonDate
|
||||
this.oldStartTime = oldStartTime
|
||||
this.oldEndTime = oldEndTime
|
||||
}
|
||||
Lesson.TYPE_SHIFTED_TARGET -> {
|
||||
this.oldLessonNumber = oldLessonNumber
|
||||
this.oldDate = oldLessonDate
|
||||
this.oldStartTime = oldStartTime
|
||||
this.oldEndTime = oldEndTime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type != Lesson.TYPE_NORMAL) {
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
|
||||
dates.add(lessonDate.value)
|
||||
lessons.add(lessonObject)
|
||||
this.id = buildId()
|
||||
}
|
||||
|
||||
val date: Date = weekStart.clone()
|
||||
while (date <= weekEnd) {
|
||||
if (!dates.contains(date.value)) {
|
||||
lessons.add(Lesson(profileId, date.value.toLong()).apply {
|
||||
this.type = Lesson.TYPE_NO_LESSONS
|
||||
this.date = date.clone()
|
||||
})
|
||||
}
|
||||
val seen = profile.empty || lessonDate < Date.getToday()
|
||||
|
||||
date.stepForward(0, 0, 1)
|
||||
if (type != Lesson.TYPE_NORMAL) {
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
lessonObject.id,
|
||||
seen,
|
||||
seen,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
|
||||
d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate")
|
||||
|
||||
data.lessonNewList.addAll(lessons)
|
||||
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
|
||||
|
||||
data.setSyncNext(ENDPOINT_VULCAN_API_TIMETABLE, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
dates.add(lessonDate.value)
|
||||
lessons.add(lessonObject)
|
||||
}
|
||||
|
||||
val date: Date = weekStart.clone()
|
||||
while (date <= weekEnd) {
|
||||
if (!dates.contains(date.value)) {
|
||||
lessons.add(Lesson(profileId, date.value.toLong()).apply {
|
||||
this.type = Lesson.TYPE_NO_LESSONS
|
||||
this.date = date.clone()
|
||||
})
|
||||
}
|
||||
|
||||
date.stepForward(0, 0, 1)
|
||||
}
|
||||
|
||||
d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate")
|
||||
|
||||
data.lessonNewList.addAll(lessons)
|
||||
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
|
||||
|
||||
data.setSyncNext(ENDPOINT_VULCAN_API_TIMETABLE, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -38,10 +38,12 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
|
||||
if (data.apiToken?.get(0) == 'F') VULCAN_API_PASSWORD_FAKELOG else VULCAN_API_PASSWORD,
|
||||
data.apiCertificatePfx ?: ""
|
||||
)
|
||||
onSuccess()
|
||||
return@run
|
||||
data.loginStore.removeLoginData("certificatePfx")
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
onSuccess()
|
||||
return@run
|
||||
}
|
||||
}
|
||||
if (data.symbol.isNotNullNorEmpty() && data.apiToken.isNotNullNorEmpty() && data.apiPin.isNotNullNorEmpty()) {
|
||||
|
@ -1,16 +1,17 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.events;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.RawQuery;
|
||||
import androidx.room.Transaction;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -130,6 +131,12 @@ public abstract class EventDao {
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate")
|
||||
public abstract void removeFuture(int profileId, Date todayDate);
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType = :type")
|
||||
public abstract void removeFutureWithType(int profileId, Date todayDate, int type);
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType != :exceptType")
|
||||
public abstract void removeFutureExceptType(int profileId, Date todayDate, int exceptType);
|
||||
|
||||
@Query("UPDATE metadata SET seen = :seen WHERE profileId = :profileId AND (thingType = "+TYPE_EVENT+" OR thingType = "+TYPE_LESSON_CHANGE+" OR thingType = "+TYPE_HOMEWORK+") AND thingId IN (SELECT eventId FROM events WHERE profileId = :profileId AND eventDate = :date)")
|
||||
public abstract void setSeenByDate(int profileId, Date date, boolean seen);
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.modules.grades;
|
||||
|
||||
import androidx.room.Entity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.room.Entity;
|
||||
|
||||
@Entity(tableName = "gradeCategories",
|
||||
primaryKeys = {"profileId", "categoryId"})
|
||||
public class GradeCategory {
|
||||
@ -25,6 +25,9 @@ public class GradeCategory {
|
||||
*/
|
||||
public int type = 0;
|
||||
|
||||
public static final int TYPE_NORMAL = 0;
|
||||
public static final int TYPE_COMMENT = 1;
|
||||
|
||||
public GradeCategory(int profileId, long categoryId, float weight, int color, String text) {
|
||||
this.profileId = profileId;
|
||||
this.categoryId = categoryId;
|
||||
|
@ -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);
|
||||
|
@ -21,7 +21,10 @@ interface NotificationDao {
|
||||
@Query("DELETE FROM notifications WHERE profileId = :profileId")
|
||||
fun clear(profileId: Int)
|
||||
|
||||
@Query("SELECT * FROM notifications")
|
||||
@Query("DELETE FROM notifications")
|
||||
fun clearAll()
|
||||
|
||||
@Query("SELECT * FROM notifications ORDER BY addedDate DESC")
|
||||
fun getAll(): LiveData<List<Notification>>
|
||||
|
||||
@Query("SELECT * FROM notifications")
|
||||
|
@ -6,10 +6,10 @@ import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ATTENDANCE
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.*
|
||||
@ -74,6 +74,7 @@ class ProfileFull : Profile {
|
||||
fragmentIds.add(DRAWER_ITEM_AGENDA)
|
||||
fragmentIds.add(DRAWER_ITEM_GRADES)
|
||||
fragmentIds.add(DRAWER_ITEM_MESSAGES)
|
||||
fragmentIds.add(DRAWER_ITEM_HOMEWORK)
|
||||
fragmentIds.add(DRAWER_ITEM_BEHAVIOUR)
|
||||
fragmentIds.add(DRAWER_ITEM_ATTENDANCE)
|
||||
fragmentIds.add(DRAWER_ITEM_ANNOUNCEMENTS)
|
||||
@ -87,9 +88,7 @@ class ProfileFull : Profile {
|
||||
return fragmentIds
|
||||
}
|
||||
|
||||
constructor() : super() {
|
||||
|
||||
}
|
||||
constructor() : super()
|
||||
|
||||
constructor(profile: Profile, loginStore: LoginStore) {
|
||||
|
||||
@ -122,7 +121,7 @@ class ProfileFull : Profile {
|
||||
}*/
|
||||
}
|
||||
|
||||
constructor(context: Context) : super(context) {}
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
fun canChangeLoginPassword(): Boolean {
|
||||
return loginStoreType == LOGIN_TYPE_MOBIDZIENNIK || loginStoreType == LOGIN_TYPE_LIBRUS || loginStoreType == LOGIN_TYPE_IUCZNIOWIE
|
||||
|
@ -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,27 @@ 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
|
||||
}
|
||||
|
||||
val isCancelled
|
||||
get() = type == TYPE_CANCELLED || type == TYPE_SHIFTED_SOURCE
|
||||
val isChange
|
||||
get() = type == TYPE_CHANGE || type == TYPE_SHIFTED_TARGET
|
||||
|
||||
fun buildId(): Long = (displayDate?.combineWith(displayStartTime) ?: 0L) / 6L * 10L + (hashCode() and 0xFFFF)
|
||||
|
||||
override fun toString(): String {
|
||||
return "Lesson(profileId=$profileId, " +
|
||||
"id=$id, " +
|
||||
@ -66,32 +87,53 @@ open class Lesson(val profileId: Int, @PrimaryKey val id: Long) {
|
||||
"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,
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Lesson) return false
|
||||
|
||||
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,
|
||||
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
|
||||
|
||||
PRIMARY KEY(profileId)
|
||||
);
|
||||
*/
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int { // intentionally ignoring ID and display* here
|
||||
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
|
||||
}
|
||||
}
|
@ -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,8 +75,35 @@ 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
|
||||
var addedDate: Long = 0
|
||||
}
|
||||
}
|
||||
|
@ -38,27 +38,42 @@ interface TimetableDao {
|
||||
@Query("DELETE FROM timetable WHERE profileId = :profileId")
|
||||
fun clear(profileId: Int)
|
||||
|
||||
@Query("DELETE FROM timetable WHERE profileId = :profileId AND (type != 3 AND date >= :dateFrom) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom)")
|
||||
@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)")
|
||||
@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)")
|
||||
@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("""
|
||||
$QUERY
|
||||
WHERE timetable.profileId = :profileId AND (type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date)
|
||||
WHERE timetable.profileId = :profileId AND ((type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date))
|
||||
ORDER BY id, type
|
||||
""")
|
||||
fun getForDate(profileId: Int, date: Date) : LiveData<List<LessonFull>>
|
||||
|
||||
@Query("""
|
||||
$QUERY
|
||||
WHERE timetable.profileId = :profileId AND ((type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date))
|
||||
ORDER BY id, type
|
||||
""")
|
||||
fun getForDateNow(profileId: Int, date: Date) : List<LessonFull>
|
||||
|
||||
@Query("""
|
||||
$QUERY
|
||||
WHERE timetable.profileId = :profileId AND ((type != 3 AND date > :today) OR ((type = 3 OR type = 1) AND oldDate > :today)) AND timetable.subjectId = :subjectId
|
||||
ORDER BY id, type
|
||||
LIMIT 1
|
||||
""")
|
||||
fun getNextWithSubject(profileId: Int, today: Date, subjectId: Long) : LiveData<LessonFull>
|
||||
fun getNextWithSubject(profileId: Int, today: Date, subjectId: Long) : LiveData<LessonFull?>
|
||||
|
||||
@Query("""
|
||||
$QUERY
|
||||
WHERE timetable.profileId = :profileId AND ((type != 3 AND date > :today) OR ((type = 3 OR type = 1) AND oldDate > :today)) AND timetable.subjectId = :subjectId AND timetable.teamId = :teamId
|
||||
ORDER BY id, type
|
||||
LIMIT 1
|
||||
""")
|
||||
fun getNextWithSubjectAndTeam(profileId: Int, today: Date, subjectId: Long, teamId: Long): LiveData<LessonFull?>
|
||||
|
||||
@Query("""
|
||||
$QUERY
|
||||
@ -67,10 +82,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>
|
||||
}
|
||||
|
@ -125,19 +125,22 @@ public class BootReceiver extends BroadcastReceiver {
|
||||
String updateUrl = result.get("update_url").getAsString();
|
||||
String updateFilename = result.get("update_filename").getAsString();
|
||||
boolean updateMandatory = result.get("update_mandatory").getAsBoolean();
|
||||
boolean updateDirect = result.get("update_direct").getAsBoolean();
|
||||
|
||||
if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals(updateVersion)) {
|
||||
app.appConfig.updateVersion = updateVersion;
|
||||
app.appConfig.updateUrl = updateUrl;
|
||||
app.appConfig.updateFilename = updateFilename;
|
||||
app.appConfig.updateMandatory = updateMandatory;
|
||||
app.appConfig.updateDirect = updateDirect;
|
||||
app.saveConfig();
|
||||
}
|
||||
if (!UPDATES_ON_PLAY_STORE || intent.getBooleanExtra("UserChecked", false)) {
|
||||
app.notifier.notificationUpdatesShow(
|
||||
updateVersion,
|
||||
updateUrl,
|
||||
updateFilename);
|
||||
updateFilename,
|
||||
updateDirect);
|
||||
}
|
||||
} else {
|
||||
if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals("")) {
|
||||
@ -259,7 +262,7 @@ public class BootReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
if (UPDATES_ON_PLAY_STORE) {
|
||||
if (UPDATES_ON_PLAY_STORE && !intent.getBooleanExtra("update_direct", false)) {
|
||||
Utils.openGooglePlay(this, "pl.szczodrzynski.edziennik");
|
||||
return;
|
||||
}
|
||||
@ -270,7 +273,8 @@ public class BootReceiver extends BroadcastReceiver {
|
||||
app.notifier.notificationUpdatesShow(
|
||||
intent.getStringExtra("update_version"),
|
||||
intent.getStringExtra("update_url"),
|
||||
intent.getStringExtra("update_filename"));
|
||||
intent.getStringExtra("update_filename"),
|
||||
intent.getBooleanExtra("update_direct", false));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -154,19 +154,22 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
|
||||
String updateUrl = remoteMessage.getData().get("update_url");
|
||||
String updateFilename = remoteMessage.getData().get("update_filename");
|
||||
boolean updateMandatory = Boolean.parseBoolean(remoteMessage.getData().get("update_mandatory"));
|
||||
boolean updateDirect = Boolean.parseBoolean(remoteMessage.getData().get("update_direct"));
|
||||
|
||||
if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals(updateVersion)) {
|
||||
app.appConfig.updateVersion = updateVersion;
|
||||
app.appConfig.updateUrl = updateUrl;
|
||||
app.appConfig.updateFilename = updateFilename;
|
||||
app.appConfig.updateMandatory = updateMandatory;
|
||||
app.appConfig.updateDirect = updateDirect;
|
||||
app.saveConfig("updateVersion", "updateUrl", "updateFilename", "updateMandatory");
|
||||
}
|
||||
if (!remoteMessage.getData().containsKey("update_silent")) {
|
||||
app.notifier.notificationUpdatesShow(
|
||||
updateVersion,
|
||||
updateUrl,
|
||||
updateFilename);
|
||||
updateFilename,
|
||||
updateDirect);
|
||||
}
|
||||
} else {
|
||||
if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals("")) {
|
||||
|
@ -517,7 +517,7 @@ public class EventManualDialog {
|
||||
|
||||
registerEventManualDateLayout = dialogView.findViewById(R.id.registerEventManualDateLayout);
|
||||
registerEventManualDate = dialogView.findViewById(R.id.registerEventManualDate);
|
||||
registerEventManualDate.setCompoundDrawablesWithIntrinsicBounds(null, null, new IconicsDrawable(context, CommunityMaterial.Icon.cmd_calendar).size(IconicsSize.dp(16)).color(IconicsColor.colorInt(primaryTextColor)), null);
|
||||
registerEventManualDate.setCompoundDrawablesWithIntrinsicBounds(null, null, new IconicsDrawable(context, CommunityMaterial.Icon.cmd_calendar_outline).size(IconicsSize.dp(16)).color(IconicsColor.colorInt(primaryTextColor)), null);
|
||||
//registerEventManualDate.setCompoundDrawablePadding(Utils.dpToPx(6));
|
||||
registerEventManualLessonLayout = dialogView.findViewById(R.id.registerEventManualLessonLayout);
|
||||
registerEventManualLesson = dialogView.findViewById(R.id.registerEventManualLesson);
|
||||
|
@ -4,19 +4,26 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.dialogs.event
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.material.datepicker.MaterialDatePicker
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialog
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
|
||||
import kotlinx.coroutines.*
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventType
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull
|
||||
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
|
||||
import pl.szczodrzynski.edziennik.utils.Anim
|
||||
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
@ -46,9 +53,11 @@ class EventManualV2Dialog(
|
||||
private val app by lazy { activity.application as App }
|
||||
private lateinit var b: DialogEventManualV2Binding
|
||||
private lateinit var dialog: AlertDialog
|
||||
private lateinit var event: Event
|
||||
private var defaultLoaded = false
|
||||
|
||||
private lateinit var event: Event
|
||||
private var customColor: Int? = null
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
return@run
|
||||
@ -78,11 +87,46 @@ class EventManualV2Dialog(
|
||||
defaultType?.let {
|
||||
event.type = it
|
||||
}*/
|
||||
b.shareSwitch.isChecked = event.sharedBy != null
|
||||
}
|
||||
|
||||
b.showMore.onClick { // TODO iconics is broken
|
||||
it.apply {
|
||||
refreshDrawableState()
|
||||
|
||||
if (isChecked)
|
||||
Anim.expand(b.moreLayout, 200, null)
|
||||
else
|
||||
Anim.collapse(b.moreLayout, 200, null)
|
||||
}
|
||||
}
|
||||
|
||||
updateShareText()
|
||||
b.shareSwitch.onChange { _, isChecked ->
|
||||
updateShareText(isChecked)
|
||||
}
|
||||
|
||||
loadLists()
|
||||
}}
|
||||
|
||||
private fun updateShareText(checked: Boolean = b.shareSwitch.isChecked) {
|
||||
val editingShared = editingEvent?.sharedBy != null
|
||||
val editingOwn = editingEvent?.sharedBy == "self"
|
||||
|
||||
b.shareDetails.visibility = if (checked || editingShared)
|
||||
View.VISIBLE
|
||||
else View.GONE
|
||||
|
||||
val text = when {
|
||||
checked && editingShared && editingOwn -> R.string.dialog_event_manual_share_will_change
|
||||
checked && editingShared -> R.string.dialog_event_manual_share_will_request
|
||||
!checked && editingShared -> R.string.dialog_event_manual_share_will_remove
|
||||
else -> R.string.dialog_event_manual_share_first_notice
|
||||
}
|
||||
|
||||
b.shareDetails.setText(text)
|
||||
}
|
||||
|
||||
private fun loadLists() { launch {
|
||||
val deferred = async(Dispatchers.Default) {
|
||||
// get the team list
|
||||
@ -114,18 +158,33 @@ class EventManualV2Dialog(
|
||||
""
|
||||
)
|
||||
b.teacherDropdown += teachers.map { TextInputDropDown.Item(it.id, it.fullName, tag = it) }
|
||||
|
||||
// get the event type list
|
||||
val eventTypes = app.db.eventTypeDao().getAllNow(profileId)
|
||||
b.typeDropdown.clear()
|
||||
b.typeDropdown += eventTypes.map { TextInputDropDown.Item(it.id, it.name, tag = it) }
|
||||
}
|
||||
deferred.await()
|
||||
|
||||
b.teamDropdown.isEnabled = true
|
||||
b.subjectDropdown.isEnabled = true
|
||||
b.teacherDropdown.isEnabled = true
|
||||
b.typeDropdown.isEnabled = true
|
||||
|
||||
b.typeDropdown.selected?.let { item ->
|
||||
customColor = (item.tag as EventType).color
|
||||
}
|
||||
|
||||
// copy IDs from event being edited
|
||||
editingEvent?.let {
|
||||
b.teamDropdown.select(it.teamId)
|
||||
b.subjectDropdown.select(it.subjectId)
|
||||
b.teacherDropdown.select(it.teacherId)
|
||||
b.typeDropdown.select(it.type)?.let { item ->
|
||||
customColor = (item.tag as EventType).color
|
||||
}
|
||||
if (it.color != -1)
|
||||
customColor = it.color
|
||||
}
|
||||
|
||||
// copy IDs from the LessonFull
|
||||
@ -135,6 +194,30 @@ class EventManualV2Dialog(
|
||||
b.teacherDropdown.select(it.displayTeacherId)
|
||||
}
|
||||
|
||||
b.typeDropdown.setOnChangeListener {
|
||||
b.typeColor.background.colorFilter = PorterDuffColorFilter((it.tag as EventType).color, PorterDuff.Mode.SRC_ATOP)
|
||||
customColor = null
|
||||
return@setOnChangeListener true
|
||||
}
|
||||
(customColor ?: Event.COLOR_DEFAULT).let {
|
||||
b.typeColor.background.colorFilter = PorterDuffColorFilter(it, PorterDuff.Mode.SRC_ATOP)
|
||||
}
|
||||
b.typeColor.onClick {
|
||||
val currentColor = (b.typeDropdown?.selected?.tag as EventType?)?.color ?: Event.COLOR_DEFAULT
|
||||
val colorPickerDialog = ColorPickerDialog.newBuilder()
|
||||
.setColor(currentColor)
|
||||
.create()
|
||||
colorPickerDialog.setColorPickerDialogListener(
|
||||
object : ColorPickerDialogListener {
|
||||
override fun onDialogDismissed(dialogId: Int) {}
|
||||
override fun onColorSelected(dialogId: Int, color: Int) {
|
||||
b.typeColor.background.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)
|
||||
customColor = color
|
||||
}
|
||||
})
|
||||
colorPickerDialog.show(activity.fragmentManager, "color-picker-dialog")
|
||||
}
|
||||
|
||||
loadDates()
|
||||
}}
|
||||
|
||||
@ -205,12 +288,20 @@ class EventManualV2Dialog(
|
||||
val dates = deferred.await()
|
||||
b.dateDropdown.clear().append(dates)
|
||||
|
||||
editingEvent?.let {
|
||||
b.dateDropdown.select(it.eventDate.value.toLong())
|
||||
editingEvent?.eventDate?.let {
|
||||
b.dateDropdown.select(TextInputDropDown.Item(
|
||||
it.value.toLong(),
|
||||
it.formattedString,
|
||||
tag = it
|
||||
))
|
||||
}
|
||||
|
||||
defaultLesson?.let {
|
||||
b.dateDropdown.select(it.displayDate?.value?.toLong())
|
||||
defaultLesson?.displayDate?.let {
|
||||
b.dateDropdown.select(TextInputDropDown.Item(
|
||||
it.value.toLong(),
|
||||
it.formattedString,
|
||||
tag = it
|
||||
))
|
||||
}
|
||||
|
||||
if (b.dateDropdown.selected == null) {
|
||||
@ -223,22 +314,24 @@ class EventManualV2Dialog(
|
||||
when {
|
||||
// next lesson with specified subject
|
||||
item.id < -1 -> {
|
||||
app.db.timetableDao().getNextWithSubject(profileId, Date.getToday(), -item.id).observeOnce(activity, Observer {
|
||||
val teamId = defaultLesson?.teamId ?: -1
|
||||
val selectedLessonDate = defaultLesson?.date ?: Date.getToday()
|
||||
|
||||
when (teamId) {
|
||||
-1L -> app.db.timetableDao().getNextWithSubject(profileId, selectedLessonDate, -item.id)
|
||||
else -> app.db.timetableDao().getNextWithSubjectAndTeam(profileId, selectedLessonDate, -item.id, teamId)
|
||||
}.observeOnce(activity, Observer {
|
||||
val lessonDate = it?.displayDate ?: return@Observer
|
||||
b.dateDropdown.selected = TextInputDropDown.Item(
|
||||
b.dateDropdown.select(TextInputDropDown.Item(
|
||||
lessonDate.value.toLong(),
|
||||
lessonDate.formattedString,
|
||||
tag = lessonDate
|
||||
)
|
||||
// TODO load correct hour when selecting next lesson
|
||||
b.dateDropdown.updateText()
|
||||
it.let {
|
||||
b.teamDropdown.select(it.displayTeamId)
|
||||
b.subjectDropdown.select(it.displaySubjectId)
|
||||
b.teacherDropdown.select(it.displayTeacherId)
|
||||
}
|
||||
))
|
||||
b.teamDropdown.select(it.displayTeamId)
|
||||
b.subjectDropdown.select(it.displaySubjectId)
|
||||
b.teacherDropdown.select(it.displayTeacherId)
|
||||
defaultLoaded = false
|
||||
loadHours()
|
||||
loadHours(it.displayStartTime)
|
||||
})
|
||||
return@setOnChangeListener false
|
||||
}
|
||||
@ -246,17 +339,17 @@ class EventManualV2Dialog(
|
||||
item.id == -1L -> {
|
||||
MaterialDatePicker.Builder
|
||||
.datePicker()
|
||||
.setSelection((b.dateDropdown.selectedId?.let { Date.fromValue(it.toInt()) } ?: Date.getToday()).inMillis)
|
||||
.setSelection((b.dateDropdown.selectedId?.let { Date.fromValue(it.toInt()) }
|
||||
?: Date.getToday()).inMillis)
|
||||
.build()
|
||||
.apply {
|
||||
addOnPositiveButtonClickListener {
|
||||
val dateSelected = Date.fromMillis(it)
|
||||
b.dateDropdown.selected = TextInputDropDown.Item(
|
||||
b.dateDropdown.select(TextInputDropDown.Item(
|
||||
dateSelected.value.toLong(),
|
||||
dateSelected.formattedString,
|
||||
tag = dateSelected
|
||||
)
|
||||
b.dateDropdown.updateText()
|
||||
))
|
||||
loadHours()
|
||||
}
|
||||
show(this@EventManualV2Dialog.activity.supportFragmentManager, "MaterialDatePicker")
|
||||
@ -276,7 +369,7 @@ class EventManualV2Dialog(
|
||||
loadHours()
|
||||
}}
|
||||
|
||||
private fun loadHours() {
|
||||
private fun loadHours(defaultHour: Time? = null) {
|
||||
b.timeDropdown.isEnabled = false
|
||||
// get the selected date
|
||||
val date = b.dateDropdown.selectedId?.let { Date.fromValue(it.toInt()) } ?: return
|
||||
@ -302,7 +395,8 @@ class EventManualV2Dialog(
|
||||
lesson.displayStartTime?.stringHM ?: "",
|
||||
lesson.displaySubjectName?.let {
|
||||
when {
|
||||
lesson.type == Lesson.TYPE_CANCELLED -> it.asStrikethroughSpannable()
|
||||
lesson.type == Lesson.TYPE_CANCELLED
|
||||
|| lesson.type == Lesson.TYPE_SHIFTED_SOURCE -> it.asStrikethroughSpannable()
|
||||
lesson.type != Lesson.TYPE_NORMAL -> it.asItalicSpannable()
|
||||
else -> it
|
||||
}
|
||||
@ -338,6 +432,10 @@ class EventManualV2Dialog(
|
||||
defaultLesson?.let {
|
||||
b.timeDropdown.select(it.displayStartTime?.value?.toLong())
|
||||
}
|
||||
|
||||
defaultHour?.let {
|
||||
b.timeDropdown.select(it.value.toLong())
|
||||
}
|
||||
}
|
||||
defaultLoaded = true
|
||||
b.timeDropdown.isEnabled = true
|
||||
@ -345,16 +443,16 @@ class EventManualV2Dialog(
|
||||
// attach a listener to time dropdown
|
||||
b.timeDropdown.setOnChangeListener { item ->
|
||||
when {
|
||||
// custom start hour
|
||||
item.id == -1L -> {
|
||||
|
||||
return@setOnChangeListener false
|
||||
}
|
||||
// no lessons this day
|
||||
item.id == -2L -> {
|
||||
b.timeDropdown.deselect()
|
||||
return@setOnChangeListener false
|
||||
}
|
||||
// custom start hour
|
||||
item.id == -1L -> {
|
||||
|
||||
return@setOnChangeListener false
|
||||
}
|
||||
// selected a specific lesson
|
||||
else -> {
|
||||
if (item.tag is LessonFull) {
|
||||
@ -383,4 +481,4 @@ class EventManualV2Dialog(
|
||||
private fun saveEvent() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,15 +94,23 @@ 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,
|
||||
selectedViewIds
|
||||
).enqueue(activity)
|
||||
EdziennikTask.syncProfile(App.profileId).enqueue(activity)
|
||||
}
|
||||
.setNegativeButton(R.string.cancel) { _, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.show()
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
@ -132,29 +132,29 @@ class LessonDetailsDialog(
|
||||
if (lesson.type < Lesson.TYPE_SHIFTED_SOURCE && lesson.oldSubjectId != null && lesson.subjectId != lesson.oldSubjectId) {
|
||||
b.oldSubjectName = lesson.oldSubjectName
|
||||
}
|
||||
if (lesson.type != Lesson.TYPE_CANCELLED && lesson.subjectId != null) {
|
||||
if (lesson.type != Lesson.TYPE_CANCELLED && lesson.displaySubjectId != null) {
|
||||
b.subjectName = lesson.subjectName
|
||||
}
|
||||
|
||||
if (lesson.type < Lesson.TYPE_SHIFTED_SOURCE && lesson.oldTeacherId != null && lesson.teacherId != lesson.oldTeacherId) {
|
||||
b.oldTeacherName = lesson.oldTeacherName
|
||||
}
|
||||
if (lesson.type != Lesson.TYPE_CANCELLED && lesson.teacherId != null) {
|
||||
if (lesson.type != Lesson.TYPE_CANCELLED && lesson.displayTeacherId != null) {
|
||||
b.teacherName = lesson.teacherName
|
||||
}
|
||||
|
||||
if (lesson.oldClassroom != null && lesson.classroom != lesson.oldClassroom) {
|
||||
b.oldClassroom = lesson.oldClassroom
|
||||
}
|
||||
if (lesson.type != Lesson.TYPE_CANCELLED && lesson.classroom != null) {
|
||||
if (lesson.type != Lesson.TYPE_CANCELLED && lesson.displayClassroom != null) {
|
||||
b.classroom = lesson.classroom
|
||||
}
|
||||
|
||||
if (lesson.type < Lesson.TYPE_SHIFTED_SOURCE && lesson.oldTeamId != null && lesson.teamId != lesson.oldTeamId) {
|
||||
b.oldTeamName = lesson.oldTeamName
|
||||
}
|
||||
if (lesson.type != Lesson.TYPE_CANCELLED && lesson.teamId != null) {
|
||||
if (lesson.type != Lesson.TYPE_CANCELLED && lesson.displayTeamId != null) {
|
||||
b.teamName = lesson.teamName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,15 +6,16 @@ import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.applandeo.materialcalendarview.CalendarView;
|
||||
import com.applandeo.materialcalendarview.EventDay;
|
||||
@ -27,6 +28,7 @@ import com.mikepenz.iconics.IconicsColor;
|
||||
import com.mikepenz.iconics.IconicsDrawable;
|
||||
import com.mikepenz.iconics.IconicsSize;
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
@ -34,13 +36,13 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.MainActivity;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceFull;
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaCalendarBinding;
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull;
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListDialog;
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog;
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog;
|
||||
@ -51,11 +53,11 @@ import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange.LessonChangeEve
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceCounter;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEvent;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEventRenderer;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time;
|
||||
import pl.szczodrzynski.edziennik.utils.Colors;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time;
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem;
|
||||
|
||||
@ -102,7 +104,7 @@ public class AgendaFragment extends Fragment {
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_add_event)
|
||||
.withDescription(R.string.menu_add_event_desc)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar_plus)
|
||||
.withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
new MaterialDialog.Builder(activity)
|
||||
@ -122,7 +124,7 @@ public class AgendaFragment extends Fragment {
|
||||
}),
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_agenda_change_view)
|
||||
.withIcon(viewType == AGENDA_DEFAULT ? CommunityMaterial.Icon.cmd_calendar : CommunityMaterial.Icon2.cmd_view_list)
|
||||
.withIcon(viewType == AGENDA_DEFAULT ? CommunityMaterial.Icon.cmd_calendar_outline : CommunityMaterial.Icon.cmd_format_list_bulleted_square)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
viewType = viewType == AGENDA_DEFAULT ? AGENDA_CALENDAR : AGENDA_DEFAULT;
|
||||
@ -133,7 +135,7 @@ public class AgendaFragment extends Fragment {
|
||||
new BottomSheetSeparatorItem(true),
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_EVENT, true));
|
||||
|
@ -58,7 +58,7 @@ public class AnnouncementsFragment extends Fragment {
|
||||
activity.getBottomSheet().prependItems(
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_ANNOUNCEMENT, true));
|
||||
|
@ -95,7 +95,7 @@ public class AttendanceFragment extends Fragment {
|
||||
activity.getBottomSheet().prependItems(
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_ATTENDANCE, true));
|
||||
@ -133,7 +133,7 @@ public class AttendanceFragment extends Fragment {
|
||||
CafeBar.builder(activity)
|
||||
.to(activity.getNavView().getCoordinator())
|
||||
.content(R.string.sync_old_data_info)
|
||||
.icon(new IconicsDrawable(activity).icon(CommunityMaterial.Icon2.cmd_sync).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.INSTANCE.getPrimaryTextColor(activity))))
|
||||
.icon(new IconicsDrawable(activity).icon(CommunityMaterial.Icon.cmd_download_outline).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.INSTANCE.getPrimaryTextColor(activity))))
|
||||
.positiveText(R.string.refresh)
|
||||
.positiveColor(0xff4caf50)
|
||||
.negativeText(R.string.ok)
|
||||
|
@ -69,7 +69,7 @@ public class BehaviourFragment extends Fragment {
|
||||
activity.getBottomSheet().prependItems(
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_NOTICE, true));
|
||||
|
@ -9,21 +9,20 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||
import com.mikepenz.iconics.utils.colorRes
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notices.NoticeFull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.bs
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class NoticesAdapter//getting the context and product list with constructor
|
||||
(private val context: Context, var noticeList: List<NoticeFull>) : RecyclerView.Adapter<NoticesAdapter.ViewHolder>() {
|
||||
@ -50,15 +49,15 @@ class NoticesAdapter//getting the context and product list with constructor
|
||||
holder.noticesItemAddedDate.text = Date.fromMillis(notice.addedDate).formattedString
|
||||
|
||||
if (notice.type == Notice.TYPE_POSITIVE) {
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon2.cmd_plus_circle)
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon2.cmd_plus_circle_outline)
|
||||
.colorRes(R.color.md_green_600)
|
||||
.sizeDp(36))
|
||||
} else if (notice.type == Notice.TYPE_NEGATIVE) {
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon.cmd_alert_decagram)
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon.cmd_alert_decagram_outline)
|
||||
.colorRes(R.color.md_red_600)
|
||||
.sizeDp(36))
|
||||
} else {
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, CommunityMaterial.Icon2.cmd_message_outline)
|
||||
holder.noticesItemType.setImageDrawable(IconicsDrawable(context, SzkolnyFont.Icon.szf_message_processing_outline)
|
||||
.colorRes(R.color.md_blue_500)
|
||||
.sizeDp(36))
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ public class GradesFragment extends Fragment {
|
||||
}),
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_grades_color_mode)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_palette)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_palette_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
new MaterialDialog.Builder(activity)
|
||||
@ -195,7 +195,7 @@ public class GradesFragment extends Fragment {
|
||||
new BottomSheetSeparatorItem(true),
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_GRADE, true));
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.home;
|
||||
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -14,8 +15,8 @@ import java.util.TimerTask;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.databinding.ActivityCounterBinding;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull;
|
||||
import pl.szczodrzynski.edziennik.databinding.ActivityCounterBinding;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time;
|
||||
|
||||
@ -88,7 +89,7 @@ public class CounterActivity extends AppCompatActivity {
|
||||
private void findLessons(Time syncedNow) {
|
||||
AsyncTask.execute(() -> {
|
||||
Date today = Date.getToday();
|
||||
lessons = app.db.lessonDao().getAllNearestNow(App.profileId, today.clone().stepForward(0, 0, -today.getWeekDay()), today, syncedNow);
|
||||
lessons = app.db.lessonDao().getAllNearestNow(App.profileId, today.getWeekStart(), today, syncedNow);
|
||||
|
||||
if (lessons != null && lessons.size() != 0) {
|
||||
Date displayingDate = lessons.get(0).lessonDate;
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ import com.mikepenz.iconics.IconicsColor;
|
||||
import com.mikepenz.iconics.IconicsDrawable;
|
||||
import com.mikepenz.iconics.IconicsSize;
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
@ -268,7 +269,7 @@ public class HomeFragment extends Fragment {
|
||||
activity.getBottomSheet().prependItems(
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_set_student_number)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_counter)
|
||||
.withIcon(SzkolnyFont.Icon.szf_clipboard_list_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
setNumberDialog();
|
||||
@ -276,7 +277,7 @@ public class HomeFragment extends Fragment {
|
||||
new BottomSheetSeparatorItem(true),
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_everything_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, true));
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-22
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.home
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.AsyncTask
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import com.afollestad.materialdialogs.DialogAction
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
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.data.db.modules.events.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.databinding.CardTimetableBinding
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment.updateInterval
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import java.util.*
|
||||
|
||||
class HomeTimetableCard(
|
||||
private val app: App,
|
||||
private val activity: MainActivity,
|
||||
private val homeFragment: HomeFragment,
|
||||
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 var counterType = TIME_TILL
|
||||
private val counterTarget = Time(0, 0, 0)
|
||||
|
||||
private val lessons = mutableListOf<Lesson>()
|
||||
private val events = mutableListOf<Event>()
|
||||
|
||||
fun run() {
|
||||
timetableTimer = Timer()
|
||||
b = DataBindingUtil.inflate(layoutInflater, R.layout.card_timetable, null, false)
|
||||
|
||||
update()
|
||||
|
||||
insertPoint.addView(b.root, ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT))
|
||||
|
||||
b.cardTimetableFullscreenCounter.setOnClickListener {
|
||||
activity.startActivity(Intent(activity, CounterActivity::class.java))
|
||||
}
|
||||
|
||||
b.cardTimetableBellSync.setOnClickListener {
|
||||
if (bellSyncTime == null) {
|
||||
MaterialDialog.Builder(activity)
|
||||
.title(R.string.bell_sync_title)
|
||||
.content(R.string.bell_sync_cannot_now)
|
||||
.positiveText(R.string.ok)
|
||||
.show()
|
||||
} else {
|
||||
MaterialDialog.Builder(activity)
|
||||
.title(R.string.bell_sync_title)
|
||||
.content(app.getString(R.string.bell_sync_howto, bellSyncTime!!.stringHM).toString() +
|
||||
when {
|
||||
app.appConfig.bellSyncDiff != null -> app.getString(R.string.bell_sync_current_dialog,
|
||||
(if (app.appConfig.bellSyncMultiplier == -1) "-" else "+") + app.appConfig.bellSyncDiff.stringHMS)
|
||||
else -> ""
|
||||
})
|
||||
.positiveText(R.string.ok)
|
||||
.negativeText(R.string.cancel)
|
||||
.neutralText(R.string.reset)
|
||||
.onPositive { _, _: DialogAction? ->
|
||||
val bellDiff = Time.diff(Time.getNow(), bellSyncTime)
|
||||
app.appConfig.bellSyncDiff = bellDiff
|
||||
app.appConfig.bellSyncMultiplier = if (bellSyncTime!!.value > Time.getNow().value) -1 else 1
|
||||
app.saveConfig("bellSyncDiff", "bellSyncMultiplier")
|
||||
|
||||
MaterialDialog.Builder(activity)
|
||||
.title(R.string.bell_sync_title)
|
||||
.content(app.getString(R.string.bell_sync_results, if (bellSyncTime!!.value > Time.getNow().value) "-" else "+", bellDiff.stringHMS))
|
||||
.positiveText(R.string.ok)
|
||||
.show()
|
||||
}
|
||||
.onNeutral { _, _ ->
|
||||
MaterialDialog.Builder(activity)
|
||||
.title(R.string.bell_sync_title)
|
||||
.content(R.string.bell_sync_reset_confirm)
|
||||
.positiveText(R.string.yes)
|
||||
.negativeText(R.string.no)
|
||||
.onPositive { _, _ ->
|
||||
app.appConfig.bellSyncDiff = null
|
||||
app.appConfig.bellSyncMultiplier = 0
|
||||
app.saveConfig("bellSyncDiff", "bellSyncMultiplier")
|
||||
}
|
||||
.show()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
HomeFragment.buttonAddDrawable(activity, b.cardTimetableButton, CommunityMaterial.Icon.cmd_arrow_right)
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
try {
|
||||
timetableTimer.apply {
|
||||
cancel()
|
||||
purge()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun update() {
|
||||
if (!homeFragment.isAdded) return
|
||||
|
||||
val now = Time.getNow()
|
||||
|
||||
val syncedNow: Time = when (app.appConfig.bellSyncDiff != null) {
|
||||
true -> when {
|
||||
app.appConfig.bellSyncMultiplier < 0 -> Time.sum(now, app.appConfig.bellSyncDiff)
|
||||
app.appConfig.bellSyncMultiplier > 0 -> Time.diff(now, app.appConfig.bellSyncDiff)
|
||||
else -> now
|
||||
}
|
||||
else -> now
|
||||
}
|
||||
|
||||
if (lessons.size == 0 || syncedNow.value > counterTarget.value) {
|
||||
findLessons(syncedNow)
|
||||
} else {
|
||||
scheduleUpdate(updateCounter(syncedNow))
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCounter(syncedNow: Time): Long {
|
||||
val diff = Time.diff(counterTarget, syncedNow)
|
||||
b.cardTimetableTimeLeft.text = when (counterType) {
|
||||
TIME_TILL -> HomeFragment.timeTill(app, diff, app.appConfig.countInSeconds)
|
||||
else -> HomeFragment.timeLeft(app, diff, app.appConfig.countInSeconds)
|
||||
}
|
||||
bellSyncTime = counterTarget.clone()
|
||||
b.cardTimetableFullscreenCounter.visibility = View.VISIBLE
|
||||
return updateInterval(app, diff)
|
||||
}
|
||||
|
||||
private fun scheduleUpdate(newRefreshInterval: Long) {
|
||||
timetableTimer.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
activity.runOnUiThread { update() }
|
||||
}
|
||||
}, newRefreshInterval)
|
||||
}
|
||||
|
||||
private fun findLessons(syncedNow: Time) {
|
||||
AsyncTask.execute {
|
||||
val today = Date.getToday()
|
||||
val searchEnd = Date.getToday().stepForward(0, 0, -today.weekDay)
|
||||
|
||||
lessons.apply {
|
||||
clear()
|
||||
addAll(app.db.timetableDao().getBetweenDatesNow(today, searchEnd))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -21,11 +21,11 @@ import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.MainActivity;
|
||||
import pl.szczodrzynski.edziennik.databinding.CardTimetableBinding;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull;
|
||||
import pl.szczodrzynski.edziennik.databinding.CardTimetableBinding;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Week;
|
||||
@ -35,8 +35,8 @@ import static pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange.TY
|
||||
import static pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment.updateInterval;
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.bs;
|
||||
|
||||
public class HomeTimetableCard {
|
||||
private static final String TAG = "HomeTimetableCard";
|
||||
public class HomeTimetableCardOld {
|
||||
private static final String TAG = "HomeTimetableCardOld";
|
||||
private App app;
|
||||
private MainActivity a;
|
||||
private HomeFragment f;
|
||||
@ -46,7 +46,7 @@ public class HomeTimetableCard {
|
||||
private Timer timetableTimer;
|
||||
private Time bellSyncTime = null;
|
||||
|
||||
public HomeTimetableCard(App app, MainActivity a, HomeFragment f, LayoutInflater layoutInflater, ViewGroup insertPoint) {
|
||||
public HomeTimetableCardOld(App app, MainActivity a, HomeFragment f, LayoutInflater layoutInflater, ViewGroup insertPoint) {
|
||||
this.app = app;
|
||||
this.a = a;
|
||||
this.f = f;
|
@ -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,269 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-24.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.home.cards
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.plusAssign
|
||||
import androidx.core.view.setMargins
|
||||
import androidx.lifecycle.Observer
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
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 pl.szczodrzynski.edziennik.utils.models.Week
|
||||
import pl.szczodrzynski.navlib.colorAttr
|
||||
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>()
|
||||
|
||||
private var bellSyncDiffMillis = 0L
|
||||
private val syncedNow: Time
|
||||
get() = Time.fromMillis(Time.getNow().inMillis + bellSyncDiffMillis)
|
||||
|
||||
private var counterJob: Job? = null
|
||||
private var counterStart: Time? = null
|
||||
private var counterEnd: Time? = null
|
||||
private var subjectSpannable: CharSequence? = null
|
||||
|
||||
private val ignoreCancelled = true
|
||||
|
||||
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
|
||||
|
||||
b.settings.setImageDrawable(IconicsDrawable(activity, CommunityMaterial.Icon2.cmd_settings_outline)
|
||||
.colorAttr(activity, R.attr.colorIcon)
|
||||
.sizeDp(20))
|
||||
|
||||
// get current bell-sync params
|
||||
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 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 the current bell-synced time
|
||||
val now = syncedNow
|
||||
|
||||
// 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
|
||||
&& !(it.isCancelled && ignoreCancelled)
|
||||
}
|
||||
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
|
||||
&& !(it.isCancelled && ignoreCancelled)
|
||||
}
|
||||
|
||||
checkedDays++
|
||||
}
|
||||
timetableDate
|
||||
}
|
||||
val timetableDate = deferred.await()
|
||||
|
||||
val isToday = today == timetableDate
|
||||
|
||||
b.progress.visibility = View.GONE
|
||||
b.counter.visibility = View.GONE
|
||||
|
||||
val now = syncedNow
|
||||
val firstLesson = lessons.firstOrNull()
|
||||
val lastLesson = lessons.lastOrNull()
|
||||
|
||||
if (isToday) {
|
||||
// today
|
||||
b.dayInfo.setText(R.string.home_timetable_today)
|
||||
counterStart = firstLesson?.displayStartTime
|
||||
counterEnd = firstLesson?.displayEndTime
|
||||
val isOngoing = counterStart <= now && now <= counterEnd
|
||||
val lessonRes = if (isOngoing)
|
||||
R.string.home_timetable_lesson_ongoing
|
||||
else
|
||||
R.string.home_timetable_lesson_not_started
|
||||
b.lessonBig.setText(lessonRes, firstLesson.subjectSpannable)
|
||||
firstLesson?.displayClassroom?.let {
|
||||
b.classroom.visibility = View.VISIBLE
|
||||
b.classroom.text = it
|
||||
} ?: run {
|
||||
b.classroom.visibility = View.GONE
|
||||
}
|
||||
|
||||
subjectSpannable = firstLesson.subjectSpannable
|
||||
|
||||
counterJob = startCoroutineTimer(repeatMillis = 1000) {
|
||||
count()
|
||||
}
|
||||
}
|
||||
else {
|
||||
val isTomorrow = today.clone().stepForward(0, 0, 1) == timetableDate
|
||||
val dayInfoRes = if (isTomorrow) {
|
||||
// tomorrow
|
||||
R.string.home_timetable_tomorrow
|
||||
}
|
||||
else {
|
||||
val todayWeekStart = today.weekStart
|
||||
val dateWeekStart = timetableDate.weekStart
|
||||
if (todayWeekStart == dateWeekStart) {
|
||||
// this week
|
||||
R.string.home_timetable_date_this_week
|
||||
}
|
||||
else {
|
||||
// future: not this week
|
||||
R.string.home_timetable_date_future
|
||||
}
|
||||
}
|
||||
b.dayInfo.setText(dayInfoRes, Week.getFullDayName(timetableDate.weekDay), timetableDate.formattedString)
|
||||
b.lessonInfo.setText(
|
||||
R.string.home_timetable_lessons_info,
|
||||
lessons.size,
|
||||
firstLesson?.displayStartTime?.stringHM ?: "?",
|
||||
lastLesson?.displayEndTime?.stringHM ?: "?"
|
||||
)
|
||||
|
||||
b.lessonBig.setText(R.string.home_timetable_lesson_first, firstLesson.subjectSpannable)
|
||||
firstLesson?.displayClassroom?.let {
|
||||
b.classroom.visibility = View.VISIBLE
|
||||
b.classroom.text = it
|
||||
} ?: run {
|
||||
b.classroom.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
val text = mutableListOf<CharSequence>(
|
||||
activity.getString(R.string.home_timetable_later)
|
||||
)
|
||||
var first = true
|
||||
for (lesson in lessons) {
|
||||
if (first) { first = false; continue }
|
||||
text += listOf(
|
||||
lesson.displayStartTime?.stringHM,
|
||||
lesson.subjectSpannable
|
||||
).concat(" ")
|
||||
}
|
||||
if (text.size == 1)
|
||||
text += activity.getString(R.string.home_timetable_later_no_lessons)
|
||||
b.nextLessons.text = text.concat("\n")
|
||||
}}
|
||||
|
||||
private val LessonFull?.subjectSpannable: CharSequence
|
||||
get() = if (this == null) "?" else when {
|
||||
isCancelled -> displaySubjectName.asStrikethroughSpannable()
|
||||
isChange -> displaySubjectName.asItalicSpannable()
|
||||
else -> displaySubjectName ?: "?"
|
||||
}
|
||||
|
||||
private fun count() {
|
||||
val counterStart = counterStart
|
||||
val counterEnd = counterEnd
|
||||
if (counterStart == null || counterEnd == null) {
|
||||
// there is no lesson to count
|
||||
b.progress.visibility = View.GONE
|
||||
b.counter.visibility = View.GONE
|
||||
this.counterJob?.cancel()
|
||||
return
|
||||
}
|
||||
|
||||
val now = syncedNow
|
||||
if (now > counterEnd) {
|
||||
// the lesson is already over
|
||||
b.progress.visibility = View.GONE
|
||||
b.counter.visibility = View.GONE
|
||||
this.counterJob?.cancel()
|
||||
this.counterStart = null
|
||||
this.counterEnd = null
|
||||
update() // check for new lessons to display
|
||||
return
|
||||
}
|
||||
|
||||
val isOngoing = counterStart <= now && now <= counterEnd
|
||||
val lessonRes = if (isOngoing)
|
||||
R.string.home_timetable_lesson_ongoing
|
||||
else
|
||||
R.string.home_timetable_lesson_not_started
|
||||
b.lessonBig.setText(lessonRes, subjectSpannable ?: "")
|
||||
|
||||
if (now < counterStart) {
|
||||
// the lesson hasn't yet started
|
||||
b.progress.visibility = View.GONE
|
||||
b.counter.visibility = View.VISIBLE
|
||||
val diff = counterStart - now
|
||||
b.counter.text = activity.timeTill(diff.toInt(), "\n")
|
||||
}
|
||||
else {
|
||||
// the lesson is right now
|
||||
b.progress.visibility = View.VISIBLE
|
||||
b.counter.visibility = View.VISIBLE
|
||||
val lessonLength = counterEnd - counterStart
|
||||
val timePassed = now - counterStart
|
||||
val timeLeft = counterEnd - now
|
||||
b.counter.text = activity.timeLeft(timeLeft.toInt(), "\n")
|
||||
b.progress.max = lessonLength.toInt()
|
||||
b.progress.progress = timePassed.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
override fun unbind(position: Int, holder: HomeCardAdapter.ViewHolder) = Unit
|
||||
}
|
@ -9,6 +9,7 @@ import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
@ -52,7 +53,7 @@ class HomeworkFragment : Fragment() {
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_add_event)
|
||||
.withDescription(R.string.menu_add_event_desc)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar_plus)
|
||||
.withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
|
||||
.withOnClickListener(View.OnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
EventManualDialog(activity).show(app, null, null, null, EventManualDialog.DIALOG_HOMEWORK)
|
||||
@ -60,7 +61,7 @@ class HomeworkFragment : Fragment() {
|
||||
BottomSheetSeparatorItem(true),
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(View.OnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
AsyncTask.execute { app.db.metadataDao().setAllSeen(App.profileId, Metadata.TYPE_HOMEWORK, true) }
|
||||
|
@ -104,7 +104,7 @@ public class LoginVulcanFragment extends Fragment {
|
||||
b.helpButton.setOnClickListener((v) -> nav.navigate(R.id.loginVulcanHelpFragment, null, LoginActivity.navOptions));
|
||||
b.backButton.setOnClickListener((v) -> nav.navigateUp());
|
||||
|
||||
b.loginQrScan.setImageDrawable(new IconicsDrawable(getActivity()).icon(CommunityMaterial.Icon2.cmd_qrcode).color(IconicsColor.colorInt(Color.BLACK)).size(IconicsSize.dp(72)));
|
||||
b.loginQrScan.setImageDrawable(new IconicsDrawable(getActivity()).icon(CommunityMaterial.Icon2.cmd_qrcode_scan).color(IconicsColor.colorInt(Color.BLACK)).size(IconicsSize.dp(72)));
|
||||
b.loginQrScan.setOnClickListener((v -> {
|
||||
QrScannerActivity.resultHandler = result -> {
|
||||
try {
|
||||
|
@ -22,6 +22,7 @@ import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.IconicsSize
|
||||
import com.mikepenz.iconics.typeface.IIcon
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
@ -30,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
|
||||
@ -224,17 +228,17 @@ class MessageFragment : Fragment(), CoroutineScope {
|
||||
attachmentChip.ellipsize = TextUtils.TruncateAt.MIDDLE
|
||||
|
||||
// create an icon for the attachment
|
||||
var icon: IIcon = CommunityMaterial.Icon.cmd_file
|
||||
when (Utils.getExtensionFromFileName(name)) {
|
||||
"txt" -> icon = CommunityMaterial.Icon.cmd_file_document
|
||||
"doc", "docx", "odt", "rtf" -> icon = CommunityMaterial.Icon.cmd_file_word
|
||||
"xls", "xlsx", "ods" -> icon = CommunityMaterial.Icon.cmd_file_excel
|
||||
"ppt", "pptx", "odp" -> icon = CommunityMaterial.Icon.cmd_file_powerpoint
|
||||
"pdf" -> icon = CommunityMaterial.Icon.cmd_file_pdf
|
||||
"mp3", "wav", "aac" -> icon = CommunityMaterial.Icon.cmd_file_music
|
||||
"mp4", "avi", "3gp", "mkv", "flv" -> icon = CommunityMaterial.Icon.cmd_file_video
|
||||
"jpg", "jpeg", "png", "bmp", "gif" -> icon = CommunityMaterial.Icon.cmd_file_image
|
||||
"zip", "rar", "tar", "7z" -> icon = CommunityMaterial.Icon.cmd_file_lock
|
||||
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)))
|
||||
@ -243,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)
|
||||
@ -267,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()
|
||||
@ -284,7 +342,6 @@ class MessageFragment : Fragment(), CoroutineScope {
|
||||
e.printStackTrace()
|
||||
//app.apiEdziennik.guiReportException(activity, 355, e)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,104 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.notifications;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Notification;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.utils.Utils.d;
|
||||
|
||||
public class NotificationsAdapter extends RecyclerView.Adapter<NotificationsAdapter.ViewHolder> {
|
||||
private static final String TAG = "NotificationsAdapter";
|
||||
private Context context;
|
||||
private List<Notification> notificationList;
|
||||
|
||||
//getting the context and product list with constructor
|
||||
public NotificationsAdapter(Context mCtx, List<Notification> notificationList) {
|
||||
this.context = mCtx;
|
||||
this.notificationList = notificationList;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
//inflating and returning our view holder
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
View view = inflater.inflate(R.layout.row_notifications_item, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
App app = (App) context.getApplicationContext();
|
||||
|
||||
Notification notification = notificationList.get(position);
|
||||
|
||||
holder.notificationsItemDate.setText(Date.fromMillis(notification.addedDate).getFormattedString());
|
||||
holder.notificationsItemText.setText(notification.text);
|
||||
holder.notificationsItemTitle.setText(notification.title);
|
||||
holder.notificationsItemType.setText(Notification.stringType(context, notification.type));
|
||||
|
||||
holder.notificationsItemCard.setOnClickListener((v -> {
|
||||
Intent intent = new Intent("android.intent.action.MAIN");
|
||||
notification.fillIntent(intent);
|
||||
|
||||
d(TAG, "notification with item "+notification.redirectFragmentId+" extras "+(intent.getExtras() == null ? "null" : intent.getExtras().toString()));
|
||||
|
||||
//Log.d(TAG, "Got date "+intent.getLongExtra("timetableDate", 0));
|
||||
|
||||
if (notification.profileId != -1 && notification.profileId != app.profile.getId() && context instanceof Activity) {
|
||||
Toast.makeText(app, app.getString(R.string.toast_changing_profile), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
app.sendBroadcast(intent);
|
||||
}));
|
||||
|
||||
if (!notification.seen) {
|
||||
holder.notificationsItemText.setBackground(context.getResources().getDrawable(R.drawable.bg_rounded_8dp));
|
||||
holder.notificationsItemText.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
|
||||
}
|
||||
else {
|
||||
holder.notificationsItemText.setBackground(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return notificationList.size();
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
CardView notificationsItemCard;
|
||||
TextView notificationsItemDate;
|
||||
TextView notificationsItemText;
|
||||
TextView notificationsItemTitle;
|
||||
TextView notificationsItemType;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
notificationsItemCard = itemView.findViewById(R.id.notificationsItemCard);
|
||||
notificationsItemDate = itemView.findViewById(R.id.notificationsItemDate);
|
||||
notificationsItemText = itemView.findViewById(R.id.notificationsItemText);
|
||||
notificationsItemTitle = itemView.findViewById(R.id.notificationsItemTitle);
|
||||
notificationsItemType = itemView.findViewById(R.id.notificationsItemType);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.notifications
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.getNotificationTitle
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class NotificationsAdapter(
|
||||
private val context: Context
|
||||
) : RecyclerView.Adapter<NotificationsAdapter.ViewHolder>() {
|
||||
companion object {
|
||||
private const val TAG = "NotificationsAdapter"
|
||||
}
|
||||
|
||||
var items = listOf<Notification>()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val view = inflater.inflate(R.layout.row_notifications_item, parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val app = context.applicationContext as App
|
||||
|
||||
val notification = items[position]
|
||||
|
||||
val date = Date.fromMillis(notification.addedDate).formattedString
|
||||
val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(context)
|
||||
|
||||
holder.title.text = notification.text
|
||||
holder.profileDate.text = listOf(
|
||||
notification.profileName ?: "",
|
||||
" • ",
|
||||
date.asColoredSpannable(colorSecondary)
|
||||
).concat()
|
||||
holder.type.text = context.getNotificationTitle(notification.type)
|
||||
|
||||
holder.root.onClick {
|
||||
val intent = Intent("android.intent.action.MAIN")
|
||||
notification.fillIntent(intent)
|
||||
|
||||
d(TAG, "notification with item " + notification.viewId + " extras " + if (intent.extras == null) "null" else intent.extras!!.toString())
|
||||
|
||||
//Log.d(TAG, "Got date "+intent.getLongExtra("timetableDate", 0));
|
||||
|
||||
if (notification.profileId != -1 && notification.profileId != app.profile.id && context is Activity) {
|
||||
Toast.makeText(app, app.getString(R.string.toast_changing_profile), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
app.sendBroadcast(intent)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
var root = itemView
|
||||
var title: TextView = itemView.findViewById(R.id.title)
|
||||
var profileDate: TextView = itemView.findViewById(R.id.profileDate)
|
||||
var type: TextView = itemView.findViewById(R.id.type)
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.notifications;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import pl.szczodrzynski.edziennik.App;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentNotificationsBinding;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
|
||||
public class NotificationsFragment extends Fragment {
|
||||
|
||||
private App app = null;
|
||||
private Activity activity = null;
|
||||
private FragmentNotificationsBinding b = null;
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
activity = getActivity();
|
||||
if (getActivity() == null || getContext() == null)
|
||||
return null;
|
||||
app = (App) activity.getApplication();
|
||||
getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true);
|
||||
if (app.profile == null)
|
||||
return inflater.inflate(R.layout.fragment_loading, container, false);
|
||||
// activity, context and profile is valid
|
||||
b = DataBindingUtil.inflate(inflater, R.layout.fragment_notifications, container, false);
|
||||
return b.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
|
||||
return;
|
||||
|
||||
RecyclerView recyclerView = b.notificationsView;
|
||||
recyclerView.setHasFixedSize(true);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
|
||||
if (app.appConfig.notifications.size() > 0) {
|
||||
NotificationsAdapter adapter = new NotificationsAdapter(getContext(), app.appConfig.notifications);
|
||||
recyclerView.setAdapter(adapter);
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
|
||||
//linearLayoutManager.setReverseLayout(true);
|
||||
//linearLayoutManager.setStackFromEnd(true);
|
||||
recyclerView.setLayoutManager(linearLayoutManager);
|
||||
b.notificationsNoData.setVisibility(View.GONE);
|
||||
}
|
||||
else {
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
b.notificationsNoData.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-22.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.notifications
|
||||
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.DividerItemDecoration.HORIZONTAL
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
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.databinding.FragmentNotificationsBinding
|
||||
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||
|
||||
|
||||
class NotificationsFragment : Fragment() {
|
||||
private lateinit var app: App
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var b: FragmentNotificationsBinding
|
||||
|
||||
private val adapter by lazy {
|
||||
NotificationsAdapter(activity)
|
||||
}
|
||||
|
||||
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 = FragmentNotificationsBinding.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
|
||||
|
||||
activity.bottomSheet.prependItems(
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_remove_notifications)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_delete_sweep_outline)
|
||||
.withOnClickListener(View.OnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
AsyncTask.execute { app.db.notificationDao().clearAll() }
|
||||
Toast.makeText(activity, R.string.menu_remove_notifications_success, Toast.LENGTH_SHORT).show()
|
||||
}))
|
||||
|
||||
app.db.notificationDao()
|
||||
.getAll()
|
||||
.observe(this, Observer { notifications ->
|
||||
if (app.profile == null || !isAdded) return@Observer
|
||||
|
||||
adapter.items = notifications
|
||||
if (b.notificationsView.adapter == null) {
|
||||
b.notificationsView.adapter = adapter
|
||||
b.notificationsView.apply {
|
||||
setHasFixedSize(true)
|
||||
layoutManager = LinearLayoutManager(context).apply {
|
||||
addItemDecoration(SimpleDividerItemDecoration(context))
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter.notifyDataSetChanged()
|
||||
|
||||
if (notifications != null && notifications.isNotEmpty()) {
|
||||
b.notificationsView.visibility = View.VISIBLE
|
||||
b.notificationsNoData.visibility = View.GONE
|
||||
} else {
|
||||
b.notificationsView.visibility = View.GONE
|
||||
b.notificationsNoData.visibility = View.VISIBLE
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -39,7 +39,7 @@ class SettingsLicenseActivity : MaterialAboutActivity() {
|
||||
libraryUrl: String): MaterialAboutCard {
|
||||
val licenseItem = MaterialAboutActionItem.Builder()
|
||||
.icon(IconicsDrawable(this)
|
||||
.icon(CommunityMaterial.Icon.cmd_book)
|
||||
.icon(CommunityMaterial.Icon.cmd_book_outline)
|
||||
.colorInt(foregroundColor)
|
||||
.sizeDp(18))
|
||||
.setIconGravity(MaterialAboutActionItem.GRAVITY_TOP)
|
||||
|
@ -29,6 +29,7 @@ import com.mikepenz.iconics.IconicsColor;
|
||||
import com.mikepenz.iconics.IconicsDrawable;
|
||||
import com.mikepenz.iconics.IconicsSize;
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont;
|
||||
import com.theartofdev.edmodo.cropper.CropImage;
|
||||
import com.theartofdev.edmodo.cropper.CropImageView;
|
||||
import com.wdullaer.materialdatetimepicker.time.TimePickerDialog;
|
||||
@ -259,7 +260,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_profile_remove_text),
|
||||
getString(R.string.settings_profile_remove_subtext),
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon.cmd_delete_empty)
|
||||
.icon(SzkolnyFont.Icon.szf_delete_empty_outline)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
)
|
||||
@ -287,7 +288,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_theme_theme_text),
|
||||
Themes.INSTANCE.getThemeName(activity),
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon2.cmd_palette)
|
||||
.icon(CommunityMaterial.Icon2.cmd_palette_outline)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
)
|
||||
@ -313,7 +314,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_theme_mini_drawer_text),
|
||||
getString(R.string.settings_theme_mini_drawer_subtext),
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon.cmd_chevron_left)
|
||||
.icon(CommunityMaterial.Icon.cmd_dots_vertical)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
)
|
||||
@ -339,7 +340,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_theme_mini_drawer_buttons_text),
|
||||
null,
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon2.cmd_menu)
|
||||
.icon(CommunityMaterial.Icon.cmd_format_list_checks)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
)
|
||||
@ -403,7 +404,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_theme_drawer_header_text),
|
||||
null,
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon2.cmd_image)
|
||||
.icon(CommunityMaterial.Icon2.cmd_image_outline)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
)
|
||||
@ -504,7 +505,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_sync_wifi_text),
|
||||
getString(R.string.settings_sync_wifi_subtext),
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon2.cmd_wifi_strength_4)
|
||||
.icon(CommunityMaterial.Icon2.cmd_wifi_strength_2)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
)
|
||||
@ -526,7 +527,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_sync_sync_interval_text),
|
||||
getString(R.string.settings_sync_sync_interval_subtext_disabled),
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon2.cmd_sync)
|
||||
.icon(CommunityMaterial.Icon.cmd_download_outline)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
);
|
||||
@ -619,7 +620,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_sync_quiet_hours_text),
|
||||
getString(R.string.settings_sync_quiet_hours_subtext_disabled),
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon.cmd_bell_sleep)
|
||||
.icon(CommunityMaterial.Icon.cmd_bell_sleep_outline)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
);
|
||||
@ -740,7 +741,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_sync_notifications_settings_text),
|
||||
getString(R.string.settings_sync_notifications_settings_subtext),
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon2.cmd_settings)
|
||||
.icon(CommunityMaterial.Icon2.cmd_settings_outline)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
)
|
||||
@ -820,7 +821,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_register_shared_events_text),
|
||||
getString(R.string.settings_register_shared_events_subtext),
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon2.cmd_share_variant)
|
||||
.icon(CommunityMaterial.Icon2.cmd_share_outline)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
)
|
||||
@ -888,7 +889,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_register_allow_registration_text),
|
||||
getString(R.string.settings_register_allow_registration_subtext),
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon.cmd_account_circle)
|
||||
.icon(CommunityMaterial.Icon.cmd_account_circle_outline)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
);
|
||||
@ -958,7 +959,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_register_bell_sync_text),
|
||||
getRegisterCardBellSyncSubText(),
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon.cmd_alarm_bell)
|
||||
.icon(SzkolnyFont.Icon.szf_alarm_bell_outline)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
);
|
||||
@ -1030,7 +1031,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_register_dont_count_zero_text),
|
||||
null,
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon2.cmd_numeric_0_box)
|
||||
.icon(CommunityMaterial.Icon2.cmd_numeric_0_box_outline)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
)
|
||||
@ -1065,7 +1066,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
getString(R.string.settings_register_show_teacher_absences_text),
|
||||
null,
|
||||
new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon.cmd_account_arrow_right)
|
||||
.icon(CommunityMaterial.Icon.cmd_account_arrow_right_outline)
|
||||
.size(IconicsSize.dp(iconSizeDp))
|
||||
.color(IconicsColor.colorInt(iconColor))
|
||||
)
|
||||
@ -1109,7 +1110,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
.subTextColor(secondaryTextOnPrimaryBg)
|
||||
.subText(BuildConfig.VERSION_NAME + ", " + BuildConfig.BUILD_TYPE)
|
||||
.icon(new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon2.cmd_information)
|
||||
.icon(CommunityMaterial.Icon2.cmd_information_outline)
|
||||
.color(IconicsColor.colorInt(primaryTextOnPrimaryBg))
|
||||
.size(IconicsSize.dp(iconSizeDp)))
|
||||
.build();
|
||||
@ -1133,7 +1134,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
.textColor(primaryTextOnPrimaryBg)
|
||||
.subTextColor(secondaryTextOnPrimaryBg)
|
||||
.icon(new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon2.cmd_shield_half_full)
|
||||
.icon(CommunityMaterial.Icon2.cmd_shield_outline)
|
||||
.color(IconicsColor.colorInt(primaryTextOnPrimaryBg))
|
||||
.size(IconicsSize.dp(iconSizeDp)))
|
||||
.setOnClickAction(ConvenienceBuilder.createWebsiteOnClickAction(activity, Uri.parse("https://szkolny.eu/privacy-policy")))
|
||||
@ -1145,7 +1146,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
.subTextColor(secondaryTextOnPrimaryBg)
|
||||
.subText(R.string.settings_about_discord_subtext)
|
||||
.icon(new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon.cmd_discord)
|
||||
.icon(SzkolnyFont.Icon.szf_discord_outline)
|
||||
.color(IconicsColor.colorInt(primaryTextOnPrimaryBg))
|
||||
.size(IconicsSize.dp(iconSizeDp)))
|
||||
.setOnClickAction(ConvenienceBuilder.createWebsiteOnClickAction(activity, Uri.parse("https://discord.gg/n9e8pWr")))
|
||||
@ -1270,7 +1271,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
|
||||
.textColor(primaryTextOnPrimaryBg)
|
||||
.subTextColor(secondaryTextOnPrimaryBg)
|
||||
.icon(new IconicsDrawable(activity)
|
||||
.icon(CommunityMaterial.Icon.cmd_bug)
|
||||
.icon(CommunityMaterial.Icon.cmd_bug_outline)
|
||||
.color(IconicsColor.colorInt(primaryTextOnPrimaryBg))
|
||||
.size(IconicsSize.dp(iconSizeDp)))
|
||||
.setOnClickAction(() -> {
|
||||
|
@ -37,6 +37,7 @@ import androidx.viewpager.widget.ViewPager;
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@ -103,7 +104,7 @@ public class TimetableFragment extends Fragment {
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_add_event)
|
||||
.withDescription(R.string.menu_add_event_desc)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_calendar_plus)
|
||||
.withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
new MaterialDialog.Builder(activity)
|
||||
@ -132,7 +133,7 @@ public class TimetableFragment extends Fragment {
|
||||
new BottomSheetSeparatorItem(true),
|
||||
new BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(v3 -> {
|
||||
activity.getBottomSheet().close();
|
||||
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_LESSON_CHANGE, true));
|
||||
@ -183,7 +184,7 @@ public class TimetableFragment extends Fragment {
|
||||
if (app == null || app.profile == null || activity == null || b == null || !isAdded())
|
||||
return;
|
||||
|
||||
List<LessonFull> lessons = app.db.lessonDao().getAllWeekNow(App.profileId, today.clone().stepForward(0, 0, -today.getWeekDay()), today);
|
||||
List<LessonFull> lessons = app.db.lessonDao().getAllWeekNow(App.profileId, today.getWeekStart(), today);
|
||||
displayingDate = HomeFragment.findDateWithLessons(App.profileId, lessons);
|
||||
pageSelection = app.appConfig.timetableDisplayDaysBackward + Date.diffDays(displayingDate, today); // DEFAULT HERE
|
||||
|
||||
|
@ -4,21 +4,28 @@ import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.google.android.material.datepicker.MaterialDatePicker
|
||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||
import kotlinx.coroutines.*
|
||||
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.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2Binding
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
|
||||
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class TimetableFragment : Fragment(), CoroutineScope {
|
||||
@ -60,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)
|
||||
}
|
||||
@ -148,14 +155,45 @@ 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)
|
||||
.withTitle(R.string.timetable_select_day)
|
||||
.withIcon(SzkolnyFont.Icon.szf_calendar_today_outline)
|
||||
.withOnClickListener(View.OnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
MaterialDatePicker.Builder
|
||||
.datePicker()
|
||||
.setSelection(Date.getToday().inMillis)
|
||||
.build()
|
||||
.apply {
|
||||
addOnPositiveButtonClickListener { dateInMillis ->
|
||||
val dateSelected = Date.fromMillis(dateInMillis)
|
||||
b.tabLayout.setCurrentItem(items.indexOfFirst { it == dateSelected }, true)
|
||||
}
|
||||
show(this@TimetableFragment.activity.supportFragmentManager, "MaterialDatePicker")
|
||||
}
|
||||
}),
|
||||
BottomSheetSeparatorItem(true),
|
||||
BottomSheetPrimaryItem(true)
|
||||
.withTitle(R.string.menu_mark_as_read)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.withOnClickListener(View.OnClickListener {
|
||||
activity.bottomSheet.close()
|
||||
AsyncTask.execute { app.db.metadataDao().setAllSeen(App.profileId, Metadata.TYPE_LESSON_CHANGE, true) }
|
||||
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
|
||||
})
|
||||
)
|
||||
|
||||
//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.bottomBar.fabIcon = SzkolnyFont.Icon.szf_calendar_today_outline
|
||||
activity.navView.setFabOnClickListener(View.OnClickListener {
|
||||
b.tabLayout.setCurrentItem(items.indexOfFirst { it.value == today }, true)
|
||||
})
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ class TimetablePagerAdapter(
|
||||
}
|
||||
|
||||
private val today by lazy { Date.getToday() }
|
||||
private val weekStart by lazy { today.clone().stepForward(0, 0, -today.weekDay) }
|
||||
private val weekStart by lazy { today.weekStart }
|
||||
private val weekEnd by lazy { weekStart.clone().stepForward(0, 0, 6) }
|
||||
|
||||
override fun getItem(position: Int): Fragment {
|
||||
@ -49,4 +49,4 @@ class TimetablePagerAdapter(
|
||||
}
|
||||
return pageTitle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
|
||||
parent?.removeAllViews()
|
||||
parent?.addView(view)
|
||||
val b = TimetableNoTimetableBinding.bind(view)
|
||||
val weekStart = date.clone().stepForward(0, 0, -date.weekDay).stringY_m_d
|
||||
val weekStart = date.weekStart.stringY_m_d
|
||||
b.noTimetableSync.onClick {
|
||||
it.isEnabled = false
|
||||
EdziennikTask.syncProfile(
|
||||
@ -247,6 +247,8 @@ class TimetableDayFragment : Fragment(), CoroutineScope {
|
||||
lb.detailsFirst.text = listOfNotEmpty(timeRange, classroomInfo).concat(bullet)
|
||||
lb.detailsSecond.text = listOfNotEmpty(teacherInfo, teamInfo).concat(bullet)
|
||||
|
||||
lb.unread = lesson.type != Lesson.TYPE_NORMAL && !lesson.seen
|
||||
|
||||
//lb.subjectName.typeface = Typeface.create("sans-serif-light", Typeface.BOLD)
|
||||
when (lesson.type) {
|
||||
Lesson.TYPE_NORMAL -> {
|
||||
|
@ -157,8 +157,10 @@ public class PermissionChecker {
|
||||
|
||||
public Intent intentApkInstall() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
return new Intent("android.settings.MANAGE_UNKNOWN_APP_SOURCES",
|
||||
Intent intent = new Intent("android.settings.MANAGE_UNKNOWN_APP_SOURCES",
|
||||
Uri.parse("package:" + mContext.getPackageName()));
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
return intent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-22.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
|
||||
public class SimpleDividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||
private Drawable mDivider;
|
||||
|
||||
public SimpleDividerItemDecoration(Context context) {
|
||||
mDivider = context.getResources().getDrawable(R.drawable.divider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
int left = parent.getPaddingLeft();
|
||||
int right = parent.getWidth() - parent.getPaddingRight();
|
||||
|
||||
int childCount = parent.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = parent.getChildAt(i);
|
||||
|
||||
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
|
||||
|
||||
int top = child.getBottom() + params.bottomMargin;
|
||||
int bottom = top + mDivider.getIntrinsicHeight();
|
||||
|
||||
mDivider.setBounds(left, top, right, bottom);
|
||||
mDivider.draw(c);
|
||||
}
|
||||
}
|
||||
}
|
@ -75,21 +75,22 @@ class TextInputDropDown : TextInputEditText {
|
||||
}
|
||||
}
|
||||
|
||||
fun select(item: Item) {
|
||||
fun select(item: Item): Item? {
|
||||
selected = item
|
||||
updateText()
|
||||
return item
|
||||
}
|
||||
|
||||
fun select(id: Long?) {
|
||||
items.singleOrNull { it.id == id }?.let { select(it) }
|
||||
fun select(id: Long?): Item? {
|
||||
return items.singleOrNull { it.id == id }?.let { select(it) }
|
||||
}
|
||||
|
||||
fun select(tag: Any?) {
|
||||
items.singleOrNull { it.tag == tag }?.let { select(it) }
|
||||
fun select(tag: Any?): Item? {
|
||||
return items.singleOrNull { it.tag == tag }?.let { select(it) }
|
||||
}
|
||||
|
||||
fun select(index: Int) {
|
||||
items.getOrNull(index)?.let { select(it) }
|
||||
fun select(index: Int): Item? {
|
||||
return items.getOrNull(index)?.let { select(it) }
|
||||
}
|
||||
|
||||
fun deselect(): TextInputDropDown {
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-22.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.util.AttributeSet
|
||||
import android.view.KeyEvent
|
||||
import android.view.KeyEvent.KEYCODE_BACK
|
||||
import androidx.annotation.NonNull
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
|
||||
class TextInputKeyboardEdit : TextInputEditText {
|
||||
|
||||
/**
|
||||
* Keyboard Listener
|
||||
*/
|
||||
internal var listener: KeyboardListener? = null
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
|
||||
super.onFocusChanged(focused, direction, previouslyFocusedRect)
|
||||
if (listener != null)
|
||||
listener!!.onStateChanged(this, true)
|
||||
}
|
||||
|
||||
override fun onKeyPreIme(keyCode: Int, @NonNull event: KeyEvent): Boolean {
|
||||
if (event.keyCode == KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
|
||||
if (listener != null)
|
||||
listener!!.onStateChanged(this, false)
|
||||
|
||||
// Hide cursor
|
||||
isFocusable = false
|
||||
|
||||
// Set EditText to be focusable again
|
||||
isFocusable = true
|
||||
isFocusableInTouchMode = true
|
||||
}
|
||||
return super.onKeyPreIme(keyCode, event)
|
||||
}
|
||||
|
||||
fun setOnKeyboardListener(listener: KeyboardListener) {
|
||||
this.listener = listener
|
||||
}
|
||||
|
||||
interface KeyboardListener {
|
||||
fun onStateChanged(keyboardEditText: TextInputKeyboardEdit, showing: Boolean)
|
||||
}
|
||||
}
|
@ -95,6 +95,7 @@ public class AppConfig {
|
||||
public String updateUrl = "";
|
||||
public String updateFilename = "";
|
||||
public boolean updateMandatory = false;
|
||||
public boolean updateDirect = false;
|
||||
|
||||
public boolean webPushEnabled = false;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user