Merge branch 'develop' into feature/messages-fixes

This commit is contained in:
Kuba Szczodrzyński 2021-04-14 22:42:54 +02:00 committed by GitHub
commit e2b47db3fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 1967 additions and 1148 deletions

View File

@ -153,7 +153,7 @@ dependencies {
// Szkolny.eu libraries/forks
implementation "eu.szkolny:android-snowfall:1ca9ea2da3"
implementation "eu.szkolny:agendacalendarview:1799f8ef47"
implementation "eu.szkolny:agendacalendarview:5431f03098"
implementation "eu.szkolny:cafebar:5bf0c618de"
implementation "eu.szkolny.fslogin:lib:2.0.0"
implementation "eu.szkolny:material-about-library:1d5ebaf47c"

View File

@ -15,6 +15,36 @@ class ProfileConfigUI(private val config: ProfileConfig) {
get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: AGENDA_DEFAULT }
set(value) { config.set("agendaViewType", value); mAgendaViewType = value }
private var mAgendaCompactMode: Boolean? = null
var agendaCompactMode: Boolean
get() { mAgendaCompactMode = mAgendaCompactMode ?: config.values.get("agendaCompactMode", false); return mAgendaCompactMode ?: false }
set(value) { config.set("agendaCompactMode", value); mAgendaCompactMode = value }
private var mAgendaGroupByType: Boolean? = null
var agendaGroupByType: Boolean
get() { mAgendaGroupByType = mAgendaGroupByType ?: config.values.get("agendaGroupByType", false); return mAgendaGroupByType ?: false }
set(value) { config.set("agendaGroupByType", value); mAgendaGroupByType = value }
private var mAgendaLessonChanges: Boolean? = null
var agendaLessonChanges: Boolean
get() { mAgendaLessonChanges = mAgendaLessonChanges ?: config.values.get("agendaLessonChanges", true); return mAgendaLessonChanges ?: true }
set(value) { config.set("agendaLessonChanges", value); mAgendaLessonChanges = value }
private var mAgendaTeacherAbsence: Boolean? = null
var agendaTeacherAbsence: Boolean
get() { mAgendaTeacherAbsence = mAgendaTeacherAbsence ?: config.values.get("agendaTeacherAbsence", true); return mAgendaTeacherAbsence ?: true }
set(value) { config.set("agendaTeacherAbsence", value); mAgendaTeacherAbsence = value }
private var mAgendaElearningMark: Boolean? = null
var agendaElearningMark: Boolean
get() { mAgendaElearningMark = mAgendaElearningMark ?: config.values.get("agendaElearningMark", false); return mAgendaElearningMark ?: false }
set(value) { config.set("agendaElearningMark", value); mAgendaElearningMark = value }
private var mAgendaElearningGroup: Boolean? = null
var agendaElearningGroup: Boolean
get() { mAgendaElearningGroup = mAgendaElearningGroup ?: config.values.get("agendaElearningGroup", true); return mAgendaElearningGroup ?: true }
set(value) { config.set("agendaElearningGroup", value); mAgendaElearningGroup = value }
private var mHomeCards: List<HomeCardModel>? = null
var homeCards: List<HomeCardModel>
get() { mHomeCards = mHomeCards ?: config.values.get("homeCards", listOf(), HomeCardModel::class.java); return mHomeCards ?: listOf() }

View File

@ -84,6 +84,8 @@ abstract class TimetableDao : BaseDao<Lesson, LessonFull> {
"LIMIT 1")
fun getBetweenDates(dateFrom: Date, dateTo: Date) =
getRaw("$QUERY WHERE (type != 3 AND date >= '${dateFrom.stringY_m_d}' AND date <= '${dateTo.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate >= '${dateFrom.stringY_m_d}' AND oldDate <= '${dateTo.stringY_m_d}') $ORDER_BY")
fun getChanges(profileId: Int) =
getRaw("$QUERY WHERE timetable.profileId = $profileId AND $IS_CHANGED $ORDER_BY")
// GET ALL - NOW
fun getAllNow(profileId: Int) =

View File

@ -116,14 +116,7 @@ open class Event(
var showAsUnseen: Boolean? = null
val startTimeCalendar: Calendar
get() = Calendar.getInstance().also { it.set(
date.year,
date.month - 1,
date.day,
time?.hour ?: 0,
time?.minute ?: 0,
time?.second ?: 0
) }
get() = date.getAsCalendar(time)
val endTimeCalendar: Calendar
get() = startTimeCalendar.also {

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-10.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.agenda
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.REGISTRATION_ENABLED
import pl.szczodrzynski.edziennik.databinding.DialogConfigAgendaBinding
import pl.szczodrzynski.edziennik.ui.dialogs.sync.RegistrationConfigDialog
import java.util.*
class AgendaConfigDialog(
private val activity: AppCompatActivity,
private val reloadOnDismiss: Boolean = true,
private val onShowListener: ((tag: String) -> Unit)? = null,
private val onDismissListener: ((tag: String) -> Unit)? = null
) {
companion object {
const val TAG = "AgendaConfigDialog"
}
private val app by lazy { activity.application as App }
private val config by lazy { app.config.ui }
private val profileConfig by lazy { app.config.forProfile().ui }
private lateinit var b: DialogConfigAgendaBinding
private lateinit var dialog: AlertDialog
init { run {
if (activity.isFinishing)
return@run
b = DialogConfigAgendaBinding.inflate(activity.layoutInflater)
onShowListener?.invoke(TAG)
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.menu_agenda_config)
.setView(b.root)
.setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() }
.setOnDismissListener {
saveConfig()
onDismissListener?.invoke(TAG)
if (reloadOnDismiss) (activity as? MainActivity)?.reloadTarget()
}
.create()
loadConfig()
dialog.show()
}}
private fun loadConfig() {
b.config = profileConfig
b.isAgendaMode = profileConfig.agendaViewType == Profile.AGENDA_DEFAULT
b.eventSharingEnabled.isChecked = app.profile.enableSharedEvents
&& app.profile.registration == REGISTRATION_ENABLED
b.eventSharingEnabled.onChange { _, isChecked ->
if (isChecked && app.profile.registration != REGISTRATION_ENABLED) {
b.eventSharingEnabled.isChecked = false
val dialog = RegistrationConfigDialog(activity, app.profile, onChangeListener = { enabled ->
b.eventSharingEnabled.isChecked = enabled
setEventSharingEnabled(enabled)
}, onShowListener, onDismissListener)
dialog.showEnableDialog()
return@onChange
}
setEventSharingEnabled(isChecked)
}
}
private fun setEventSharingEnabled(enabled: Boolean) {
if (enabled == app.profile.enableSharedEvents)
return
app.profile.enableSharedEvents = enabled
app.profileSave()
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.event_sharing)
.setMessage(
if (enabled)
R.string.settings_register_shared_events_dialog_enabled_text
else
R.string.settings_register_shared_events_dialog_disabled_text
)
.setPositiveButton(R.string.ok, null)
.show()
}
private fun saveConfig() {
}
}

View File

