mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-01-31 12:48:20 +01:00
Add excusing for absence (#533)
This commit is contained in:
parent
0c5d45717c
commit
19f495cba6
1652
app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json
Normal file
1652
app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -35,9 +35,9 @@ class AttendanceLocalTest {
|
|||||||
@Test
|
@Test
|
||||||
fun saveAndReadTest() {
|
fun saveAndReadTest() {
|
||||||
attendanceLocal.saveAttendance(listOf(
|
attendanceLocal.saveAttendance(listOf(
|
||||||
Attendance(1, 2, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false),
|
Attendance(1, 2, 3, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name),
|
||||||
Attendance(1, 2, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false),
|
Attendance(1, 2, 3, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.WAITING.name),
|
||||||
Attendance(1, 2, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false)
|
Attendance(1, 2, 3, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name)
|
||||||
))
|
))
|
||||||
|
|
||||||
val attendance = attendanceLocal
|
val attendance = attendanceLocal
|
||||||
|
@ -2,8 +2,8 @@ package io.github.wulkanowy.data.repositories.timetable
|
|||||||
|
|
||||||
import org.threeten.bp.LocalDateTime
|
import org.threeten.bp.LocalDateTime
|
||||||
import org.threeten.bp.LocalDateTime.now
|
import org.threeten.bp.LocalDateTime.now
|
||||||
import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote
|
|
||||||
import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal
|
import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal
|
||||||
|
import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote
|
||||||
|
|
||||||
fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableLocal {
|
fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableLocal {
|
||||||
return TimetableLocal(
|
return TimetableLocal(
|
||||||
|
@ -61,6 +61,7 @@ import io.github.wulkanowy.data.db.migrations.Migration18
|
|||||||
import io.github.wulkanowy.data.db.migrations.Migration19
|
import io.github.wulkanowy.data.db.migrations.Migration19
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration2
|
import io.github.wulkanowy.data.db.migrations.Migration2
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration20
|
import io.github.wulkanowy.data.db.migrations.Migration20
|
||||||
|
import io.github.wulkanowy.data.db.migrations.Migration21
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration3
|
import io.github.wulkanowy.data.db.migrations.Migration3
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||||
@ -102,7 +103,7 @@ import javax.inject.Singleton
|
|||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VERSION_SCHEMA = 20
|
const val VERSION_SCHEMA = 21
|
||||||
|
|
||||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
|
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
|
||||||
return arrayOf(
|
return arrayOf(
|
||||||
@ -124,7 +125,8 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
Migration17(),
|
Migration17(),
|
||||||
Migration18(),
|
Migration18(),
|
||||||
Migration19(sharedPrefProvider),
|
Migration19(sharedPrefProvider),
|
||||||
Migration20()
|
Migration20(),
|
||||||
|
Migration21()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,9 @@ data class Attendance(
|
|||||||
@ColumnInfo(name = "diary_id")
|
@ColumnInfo(name = "diary_id")
|
||||||
val diaryId: Int,
|
val diaryId: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "time_id")
|
||||||
|
val timeId: Int,
|
||||||
|
|
||||||
val date: LocalDate,
|
val date: LocalDate,
|
||||||
|
|
||||||
val number: Int,
|
val number: Int,
|
||||||
@ -33,7 +36,13 @@ data class Attendance(
|
|||||||
|
|
||||||
val excused: Boolean,
|
val excused: Boolean,
|
||||||
|
|
||||||
val deleted: Boolean
|
val deleted: Boolean,
|
||||||
|
|
||||||
|
val excusable: Boolean,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "excuse_status")
|
||||||
|
val excuseStatus: String?
|
||||||
|
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
package io.github.wulkanowy.data.db.migrations
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration21 : Migration(20, 21) {
|
||||||
|
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("ALTER TABLE Attendance ADD COLUMN excusable INTEGER NOT NULL DEFAULT 0")
|
||||||
|
database.execSQL("ALTER TABLE Attendance ADD COLUMN time_id INTEGER NOT NULL DEFAULT 0")
|
||||||
|
database.execSQL("ALTER TABLE Attendance ADD COLUMN excuse_status TEXT DEFAULT NULL")
|
||||||
|
}
|
||||||
|
}
|
@ -3,8 +3,11 @@ package io.github.wulkanowy.data.repositories.attendance
|
|||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
|
import io.github.wulkanowy.sdk.pojo.Absent
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
|
import org.threeten.bp.LocalDateTime
|
||||||
|
import org.threeten.bp.LocalTime
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -19,6 +22,7 @@ class AttendanceRemote @Inject constructor(private val sdk: Sdk) {
|
|||||||
studentId = semester.studentId,
|
studentId = semester.studentId,
|
||||||
diaryId = semester.diaryId,
|
diaryId = semester.diaryId,
|
||||||
date = it.date,
|
date = it.date,
|
||||||
|
timeId = it.timeId,
|
||||||
number = it.number,
|
number = it.number,
|
||||||
subject = it.subject,
|
subject = it.subject,
|
||||||
name = it.name,
|
name = it.name,
|
||||||
@ -27,9 +31,20 @@ class AttendanceRemote @Inject constructor(private val sdk: Sdk) {
|
|||||||
exemption = it.exemption,
|
exemption = it.exemption,
|
||||||
lateness = it.lateness,
|
lateness = it.lateness,
|
||||||
excused = it.excused,
|
excused = it.excused,
|
||||||
deleted = it.deleted
|
deleted = it.deleted,
|
||||||
|
excusable = it.excusable,
|
||||||
|
excuseStatus = it.excuseStatus?.name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun excuseAbsence(semester: Semester, absenceList: List<Attendance>, reason: String?): Single<Boolean> {
|
||||||
|
return sdk.switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance ->
|
||||||
|
Absent(
|
||||||
|
date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)),
|
||||||
|
timeId = attendance.timeId
|
||||||
|
)
|
||||||
|
}, reason)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,4 +41,8 @@ class AttendanceRepository @Inject constructor(
|
|||||||
}).map { list -> list.filter { it.date in startDate..endDate } }
|
}).map { list -> list.filter { it.date in startDate..endDate } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun excuseForAbsence(semester: Semester, attendanceList: List<Attendance>, reason: String? = null): Single<Boolean> {
|
||||||
|
return remote.excuseAbsence(semester, attendanceList, reason)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories.attendance
|
||||||
|
|
||||||
|
enum class SentExcuseStatus(val id: Int = 0) {
|
||||||
|
WAITING,
|
||||||
|
ACCEPTED,
|
||||||
|
DENIED
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.attendance
|
||||||
|
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
|
|
||||||
|
class AttendanceAdapter<T : IFlexible<*>> : FlexibleAdapter<T>(null, null, true) {
|
||||||
|
|
||||||
|
var excuseActionMode: Boolean = false
|
||||||
|
|
||||||
|
var onExcuseCheckboxSelect: (attendanceItem: Attendance, checked: Boolean) -> Unit = { _, _ -> }
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package io.github.wulkanowy.ui.modules.attendance
|
package io.github.wulkanowy.ui.modules.attendance
|
||||||
|
|
||||||
|
import android.content.DialogInterface.BUTTON_POSITIVE
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
@ -10,8 +11,9 @@ import android.view.View.GONE
|
|||||||
import android.view.View.INVISIBLE
|
import android.view.View.INVISIBLE
|
||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.view.ActionMode
|
||||||
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
|
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
|
||||||
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
|
||||||
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
|
||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
@ -24,6 +26,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
|
|||||||
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
|
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
|
||||||
import io.github.wulkanowy.utils.dpToPx
|
import io.github.wulkanowy.utils.dpToPx
|
||||||
import io.github.wulkanowy.utils.setOnItemClickListener
|
import io.github.wulkanowy.utils.setOnItemClickListener
|
||||||
|
import kotlinx.android.synthetic.main.dialog_excuse.*
|
||||||
import kotlinx.android.synthetic.main.fragment_attendance.*
|
import kotlinx.android.synthetic.main.fragment_attendance.*
|
||||||
import org.threeten.bp.LocalDate
|
import org.threeten.bp.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -35,7 +38,13 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
|||||||
lateinit var presenter: AttendancePresenter
|
lateinit var presenter: AttendancePresenter
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var attendanceAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
|
lateinit var attendanceAdapter: AttendanceAdapter<AbstractFlexibleItem<*>>
|
||||||
|
|
||||||
|
override val excuseSuccessString: String
|
||||||
|
get() = getString(R.string.attendance_excuse_success)
|
||||||
|
|
||||||
|
override val excuseNoSelectionString: String
|
||||||
|
get() = getString(R.string.attendance_excuse_no_selection)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val SAVED_DATE_KEY = "CURRENT_DATE"
|
private const val SAVED_DATE_KEY = "CURRENT_DATE"
|
||||||
@ -49,6 +58,34 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
|||||||
|
|
||||||
override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize
|
override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize
|
||||||
|
|
||||||
|
override val excuseActionMode: Boolean get() = attendanceAdapter.excuseActionMode
|
||||||
|
|
||||||
|
private var actionMode: ActionMode? = null
|
||||||
|
private val actionModeCallback = object : ActionMode.Callback {
|
||||||
|
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||||
|
val inflater = mode.menuInflater
|
||||||
|
inflater.inflate(R.menu.context_menu_excuse, menu)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||||
|
mode.title = getString(R.string.attendance_excuse_title)
|
||||||
|
return presenter.onPrepareActionMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyActionMode(mode: ActionMode) {
|
||||||
|
presenter.onDestroyActionMode()
|
||||||
|
actionMode = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean {
|
||||||
|
return when (menu.itemId) {
|
||||||
|
R.id.excuseMenuSubmit -> presenter.onExcuseSubmitButtonClick()
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
@ -66,6 +103,7 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
|||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
attendanceAdapter.setOnItemClickListener(presenter::onAttendanceItemSelected)
|
attendanceAdapter.setOnItemClickListener(presenter::onAttendanceItemSelected)
|
||||||
|
attendanceAdapter.onExcuseCheckboxSelect = presenter::onExcuseCheckboxSelect
|
||||||
|
|
||||||
with(attendanceRecycler) {
|
with(attendanceRecycler) {
|
||||||
layoutManager = SmoothScrollLinearLayoutManager(context)
|
layoutManager = SmoothScrollLinearLayoutManager(context)
|
||||||
@ -83,6 +121,8 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
|||||||
attendanceNavDate.setOnClickListener { presenter.onPickDate() }
|
attendanceNavDate.setOnClickListener { presenter.onPickDate() }
|
||||||
attendanceNextButton.setOnClickListener { presenter.onNextDay() }
|
attendanceNextButton.setOnClickListener { presenter.onNextDay() }
|
||||||
|
|
||||||
|
attendanceExcuseButton.setOnClickListener { presenter.onExcuseButtonClick() }
|
||||||
|
|
||||||
attendanceNavContainer.setElevationCompat(requireContext().dpToPx(8f))
|
attendanceNavContainer.setElevationCompat(requireContext().dpToPx(8f))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +155,10 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
|||||||
if (::presenter.isInitialized) presenter.onViewReselected()
|
if (::presenter.isInitialized) presenter.onViewReselected()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onFragmentChanged() {
|
||||||
|
if (::presenter.isInitialized) presenter.onMainViewChanged()
|
||||||
|
}
|
||||||
|
|
||||||
override fun popView() {
|
override fun popView() {
|
||||||
(activity as? MainActivity)?.popView()
|
(activity as? MainActivity)?.popView()
|
||||||
}
|
}
|
||||||
@ -155,6 +199,10 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
|||||||
attendanceNextButton.visibility = if (show) VISIBLE else INVISIBLE
|
attendanceNextButton.visibility = if (show) VISIBLE else INVISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun showExcuseButton(show: Boolean) {
|
||||||
|
attendanceExcuseButton.visibility = if (show) VISIBLE else GONE
|
||||||
|
}
|
||||||
|
|
||||||
override fun showAttendanceDialog(lesson: Attendance) {
|
override fun showAttendanceDialog(lesson: Attendance) {
|
||||||
(activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson))
|
(activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson))
|
||||||
}
|
}
|
||||||
@ -174,10 +222,38 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun showExcuseDialog() {
|
||||||
|
AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle(R.string.attendance_excuse_title)
|
||||||
|
.setView(R.layout.dialog_excuse)
|
||||||
|
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||||
|
.create()
|
||||||
|
.apply {
|
||||||
|
setButton(BUTTON_POSITIVE, getString(R.string.attendance_excuse_dialog_submit)) { _, _ ->
|
||||||
|
presenter.onExcuseDialogSubmit(excuseReason.text?.toString().orEmpty())
|
||||||
|
}
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
|
||||||
override fun openSummaryView() {
|
override fun openSummaryView() {
|
||||||
(activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance())
|
(activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun startActionMode() {
|
||||||
|
actionMode = (activity as MainActivity?)?.startSupportActionMode(actionModeCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showExcuseCheckboxes(show: Boolean) {
|
||||||
|
attendanceAdapter.apply {
|
||||||
|
excuseActionMode = show
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finishActionMode() {
|
||||||
|
actionMode?.finish()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
|
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
|
||||||
|
@ -1,18 +1,22 @@
|
|||||||
package io.github.wulkanowy.ui.modules.attendance
|
package io.github.wulkanowy.ui.modules.attendance
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.View.GONE
|
||||||
import android.view.View.INVISIBLE
|
import android.view.View.INVISIBLE
|
||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
import eu.davidea.viewholders.FlexibleViewHolder
|
import eu.davidea.viewholders.FlexibleViewHolder
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
|
import io.github.wulkanowy.data.repositories.attendance.SentExcuseStatus
|
||||||
import kotlinx.android.extensions.LayoutContainer
|
import kotlinx.android.extensions.LayoutContainer
|
||||||
import kotlinx.android.synthetic.main.item_attendance.*
|
import kotlinx.android.synthetic.main.item_attendance.*
|
||||||
|
|
||||||
class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
|
class AttendanceItem(val attendance: Attendance) :
|
||||||
|
AbstractFlexibleItem<AttendanceItem.ViewHolder>() {
|
||||||
|
|
||||||
override fun getLayoutRes() = R.layout.item_attendance
|
override fun getLayoutRes() = R.layout.item_attendance
|
||||||
|
|
||||||
@ -26,6 +30,34 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<Attendan
|
|||||||
attendanceItemSubject.text = attendance.subject
|
attendanceItemSubject.text = attendance.subject
|
||||||
attendanceItemDescription.text = attendance.name
|
attendanceItemDescription.text = attendance.name
|
||||||
attendanceItemAlert.visibility = attendance.run { if (absence && !excused) VISIBLE else INVISIBLE }
|
attendanceItemAlert.visibility = attendance.run { if (absence && !excused) VISIBLE else INVISIBLE }
|
||||||
|
attendanceItemNumber.visibility = GONE
|
||||||
|
attendanceItemExcuseInfo.visibility = GONE
|
||||||
|
attendanceItemExcuseCheckbox.visibility = GONE
|
||||||
|
attendanceItemExcuseCheckbox.isChecked = false
|
||||||
|
attendanceItemExcuseCheckbox.setOnCheckedChangeListener { _, checked ->
|
||||||
|
(adapter as AttendanceAdapter).onExcuseCheckboxSelect(attendance, checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (if (attendance.excuseStatus != null) SentExcuseStatus.valueOf(attendance.excuseStatus) else null) {
|
||||||
|
SentExcuseStatus.WAITING -> {
|
||||||
|
attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_waiting)
|
||||||
|
attendanceItemExcuseInfo.visibility = VISIBLE
|
||||||
|
attendanceItemAlert.visibility = INVISIBLE
|
||||||
|
}
|
||||||
|
SentExcuseStatus.DENIED -> {
|
||||||
|
attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_denied)
|
||||||
|
attendanceItemExcuseInfo.visibility = VISIBLE
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
if (attendance.excusable && (adapter as AttendanceAdapter).excuseActionMode) {
|
||||||
|
attendanceItemNumber.visibility = GONE
|
||||||
|
attendanceItemExcuseCheckbox.visibility = VISIBLE
|
||||||
|
} else {
|
||||||
|
attendanceItemNumber.visibility = VISIBLE
|
||||||
|
attendanceItemExcuseCheckbox.visibility = GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,8 +78,20 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem<Attendan
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
|
class ViewHolder(view: View, val adapter: FlexibleAdapter<*>) :
|
||||||
|
FlexibleViewHolder(view, adapter),
|
||||||
|
LayoutContainer {
|
||||||
|
|
||||||
override val containerView: View
|
override val containerView: View
|
||||||
get() = contentView
|
get() = contentView
|
||||||
|
|
||||||
|
override fun onClick(view: View?) {
|
||||||
|
super.onClick(view)
|
||||||
|
attendanceItemExcuseCheckbox.apply {
|
||||||
|
if ((adapter as AttendanceAdapter).excuseActionMode && isVisible) {
|
||||||
|
isChecked = !isChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.attendance
|
||||||
|
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
|
|
||||||
|
@Module
|
||||||
|
class AttendanceModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideAttendanceFlexibleAdapter() = AttendanceAdapter<AbstractFlexibleItem<*>>()
|
||||||
|
}
|
@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.attendance
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||||
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository
|
import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository
|
||||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||||
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
|
||||||
@ -40,6 +41,8 @@ class AttendancePresenter @Inject constructor(
|
|||||||
|
|
||||||
private lateinit var lastError: Throwable
|
private lateinit var lastError: Throwable
|
||||||
|
|
||||||
|
private val attendanceToExcuseList = mutableListOf<Attendance>()
|
||||||
|
|
||||||
fun onAttachView(view: AttendanceView, date: Long?) {
|
fun onAttachView(view: AttendanceView, date: Long?) {
|
||||||
super.onAttachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.initView()
|
||||||
@ -51,11 +54,15 @@ class AttendancePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onPreviousDay() {
|
fun onPreviousDay() {
|
||||||
|
view?.finishActionMode()
|
||||||
|
attendanceToExcuseList.clear()
|
||||||
loadData(currentDate.previousSchoolDay)
|
loadData(currentDate.previousSchoolDay)
|
||||||
reloadView()
|
reloadView()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onNextDay() {
|
fun onNextDay() {
|
||||||
|
view?.finishActionMode()
|
||||||
|
attendanceToExcuseList.clear()
|
||||||
loadData(currentDate.nextSchoolDay)
|
loadData(currentDate.nextSchoolDay)
|
||||||
reloadView()
|
reloadView()
|
||||||
}
|
}
|
||||||
@ -100,10 +107,59 @@ class AttendancePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onMainViewChanged() {
|
||||||
|
view?.finishActionMode()
|
||||||
|
}
|
||||||
|
|
||||||
fun onAttendanceItemSelected(item: AbstractFlexibleItem<*>?) {
|
fun onAttendanceItemSelected(item: AbstractFlexibleItem<*>?) {
|
||||||
if (item is AttendanceItem) {
|
view?.apply {
|
||||||
|
if (item is AttendanceItem && !excuseActionMode) {
|
||||||
Timber.i("Select attendance item ${item.attendance.id}")
|
Timber.i("Select attendance item ${item.attendance.id}")
|
||||||
view?.showAttendanceDialog(item.attendance)
|
showAttendanceDialog(item.attendance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onExcuseButtonClick() {
|
||||||
|
view?.startActionMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onExcuseCheckboxSelect(attendanceItem: Attendance, checked: Boolean) {
|
||||||
|
if (checked) attendanceToExcuseList.add(attendanceItem)
|
||||||
|
else attendanceToExcuseList.remove(attendanceItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onExcuseSubmitButtonClick(): Boolean {
|
||||||
|
view?.apply {
|
||||||
|
return if (attendanceToExcuseList.isNotEmpty()) {
|
||||||
|
showExcuseDialog()
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
showMessage(excuseNoSelectionString)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onExcuseDialogSubmit(reason: String) {
|
||||||
|
view?.finishActionMode()
|
||||||
|
excuseAbsence(if (reason != "") reason else null, attendanceToExcuseList.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onPrepareActionMode(): Boolean {
|
||||||
|
view?.apply {
|
||||||
|
showExcuseCheckboxes(true)
|
||||||
|
showExcuseButton(false)
|
||||||
|
}
|
||||||
|
attendanceToExcuseList.clear()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDestroyActionMode() {
|
||||||
|
view?.apply {
|
||||||
|
showExcuseCheckboxes(false)
|
||||||
|
showExcuseButton(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +213,7 @@ class AttendancePresenter @Inject constructor(
|
|||||||
showEmpty(it.isEmpty())
|
showEmpty(it.isEmpty())
|
||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
showContent(it.isNotEmpty())
|
showContent(it.isNotEmpty())
|
||||||
|
showExcuseButton(it.any { item -> item.attendance.excusable })
|
||||||
}
|
}
|
||||||
analytics.logEvent("load_attendance", "items" to it.size, "force_refresh" to forceRefresh)
|
analytics.logEvent("load_attendance", "items" to it.size, "force_refresh" to forceRefresh)
|
||||||
}) {
|
}) {
|
||||||
@ -167,6 +224,39 @@ class AttendancePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun excuseAbsence(reason: String?, toExcuseList: List<Attendance>) {
|
||||||
|
Timber.i("Excusing absence started")
|
||||||
|
disposable.apply {
|
||||||
|
add(studentRepository.getCurrentStudent()
|
||||||
|
.delay(200, MILLISECONDS)
|
||||||
|
.flatMap { semesterRepository.getCurrentSemester(it) }
|
||||||
|
.flatMap { attendanceRepository.excuseForAbsence(it, toExcuseList, reason) }
|
||||||
|
.subscribeOn(schedulers.backgroundThread)
|
||||||
|
.observeOn(schedulers.mainThread)
|
||||||
|
.doOnSubscribe {
|
||||||
|
view?.apply {
|
||||||
|
showProgress(true)
|
||||||
|
showContent(false)
|
||||||
|
showExcuseButton(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.subscribe({
|
||||||
|
Timber.i("Excusing for absence result: Success")
|
||||||
|
analytics.logEvent("excuse_absence", "items" to attendanceToExcuseList.size)
|
||||||
|
attendanceToExcuseList.clear()
|
||||||
|
view?.apply {
|
||||||
|
showExcuseButton(false)
|
||||||
|
showMessage(excuseSuccessString)
|
||||||
|
}
|
||||||
|
loadData(currentDate, true)
|
||||||
|
}) {
|
||||||
|
Timber.i("Excusing for absence result: An exception occurred")
|
||||||
|
view?.showProgress(false)
|
||||||
|
errorHandler.dispatch(it)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun showErrorViewOnError(message: String, error: Throwable) {
|
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||||
view?.run {
|
view?.run {
|
||||||
if (isViewEmpty) {
|
if (isViewEmpty) {
|
||||||
|
@ -10,6 +10,12 @@ interface AttendanceView : BaseView {
|
|||||||
|
|
||||||
val currentStackSize: Int?
|
val currentStackSize: Int?
|
||||||
|
|
||||||
|
val excuseSuccessString: String
|
||||||
|
|
||||||
|
val excuseNoSelectionString: String
|
||||||
|
|
||||||
|
val excuseActionMode: Boolean
|
||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
|
||||||
fun updateData(data: List<AttendanceItem>)
|
fun updateData(data: List<AttendanceItem>)
|
||||||
@ -38,11 +44,21 @@ interface AttendanceView : BaseView {
|
|||||||
|
|
||||||
fun showNextButton(show: Boolean)
|
fun showNextButton(show: Boolean)
|
||||||
|
|
||||||
|
fun showExcuseButton(show: Boolean)
|
||||||
|
|
||||||
fun showAttendanceDialog(lesson: Attendance)
|
fun showAttendanceDialog(lesson: Attendance)
|
||||||
|
|
||||||
fun showDatePickerDialog(currentDate: LocalDate)
|
fun showDatePickerDialog(currentDate: LocalDate)
|
||||||
|
|
||||||
|
fun showExcuseDialog()
|
||||||
|
|
||||||
fun openSummaryView()
|
fun openSummaryView()
|
||||||
|
|
||||||
|
fun startActionMode()
|
||||||
|
|
||||||
|
fun showExcuseCheckboxes(show: Boolean)
|
||||||
|
|
||||||
|
fun finishActionMode()
|
||||||
|
|
||||||
fun popView()
|
fun popView()
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ import io.github.wulkanowy.utils.getThemeAttrColor
|
|||||||
import io.github.wulkanowy.utils.safelyPopFragments
|
import io.github.wulkanowy.utils.safelyPopFragments
|
||||||
import io.github.wulkanowy.utils.setOnViewChangeListener
|
import io.github.wulkanowy.utils.setOnViewChangeListener
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class MainActivity : BaseActivity<MainPresenter>(), MainView {
|
class MainActivity : BaseActivity<MainPresenter>(), MainView {
|
||||||
@ -167,6 +168,11 @@ class MainActivity : BaseActivity<MainPresenter>(), MainView {
|
|||||||
(navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected()
|
(navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun notifyMenuViewChanged() {
|
||||||
|
Timber.d("Menu view changed")
|
||||||
|
(navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentChanged()
|
||||||
|
}
|
||||||
|
|
||||||
fun showDialogFragment(dialog: DialogFragment) {
|
fun showDialogFragment(dialog: DialogFragment) {
|
||||||
navController.showDialogFragment(dialog)
|
navController.showDialogFragment(dialog)
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
|
|||||||
import io.github.wulkanowy.ui.modules.about.license.LicenseModule
|
import io.github.wulkanowy.ui.modules.about.license.LicenseModule
|
||||||
import io.github.wulkanowy.ui.modules.account.AccountDialog
|
import io.github.wulkanowy.ui.modules.account.AccountDialog
|
||||||
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.attendance.AttendanceModule
|
||||||
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
|
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
|
||||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||||
@ -51,7 +52,7 @@ abstract class MainModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PerFragment
|
@PerFragment
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector(modules = [AttendanceModule::class])
|
||||||
abstract fun bindAttendanceFragment(): AttendanceFragment
|
abstract fun bindAttendanceFragment(): AttendanceFragment
|
||||||
|
|
||||||
@PerFragment
|
@PerFragment
|
||||||
|
@ -75,6 +75,7 @@ class MainPresenter @Inject constructor(
|
|||||||
notifyMenuViewReselected()
|
notifyMenuViewReselected()
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
|
notifyMenuViewChanged()
|
||||||
switchMenuView(index)
|
switchMenuView(index)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ interface MainView : BaseView {
|
|||||||
|
|
||||||
fun notifyMenuViewReselected()
|
fun notifyMenuViewReselected()
|
||||||
|
|
||||||
|
fun notifyMenuViewChanged()
|
||||||
|
|
||||||
fun setViewTitle(title: String)
|
fun setViewTitle(title: String)
|
||||||
|
|
||||||
fun popView(depth: Int = 1)
|
fun popView(depth: Int = 1)
|
||||||
@ -33,6 +35,8 @@ interface MainView : BaseView {
|
|||||||
interface MainChildView {
|
interface MainChildView {
|
||||||
|
|
||||||
fun onFragmentReselected()
|
fun onFragmentReselected()
|
||||||
|
|
||||||
|
fun onFragmentChanged() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TitledView {
|
interface TitledView {
|
||||||
|
10
app/src/main/res/drawable/ic_all_done.xml
Normal file
10
app/src/main/res/drawable/ic_all_done.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z" />
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_all_done_all.xml
Normal file
10
app/src/main/res/drawable/ic_all_done_all.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zM22.24,5.59L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z" />
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_excuse_denied.xml
Normal file
10
app/src/main/res/drawable/ic_excuse_denied.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M7,11v2h10v-2L7,11zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z" />
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_excuse_waiting.xml
Normal file
10
app/src/main/res/drawable/ic_excuse_waiting.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z" />
|
||||||
|
</vector>
|
21
app/src/main/res/layout/dialog_excuse.xml
Normal file
21
app/src/main/res/layout/dialog_excuse.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/excuseReason"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/attendance_excuse_dialog_reason" />
|
||||||
|
|
||||||
|
<requestFocus />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
</LinearLayout>
|
@ -40,16 +40,16 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/gradeDialogColorAndWeightValue"
|
android:id="@+id/gradeDialogColorAndWeightValue"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_marginTop="2dp"
|
|
||||||
android:minHeight="32dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
android:background="@color/grade_black"
|
android:background="@color/grade_black"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textColor="@android:color/white"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
|
android:minHeight="32dp"
|
||||||
|
android:textColor="@android:color/white"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:textSize="14sp"
|
||||||
tools:text="Waga: 1.00" />
|
tools:text="Waga: 1.00" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@ -57,16 +57,17 @@
|
|||||||
android:id="@+id/gradeDialogHeader"
|
android:id="@+id/gradeDialogHeader"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="120dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:gravity="center_vertical"
|
android:layout_marginRight="12dp"
|
||||||
android:layout_toLeftOf="@+id/gradeDialogValueLayout"
|
|
||||||
android:layout_toStartOf="@+id/gradeDialogValueLayout"
|
android:layout_toStartOf="@+id/gradeDialogValueLayout"
|
||||||
|
android:layout_toLeftOf="@+id/gradeDialogValueLayout"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_marginRight="12dp"
|
android:gravity="center_vertical"
|
||||||
android:layout_marginEnd="12dp"
|
android:minHeight="120dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/gradeDialogSubject"
|
android:id="@+id/gradeDialogSubject"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -56,6 +56,20 @@
|
|||||||
android:textSize="20sp" />
|
android:textSize="20sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
|
android:id="@+id/attendanceExcuseButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:tint="?colorOnSecondary"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:text="@string/attendance_excuse_title"
|
||||||
|
app:icon="@drawable/ic_all_done_all"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/attendanceError"
|
android:id="@+id/attendanceError"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout 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:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/gradeHeaderContainer"
|
android:id="@+id/gradeHeaderContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -53,15 +52,15 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/gradeHeaderNote"
|
android:id="@+id/gradeHeaderNote"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:minWidth="20dp"
|
|
||||||
android:layout_height="20dp"
|
android:layout_height="20dp"
|
||||||
android:paddingLeft="5dp"
|
android:background="@drawable/background_header_note"
|
||||||
android:paddingRight="5dp"
|
android:gravity="center"
|
||||||
|
android:minWidth="20dp"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:gravity="center"
|
android:paddingLeft="5dp"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:background="@drawable/background_header_note"
|
android:paddingRight="5dp"
|
||||||
android:textColor="?colorOnPrimary"
|
android:textColor="?colorOnPrimary"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
tools:text="255" />
|
tools:text="255" />
|
||||||
|
@ -13,17 +13,41 @@
|
|||||||
android:paddingBottom="7dp"
|
android:paddingBottom="7dp"
|
||||||
tools:context=".ui.modules.attendance.AttendanceItem">
|
tools:context=".ui.modules.attendance.AttendanceItem">
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/attendanceItemNumber"
|
android:id="@+id/attendanceItemNumberContainer"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:layout_centerVertical="true"
|
android:gravity="center"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/attendanceItemNumber"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:includeFontPadding="false"
|
android:includeFontPadding="false"
|
||||||
android:maxLength="2"
|
android:maxLength="2"
|
||||||
android:textSize="32sp"
|
android:textSize="32sp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"
|
||||||
tools:text="5" />
|
tools:text="5" />
|
||||||
|
|
||||||
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
|
android:id="@+id/attendanceItemExcuseCheckbox"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:text="@null"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/attendanceItemExcuseInfo"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:srcCompat="@drawable/ic_excuse_waiting"
|
||||||
|
app:tint="?attr/colorOnSurface" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/attendanceItemSubject"
|
android:id="@+id/attendanceItemSubject"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -35,8 +59,8 @@
|
|||||||
android:layout_marginRight="40dp"
|
android:layout_marginRight="40dp"
|
||||||
android:layout_toStartOf="@id/attendanceItemAlert"
|
android:layout_toStartOf="@id/attendanceItemAlert"
|
||||||
android:layout_toLeftOf="@id/attendanceItemAlert"
|
android:layout_toLeftOf="@id/attendanceItemAlert"
|
||||||
android:layout_toEndOf="@+id/attendanceItemNumber"
|
android:layout_toEndOf="@+id/attendanceItemNumberContainer"
|
||||||
android:layout_toRightOf="@+id/attendanceItemNumber"
|
android:layout_toRightOf="@+id/attendanceItemNumberContainer"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textSize="17sp"
|
android:textSize="17sp"
|
||||||
@ -48,7 +72,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignStart="@id/attendanceItemSubject"
|
android:layout_alignStart="@id/attendanceItemSubject"
|
||||||
android:layout_alignLeft="@id/attendanceItemSubject"
|
android:layout_alignLeft="@id/attendanceItemSubject"
|
||||||
android:layout_alignBottom="@+id/attendanceItemNumber"
|
android:layout_alignBottom="@+id/attendanceItemNumberContainer"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textColor="?android:textColorSecondary"
|
android:textColor="?android:textColorSecondary"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
11
app/src/main/res/menu/context_menu_excuse.xml
Normal file
11
app/src/main/res/menu/context_menu_excuse.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/excuseMenuSubmit"
|
||||||
|
android:icon="@drawable/ic_all_done"
|
||||||
|
android:title="@string/attendance_excuse_dialog_submit"
|
||||||
|
app:iconTint="@color/material_on_surface_emphasis_medium"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
</menu>
|
@ -141,6 +141,11 @@
|
|||||||
<item quantity="many">%1$d nieobecności</item>
|
<item quantity="many">%1$d nieobecności</item>
|
||||||
<item quantity="other">%1$d nieobecności</item>
|
<item quantity="other">%1$d nieobecności</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<string name="attendance_excuse_dialog_reason">Powód nieobecności (opcjonalny)</string>
|
||||||
|
<string name="attendance_excuse_dialog_submit">Wyślij</string>
|
||||||
|
<string name="attendance_excuse_success">Usprawiedliwiono pomyślnie!</string>
|
||||||
|
<string name="attendance_excuse_no_selection">Musisz wybrać co najmniej jedną nieobecność!</string>
|
||||||
|
<string name="attendance_excuse_title">Usprawiedliw</string>
|
||||||
|
|
||||||
|
|
||||||
<!--Attendance summary-->
|
<!--Attendance summary-->
|
||||||
|
@ -135,6 +135,11 @@
|
|||||||
<item quantity="one">%1$d absence</item>
|
<item quantity="one">%1$d absence</item>
|
||||||
<item quantity="other">%1$d absences</item>
|
<item quantity="other">%1$d absences</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<string name="attendance_excuse_dialog_reason">Absence reason (optional)</string>
|
||||||
|
<string name="attendance_excuse_dialog_submit">Send</string>
|
||||||
|
<string name="attendance_excuse_success">Absence excused successfully!</string>
|
||||||
|
<string name="attendance_excuse_no_selection">You must select at least one absence!</string>
|
||||||
|
<string name="attendance_excuse_title">Excuse</string>
|
||||||
|
|
||||||
|
|
||||||
<!--Attendance summary-->
|
<!--Attendance summary-->
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
<style name="WulkanowyTheme.NoActionBar" parent="WulkanowyTheme">
|
<style name="WulkanowyTheme.NoActionBar" parent="WulkanowyTheme">
|
||||||
<item name="windowActionBar">false</item>
|
<item name="windowActionBar">false</item>
|
||||||
<item name="windowNoTitle">true</item>
|
<item name="windowNoTitle">true</item>
|
||||||
|
<item name="windowActionModeOverlay">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="WulkanowyTheme.SplashScreen" parent="WulkanowyTheme.NoActionBar">
|
<style name="WulkanowyTheme.SplashScreen" parent="WulkanowyTheme.NoActionBar">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user