@ -7,7 +7,7 @@ package pl.szczodrzynski.edziennik.ui.dialogs.day
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.*
@ -19,6 +19,10 @@ import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListAdapter
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEvent
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEventRenderer
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEvent
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEventRenderer
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@ -29,6 +33,7 @@ class DayDialog(
val activity: AppCompatActivity,
val profileId: Int,
val date: Date,
val eventTypeId: Long? = null,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
@ -109,10 +114,16 @@ class DayDialog(
}
lessonChanges.ifNotEmpty {
b.lessonChangeContainer.root.visibility = View.VISIBLE
b.lessonChangeContainer.lessonChangeCount.text = it.size.toString()
LessonChangesEventRenderer().render(
b.lessonChanges, LessonChangesEvent(
profileId = profileId,
date = date,
count = it.size,
showBadge = false
)
)
b.lessonChangeLayout.onClick {
b.lessonChangesFrame.onClick {
LessonChangeDialog(
activity,
profileId,
@ -122,16 +133,22 @@ class DayDialog(
)
}
}
b.lessonChangesFrame.isVisible = lessonChanges.isNotEmpty()
val teacherAbsences = withContext(Dispatchers.Default) {
app.db.teacherAbsenceDao().getAllByDateNow(profileId, date)
}
teacherAbsences.ifNotEmpty {
b.teacherAbsenceContainer.root.visibility = View.VISIBLE
b.teacherAbsenceContainer.teacherAbsenceCount.text = it.size.toString()
TeacherAbsenceEventRenderer().render(
b.teacherAbsence, TeacherAbsenceEvent(
profileId = profileId,
date = date,
count = it.size
)
)
b.teacherAbsenceLayout.onClick {
b.teacherAbsenceFrame.onClick {
TeacherAbsenceDialog(
activity,
profileId,
@ -141,6 +158,7 @@ class DayDialog(
)
}
}
b.teacherAbsenceFrame.isVisible = teacherAbsences.isNotEmpty()
adapter = EventListAdapter(
activity,
@ -169,8 +187,12 @@ class DayDialog(
}
)
app.db.eventDao().getAllByDate(profileId, date).observe(activity, Observer { events ->
adapter.items = events
app.db.eventDao().getAllByDate(profileId, date).observe(activity) { events ->
adapter.items = if (eventTypeId != null)
events.filter { it.type == eventTypeId }
else
events
if (b.eventsView.adapter == null) {
b.eventsView.adapter = adapter
b.eventsView.apply {
@ -189,6 +211,6 @@ class DayDialog(
b.eventsView.visibility = View.GONE
b.eventsNoData.visibility = View.VISIBLE
}
})
}
}}
}

View File

@ -32,7 +32,7 @@ import kotlin.coroutines.CoroutineContext
class EventDetailsDialog(
val activity: AppCompatActivity,
val event: EventFull,
var event: EventFull,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
@ -46,6 +46,8 @@ class EventDetailsDialog(
private var removeEventDialog: AlertDialog? = null
private val eventShared = event.sharedBy != null
private val eventOwn = event.sharedBy == "self"
private val manager
get() = app.eventManager
private val job = Job()
override val coroutineContext: CoroutineContext
@ -92,6 +94,10 @@ class EventDetailsDialog(
b.eventShared = eventShared
b.eventOwn = eventOwn
if (!event.seen) {
manager.markAsSeen(event)
}
val bullet = ""
val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity)
@ -100,6 +106,8 @@ class EventDetailsDialog(
}
catch (_: Exception) {}
manager.setLegendText(b.legend, event)
b.typeColor.background?.setTintColor(event.eventColor)
b.details = mutableListOf(
@ -135,6 +143,7 @@ class EventDetailsDialog(
launch(Dispatchers.Default) {
app.db.eventDao().replace(event)
}
update()
b.checkDoneButton.isChecked = true
}
.setNegativeButton(R.string.cancel, null)
@ -145,6 +154,7 @@ class EventDetailsDialog(
launch(Dispatchers.Default) {
app.db.eventDao().replace(event)
}
update()
}
}
b.checkDoneButton.attachToastHint(R.string.hint_mark_as_done)
@ -156,6 +166,14 @@ class EventDetailsDialog(
activity,
event.profileId,
editingEvent = event,
onSaveListener = {
if (it == null) {
dialog.dismiss()
return@EventManualDialog
}
event = it
update()
},
onShowListener = onShowListener,
onDismissListener = onDismissListener
)
@ -323,8 +341,6 @@ class EventDetailsDialog(
removeEventDialog?.dismiss()
dialog.dismiss()
Toast.makeText(activity, R.string.removed, Toast.LENGTH_SHORT).show()
if (activity is MainActivity && activity.navTargetId == MainActivity.DRAWER_ITEM_AGENDA)
activity.reloadTarget()
}
private fun openInCalendar() { launch {

View File

@ -33,7 +33,8 @@ class EventListAdapter(
) : RecyclerView.Adapter<EventListAdapter.ViewHolder>(), CoroutineScope {
private val app = context.applicationContext as App
private val manager = app.eventManager
private val manager
get() = app.eventManager
private val job = Job()
override val coroutineContext: CoroutineContext
@ -67,7 +68,7 @@ class EventListAdapter(
b.simpleMode = simpleMode
b.topic.text = event.topic
manager.setEventTopic(b.topic, event, showType = false)
b.topic.maxLines = if (simpleMode) 2 else 3
b.details.text = mutableListOf<CharSequence?>(
@ -102,8 +103,6 @@ class EventListAdapter(
}
b.editButton.attachToastHint(R.string.hint_edit_event)
b.isDone.isVisible = event.isDone
if (event.showAsUnseen == null)
event.showAsUnseen = !event.seen

View File

@ -4,8 +4,6 @@
package pl.szczodrzynski.edziennik.ui.dialogs.event
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
@ -20,23 +18,18 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskAllFinishedEvent
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskFinishedEvent
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.EventType
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
import pl.szczodrzynski.edziennik.ui.dialogs.sync.RegistrationConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.views.TimeDropdown.Companion.DISPLAY_LESSONS
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
import kotlin.coroutines.CoroutineContext
@ -49,6 +42,7 @@ class EventManualDialog(
val defaultTime: Time? = null,
val defaultType: Long? = null,
val editingEvent: EventFull? = null,
val onSaveListener: ((event: EventFull?) -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
@ -323,57 +317,41 @@ class EventManualDialog(
selectDefault(defaultLesson?.displayTeacherId)
}
with (b.typeDropdown) {
db = app.db
profileId = this@EventManualDialog.profileId
loadItems()
selectDefault(editingEvent?.type)
selectDefault(defaultType)
val deferred = async(Dispatchers.Default) {
// get the event type list
var eventTypes = app.db.eventTypeDao().getAllNow(profileId)
if (eventTypes.none { it.id in -1L..10L }) {
eventTypes = app.db.eventTypeDao().addDefaultTypes(activity, profileId)
onTypeSelected = {
b.typeColor.background.setTintColor(it.color)
customColor = null
}
}
b.typeDropdown.clear()
b.typeDropdown += eventTypes.map { TextInputDropDown.Item(it.id, it.name, tag = it) }
}
deferred.await()
b.typeDropdown.isEnabled = true
defaultType?.let {
b.typeDropdown.select(it)
}
b.typeDropdown.selected?.let { item ->
customColor = (item.tag as EventType).color
}
// copy IDs from event being edited
// copy data from event being edited
editingEvent?.let {
b.topic.setText(it.topic)
b.typeDropdown.select(it.type)?.let { item ->
customColor = (item.tag as EventType).color
}
if (it.color != null && it.color != -1)
if (it.color != -1)
customColor = it.color
}
b.typeColor.background.setTintColor(
customColor
?: b.typeDropdown.getSelected()?.color
?: Event.COLOR_DEFAULT
)
// copy IDs from the LessonFull
defaultLesson?.let {
b.teamDropdown.select(it.displayTeamId)
}
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 currentColor = customColor
?: b.typeDropdown.getSelected()?.color
?: Event.COLOR_DEFAULT
val colorPickerDialog = ColorPickerDialog.newBuilder()
.setColor(currentColor)
.create()
@ -381,7 +359,7 @@ class EventManualDialog(
object : ColorPickerDialogListener {
override fun onDialogDismissed(dialogId: Int) {}
override fun onColorSelected(dialogId: Int, color: Int) {
b.typeColor.background.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP)
b.typeColor.background.setTintColor(color)
customColor = color
}
})
@ -416,11 +394,11 @@ class EventManualDialog(
private fun saveEvent() {
val date = b.dateDropdown.getSelected() as? Date
val timeSelected = b.timeDropdown.getSelected()
val teamId = b.teamDropdown.getSelected() as? Long
val type = b.typeDropdown.selected?.id
val team = b.teamDropdown.getSelected()
val type = b.typeDropdown.getSelected()
val topic = b.topic.text?.toString()
val subjectId = b.subjectDropdown.getSelected() as? Long
val teacherId = b.teacherDropdown.getSelected()
val subject = b.subjectDropdown.getSelected() as? Subject
val teacher = b.teacherDropdown.getSelected()
val share = b.shareSwitch.isChecked
@ -451,7 +429,7 @@ class EventManualDialog(
isError = true
}
if (share && teamId == null) {
if (share && team == null) {
b.teamDropdown.error = app.getString(R.string.dialog_event_manual_team_choose)
if (!isError) b.teamDropdown.parent.requestChildFocus(b.teamDropdown, b.teamDropdown)
isError = true
@ -487,10 +465,10 @@ class EventManualDialog(
time = startTime,
topic = topic,
color = customColor,
type = type ?: Event.TYPE_DEFAULT,
teacherId = teacherId ?: -1,
subjectId = subjectId ?: -1,
teamId = teamId ?: -1,
type = type?.id ?: Event.TYPE_DEFAULT,
teacherId = teacher?.id ?: -1,
subjectId = subject?.id ?: -1,
teamId = team?.id ?: -1,
addedDate = editingEvent?.addedDate ?: System.currentTimeMillis()
).also {
it.addedManually = true
@ -498,7 +476,7 @@ class EventManualDialog(
val metadataObject = Metadata(
profileId,
when (type) {
when (type?.id) {
Event.TYPE_HOMEWORK -> Metadata.TYPE_HOMEWORK
else -> Metadata.TYPE_EVENT
},
@ -597,10 +575,14 @@ class EventManualDialog(
}
}
onSaveListener?.invoke(eventObject.withMetadata(metadataObject).also {
it.subjectLongName = (b.subjectDropdown.getSelected() as? Subject)?.longName
it.teacherName = b.teacherDropdown.getSelected()?.fullName
it.teamName = b.teamDropdown.getSelected()?.name
it.typeName = b.typeDropdown.getSelected()?.name
})
dialog.dismiss()
Toast.makeText(activity, R.string.saved, Toast.LENGTH_SHORT).show()
if (activity is MainActivity && activity.navTargetId == DRAWER_ITEM_AGENDA)
activity.reloadTarget()
}
private fun finishRemoving() {
editingEvent ?: return
@ -611,9 +593,8 @@ class EventManualDialog(
}
removeEventDialog?.dismiss()
onSaveListener?.invoke(null)
dialog.dismiss()
Toast.makeText(activity, R.string.removed, Toast.LENGTH_SHORT).show()
if (activity is MainActivity && activity.navTargetId == DRAWER_ITEM_AGENDA)
activity.reloadTarget()
}
}

View File

@ -50,7 +50,8 @@ class LessonDetailsDialog(
get() = job + Dispatchers.Main
private lateinit var adapter: EventListAdapter
private val manager by lazy { app.timetableManager }
private val manager
get() = app.timetableManager
init { run {
if (activity.isFinishing)

View File

@ -12,10 +12,6 @@ import android.widget.Toast
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import com.applandeo.materialcalendarview.EventDay
import com.github.tibolte.agendacalendarview.CalendarPickerController
import com.github.tibolte.agendacalendarview.models.BaseCalendarEvent
import com.github.tibolte.agendacalendarview.models.CalendarEvent
import com.github.tibolte.agendacalendarview.models.IDayItem
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.colorInt
@ -29,17 +25,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaCalendarBinding
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding
import pl.szczodrzynski.edziennik.ui.dialogs.agenda.AgendaConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.day.DayDialog
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange.LessonChangeCounter
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange.LessonChangeEvent
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange.LessonChangeEventRenderer
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.Colors
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
@ -59,7 +47,8 @@ class AgendaFragment : Fragment(), CoroutineScope {
get() = job + Dispatchers.Main
private var type: Int = Profile.AGENDA_DEFAULT
private var actualDate: Date? = null
private var agendaDefault: AgendaFragmentDefault? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
if (getActivity() == null || context == null) return null
@ -82,38 +71,61 @@ class AgendaFragment : Fragment(), CoroutineScope {
.withTitle(R.string.menu_add_event)
.withDescription(R.string.menu_add_event_desc)
.withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
.withOnClickListener(View.OnClickListener {
.withOnClickListener {
activity.bottomSheet.close()
EventManualDialog(activity, app.profileId, defaultDate = actualDate)
}),
EventManualDialog(
activity,
app.profileId,
defaultDate = AgendaFragmentDefault.selectedDate
)
},
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_agenda_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener {
activity.bottomSheet.close()
AgendaConfigDialog(activity, true, null, null)
},
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_agenda_change_view)
.withIcon(if (type == Profile.AGENDA_DEFAULT) CommunityMaterial.Icon.cmd_calendar_outline else CommunityMaterial.Icon2.cmd_format_list_bulleted_square)
.withOnClickListener(View.OnClickListener {
.withOnClickListener {
activity.bottomSheet.close()
type = if (type == Profile.AGENDA_DEFAULT) Profile.AGENDA_CALENDAR else Profile.AGENDA_DEFAULT
type =
if (type == Profile.AGENDA_DEFAULT) Profile.AGENDA_CALENDAR else Profile.AGENDA_DEFAULT
app.config.forProfile().ui.agendaViewType = type
activity.reloadTarget()
}),
},
BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_as_read)
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener(View.OnClickListener { launch {
.withOnClickListener {
launch {
activity.bottomSheet.close()
withContext(Dispatchers.Default) {
App.db.metadataDao().setAllSeen(app.profileId, Metadata.TYPE_EVENT, true)
App.db.metadataDao()
.setAllSeen(app.profileId, Metadata.TYPE_EVENT, true)
}
Toast.makeText(
activity,
R.string.main_menu_mark_as_read_success,
Toast.LENGTH_SHORT
).show()
}
}
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.add)
activity.navView.bottomBar.fabIcon = CommunityMaterial.Icon3.cmd_plus
activity.navView.setFabOnClickListener(View.OnClickListener {
EventManualDialog(activity, app.profileId, defaultDate = actualDate)
})
activity.navView.setFabOnClickListener {
EventManualDialog(
activity,
app.profileId,
defaultDate = AgendaFragmentDefault.selectedDate
)
}
activity.gainAttention()
activity.gainAttentionFAB()
@ -129,143 +141,8 @@ class AgendaFragment : Fragment(), CoroutineScope {
return@launch
delay(500)
val eventList = mutableListOf<CalendarEvent>()
val minDate = Calendar.getInstance().apply {
add(Calendar.MONTH, -2)
set(Calendar.DAY_OF_MONTH, 1)
}
val maxDate = Calendar.getInstance().apply { add(Calendar.MONTH, 2) }
/**
* LESSON CHANGES
*/
if (!isAdded)
return@launch
val lessons = withContext(Dispatchers.Default) { app.db.timetableDao().getChangesNow(app.profileId) }
val lessonChangeCounters = mutableListOf<LessonChangeCounter>()
lessons.forEach { lesson ->
lessonChangeCounters.firstOrNull { it.lessonChangeDate == lesson.displayDate }?.let {
it.lessonChangeCount += 1
} ?: run {
lessonChangeCounters.add(LessonChangeCounter(
lesson.displayDate ?: return@forEach,
1
))
}
}
lessonChangeCounters.forEach { counter ->
eventList.add(LessonChangeEvent(
counter.lessonChangeDate.inMillis,
0xff78909c.toInt(),
Colors.legibleTextColor(0xff78909c.toInt()),
counter.startTime,
counter.endTime,
app.profileId,
counter.lessonChangeDate,
counter.lessonChangeCount
))
}
/**
* TEACHER ABSENCES
*/
if (!isAdded)
return@launch
val showTeacherAbsences = app.profile.getStudentData("showTeacherAbsences", true)
if (showTeacherAbsences) {
val teacherAbsenceList = withContext(Dispatchers.Default) { app.db.teacherAbsenceDao().getAllNow(app.profileId) }
val teacherAbsenceCounters = mutableListOf<TeacherAbsenceCounter>()
teacherAbsenceList.forEach { absence ->
val date = absence.dateFrom.clone()
while (date <= absence.dateTo) {
teacherAbsenceCounters.firstOrNull { it.teacherAbsenceDate == date }?.let {
it.teacherAbsenceCount += 1
} ?: run {
teacherAbsenceCounters.add(TeacherAbsenceCounter(date.clone(), 1))
}
date.stepForward(0, 0, 1)
}
}
teacherAbsenceCounters.forEach { counter ->
eventList.add(TeacherAbsenceEvent(
counter.teacherAbsenceDate.inMillis,
0xffff1744.toInt(),
Colors.legibleTextColor(0xffff1744.toInt()),
counter.startTime,
counter.endTime,
app.profileId,
counter.teacherAbsenceDate,
counter.teacherAbsenceCount
))
}
}
/**
* EVENTS
*/
if (!isAdded)
return@launch
val events = withContext(Dispatchers.Default) { app.db.eventDao().getAllNow(app.profileId) }
val unreadEventDates = mutableSetOf<Int>()
events.forEach { event ->
eventList.add(BaseCalendarEvent(
"${event.typeName ?: "wydarzenie"} - ${event.topic}",
"",
(if (event.time == null) getString(R.string.agenda_event_all_day) else event.time!!.stringHM) +
(event.subjectLongName?.let { ", $it" } ?: "") +
(event.teacherName?.let { ", $it" } ?: "") +
(event.teamName?.let { ", $it" } ?: ""),
event.eventColor,
Colors.legibleTextColor(event.eventColor),
event.startTimeCalendar,
event.endTimeCalendar,
event.time == null,
event.id,
!event.seen
))
if (!event.seen) unreadEventDates.add(event.date.value)
}
b.agendaDefaultView.init(eventList, minDate, maxDate, Locale.getDefault(), object : CalendarPickerController {
override fun onDaySelected(dayItem: IDayItem?) {}
override fun onScrollToDate(calendar: Calendar) { this@AgendaFragment.launch {
val date = Date.fromCalendar(calendar)
actualDate = date
// Mark as read scrolled date
if (date.value in unreadEventDates) {
withContext(Dispatchers.Default) { app.db.eventDao().setSeenByDate(app.profileId, date, true) }
unreadEventDates.remove(date.value)
}
}}
override fun onEventSelected(event: CalendarEvent) {
val date = Date.fromCalendar(event.instanceDay)
when (event) {
is BaseCalendarEvent -> DayDialog(activity, app.profileId, date)
is LessonChangeEvent -> LessonChangeDialog(activity, app.profileId, date)
is TeacherAbsenceEvent -> TeacherAbsenceDialog(activity, app.profileId, date)
}
}
}, LessonChangeEventRenderer(), TeacherAbsenceEventRenderer())
b.progressBar.visibility = View.GONE
agendaDefault = AgendaFragmentDefault(activity, app, b)
agendaDefault?.initView(this@AgendaFragment)
}}}
private fun createCalendarAgendaView() { (b as? FragmentAgendaCalendarBinding)?.let { b -> launch {

View File

@ -0,0 +1,310 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
*/
package pl.szczodrzynski.edziennik.ui.modules.agenda
import android.util.SparseIntArray
import android.widget.AbsListView
import android.widget.AbsListView.OnScrollListener
import androidx.core.util.forEach
import androidx.core.util.set
import androidx.core.view.isVisible
import com.github.tibolte.agendacalendarview.CalendarManager
import com.github.tibolte.agendacalendarview.CalendarPickerController
import com.github.tibolte.agendacalendarview.agenda.AgendaAdapter
import com.github.tibolte.agendacalendarview.models.BaseCalendarEvent
import com.github.tibolte.agendacalendarview.models.CalendarEvent
import com.github.tibolte.agendacalendarview.models.IDayItem
import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding
import pl.szczodrzynski.edziennik.ui.dialogs.day.DayDialog
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventDetailsDialog
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog
import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
import pl.szczodrzynski.edziennik.ui.modules.agenda.event.AgendaEvent
import pl.szczodrzynski.edziennik.ui.modules.agenda.event.AgendaEventGroup
import pl.szczodrzynski.edziennik.ui.modules.agenda.event.AgendaEventGroupRenderer
import pl.szczodrzynski.edziennik.ui.modules.agenda.event.AgendaEventRenderer
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEvent
import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges.LessonChangesEventRenderer
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 java.util.*
class AgendaFragmentDefault(
private val activity: MainActivity,
private val app: App,
private val b: FragmentAgendaDefaultBinding
) : OnScrollListener, CoroutineScope {
companion object {
var selectedDate: Date = Date.getToday()
}
override val coroutineContext = Job() + Dispatchers.Main
private val unreadDates = mutableSetOf<Int>()
private val events = mutableListOf<CalendarEvent>()
private var isInitialized = false
private val profileConfig by lazy { app.config.forProfile().ui }
private val listView
get() = b.agendaDefaultView.agendaView.agendaListView
private val adapter
get() = listView.adapter as? AgendaAdapter
private val manager
get() = CalendarManager.getInstance()
private var scrollState = OnScrollListener.SCROLL_STATE_IDLE
private var updatePending = false
private var notifyPending = false
override fun onScrollStateChanged(view: AbsListView?, newScrollState: Int) {
b.agendaDefaultView.agendaScrollListener.onScrollStateChanged(view, scrollState)
scrollState = newScrollState
if (updatePending) updateData()
if (notifyPending) notifyDataSetChanged()
}
override fun onScroll(
view: AbsListView?,
firstVisibleItem: Int,
visibleItemCount: Int,
totalItemCount: Int
) = b.agendaDefaultView.agendaScrollListener.onScroll(
view,
firstVisibleItem,
visibleItemCount,
totalItemCount
)
/**
* Mark the data as needing update, either after 1 second (when
* not scrolling) or 1 second after scrolling stops.
*/
private fun updateData() = launch {
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
updatePending = false
delay(1000)
notifyDataSetChanged()
} else updatePending = true
}
/**
* Notify the adapter about changes, either instantly or after
* scrolling stops.
*/
private fun notifyDataSetChanged() {
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
notifyPending = false
adapter?.notifyDataSetChanged()
} else notifyPending = true
}
suspend fun initView(fragment: AgendaFragment) {
isInitialized = false
withContext(Dispatchers.Default) {
if (profileConfig.agendaLessonChanges)
addLessonChanges(events)
if (profileConfig.agendaTeacherAbsence)
addTeacherAbsence(events)
}
app.db.eventDao().getAll(app.profileId).observe(fragment) {
addEvents(events, it)
if (isInitialized)
updateView()
else
initViewPriv()
}
}
private fun initViewPriv() {
val dateStart = app.profile.dateSemester1Start.asCalendar
val dateEnd = app.profile.dateYearEnd.asCalendar
val isCompactMode = profileConfig.agendaCompactMode
b.agendaDefaultView.init(
events,
dateStart,
dateEnd,
Locale.getDefault(),
object : CalendarPickerController {
override fun onDaySelected(dayItem: IDayItem) {
val c = Calendar.getInstance()
c.time = dayItem.date
if (c.timeInMillis == selectedDate.inMillis) {
DayDialog(activity, app.profileId, selectedDate)
}
}
override fun onEventSelected(event: CalendarEvent) {
val date = Date.fromCalendar(event.instanceDay)
when (event) {
is AgendaEvent -> EventDetailsDialog(activity, event.event)
is LessonChangesEvent -> LessonChangeDialog(activity, app.profileId, date)
is TeacherAbsenceEvent -> TeacherAbsenceDialog(
activity,
app.profileId,
date
)
is AgendaEventGroup -> DayDialog(activity, app.profileId, date, eventTypeId = event.typeId)
is BaseCalendarEvent -> if (event.isPlaceHolder)
DayDialog(activity, app.profileId, date)
}
if (event is BaseEvent && event.showItemBadge) {
val unreadCount = manager.events.count {
it.instanceDay.equals(event.instanceDay) && it.showBadge
}
// only clicked event is unread, remove the day badge
if (unreadCount == 1 && event.showBadge) {
event.dayReference.showBadge = false
unreadDates.remove(date.value)
}
setAsRead(event)
}
}
override fun onScrollToDate(calendar: Calendar) {
selectedDate = Date.fromCalendar(calendar)
// Mark as read scrolled date
if (selectedDate.value in unreadDates) {
setAsRead(calendar)
activity.launch(Dispatchers.Default) {
app.db.eventDao().setSeenByDate(app.profileId, selectedDate, true)
}
unreadDates.remove(selectedDate.value)
}
}
},
AgendaEventRenderer(app.eventManager, isCompactMode),
AgendaEventGroupRenderer(),
LessonChangesEventRenderer(),
TeacherAbsenceEventRenderer()
)
listView.setOnScrollListener(this)
isInitialized = true
b.progressBar.isVisible = false
}
private fun updateView() {
manager.events.clear()
manager.loadEvents(events, BaseCalendarEvent())
adapter?.updateEvents(manager.events)
//listView.scrollToCurrentDate(selectedDate.asCalendar)
}
private fun setAsRead(date: Calendar) {
// get all events matching the date
val events = manager.events.filter {
if (it.instanceDay.equals(date) && it.showBadge && it is AgendaEvent) {
// hide the day badge for the date
it.dayReference.showBadge = false
return@filter true
}
false
}
// set this date's events as read
setAsRead(*events.toTypedArray())
}
private fun setAsRead(vararg event: CalendarEvent) {
// hide per-event badges
for (e in event) {
events.firstOrNull {
it == e
}?.showBadge = false
e.showBadge = false
}
listView.setOnScrollListener(this)
updateData()
}
private fun addEvents(
events: MutableList<CalendarEvent>,
eventList: List<EventFull>
) {
events.removeAll { it is AgendaEvent || it is AgendaEventGroup }
if (!profileConfig.agendaGroupByType) {
events += eventList.map {
if (!it.seen)
unreadDates.add(it.date.value)
AgendaEvent(it)
}
return
}
eventList.groupBy {
it.date.value to it.type
}.forEach { (_, list) ->
val event = list.first()
if (list.size == 1) {
if (!event.seen)
unreadDates.add(event.date.value)
events += AgendaEvent(event)
} else {
events.add(0, AgendaEventGroup(
profileId = event.profileId,
date = event.date,
typeId = event.type,
typeName = event.typeName ?: "-",
typeColor = event.typeColor ?: event.eventColor,
count = list.size,
showBadge = list.any { !it.seen }
))
}
}
}
private fun addLessonChanges(events: MutableList<CalendarEvent>) {
val lessons = app.db.timetableDao().getChangesNow(app.profileId)
val grouped = lessons.groupBy {
it.displayDate
}
events += grouped.mapNotNull { (date, changes) ->
LessonChangesEvent(
app.profileId,
date = date ?: return@mapNotNull null,
count = changes.size,
showBadge = changes.any { !it.seen }
)
}
}
private fun addTeacherAbsence(events: MutableList<CalendarEvent>) {
val teacherAbsence = app.db.teacherAbsenceDao().getAllNow(app.profileId)
val countMap = SparseIntArray()
for (absence in teacherAbsence) {
while (absence.dateFrom <= absence.dateTo) {
countMap[absence.dateFrom.value] += 1
absence.dateFrom.stepForward(0, 0, 1)
}
}
countMap.forEach { dateInt, count ->
events += TeacherAbsenceEvent(
app.profileId,
date = Date.fromValue(dateInt),
count = count
)
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-9.
*/
package pl.szczodrzynski.edziennik.ui.modules.agenda
import com.github.tibolte.agendacalendarview.models.CalendarEvent
import com.github.tibolte.agendacalendarview.models.IDayItem
import com.github.tibolte.agendacalendarview.models.IWeekItem
import java.util.*
open class BaseEvent(
private val id: Long,
private val time: Calendar,
private val color: Int,
private var showBadge: Boolean,
var showItemBadge: Boolean = showBadge
) : CalendarEvent {
override fun copy() = BaseEvent(id, time, color, showBadge)
private lateinit var date: Calendar
override fun getInstanceDay() = date
override fun setInstanceDay(value: Calendar) {
date = value
}
private lateinit var dayReference: IDayItem
override fun getDayReference() = dayReference
override fun setDayReference(value: IDayItem) {
dayReference = value
}
private lateinit var weekReference: IWeekItem
override fun getWeekReference() = weekReference
override fun setWeekReference(value: IWeekItem) {
weekReference = value
}
override fun getShowBadge() = showBadge
override fun setShowBadge(value: Boolean) {
showBadge = value
showItemBadge = value
}
override fun getId() = id
override fun getStartTime() = time
override fun getEndTime() = time
override fun getTitle() = ""
override fun getDescription() = ""
override fun getLocation() = ""
override fun getColor() = color
override fun getTextColor() = 0
override fun isPlaceholder() = false
override fun isAllDay() = false
override fun setId(value: Long) = Unit
override fun setStartTime(value: Calendar) = Unit
override fun setEndTime(value: Calendar) = Unit
override fun setTitle(value: String) = Unit
override fun setDescription(value: String) = Unit
override fun setLocation(value: String) = Unit
override fun setTextColor(value: Int) = Unit
override fun setPlaceholder(value: Boolean) = Unit
override fun setAllDay(value: Boolean) = Unit
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
*/
package pl.szczodrzynski.edziennik.ui.modules.agenda.event
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.ui.modules.agenda.BaseEvent
class AgendaEvent(
val event: EventFull,
showBadge: Boolean = !event.seen
) : BaseEvent(
id = event.id,
time = event.startTimeCalendar,
color = event.eventColor,
showBadge = showBadge
) {
override fun copy() = AgendaEvent(event, showBadge)
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-10.
*/
package pl.szczodrzynski.edziennik.ui.modules.agenda.event
import pl.szczodrzynski.edziennik.ui.modules.agenda.BaseEvent
import pl.szczodrzynski.edziennik.utils.models.Date
class AgendaEventGroup(
val profileId: Int,
val date: Date,
val typeId: Long,
val typeName: String,
val typeColor: Int,
val count: Int,
showBadge: Boolean
) : BaseEvent(
id = date.value.toLong(),
time = date.asCalendar,
color = typeColor,
showBadge = showBadge
) {
override fun copy() = AgendaEventGroup(profileId, date, typeId, typeName, typeColor, count, showBadge)
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-10.
*/
package pl.szczodrzynski.edziennik.ui.modules.agenda.event
import android.view.View
import androidx.core.view.isVisible
import com.github.tibolte.agendacalendarview.render.EventRenderer
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedGroupBinding
import pl.szczodrzynski.edziennik.resolveAttr
import pl.szczodrzynski.edziennik.setTintColor
import pl.szczodrzynski.edziennik.utils.Colors
class AgendaEventGroupRenderer : EventRenderer<AgendaEventGroup>() {
override fun render(view: View, event: AgendaEventGroup) {
val b = AgendaWrappedGroupBinding.bind(view).item
b.card.foreground.setTintColor(event.color)
b.card.background.setTintColor(event.color)
b.name.text = event.typeName
b.name.setTextColor(Colors.legibleTextColor(event.color))
b.count.text = event.count.toString()
b.count.background.setTintColor(android.R.attr.colorBackground.resolveAttr(view.context))
b.badge.isVisible = event.showItemBadge
}
override fun getEventLayout(): Int = R.layout.agenda_wrapped_group
}

View File

@ -0,0 +1,81 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
*/
package pl.szczodrzynski.edziennik.ui.modules.agenda.event
import android.annotation.SuppressLint
import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import androidx.core.view.isVisible
import com.github.tibolte.agendacalendarview.render.EventRenderer
import com.mikepenz.iconics.view.IconicsTextView
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedEventBinding
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedEventCompactBinding
import pl.szczodrzynski.edziennik.join
import pl.szczodrzynski.edziennik.resolveAttr
import pl.szczodrzynski.edziennik.setTintColor
import pl.szczodrzynski.edziennik.utils.Colors
import pl.szczodrzynski.edziennik.utils.managers.EventManager
class AgendaEventRenderer(
val manager: EventManager,
val isCompact: Boolean
) : EventRenderer<AgendaEvent>() {
@SuppressLint("SetTextI18n")
override fun render(view: View, aEvent: AgendaEvent) {
if (isCompact) {
val b = AgendaWrappedEventCompactBinding.bind(view).item
bindView(aEvent, b.card, b.title, null, b.badgeBackground, b.badge)
} else {
val b = AgendaWrappedEventBinding.bind(view).item
bindView(aEvent, b.card, b.title, b.subtitle, b.badgeBackground, b.badge)
}
}
private fun bindView(
aEvent: AgendaEvent,
card: FrameLayout,
title: IconicsTextView,
subtitle: TextView?,
badgeBackground: View,
badge: View
) {
val event = aEvent.event
val textColor = Colors.legibleTextColor(event.eventColor)
val timeText = if (event.time == null)
card.context.getString(R.string.agenda_event_all_day)
else
event.time!!.stringHM
val eventSubtitle = listOfNotNull(
timeText,
event.subjectLongName,
event.teacherName,
event.teamName
).join(", ")
card.foreground.setTintColor(event.eventColor)
card.background.setTintColor(event.eventColor)
manager.setEventTopic(title, event, doneIconColor = textColor)
title.setTextColor(textColor)
subtitle?.text = eventSubtitle
subtitle?.setTextColor(textColor)
badgeBackground.isVisible = aEvent.showItemBadge
badgeBackground.background.setTintColor(
android.R.attr.colorBackground.resolveAttr(card.context)
)
badge.isVisible = aEvent.showItemBadge
}
override fun getEventLayout() = if (isCompact)
R.layout.agenda_wrapped_event_compact
else
R.layout.agenda_wrapped_event
}

View File

@ -1,19 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange
import pl.szczodrzynski.edziennik.utils.models.Date
import java.util.*
class LessonChangeCounter(
val lessonChangeDate: Date,
var lessonChangeCount: Int
) {
val startTime: Calendar
get() = Calendar.getInstance().apply {
set(lessonChangeDate.year, lessonChangeDate.month - 1, lessonChangeDate.day, 10, 0, 0)
}
val endTime: Calendar
get() = Calendar.getInstance().apply {
timeInMillis = startTime.timeInMillis + (45 * 60 * 1000)
}
}

View File

@ -1,243 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange;
import com.github.tibolte.agendacalendarview.models.CalendarEvent;
import com.github.tibolte.agendacalendarview.models.IDayItem;
import com.github.tibolte.agendacalendarview.models.IWeekItem;
import java.util.Calendar;
import pl.szczodrzynski.edziennik.utils.models.Date;
public class LessonChangeEvent implements CalendarEvent {
/**
* Id of the event.
*/
private long mId;
/**
* Color to be displayed in the agenda view.
*/
private int mColor;
/**
* Text color displayed on the background color
*/
private int mTextColor;
/**
* Calendar instance helping sorting the events per section in the agenda view.
*/
private Calendar mInstanceDay;
/**
* Start time of the event.
*/
private Calendar mStartTime;
/**
* End time of the event.
*/
private Calendar mEndTime;
/**
* References to a DayItem instance for that event, used to link interaction between the
* calendar view and the agenda view.
*/
private IDayItem mDayReference;
/**
* References to a WeekItem instance for that event, used to link interaction between the
* calendar view and the agenda view.
*/
private IWeekItem mWeekReference;
private int profileId;
private Date lessonChangeDate;
private int lessonChangeCount;
public LessonChangeEvent(LessonChangeEvent calendarEvent) {
this.mId = calendarEvent.getId();
this.mColor = calendarEvent.getColor();
this.mTextColor = calendarEvent.getTextColor();
this.mStartTime = calendarEvent.getStartTime();
this.mEndTime = calendarEvent.getEndTime();
this.profileId = calendarEvent.getProfileId();
this.lessonChangeDate = calendarEvent.getLessonChangeDate();
this.lessonChangeCount = calendarEvent.getLessonChangeCount();
}
public LessonChangeEvent(long mId, int mColor, int mTextColor, Calendar mStartTime, Calendar mEndTime, int profileId, Date lessonChangeDate, int lessonChangeCount) {
this.mId = mId;
this.mColor = mColor;
this.mTextColor = mTextColor;
this.mStartTime = mStartTime;
this.mEndTime = mEndTime;
this.profileId = profileId;
this.lessonChangeDate = lessonChangeDate;
this.lessonChangeCount = lessonChangeCount;
}
public int getProfileId() {
return profileId;
}
public Date getLessonChangeDate() {
return lessonChangeDate;
}
public int getLessonChangeCount() {
return lessonChangeCount;
}
public void setProfileId(int profileId) {
this.profileId = profileId;
}
public void setLessonChangeDate(Date lessonChangeDate) {
this.lessonChangeDate = lessonChangeDate;
}
public void setLessonChangeCount(int lessonChangeCount) {
this.lessonChangeCount = lessonChangeCount;
}
@Override
public void setPlaceholder(boolean placeholder) {
}
@Override
public boolean isPlaceholder() {
return false;
}
@Override
public String getLocation() {
return null;
}
@Override
public void setLocation(String mLocation) {
}
@Override
public long getId() {
return mId;
}
@Override
public void setId(long mId) {
this.mId = mId;
}
@Override
public boolean getShowBadge() {
return false;
}
@Override
public void setShowBadge(boolean mShowBadge) {
}
@Override
public int getTextColor() {
return mTextColor;
}
@Override
public void setTextColor(int mTextColor) {
this.mTextColor = mTextColor;
}
@Override
public String getDescription() {
return null;
}
@Override
public void setDescription(String mDescription) {
}
@Override
public boolean isAllDay() {
return false;
}
@Override
public void setAllDay(boolean allDay) {
}
@Override
public Calendar getStartTime() {
return mStartTime;
}
@Override
public void setStartTime(Calendar mStartTime) {
this.mStartTime = mStartTime;
}
@Override
public Calendar getEndTime() {
return mEndTime;
}
@Override
public void setEndTime(Calendar mEndTime) {
this.mEndTime = mEndTime;
}
@Override
public String getTitle() {
return null;
}
@Override
public void setTitle(String mTitle) {
}
@Override
public Calendar getInstanceDay() {
return mInstanceDay;
}
@Override
public void setInstanceDay(Calendar mInstanceDay) {
this.mInstanceDay = mInstanceDay;
this.mInstanceDay.set(Calendar.HOUR, 0);
this.mInstanceDay.set(Calendar.MINUTE, 0);
this.mInstanceDay.set(Calendar.SECOND, 0);
this.mInstanceDay.set(Calendar.MILLISECOND, 0);
this.mInstanceDay.set(Calendar.AM_PM, 0);
}
@Override
public IDayItem getDayReference() {
return mDayReference;
}
@Override
public void setDayReference(IDayItem mDayReference) {
this.mDayReference = mDayReference;
}
@Override
public IWeekItem getWeekReference() {
return mWeekReference;
}
@Override
public void setWeekReference(IWeekItem mWeekReference) {
this.mWeekReference = mWeekReference;
}
@Override
public CalendarEvent copy() {
return new LessonChangeEvent(this);
}
@Override
public int getColor() {
return mColor;
}
}

View File

@ -1,21 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange
import android.view.View
import android.widget.TextView
import androidx.cardview.widget.CardView
import com.github.tibolte.agendacalendarview.render.EventRenderer
import pl.szczodrzynski.edziennik.R
class LessonChangeEventRenderer : EventRenderer<LessonChangeEvent>() {
override fun render(view: View?, event: LessonChangeEvent) {
val card = view?.findViewById<CardView>(R.id.lesson_change_card)
val changeText = view?.findViewById<TextView>(R.id.lesson_change_text)
val changeCount = view?.findViewById<TextView>(R.id.lessonChangeCount)
card?.setCardBackgroundColor(event.color)
changeText?.setTextColor(event.textColor)
changeCount?.setTextColor(event.textColor)
changeCount?.text = event.lessonChangeCount.toString()
}
override fun getEventLayout(): Int = R.layout.agenda_event_lesson_change
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
*/
package pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges
import pl.szczodrzynski.edziennik.ui.modules.agenda.BaseEvent
import pl.szczodrzynski.edziennik.utils.models.Date
class LessonChangesEvent(
val profileId: Int,
val date: Date,
val count: Int,
showBadge: Boolean
) : BaseEvent(
id = date.value.toLong(),
time = date.asCalendar,
color = 0xff78909c.toInt(),
showBadge = false,
showItemBadge = showBadge
) {
override fun copy() = LessonChangesEvent(profileId, date, count, showItemBadge)
override fun getShowBadge() = false
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
*/
package pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchanges
import android.view.View
import androidx.core.view.isVisible
import com.github.tibolte.agendacalendarview.render.EventRenderer
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.AgendaCounterItemBinding
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedCounterBinding
import pl.szczodrzynski.edziennik.resolveAttr
import pl.szczodrzynski.edziennik.setTintColor
import pl.szczodrzynski.edziennik.utils.Colors
class LessonChangesEventRenderer : EventRenderer<LessonChangesEvent>() {
override fun render(view: View, event: LessonChangesEvent) {
val b = AgendaWrappedCounterBinding.bind(view).item
val textColor = Colors.legibleTextColor(event.color)
b.card.foreground.setTintColor(event.color)
b.card.background.setTintColor(event.color)
b.name.setText(R.string.agenda_lesson_changes)
b.name.setTextColor(textColor)
b.count.text = event.count.toString()
b.count.setTextColor(textColor)
b.badgeBackground.isVisible = event.showItemBadge
b.badgeBackground.background.setTintColor(
android.R.attr.colorBackground.resolveAttr(view.context)
)
b.badge.isVisible = event.showItemBadge
}
fun render(b: AgendaCounterItemBinding, event: LessonChangesEvent) {
val textColor = Colors.legibleTextColor(event.color)
b.card.foreground.setTintColor(event.color)
b.card.background.setTintColor(event.color)
b.name.setText(R.string.agenda_lesson_changes)
b.name.setTextColor(textColor)
b.count.text = event.count.toString()
b.count.setTextColor(textColor)
b.badgeBackground.isVisible = event.showItemBadge
b.badgeBackground.background.setTintColor(
android.R.attr.colorBackground.resolveAttr(b.root.context)
)
b.badge.isVisible = event.showItemBadge
}
override fun getEventLayout(): Int = R.layout.agenda_wrapped_counter
}

View File

@ -1,19 +0,0 @@
package pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence
import pl.szczodrzynski.edziennik.utils.models.Date
import java.util.*
class TeacherAbsenceCounter (
val teacherAbsenceDate: Date,
var teacherAbsenceCount: Int = 0
) {
val startTime: Calendar
get() = Calendar.getInstance().apply {
set(teacherAbsenceDate.year, teacherAbsenceDate.month - 1, teacherAbsenceDate.day, 10, 0, 0)
}
val endTime: Calendar
get() = Calendar.getInstance().apply {
timeInMillis = startTime.timeInMillis + (45 * 60 * 1000)
}
}

View File

@ -1,188 +1,21 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
*/
package pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence
import com.github.tibolte.agendacalendarview.models.CalendarEvent
import com.github.tibolte.agendacalendarview.models.IDayItem
import com.github.tibolte.agendacalendarview.models.IWeekItem
import pl.szczodrzynski.edziennik.ui.modules.agenda.BaseEvent
import pl.szczodrzynski.edziennik.utils.models.Date
import java.util.*
class TeacherAbsenceEvent : CalendarEvent {
/**
* Id of the event.
*/
private var mId: Long = 0
/**
* Color to be displayed in the agenda view.
*/
private var mColor: Int = 0
/**
* Text color displayed on the background color
*/
private var mTextColor: Int = 0
/**
* Calendar instance helping sorting the events per section in the agenda view.
*/
private var mInstanceDay: Calendar? = null
/**
* Start time of the event.
*/
private var mStartTime: Calendar? = null
/**
* End time of the event.
*/
private var mEndTime: Calendar? = null
/**
* References to a DayItem instance for that event, used to link interaction between the
* calendar view and the agenda view.
*/
private var mDayReference: IDayItem? = null
/**
* References to a WeekItem instance for that event, used to link interaction between the
* calendar view and the agenda view.
*/
private var mWeekReference: IWeekItem? = null
private var profileId: Int = 0
var teacherAbsenceDate: Date? = null
var teacherAbsenceCount: Int = 0
constructor(calendarEvent: TeacherAbsenceEvent) {
this.mId = calendarEvent.id
this.mColor = calendarEvent.color
this.mTextColor = calendarEvent.textColor
this.mStartTime = calendarEvent.startTime
this.mEndTime = calendarEvent.endTime
this.profileId = calendarEvent.profileId
this.teacherAbsenceDate = calendarEvent.teacherAbsenceDate
this.teacherAbsenceCount = calendarEvent.teacherAbsenceCount
}
constructor(mId: Long, mColor: Int, mTextColor: Int, mStartTime: Calendar, mEndTime: Calendar, profileId: Int, teacherAbsenceDate: Date, teacherAbsenceCount: Int) {
this.mId = mId
this.mColor = mColor
this.mTextColor = mTextColor
this.mStartTime = mStartTime
this.mEndTime = mEndTime
this.profileId = profileId
this.teacherAbsenceDate = teacherAbsenceDate
this.teacherAbsenceCount = teacherAbsenceCount
}
override fun setPlaceholder(placeholder: Boolean) {
}
override fun isPlaceholder(): Boolean {
return false
}
override fun getLocation(): String? {
return null
}
override fun setLocation(mLocation: String) {
}
override fun getId(): Long {
return mId
}
override fun setId(mId: Long) {
this.mId = mId
}
override fun getShowBadge(): Boolean {
return false
}
override fun setShowBadge(mShowBadge: Boolean) {
}
override fun getTextColor(): Int {
return mTextColor
}
override fun setTextColor(mTextColor: Int) {
this.mTextColor = mTextColor
}
override fun getDescription(): String? {
return null
}
override fun setDescription(mDescription: String) {
}
override fun isAllDay(): Boolean {
return false
}
override fun setAllDay(allDay: Boolean) {
}
override fun getStartTime(): Calendar? {
return mStartTime
}
override fun setStartTime(mStartTime: Calendar) {
this.mStartTime = mStartTime
}
override fun getEndTime(): Calendar? {
return mEndTime
}
override fun setEndTime(mEndTime: Calendar) {
this.mEndTime = mEndTime
}
override fun getTitle(): String? {
return null
}
override fun setTitle(mTitle: String) {
}
override fun getInstanceDay(): Calendar? {
return mInstanceDay
}
override fun setInstanceDay(mInstanceDay: Calendar) {
this.mInstanceDay = mInstanceDay
this.mInstanceDay!!.set(Calendar.HOUR, 0)
this.mInstanceDay!!.set(Calendar.MINUTE, 0)
this.mInstanceDay!!.set(Calendar.SECOND, 0)
this.mInstanceDay!!.set(Calendar.MILLISECOND, 0)
this.mInstanceDay!!.set(Calendar.AM_PM, 0)
}
override fun getDayReference(): IDayItem? {
return mDayReference
}
override fun setDayReference(mDayReference: IDayItem) {
this.mDayReference = mDayReference
}
override fun getWeekReference(): IWeekItem? {
return mWeekReference
}
override fun setWeekReference(mWeekReference: IWeekItem) {
this.mWeekReference = mWeekReference
}
override fun copy(): CalendarEvent {
return TeacherAbsenceEvent(this)
}
override fun getColor(): Int {
return mColor
}
class TeacherAbsenceEvent(
val profileId: Int,
val date: Date,
val count: Int
) : BaseEvent(
id = date.value.toLong(),
time = date.asCalendar,
color = 0xffff1744.toInt(),
showBadge = false
) {
override fun copy() = TeacherAbsenceEvent(profileId, date, count)
}

View File

@ -1,21 +1,48 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-8.
*/
package pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence
import android.view.View
import android.widget.TextView
import androidx.cardview.widget.CardView
import androidx.core.view.isVisible
import com.github.tibolte.agendacalendarview.render.EventRenderer
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.AgendaCounterItemBinding
import pl.szczodrzynski.edziennik.databinding.AgendaWrappedCounterBinding
import pl.szczodrzynski.edziennik.setTintColor
import pl.szczodrzynski.edziennik.utils.Colors
class TeacherAbsenceEventRenderer : EventRenderer<TeacherAbsenceEvent>() {
override fun render(view: View?, event: TeacherAbsenceEvent) {
val card = view?.findViewById<CardView>(R.id.teacherAbsenceCard)
val changeText = view?.findViewById<TextView>(R.id.teacherAbsenceText)
val changeCount = view?.findViewById<TextView>(R.id.teacherAbsenceCount)
card?.setCardBackgroundColor(event.color)
changeText?.setTextColor(event.textColor)
changeCount?.setTextColor(event.textColor)
changeCount?.text = event.teacherAbsenceCount.toString()
override fun render(view: View, event: TeacherAbsenceEvent) {
val b = AgendaWrappedCounterBinding.bind(view).item
val textColor = Colors.legibleTextColor(event.color)
b.card.foreground.setTintColor(event.color)
b.card.background.setTintColor(event.color)
b.name.setText(R.string.agenda_teacher_absence)
b.name.setTextColor(textColor)
b.count.text = event.count.toString()
b.count.setTextColor(textColor)
b.badgeBackground.isVisible = false
b.badge.isVisible = false
}
override fun getEventLayout(): Int = R.layout.agenda_event_teacher_absence
fun render(b: AgendaCounterItemBinding, event: TeacherAbsenceEvent) {
val textColor = Colors.legibleTextColor(event.color)
b.card.foreground.setTintColor(event.color)
b.card.background.setTintColor(event.color)
b.name.setText(R.string.agenda_teacher_absence)
b.name.setTextColor(textColor)
b.count.text = event.count.toString()
b.count.setTextColor(textColor)
b.badgeBackground.isVisible = false
b.badge.isVisible = false
}
override fun getEventLayout(): Int = R.layout.agenda_wrapped_counter
}

View File

@ -41,7 +41,8 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
get() = job + Dispatchers.Main
// local/private variables go here
private val manager by lazy { app.attendanceManager }
private val manager
get() = app.attendanceManager
private var viewType = AttendanceFragment.VIEW_DAYS
private var expandSubjectId = 0L

View File

@ -47,7 +47,8 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
get() = job + Dispatchers.Main
// local/private variables go here
private val manager by lazy { app.attendanceManager }
private val manager
get() = app.attendanceManager
private var expandSubjectId = 0L
private var attendance = listOf<AttendanceFull>()

View File

@ -40,7 +40,8 @@ class GradesAdapter(
}
private val app = activity.applicationContext as App
private val manager = app.gradesManager
private val manager
get() = app.gradesManager
private val job = Job()
override val coroutineContext: CoroutineContext

View File

@ -48,9 +48,12 @@ class GradesListFragment : Fragment(), CoroutineScope {
get() = job + Dispatchers.Main
// local/private variables go here
private val manager by lazy { app.gradesManager }
private val dontCountEnabled by lazy { manager.dontCountEnabled }
private val dontCountGrades by lazy { manager.dontCountGrades }
private val manager
get() = app.gradesManager
private val dontCountEnabled
get() = manager.dontCountEnabled
private val dontCountGrades
get() = manager.dontCountGrades
private var expandSubjectId = 0L
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

View File

@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.after
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.REGISTRATION_ENABLED
import pl.szczodrzynski.edziennik.ui.dialogs.MessagesConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.agenda.AgendaConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.bell.BellSyncConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.AttendanceConfigDialog
@ -59,6 +60,13 @@ class SettingsRegisterCard(util: SettingsUtil) : SettingsCard(util) {
}
override fun getItems() = listOfNotNull(
util.createActionItem(
text = R.string.menu_agenda_config,
icon = CommunityMaterial.Icon.cmd_calendar_outline
) {
AgendaConfigDialog(activity, reloadOnDismiss = false)
},
util.createActionItem(
text = R.string.menu_grades_config,
icon = CommunityMaterial.Icon3.cmd_numeric_5_box_outline

View File

@ -5,16 +5,16 @@
package pl.szczodrzynski.edziennik.ui.modules.timetable
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ProgressBar
import android.widget.TextView
import androidx.asynclayoutinflater.view.AsyncLayoutInflater
import androidx.core.view.isVisible
import androidx.core.view.marginTop
import androidx.core.view.setPadding
import androidx.lifecycle.Observer
import androidx.core.view.updateLayoutParams
import com.linkedin.android.tachyon.DayView
import com.linkedin.android.tachyon.DayViewConfig
import kotlinx.coroutines.*
@ -24,14 +24,15 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.TimetableDayFragmentBinding
import pl.szczodrzynski.edziennik.databinding.TimetableLessonBinding
import pl.szczodrzynski.edziennik.databinding.TimetableNoTimetableBinding
import pl.szczodrzynski.edziennik.ui.dialogs.timetable.LessonDetailsDialog
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment.Companion.DEFAULT_END_HOUR
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment.Companion.DEFAULT_START_HOUR
import pl.szczodrzynski.edziennik.utils.ListenerScrollView
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
import java.util.*
import kotlin.coroutines.CoroutineContext
import kotlin.math.min
@ -44,33 +45,28 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
private lateinit var app: App
private lateinit var activity: MainActivity
private lateinit var inflater: AsyncLayoutInflater
private lateinit var b: TimetableDayFragmentBinding
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private var timeIndicatorJob: Job? = null
private lateinit var date: Date
private var startHour = DEFAULT_START_HOUR
private var endHour = DEFAULT_END_HOUR
private var firstEventMinute = 24 * 60
private var paddingTop = 0
private val manager by lazy { app.timetableManager }
private val manager
get() = app.timetableManager
// find SwipeRefreshLayout in the hierarchy
private val refreshLayout by lazy { view?.findParentById(R.id.refreshLayout) }
// the day ScrollView
private val dayScrollDelegate = lazy {
val dayScroll = ListenerScrollView(context!!)
dayScroll.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
dayScroll.setOnRefreshLayoutEnabledListener { enabled ->
refreshLayout?.isEnabled = enabled
}
dayScroll
}
private val dayScroll by dayScrollDelegate
// the lesson DayView
private val dayView by lazy {
val dayView = DayView(context!!, DayViewConfig(
val dayView = DayView(activity, DayViewConfig(
startHour = startHour,
endHour = endHour,
dividerHeight = 1.dp,
@ -82,37 +78,33 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
eventMargin = 2.dp
), true)
dayView.setPadding(10.dp)
dayScroll.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
dayScroll.addView(dayView)
dayView
return@lazy dayView
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
activity = (getActivity() as MainActivity?) ?: return null
context ?: return null
app = activity.application as App
this.inflater = AsyncLayoutInflater(context!!)
this.inflater = AsyncLayoutInflater(requireContext())
date = arguments?.getInt("date")?.let { Date.fromValue(it) } ?: Date.getToday()
startHour = arguments?.getInt("startHour") ?: DEFAULT_START_HOUR
endHour = arguments?.getInt("endHour") ?: DEFAULT_END_HOUR
return FrameLayout(activity).apply {
layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
addView(ProgressBar(activity).apply {
layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER)
})
}
b = TimetableDayFragmentBinding.inflate(inflater, null, false)
return b.root
}
override fun onPageCreated(): Boolean {
// observe lesson database
app.db.timetableDao().getAllForDate(App.profileId, date).observe(this, Observer { lessons ->
app.db.timetableDao().getAllForDate(App.profileId, date).observe(this) { lessons ->
launch {
val events = withContext(Dispatchers.Default) {
app.db.eventDao().getAllByDateNow(App.profileId, date)
}
processLessonList(lessons, events)
}
})
}
return true
}
@ -120,9 +112,10 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
private fun processLessonList(lessons: List<LessonFull>, events: List<EventFull>) {
// no lessons - timetable not downloaded yet
if (lessons.isEmpty()) {
inflater.inflate(R.layout.timetable_no_timetable, view as FrameLayout?) { view, _, parent ->
parent?.removeAllViews()
parent?.addView(view)
inflater.inflate(R.layout.timetable_no_timetable, b.root) { view, _, _ ->
b.root.removeAllViews()
b.root.addView(view)
val b = TimetableNoTimetableBinding.bind(view)
val weekStart = date.weekStart.stringY_m_d
b.noTimetableSync.onClick {
@ -143,9 +136,9 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
}
// one lesson indicating a day without lessons
if (lessons.size == 1 && lessons[0].type == Lesson.TYPE_NO_LESSONS) {
inflater.inflate(R.layout.timetable_no_lessons, view as FrameLayout?) { view, _, parent ->
parent?.removeAllViews()
parent?.addView(view)
inflater.inflate(R.layout.timetable_no_lessons, b.root) { view, _, _ ->
b.root.removeAllViews()
b.root.addView(view)
}
return
}
@ -157,12 +150,12 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
return
}
// clear the root view and add the ScrollView
(view as FrameLayout?)?.removeAllViews()
(view as FrameLayout?)?.addView(dayScroll)
b.scrollView.isVisible = true
b.dayFrame.removeView(b.dayView)
b.dayFrame.addView(dayView, 0)
// Inflate a label view for each hour the day view will display
val hourLabelViews = ArrayList<View>()
val hourLabelViews = mutableListOf<View>()
for (i in dayView.startHour..dayView.endHour) {
if (!isAdded)
continue
@ -171,6 +164,11 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
hourLabelViews.add(hourLabelView)
}
dayView.setHourLabelViews(hourLabelViews)
// measure dayView top padding needed for the timeIndicator
hourLabelViews.getOrNull(0)?.let {
it.measure(0, 0)
paddingTop = it.measuredHeight / 2 + dayView.paddingTop
}
lessons.forEach { it.showAsUnseen = !it.seen }
@ -201,8 +199,12 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
// Try to recycle an existing event view if there are enough left, otherwise inflate
// a new one
val eventView = (if (remaining > 0) recycled?.get(--remaining) else layoutInflater.inflate(R.layout.timetable_lesson, dayView, false))
?: continue
val eventView =
(if (remaining > 0) recycled?.get(--remaining) else layoutInflater.inflate(
R.layout.timetable_lesson,
dayView,
false
)) ?: continue
val lb = TimetableLessonBinding.bind(eventView)
eventViews += eventView
@ -290,16 +292,50 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
eventTimeRanges.add(DayView.EventTimeRange(startMinute, endMinute))
}
updateTimeIndicator()
dayView.setEventViews(eventViews, eventTimeRanges)
val firstEventTop = (firstEventMinute - dayView.startHour * 60) * dayView.minuteHeight
dayScroll.scrollTo(0, firstEventTop.toInt())
b.scrollView.scrollTo(0, firstEventTop.toInt())
b.progressBar.isVisible = false
}
private fun updateTimeIndicator() {
val time = Time.getNow()
val isTimeInView =
date == Date.getToday() && time.hour in dayView.startHour..dayView.endHour
b.timeIndicator.isVisible = isTimeInView
b.timeIndicatorMarker.isVisible = isTimeInView
if (isTimeInView) {
val startTime = Time(dayView.startHour, 0, 0)
val seconds = time.inSeconds - startTime.inSeconds * 1f
b.timeIndicator.updateLayoutParams<FrameLayout.LayoutParams> {
topMargin = (seconds * dayView.minuteHeight / 60f).toInt() + paddingTop
}
b.timeIndicatorMarker.updateLayoutParams<FrameLayout.LayoutParams> {
topMargin = b.timeIndicator.marginTop - (16.dp / 2) + (1.dp / 2)
}
}
if (timeIndicatorJob == null) {
timeIndicatorJob = startCoroutineTimer(repeatMillis = 30000) {
updateTimeIndicator()
}
}
}
override fun onResume() {
super.onResume()
if (dayScrollDelegate.isInitialized()) {
val firstEventTop = (firstEventMinute - dayView.startHour * 60) * dayView.minuteHeight
dayScroll.scrollTo(0, firstEventTop.toInt())
b.scrollView.scrollTo(0, firstEventTop.toInt())
updateTimeIndicator()
}
override fun onPause() {
super.onPause()
timeIndicatorJob?.cancel()
timeIndicatorJob = null
}
}

View File

@ -175,7 +175,7 @@ class DateDropdown : TextInputDropDown {
}
}
fun pickerDialog() {
private fun pickerDialog() {
val date = getSelected() as? Date ?: Date.getToday()
MaterialDatePicker.Builder.datePicker()

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
*/
package pl.szczodrzynski.edziennik.ui.modules.views
import android.content.Context
import android.util.AttributeSet
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.data.db.entity.EventType
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
class EventTypeDropdown : TextInputDropDown {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
lateinit var db: AppDb
var profileId: Int = 0
var onTypeSelected: ((eventType: EventType) -> Unit)? = null
override fun create(context: Context) {
super.create(context)
isEnabled = false
}
suspend fun loadItems() {
val types = withContext(Dispatchers.Default) {
val list = mutableListOf<Item>()
var types = db.eventTypeDao().getAllNow(profileId)
if (types.none { it.id in -1L..10L }) {
types = db.eventTypeDao().addDefaultTypes(context, profileId)
}
list += types.map {
Item(it.id, it.name, tag = it, icon = IconicsDrawable(context).apply {
icon = CommunityMaterial.Icon.cmd_circle
sizeDp = 24
colorInt = it.color
})
}
list
}
clear().append(types)
isEnabled = true
setOnChangeListener {
when (it.tag) {
is EventType -> {
// selected an event type
onTypeSelected?.invoke(it.tag)
true
}
else -> false
}
}
}
/**
* Select an event type by the [typeId].
*/
fun selectType(typeId: Long) = select(typeId)
/**
* Select an event type by the [typeId] **if it's not selected yet**.
*/
fun selectDefault(typeId: Long?) {
if (typeId == null || selected != null)
return
selectType(typeId)
}
/**
* Get the currently selected event type.
* ### Returns:
* - null if no valid type is selected
* - [EventType] - the selected event type
*/
fun getSelected(): EventType? {
return when (selected?.tag) {
is EventType -> selected?.tag as EventType
else -> null
}
}
}

View File

@ -15,6 +15,7 @@ import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.crc16
import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.data.db.entity.Subject
import pl.szczodrzynski.edziennik.ui.dialogs.input
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
@ -40,7 +41,7 @@ class SubjectDropdown : TextInputDropDown {
var showNoSubject = true
var showCustomSubject = false
var customSubjectName = ""
var onSubjectSelected: ((subjectId: Long?) -> Unit)? = null
var onSubjectSelected: ((subject: Subject?) -> Unit)? = null
var onCustomSubjectSelected: ((subjectName: String) -> Unit)? = null
override fun create(context: Context) {
@ -73,7 +74,7 @@ class SubjectDropdown : TextInputDropDown {
list += subjects.map { Item(
it.id,
it.longName,
tag = it.id
tag = it
) }
list
@ -91,10 +92,11 @@ class SubjectDropdown : TextInputDropDown {
}
-1L -> {
// no subject
deselect()
onSubjectSelected?.invoke(null)
true
false
}
is Long -> {
is Subject -> {
// selected a subject
onSubjectSelected?.invoke(it.tag)
true
@ -104,7 +106,7 @@ class SubjectDropdown : TextInputDropDown {
}
}
fun customNameDialog() {
private fun customNameDialog() {
activity ?: return
MaterialAlertDialogBuilder(activity!!)
.setTitle("Własny przedmiot")
@ -127,32 +129,37 @@ class SubjectDropdown : TextInputDropDown {
.show()
}
fun selectSubject(subjectId: Long) {
if (select(subjectId) == null)
select(Item(
subjectId,
"nieznany przedmiot ($subjectId)",
tag = subjectId
))
/**
* Select a subject by the [subjectId].
*/
fun selectSubject(subjectId: Long): Item? {
if (subjectId == -1L) {
deselect()
return null
}
return select(subjectId)
}
fun selectDefault(subjectId: Long?) {
/**
* Select a subject by the [subjectId] **if it's not selected yet**.
*/
fun selectDefault(subjectId: Long?): Item? {
if (subjectId == null || selected != null)
return
selectSubject(subjectId)
return null
return selectSubject(subjectId)
}
/**
* Get the currently selected subject.
* ### Returns:
* - null if no valid subject is selected
* - [Long] - the selected subject's ID
* - [Subject] - the selected subject
* - [String] - a custom subject name entered, if [showCustomSubject] == true
*/
fun getSelected(): Any? {
return when (selected?.tag) {
-1L -> null
is Long -> selected?.tag as Long
is Subject -> selected?.tag as Subject
is String -> selected?.tag as String
else -> null
}

View File

@ -5,13 +5,12 @@
package pl.szczodrzynski.edziennik.ui.modules.views
import android.content.Context
import android.content.ContextWrapper
import android.util.AttributeSet
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
class TeacherDropdown : TextInputDropDown {
@ -19,22 +18,10 @@ class TeacherDropdown : TextInputDropDown {
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
private val activity: AppCompatActivity?
get() {
var context: Context? = context ?: return null
if (context is AppCompatActivity) return context
while (context is ContextWrapper) {
if (context is AppCompatActivity)
return context
context = context.baseContext
}
return null
}
lateinit var db: AppDb
var profileId: Int = 0
var showNoTeacher = true
var onTeacherSelected: ((teacherId: Long?) -> Unit)? = null
var onTeacherSelected: ((teacher: Teacher?) -> Unit)? = null
override fun create(context: Context) {
super.create(context)
@ -58,7 +45,7 @@ class TeacherDropdown : TextInputDropDown {
list += teachers.map { Item(
it.id,
it.fullName,
tag = it.id
tag = it
) }
list
@ -71,10 +58,11 @@ class TeacherDropdown : TextInputDropDown {
when (it.tag) {
-1L -> {
// no teacher
deselect()
onTeacherSelected?.invoke(null)
true
false
}
is Long -> {
is Teacher -> {
// selected a teacher
onTeacherSelected?.invoke(it.tag)
true
@ -84,31 +72,36 @@ class TeacherDropdown : TextInputDropDown {
}
}
fun selectTeacher(teacherId: Long) {
if (select(teacherId) == null)
select(Item(
teacherId,
"nieznany nauczyciel ($teacherId)",
tag = teacherId
))
/**
* Select a teacher by the [teacherId].
*/
fun selectTeacher(teacherId: Long): Item? {
if (teacherId == -1L) {
deselect()
return null
}
return select(teacherId)
}
fun selectDefault(teacherId: Long?) {
/**
* Select a teacher by the [teacherId] **if it's not selected yet**.
*/
fun selectDefault(teacherId: Long?): Item? {
if (teacherId == null || selected != null)
return
selectTeacher(teacherId)
return null
return selectTeacher(teacherId)
}
/**
* Get the currently selected teacher.
* ### Returns:
* - null if no valid teacher is selected
* - [Long] - the selected teacher's ID
* - [Teacher] - the selected teacher
*/
fun getSelected(): Long? {
fun getSelected(): Teacher? {
return when (selected?.tag) {
-1L -> null
is Long -> selected?.tag as Long
is Teacher -> selected?.tag as Teacher
else -> null
}
}

View File

@ -5,9 +5,7 @@
package pl.szczodrzynski.edziennik.ui.modules.views
import android.content.Context
import android.content.ContextWrapper
import android.util.AttributeSet
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.R
@ -20,22 +18,10 @@ class TeamDropdown : TextInputDropDown {
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
private val activity: AppCompatActivity?
get() {
var context: Context? = context ?: return null
if (context is AppCompatActivity) return context
while (context is ContextWrapper) {
if (context is AppCompatActivity)
return context
context = context.baseContext
}
return null
}
lateinit var db: AppDb
var profileId: Int = 0
var showNoTeam = true
var onTeamSelected: ((teamId: Long?) -> Unit)? = null
var onTeamSelected: ((team: Team?) -> Unit)? = null
override fun create(context: Context) {
super.create(context)
@ -59,7 +45,7 @@ class TeamDropdown : TextInputDropDown {
list += teams.map { Item(
it.id,
it.name,
tag = it.id
tag = it
) }
list
@ -72,10 +58,11 @@ class TeamDropdown : TextInputDropDown {
when (it.tag) {
-1L -> {
// no team
deselect()
onTeamSelected?.invoke(null)
true
false
}
is Long -> {
is Team -> {
// selected a team
onTeamSelected?.invoke(it.tag)
true
@ -85,21 +72,29 @@ class TeamDropdown : TextInputDropDown {
}
}
fun selectTeam(teamId: Long) {
if (select(teamId) == null)
select(Item(
teamId,
"nieznana grupa ($teamId)",
tag = teamId
))
/**
* Select a teacher by the [teamId].
*/
fun selectTeam(teamId: Long): Item? {
if (teamId == -1L) {
deselect()
return null
}
return select(teamId)
}
fun selectDefault(teamId: Long?) {
/**
* Select a team by the [teamId] **if it's not selected yet**.
*/
fun selectDefault(teamId: Long?): Item? {
if (teamId == null || selected != null)
return
selectTeam(teamId)
return null
return selectTeam(teamId)
}
/**
* Select a team of the [Team.TYPE_CLASS] type.
*/
fun selectTeamClass() {
select(items.singleOrNull {
it.tag is Team && it.tag.type == Team.TYPE_CLASS
@ -110,12 +105,12 @@ class TeamDropdown : TextInputDropDown {
* Get the currently selected team.
* ### Returns:
* - null if no valid team is selected
* - [Long] - the team's ID
* - [Team] - the selected team
*/
fun getSelected(): Any? {
fun getSelected(): Team? {
return when (selected?.tag) {
-1L -> null
is Long -> selected?.tag as Long
is Team -> selected?.tag as Team
else -> null
}
}

View File

@ -175,7 +175,7 @@ class TimeDropdown : TextInputDropDown {
return !noTimetable
}
fun pickerDialog() {
private fun pickerDialog() {
val time = (getSelected() as? Pair<*, *>)?.first as? Time ?: Time.getNow()
MaterialTimePicker.Builder()

View File

@ -1,7 +1,11 @@
package pl.szczodrzynski.edziennik.utils
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import androidx.appcompat.view.menu.MenuBuilder
import androidx.appcompat.view.menu.MenuPopupHelper
import androidx.appcompat.widget.PopupMenu
import com.google.android.material.textfield.TextInputEditText
import com.mikepenz.iconics.IconicsDrawable
@ -29,10 +33,11 @@ open class TextInputDropDown : TextInputEditText {
val selectedId
get() = selected?.id
fun updateText() {
private fun updateText() {
setText(selected?.displayText ?: selected?.text)
}
@SuppressLint("RestrictedApi")
open fun create(context: Context) {
val drawable = IconicsDrawable(context, CommunityMaterial.Icon.cmd_chevron_down).apply {
colorInt = Themes.getPrimaryTextColor(context)
@ -58,7 +63,9 @@ open class TextInputDropDown : TextInputEditText {
val popup = PopupMenu(context, this)
items.forEachIndexed { index, item ->
popup.menu.add(0, item.id.toInt(), index, item.text)
popup.menu.add(0, item.id.toInt(), index, item.text).also {
it.icon = item.icon
}
}
popup.setOnMenuItemClickListener { menuItem ->
@ -70,29 +77,46 @@ open class TextInputDropDown : TextInputEditText {
true
}
popup.setOnDismissListener {
val helper = MenuPopupHelper(context, popup.menu as MenuBuilder, this)
helper.setForceShowIcon(true)
helper.setOnDismissListener {
clearFocus()
}
popup.show()
helper.show()
}
}
fun select(item: Item): Item? {
/**
* Select an arbitrary [item]. Allows to select an item not present
* in the original list.
*/
fun select(item: Item): Item {
selected = item
updateText()
error = null
return item
}
/**
* Select an item by its ID. Returns the selected item
* if found.
*/
fun select(id: Long?): Item? {
return items.singleOrNull { it.id == id }?.let { select(it) }
}
/**
* Select an item by its tag. Returns the selected item
* if found.
*/
fun select(tag: Any?): Item? {
return items.singleOrNull { it.tag == tag }?.let { select(it) }
}
/**
* Select an item by its index. Returns the selected item
* if the index exists.
*/
fun select(index: Int): Item? {
return items.getOrNull(index)?.let { select(it) }
}
@ -143,5 +167,11 @@ open class TextInputDropDown : TextInputEditText {
}
}
class Item(val id: Long, val text: CharSequence, val displayText: CharSequence? = null, val tag: Any? = null)
class Item(
val id: Long,
val text: CharSequence,
val displayText: CharSequence? = null,
val tag: Any? = null,
val icon: Drawable? = null
)
}

View File

@ -4,12 +4,17 @@
package pl.szczodrzynski.edziennik.utils.managers
import androidx.core.view.isVisible
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import com.mikepenz.iconics.view.IconicsTextView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.startCoroutineTimer
import kotlin.coroutines.CoroutineContext
class EventManager(val app: App) : CoroutineScope {
@ -32,4 +37,41 @@ class EventManager(val app: App) : CoroutineScope {
app.db.metadataDao().setSeen(event.profileId, event, true)
}
}
fun setEventTopic(
title: IconicsTextView,
event: EventFull,
showType: Boolean = true,
doneIconColor: Int? = null
) {
var eventTopic = if (showType)
"${event.typeName ?: "wydarzenie"} - ${event.topic}"
else
event.topic
if (event.addedManually) {
eventTopic = "{cmd-clipboard-edit-outline} $eventTopic"
}
title.text = eventTopic
title.setCompoundDrawables(
null,
null,
if (event.isDone) IconicsDrawable(title.context).apply {
icon = CommunityMaterial.Icon.cmd_check
colorInt = doneIconColor ?: R.color.md_green_500.resolveColor(title.context)
sizeDp = 24
} else null,
null
)
}
fun setLegendText(legend: IconicsTextView, event: EventFull) {
legend.text = listOfNotNull(
if (event.addedManually) R.string.legend_event_added_manually else null,
if (event.isDone) R.string.legend_event_is_done else null
).map { legend.context.getString(it) }.join("\n")
legend.isVisible = legend.text.isNotBlank()
}
}

View File

@ -94,17 +94,29 @@ public class Date implements Comparable<Date> {
}
}
public long getInMillis() {
public Calendar getAsCalendar() {
Calendar c = Calendar.getInstance();
c.set(year, month - 1, day, 0, 0, 0);
c.set(Calendar.MILLISECOND, 0);
return c;
}
public Calendar getAsCalendar(Time time) {
if (time == null)
return getAsCalendar();
Calendar c = Calendar.getInstance();
c.set(year, month - 1, day, time.hour, time.minute, time.second);
c.set(Calendar.MILLISECOND, 0);
return c;
}
public long getInMillis() {
Calendar c = getAsCalendar();
return c.getTimeInMillis();
}
public long getInMillisUtc() {
Calendar c = Calendar.getInstance();
c.set(year, month - 1, day, 0, 0, 0);
c.set(Calendar.MILLISECOND, 0);
Calendar c = getAsCalendar();
c.setTimeZone(TimeZone.getTimeZone("UTC"));
return c.getTimeInMillis();
}
@ -179,13 +191,7 @@ public class Date implements Comparable<Date> {
}
public long combineWith(Time time) {
if (time == null) {
return getInMillis();
}
Calendar c = Calendar.getInstance();
c.set(this.year, this.month - 1, this.day, time.hour, time.minute, time.second);
c.set(Calendar.MILLISECOND, 0);
return c.getTimeInMillis();
return getAsCalendar(time).getTimeInMillis();
}
public int getWeekDay() {

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2021-4-14.
-->
<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="@color/md_red_500"
android:pathData="M0,4V20L12,12.25" />
</vector>

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2021-4-11.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_rounded_8dp"
android:foreground="@drawable/bg_rounded_8dp_outline"
tools:backgroundTint="#ff1744"
tools:foregroundTint="#ff1744">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_vertical"
android:padding="10dp"
android:textAppearance="@style/NavView.TextView.Medium"
tools:text="@string/agenda_lesson_changes"
tools:textColor="@color/md_white_1000" />
<TextView
android:id="@+id/count"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginVertical="2dp"
android:gravity="center"
android:paddingHorizontal="16dp"
android:textSize="20sp"
tools:text="3"
tools:textColor="@color/md_white_1000" />
<View
android:id="@+id/badgeBackground"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/bg_rounded_8dp"
android:layout_marginRight="-24dp"
android:layout_marginEnd="-24dp"
android:layout_marginTop="-24dp"
tools:backgroundTint="?android:colorBackground"/>
</LinearLayout>
<View
android:id="@+id/badge"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_gravity="end"
android:layout_margin="8dp"
android:background="@drawable/unread_red_circle" />
</FrameLayout>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2021-4-8.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_rounded_8dp"
android:foreground="@drawable/bg_rounded_8dp_outline"
tools:backgroundTint="@color/blue_selected"
tools:foregroundTint="@color/blue_selected">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:padding="10dp">
<com.mikepenz.iconics.view.IconicsTextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ellipsize="end"
android:maxLines="2"
android:textSize="16sp"
tools:text="sprawdzian - Język polski"
tools:textColor="@color/md_white_1000" />
</LinearLayout>
<View
android:id="@+id/badgeBackground"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginTop="-24dp"
android:layout_marginEnd="-24dp"
android:layout_marginRight="-24dp"
android:background="@drawable/bg_rounded_8dp"
tools:backgroundTint="?android:colorBackground" />
</LinearLayout>
<View
android:id="@+id/badge"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_gravity="end"
android:layout_margin="8dp"
android:background="@drawable/unread_red_circle" />
</FrameLayout>

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2021-4-8.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_rounded_8dp"
android:foreground="@drawable/bg_rounded_8dp_outline"
tools:backgroundTint="@color/blue_selected"
tools:foregroundTint="@color/blue_selected">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:padding="10dp">
<com.mikepenz.iconics.view.IconicsTextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ellipsize="end"
android:maxLines="3"
android:textSize="16sp"
tools:text="sprawdzian - Język polski"
tools:textColor="@color/md_white_1000" />
<TextView
android:id="@+id/subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:textSize="12sp"
tools:text="9:05, biologia, Jan Kowalski, 7a"
tools:textColor="@color/md_white_1000" />
</LinearLayout>
<View
android:id="@+id/badgeBackground"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginTop="-24dp"
android:layout_marginEnd="-24dp"
android:layout_marginRight="-24dp"
android:background="@drawable/bg_rounded_8dp"
tools:backgroundTint="?android:colorBackground" />
</LinearLayout>
<View
android:id="@+id/badge"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_gravity="end"
android:layout_margin="8dp"
android:background="@drawable/unread_red_circle" />
</FrameLayout>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2021-4-10.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_rounded_8dp"
android:foreground="@drawable/bg_rounded_8dp_outline"
tools:backgroundTint="@color/blue_selected"
tools:foregroundTint="@color/blue_selected">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:padding="10dp"
android:textAppearance="@style/NavView.TextView.Medium"
tools:text="informacja"
tools:textColor="@color/md_white_1000" />
<TextView
android:id="@+id/count"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginVertical="2dp"
android:layout_marginRight="-1dp"
android:background="@drawable/bg_rounded_8dp"
android:gravity="center"
android:paddingStart="16dp"
android:paddingLeft="16dp"
android:paddingEnd="18dp"
android:paddingRight="18dp"
android:textSize="20sp"
tools:backgroundTint="?android:colorBackground"
tools:text="3" />
</LinearLayout>
<View
android:id="@+id/badge"
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_gravity="end"
android:layout_margin="6dp"
android:background="@drawable/unread_red_circle" />
</FrameLayout>

View File

@ -1,15 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2021-4-11.
-->
<com.github.tibolte.agendacalendarview.agenda.AgendaEventView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:gravity="center_vertical"
android:orientation="horizontal">
<include
layout="@layout/row_lesson_change_item"
android:id="@+id/item"
layout="@layout/agenda_counter_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />

View File

@ -1,13 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2021-4-9.
-->
<com.github.tibolte.agendacalendarview.agenda.AgendaEventView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<include
layout="@layout/row_teacher_absence_item"
android:id="@+id/item"
layout="@layout/agenda_event_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2021-4-11.
-->
<com.github.tibolte.agendacalendarview.agenda.AgendaEventView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<include
android:id="@+id/item"
layout="@layout/agenda_event_compact_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />
</com.github.tibolte.agendacalendarview.agenda.AgendaEventView>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2021-4-10.
-->
<com.github.tibolte.agendacalendarview.agenda.AgendaEventView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<include
android:id="@+id/item"
layout="@layout/agenda_group_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />
</com.github.tibolte.agendacalendarview.agenda.AgendaEventView>

View File

@ -0,0 +1,156 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) Kuba Szczodrzyński 2021-4-10.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
<variable
name="config"
type="pl.szczodrzynski.edziennik.config.ProfileConfigUI" />
<variable
name="isAgendaMode"
type="boolean" />
</data>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/agenda_config_appearance"
android:textAppearance="@style/NavView.TextView.Subtitle" />
<com.google.android.material.checkbox.MaterialCheckBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:checked="@={config.agendaLessonChanges}"
android:minHeight="32dp"
android:text="@string/agenda_config_lesson_changes" />
<com.google.android.material.checkbox.MaterialCheckBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:checked="@={config.agendaTeacherAbsence}"
android:minHeight="32dp"
android:text="@string/agenda_config_teacher_absence" />
<com.google.android.material.checkbox.MaterialCheckBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:checked="@={config.agendaCompactMode}"
android:enabled="@{isAgendaMode}"
android:minHeight="32dp"
android:text="@string/agenda_config_compact_mode"
tools:enabled="false" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:enabled="@{isAgendaMode}"
android:text="@string/agenda_config_compact_mode_hint"
android:textAppearance="@style/NavView.TextView.Helper"
tools:enabled="false" />
<com.google.android.material.checkbox.MaterialCheckBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:checked="@={config.agendaGroupByType}"
android:enabled="@{isAgendaMode}"
android:minHeight="32dp"
android:text="@string/agenda_config_group_by_type"
tools:enabled="false" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/agenda_config_mode_unavailable"
android:textStyle="italic"
android:visibility="@{isAgendaMode ? View.GONE : View.VISIBLE}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/agenda_config_event_sharing"
android:textAppearance="@style/NavView.TextView.Subtitle" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/eventSharingEnabled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:minHeight="32dp"
android:text="@string/agenda_config_event_sharing_enabled" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/agenda_config_elearning"
android:textAppearance="@style/NavView.TextView.Subtitle" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/agenda_config_not_available_yet"
android:textAppearance="@style/NavView.TextView.Small"
android:textStyle="italic" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/elearningEnabled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:checked="@={config.agendaElearningMark}"
android:enabled="false"
android:minHeight="32dp"
android:onClick="@{() -> elearningType.setEnabled(elearningEnabled.isChecked())}"
android:text="@string/agenda_config_elearning_mark" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/elearningType"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="@{config.agendaElearningMark}"
android:hint="@string/agenda_config_elearning_type">
<pl.szczodrzynski.edziennik.utils.TextInputDropDown
android:id="@+id/elearningTypeDropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:focusable="true"
android:focusableInTouchMode="true" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.checkbox.MaterialCheckBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:checked="@={config.agendaElearningGroup}"
android:enabled="false"
android:minHeight="32dp"
android:text="@string/agenda_config_elearning_group" />
</LinearLayout>
</ScrollView>
</layout>

View File

@ -3,15 +3,12 @@
~ Copyright (c) Kuba Szczodrzyński 2019-12-16.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
</data>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -41,47 +38,43 @@
tools:text="8:00 - 14:20 (7 lekcji, 6 godzin, 20 minut)"
tools:visibility="visible" />
<LinearLayout
android:id="@+id/lessonChangeLayout"
<FrameLayout
android:id="@+id/lessonChangesFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground">
android:foreground="?selectableItemBackground"
android:paddingHorizontal="8dp"
android:paddingVertical="5dp"
android:visibility="gone"
tools:visibility="visible">
<include
android:id="@+id/lessonChangeContainer"
layout="@layout/row_lesson_change_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="5dp"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
android:id="@+id/lessonChanges"
layout="@layout/agenda_counter_item" />
</FrameLayout>
<LinearLayout
android:id="@+id/teacherAbsenceLayout"
<FrameLayout
android:id="@+id/teacherAbsenceFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground">
android:foreground="?selectableItemBackground"
android:paddingHorizontal="8dp"
android:paddingVertical="5dp"
android:visibility="gone"
tools:visibility="visible">
<include
android:id="@+id/teacherAbsenceContainer"
layout="@layout/row_teacher_absence_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="5dp"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
android:id="@+id/teacherAbsence"
layout="@layout/agenda_counter_item" />
</FrameLayout>
<LinearLayout
android:id="@+id/eventsNoData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:paddingVertical="16dp"
android:orientation="vertical"
android:paddingVertical="16dp"
android:visibility="gone"
tools:visibility="visible">
@ -89,21 +82,20 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
app:drawableTopCompat="@drawable/ic_no_events"
android:drawablePadding="16dp"
android:fontFamily="sans-serif-light"
android:gravity="center"
android:text="@string/dialog_day_no_events"
android:textSize="24sp" />
android:textSize="24sp"
app:drawableTopCompat="@drawable/ic_no_events" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_no_events_hint"
android:layout_gravity="center"
android:gravity="center"
android:textStyle="italic"/>
android:text="@string/dialog_no_events_hint"
android:textStyle="italic" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
@ -112,9 +104,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:clipToPadding="false"
tools:visibility="visible"
tools:listitem="@layout/event_list_item" />
tools:listitem="@layout/event_list_item"
tools:visibility="visible" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</layout>
</androidx.core.widget.NestedScrollView>

View File

@ -105,9 +105,23 @@
</LinearLayout>
<com.mikepenz.iconics.view.IconicsTextView
android:id="@+id/legend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/NavView.TextView.Helper"
tools:text="[!] wydarzenie dodane ręcznie\n[V] oznaczono jako wykonane" />
<View
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@drawable/divider"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textAppearance="@style/NavView.TextView.Helper"
android:text="@string/dialog_event_details_teacher"
android:visibility="@{event.teacherName != null ? View.VISIBLE : View.GONE}"/>

View File

@ -94,7 +94,7 @@
android:layout_weight="1"
android:hint="@string/dialog_event_manual_type">
<pl.szczodrzynski.edziennik.utils.TextInputDropDown
<pl.szczodrzynski.edziennik.ui.modules.views.EventTypeDropdown
android:id="@+id/typeDropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -4,8 +4,7 @@
-->
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View"/>
@ -61,12 +60,10 @@
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
<com.mikepenz.iconics.view.IconicsTextView
android:id="@+id/topic"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="3"
@ -79,23 +76,13 @@
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginLeft="4dp"
android:fontFamily="@font/community_material_font_v5_8_55"
android:minWidth="0dp"
android:text="\uf2f4"
android:textSize="20sp"
tools:visibility="gone" />
<com.mikepenz.iconics.view.IconicsImageView
android:id="@+id/isDone"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="top"
android:layout_marginHorizontal="4dp"
android:visibility="gone"
app:iiv_color="@color/md_green_500"
app:iiv_icon="cmd-check"
tools:background="@sample/check" />
tools:visibility="visible" />
</LinearLayout>
<com.mikepenz.iconics.view.IconicsTextView

View File

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ScrollView
android:id="@+id/dayScroll"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.linkedin.android.tachyon.DayView
android:id="@+id/day"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
app:dividerHeight="1dp"
app:endHour="18"
app:eventMargin="2dp"
app:halfHourDividerColor="#e0e0e0"
app:halfHourHeight="60dp"
app:hourDividerColor="#b0b0b0"
app:hourLabelMarginEnd="10dp"
app:hourLabelWidth="40dp"
app:startHour="5"
tools:visibility="gone"/>
</ScrollView>
</FrameLayout>
</layout>

View File

@ -1,35 +0,0 @@
<androidx.cardview.widget.CardView android:id="@+id/lesson_change_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="#78909c"
app:cardCornerRadius="5dp"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="@+id/lesson_change_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:textAppearance="@style/NavView.TextView.Medium"
android:text="@string/agenda_lesson_changes" />
<TextView
android:id="@+id/lessonChangeCount"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="20sp"
tools:text="3" />
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@ -1,35 +0,0 @@
<androidx.cardview.widget.CardView android:id="@+id/teacherAbsenceCard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="#ff1744"
app:cardCornerRadius="5dp"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="@+id/teacherAbsenceText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:textAppearance="@style/NavView.TextView.Medium"
android:text="@string/agenda_teacher_absence" />
<TextView
android:id="@+id/teacherAbsenceCount"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="20sp"
tools:text="3" />
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2021-4-14.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<pl.szczodrzynski.edziennik.utils.ListenerScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible">
<FrameLayout
android:id="@+id/dayFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:layout_height="match_parent">
<View
android:id="@+id/timeIndicatorMarker"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginHorizontal="8dp"
android:background="@drawable/timetable_marker_triangle"
tools:layout_marginTop="92.5dp" />
<View
android:id="@+id/timeIndicator"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginHorizontal="8dp"
android:background="@color/md_red_500"
tools:layout_marginTop="100dp" />
<com.linkedin.android.tachyon.DayView
android:id="@+id/dayView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:layout_height="match_parent" />
</FrameLayout>
</pl.szczodrzynski.edziennik.utils.ListenerScrollView>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>

View File

@ -1427,6 +1427,23 @@
<string name="permissions_generate_timetable">Aby móc zapisać wygenerowany plan lekcji musisz przyznać uprawnienia dostępu do pamięci urządzenia.\n\nKliknij OK, aby przyznać uprawnienia.</string>
<string name="privacy_policy_dialog_html"><![CDATA[Korzystając z aplikacji potwierdzasz <a href="https://szkolny.eu/privacy-policy">przeczytanie Polityki prywatności</a> i akceptujesz jej postanowienia.<br /><br />Autorzy aplikacji nie biorą odpowiedzialności za korzystanie z aplikacji Szkolny.eu.]]></string>
<string name="login_chooser_version_format">Szkolny.eu v%s\n%s</string>
<string name="menu_agenda_config">Ustawienia terminarza</string>
<string name="agenda_config_appearance">Wygląd</string>
<string name="agenda_config_lesson_changes">Pokazuj zmiany planu lekcji</string>
<string name="agenda_config_teacher_absence">Pokazuj nieobecności nauczycieli</string>
<string name="agenda_config_compact_mode">Tryb kompaktowy</string>
<string name="agenda_config_compact_mode_hint">Mniejszy rozmiar wydarzeń na liście</string>
<string name="agenda_config_group_by_type">Grupuj wydarzenia tego samego typu</string>
<string name="agenda_config_mode_unavailable">Niedostępne w trybie kalendarza</string>
<string name="agenda_config_event_sharing">Udostępnianie wydarzeń</string>
<string name="agenda_config_event_sharing_enabled">Włącz Udostępnianie wydarzeń</string>
<string name="agenda_config_elearning">Nauczanie zdalne</string>
<string name="agenda_config_elearning_mark">Ustaw wydarzenia jako lekcje on-line</string>
<string name="agenda_config_elearning_type">Wybierz rodzaj wydarzeń</string>
<string name="agenda_config_elearning_group">Grupuj lekcje on-line na liście</string>
<string name="legend_event_added_manually">{cmd-clipboard-edit-outline} wydarzenie dodane ręcznie</string>
<string name="legend_event_is_done">{cmd-check} oznaczono jako wykonane</string>
<string name="agenda_config_not_available_yet">Funkcja jeszcze nie jest dostępna.</string>
<string name="messages_config_compose">Tworzenie wiadomości</string>
<string name="messages_config_greeting_on_compose">Dodaj podpis przy tworzeniu wiadomości</string>
<string name="messages_config_greeting_on_reply">Dodaj podpis przy odpowiadaniu na wiadomość</string>