[UI] Refactor app dialogs. (#103)

* [UI] Add new base dialog classes.

* [UI] Migrate dialogs to the new base classes.
This commit is contained in:
Kuba Szczodrzyński 2021-10-22 16:57:10 +02:00 committed by GitHub
parent fd62653d79
commit f3e2d21b89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 1812 additions and 2058 deletions

View File

@ -420,7 +420,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
App.db.profileDao().getByIdNow(profile.identifier.toInt()) App.db.profileDao().getByIdNow(profile.identifier.toInt())
} ?: return@launch } ?: return@launch
drawer.close() drawer.close()
ProfileConfigDialog(this@MainActivity, appProfile) ProfileConfigDialog(this@MainActivity, appProfile).show()
} }
true true
} else { } else {
@ -529,7 +529,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
if (app.config.appVersion < BuildConfig.VERSION_CODE) { if (app.config.appVersion < BuildConfig.VERSION_CODE) {
// force an AppSync after update // force an AppSync after update
app.config.sync.lastAppSync = 0L app.config.sync.lastAppSync = 0L
ChangelogDialog(this) ChangelogDialog(this).show()
if (app.config.appVersion < 170) { if (app.config.appVersion < 170) {
//Intent intent = new Intent(this, ChangelogIntroActivity.class); //Intent intent = new Intent(this, ChangelogIntroActivity.class);
//startActivity(intent); //startActivity(intent);
@ -587,7 +587,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
.withIcon(CommunityMaterial.Icon.cmd_download_outline) .withIcon(CommunityMaterial.Icon.cmd_download_outline)
.withOnClickListener { .withOnClickListener {
bottomSheet.close() bottomSheet.close()
SyncViewListDialog(this, navTargetId) SyncViewListDialog(this, navTargetId).show()
}, },
BottomSheetSeparatorItem(false), BottomSheetSeparatorItem(false),
BottomSheetPrimaryItem(false) BottomSheetPrimaryItem(false)
@ -690,7 +690,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
Type.NOT_AVAILABLE -> { Type.NOT_AVAILABLE -> {
swipeRefreshLayout.isRefreshing = false swipeRefreshLayout.isRefreshing = false
loadTarget(DRAWER_ITEM_HOME) loadTarget(DRAWER_ITEM_HOME)
RegisterUnavailableDialog(this, error.status!!) RegisterUnavailableDialog(this, error.status!!).show()
return return
} }
Type.API_ERROR -> { Type.API_ERROR -> {
@ -722,7 +722,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true) @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onUpdateEvent(event: Update) { fun onUpdateEvent(event: Update) {
EventBus.getDefault().removeStickyEvent(event) EventBus.getDefault().removeStickyEvent(event)
UpdateAvailableDialog(this, event) UpdateAvailableDialog(this, event).show()
} }
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true) @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
@ -730,7 +730,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
EventBus.getDefault().removeStickyEvent(event) EventBus.getDefault().removeStickyEvent(event)
val error = app.availabilityManager.check(app.profile, cacheOnly = true) val error = app.availabilityManager.check(app.profile, cacheOnly = true)
if (error != null) { if (error != null) {
RegisterUnavailableDialog(this, error.status!!) RegisterUnavailableDialog(this, error.status!!).show()
} }
} }
@ -797,7 +797,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
if (event.error.errorCode == ERROR_VULCAN_API_DEPRECATED) { if (event.error.errorCode == ERROR_VULCAN_API_DEPRECATED) {
if (event.error.profileId != App.profileId) if (event.error.profileId != App.profileId)
return return
ErrorDetailsDialog(this, listOf(event.error)) ErrorDetailsDialog(this, listOf(event.error)).show()
} }
navView.toolbar.apply { navView.toolbar.apply {
subtitleFormat = R.string.toolbar_subtitle subtitleFormat = R.string.toolbar_subtitle
@ -894,7 +894,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
this, this,
extras.getString("serverMessageTitle") ?: getString(R.string.app_name), extras.getString("serverMessageTitle") ?: getString(R.string.app_name),
extras.getString("serverMessageText") ?: "" extras.getString("serverMessageText") ?: ""
) ).show()
true true
} }
"feedbackMessage" -> { "feedbackMessage" -> {
@ -917,7 +917,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
this, this,
App.profileId, App.profileId,
defaultDate = date defaultDate = date
) ).show()
true true
} }
else -> false else -> false

View File

@ -103,7 +103,7 @@ class SzkolnyApi(val app: App) : CoroutineScope {
activity, activity,
listOf(apiError), listOf(apiError),
R.string.error_occured R.string.error_occured
) ).show()
null null
} }
null null

View File

@ -77,14 +77,14 @@ class AgendaFragment : Fragment(), CoroutineScope {
activity, activity,
app.profileId, app.profileId,
defaultDate = AgendaFragmentDefault.selectedDate defaultDate = AgendaFragmentDefault.selectedDate
) ).show()
}, },
BottomSheetPrimaryItem(true) BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_agenda_config) .withTitle(R.string.menu_agenda_config)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline) .withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener { .withOnClickListener {
activity.bottomSheet.close() activity.bottomSheet.close()
AgendaConfigDialog(activity, true, null, null) AgendaConfigDialog(activity, true, null, null).show()
}, },
BottomSheetPrimaryItem(true) BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_agenda_change_view) .withTitle(R.string.menu_agenda_change_view)
@ -124,7 +124,7 @@ class AgendaFragment : Fragment(), CoroutineScope {
activity, activity,
app.profileId, app.profileId,
defaultDate = AgendaFragmentDefault.selectedDate defaultDate = AgendaFragmentDefault.selectedDate
) ).show()
} }
activity.gainAttention() activity.gainAttention()
@ -188,7 +188,7 @@ class AgendaFragment : Fragment(), CoroutineScope {
unreadEventDates.remove(date.value) unreadEventDates.remove(date.value)
} }
DayDialog(activity, app.profileId, date) DayDialog(activity, app.profileId, date).show()
}} }}
b.progressBar.visibility = View.GONE b.progressBar.visibility = View.GONE

View File

@ -139,7 +139,7 @@ class AgendaFragmentDefault(
val c = Calendar.getInstance() val c = Calendar.getInstance()
c.time = dayItem.date c.time = dayItem.date
if (c.timeInMillis == selectedDate.inMillis) { if (c.timeInMillis == selectedDate.inMillis) {
DayDialog(activity, app.profileId, selectedDate) DayDialog(activity, app.profileId, selectedDate).show()
} }
} }
@ -147,16 +147,25 @@ class AgendaFragmentDefault(
val date = Date.fromCalendar(event.instanceDay) val date = Date.fromCalendar(event.instanceDay)
when (event) { when (event) {
is AgendaEvent -> EventDetailsDialog(activity, event.event) is AgendaEvent -> EventDetailsDialog(activity, event.event).show()
is LessonChangesEvent -> LessonChangesDialog(activity, app.profileId, date) is LessonChangesEvent -> LessonChangesDialog(
activity = activity,
profileId = app.profileId,
defaultDate = date
).show()
is TeacherAbsenceEvent -> TeacherAbsenceDialog( is TeacherAbsenceEvent -> TeacherAbsenceDialog(
activity, activity = activity,
app.profileId, profileId = app.profileId,
date date = date
) ).show()
is AgendaEventGroup -> DayDialog(activity, app.profileId, date, eventTypeId = event.typeId) is AgendaEventGroup -> DayDialog(
activity = activity,
profileId = app.profileId,
date = date,
eventTypeId = event.typeId,
).show()
is BaseCalendarEvent -> if (event.isPlaceHolder) is BaseCalendarEvent -> if (event.isPlaceHolder)
DayDialog(activity, app.profileId, date) DayDialog(activity, app.profileId, date).show()
} }
if (event is BaseEvent && event.showItemBadge) { if (event is BaseEvent && event.showItemBadge) {

View File

@ -4,12 +4,11 @@
package pl.szczodrzynski.edziennik.ui.agenda package pl.szczodrzynski.edziennik.ui.agenda
import android.view.LayoutInflater
import android.view.View import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.* import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Lesson import pl.szczodrzynski.edziennik.data.db.entity.Lesson
@ -23,6 +22,7 @@ import pl.szczodrzynski.edziennik.ui.agenda.lessonchanges.LessonChangesEventRend
import pl.szczodrzynski.edziennik.ui.agenda.teacherabsence.TeacherAbsenceDialog import pl.szczodrzynski.edziennik.ui.agenda.teacherabsence.TeacherAbsenceDialog
import pl.szczodrzynski.edziennik.ui.agenda.teacherabsence.TeacherAbsenceEvent import pl.szczodrzynski.edziennik.ui.agenda.teacherabsence.TeacherAbsenceEvent
import pl.szczodrzynski.edziennik.ui.agenda.teacherabsence.TeacherAbsenceEventRenderer import pl.szczodrzynski.edziennik.ui.agenda.teacherabsence.TeacherAbsenceEventRenderer
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.ui.event.EventDetailsDialog import pl.szczodrzynski.edziennik.ui.event.EventDetailsDialog
import pl.szczodrzynski.edziennik.ui.event.EventListAdapter import pl.szczodrzynski.edziennik.ui.event.EventListAdapter
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog import pl.szczodrzynski.edziennik.ui.event.EventManualDialog
@ -30,61 +30,39 @@ import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import pl.szczodrzynski.edziennik.utils.models.Week import pl.szczodrzynski.edziennik.utils.models.Week
import kotlin.coroutines.CoroutineContext
class DayDialog( class DayDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val profileId: Int, private val profileId: Int,
val date: Date, private val date: Date,
val eventTypeId: Long? = null, private val eventTypeId: Long? = null,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<DialogDayBinding>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "DayDialog"
}
private lateinit var app: App override val TAG = "DayDialog"
private lateinit var b: DialogDayBinding
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitleRes(): Int? = null
override val coroutineContext: CoroutineContext override fun inflate(layoutInflater: LayoutInflater) =
get() = job + Dispatchers.Main DialogDayBinding.inflate(layoutInflater)
override fun getPositiveButtonText() = R.string.close
override fun getNeutralButtonText() = R.string.add
private lateinit var adapter: EventListAdapter private lateinit var adapter: EventListAdapter
init { run { override suspend fun onNeutralClick(): Boolean {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
b = DialogDayBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setView(b.root)
.setPositiveButton(R.string.close) { dialog, _ ->
dialog.dismiss()
}
.setNeutralButton(R.string.add, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)?.onClick {
EventManualDialog( EventManualDialog(
activity, activity,
profileId, profileId,
defaultDate = date, defaultDate = date,
onShowListener = onShowListener, onShowListener = onShowListener,
onDismissListener = onDismissListener onDismissListener = onDismissListener
) ).show()
return NO_DISMISS
} }
update() override suspend fun onShow() {
}}
private fun update() { launch {
b.dayDate.setText( b.dayDate.setText(
R.string.dialog_day_date_format, R.string.dialog_day_date_format,
Week.getFullDayName(date.weekDay), Week.getFullDayName(date.weekDay),
@ -95,7 +73,8 @@ class DayDialog(
app.db.timetableDao().getAllForDateNow(profileId, date) app.db.timetableDao().getAllForDateNow(profileId, date)
}.filter { it.type != Lesson.TYPE_NO_LESSONS } }.filter { it.type != Lesson.TYPE_NO_LESSONS }
if (lessons.isNotEmpty()) { run { if (lessons.isNotEmpty()) {
run {
val startTime = lessons.first().startTime ?: return@run val startTime = lessons.first().startTime ?: return@run
val endTime = lessons.last().endTime ?: return@run val endTime = lessons.last().endTime ?: return@run
val diff = Time.diff(startTime, endTime) val diff = Time.diff(startTime, endTime)
@ -110,7 +89,8 @@ class DayDialog(
) )
b.lessonsInfo.visibility = View.VISIBLE b.lessonsInfo.visibility = View.VISIBLE
}} }
}
val lessonChanges = withContext(Dispatchers.Default) { val lessonChanges = withContext(Dispatchers.Default) {
app.db.timetableDao().getChangesForDateNow(profileId, date) app.db.timetableDao().getChangesForDateNow(profileId, date)
@ -133,7 +113,7 @@ class DayDialog(
date, date,
onShowListener = onShowListener, onShowListener = onShowListener,
onDismissListener = onDismissListener onDismissListener = onDismissListener
) ).show()
} }
} }
b.lessonChangesFrame.isVisible = lessonChanges.isNotEmpty() b.lessonChangesFrame.isVisible = lessonChanges.isNotEmpty()
@ -158,7 +138,7 @@ class DayDialog(
date, date,
onShowListener = onShowListener, onShowListener = onShowListener,
onDismissListener = onDismissListener onDismissListener = onDismissListener
) ).show()
} }
} }
b.teacherAbsenceFrame.isVisible = teacherAbsences.isNotEmpty() b.teacherAbsenceFrame.isVisible = teacherAbsences.isNotEmpty()
@ -177,7 +157,7 @@ class DayDialog(
it, it,
onShowListener = onShowListener, onShowListener = onShowListener,
onDismissListener = onDismissListener onDismissListener = onDismissListener
) ).show()
}, },
onEventEditClick = { onEventEditClick = {
EventManualDialog( EventManualDialog(
@ -186,7 +166,7 @@ class DayDialog(
editingEvent = it, editingEvent = it,
onShowListener = onShowListener, onShowListener = onShowListener,
onDismissListener = onDismissListener onDismissListener = onDismissListener
) ).show()
} }
) )
@ -217,5 +197,5 @@ class DayDialog(
b.eventsNoData.visibility = View.VISIBLE b.eventsNoData.visibility = View.VISIBLE
} }
} }
}} }
} }

View File

@ -1,55 +1,34 @@
package pl.szczodrzynski.edziennik.ui.agenda.lessonchanges package pl.szczodrzynski.edziennik.ui.agenda.lessonchanges
import androidx.appcompat.app.AlertDialog import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.* import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.DialogLessonChangeListBinding import pl.szczodrzynski.edziennik.databinding.DialogLessonChangeListBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.ui.timetable.LessonDetailsDialog import pl.szczodrzynski.edziennik.ui.timetable.LessonDetailsDialog
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.coroutines.CoroutineContext
class LessonChangesDialog( class LessonChangesDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val profileId: Int, private val profileId: Int,
private val defaultDate: Date, private val defaultDate: Date,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<DialogLessonChangeListBinding>(activity, onShowListener, onDismissListener) {
companion object {
const val TAG = "LessonChangeDialog"
}
private val app by lazy { activity.application as App } override val TAG = "LessonChangesDialog"
private lateinit var job: Job override fun getTitle(): String = defaultDate.formattedString
override val coroutineContext: CoroutineContext override fun getTitleRes(): Int? = null
get() = job + Dispatchers.Main override fun inflate(layoutInflater: LayoutInflater) =
DialogLessonChangeListBinding.inflate(layoutInflater)
private lateinit var b: DialogLessonChangeListBinding override fun getPositiveButtonText() = R.string.close
private lateinit var dialog: AlertDialog
init { run { override suspend fun onShow() {
if (activity.isFinishing)
return@run
job = Job()
onShowListener?.invoke(TAG)
b = DialogLessonChangeListBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(defaultDate.formattedString)
.setView(b.root)
.setPositiveButton(R.string.close) { dialog, _ -> dialog.dismiss() }
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.create()
loadLessonChanges()
}}
private fun loadLessonChanges() { launch {
val lessonChanges = withContext(Dispatchers.Default) { val lessonChanges = withContext(Dispatchers.Default) {
app.db.timetableDao().getChangesForDateNow(profileId, defaultDate) app.db.timetableDao().getChangesForDateNow(profileId, defaultDate)
} }
@ -62,7 +41,7 @@ class LessonChangesDialog(
it, it,
onShowListener = onShowListener, onShowListener = onShowListener,
onDismissListener = onDismissListener onDismissListener = onDismissListener
) ).show()
} }
).apply { ).apply {
items = lessonChanges items = lessonChanges
@ -70,7 +49,5 @@ class LessonChangesDialog(
b.lessonChangeView.adapter = adapter b.lessonChangeView.adapter = adapter
b.lessonChangeView.layoutManager = LinearLayoutManager(activity) b.lessonChangeView.layoutManager = LinearLayoutManager(activity)
}
dialog.show()
}}
} }

View File

@ -1,57 +1,42 @@
package pl.szczodrzynski.edziennik.ui.agenda.teacherabsence package pl.szczodrzynski.edziennik.ui.agenda.teacherabsence
import android.view.LayoutInflater
import android.view.View import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.DialogTeacherAbsenceListBinding import pl.szczodrzynski.edziennik.databinding.DialogTeacherAbsenceListBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class TeacherAbsenceDialog( class TeacherAbsenceDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val profileId: Int, private val profileId: Int,
val date: Date, private val date: Date,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) { ) : BindingDialog<DialogTeacherAbsenceListBinding>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "TeacherAbsenceDialog"
}
private val app by lazy { activity.application as App } override val TAG = "TeacherAbsenceDialog"
private lateinit var b: DialogTeacherAbsenceListBinding override fun getTitle(): String = date.formattedString
private lateinit var dialog: AlertDialog override fun getTitleRes(): Int? = null
override fun inflate(layoutInflater: LayoutInflater) =
DialogTeacherAbsenceListBinding.inflate(layoutInflater)
init { run { override fun getPositiveButtonText() = R.string.close
if (activity.isFinishing)
return@run
b = DialogTeacherAbsenceListBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(date.formattedString)
.setView(b.root)
.setPositiveButton(R.string.close) { dialog, _ -> dialog.dismiss() }
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.create()
override suspend fun onShow() {
b.teacherAbsenceView.setHasFixedSize(true) b.teacherAbsenceView.setHasFixedSize(true)
b.teacherAbsenceView.layoutManager = LinearLayoutManager(activity) b.teacherAbsenceView.layoutManager = LinearLayoutManager(activity)
app.db.teacherAbsenceDao().getAllByDate(profileId, date).observe(activity as LifecycleOwner, Observer { absenceList -> app.db.teacherAbsenceDao().getAllByDate(profileId, date).observe(
activity as LifecycleOwner
) { absenceList ->
val adapter = TeacherAbsenceAdapter(activity, date, absenceList) val adapter = TeacherAbsenceAdapter(activity, date, absenceList)
b.teacherAbsenceView.adapter = adapter b.teacherAbsenceView.adapter = adapter
b.teacherAbsenceView.visibility = View.VISIBLE b.teacherAbsenceView.visibility = View.VISIBLE
}) }
}
dialog.show()
}}
} }

View File

@ -4,54 +4,33 @@
package pl.szczodrzynski.edziennik.ui.attendance package pl.szczodrzynski.edziennik.ui.attendance
import androidx.appcompat.app.AlertDialog import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
import pl.szczodrzynski.edziennik.databinding.AttendanceDetailsDialogBinding import pl.szczodrzynski.edziennik.databinding.AttendanceDetailsDialogBinding
import pl.szczodrzynski.edziennik.ext.setTintColor import pl.szczodrzynski.edziennik.ext.setTintColor
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.utils.BetterLink import pl.szczodrzynski.edziennik.utils.BetterLink
import kotlin.coroutines.CoroutineContext
class AttendanceDetailsDialog( class AttendanceDetailsDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val attendance: AttendanceFull, private val attendance: AttendanceFull,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<AttendanceDetailsDialogBinding>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "AttendanceDetailsDialog"
}
private lateinit var app: App override val TAG = "AttendanceDetailsDialog"
private lateinit var b: AttendanceDetailsDialogBinding
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitleRes(): Int? = null
override val coroutineContext: CoroutineContext override fun inflate(layoutInflater: LayoutInflater) =
get() = job + Dispatchers.Main AttendanceDetailsDialogBinding.inflate(layoutInflater)
// local variables go here override fun getPositiveButtonText() = R.string.close
init { run { override suspend fun onShow() {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
b = AttendanceDetailsDialogBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setView(b.root)
.setPositiveButton(R.string.close, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
val manager = app.attendanceManager val manager = app.attendanceManager
val attendanceColor = manager.getAttendanceColor(attendance) val attendanceColor = manager.getAttendanceColor(attendance)
@ -69,5 +48,5 @@ class AttendanceDetailsDialog(
onActionSelected = dialog::dismiss onActionSelected = dialog::dismiss
) )
} }
}} }
} }

View File

@ -67,7 +67,7 @@ class AttendanceFragment : Fragment(), CoroutineScope {
.withIcon(CommunityMaterial.Icon.cmd_cog_outline) .withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener(View.OnClickListener { .withOnClickListener(View.OnClickListener {
activity.bottomSheet.close() activity.bottomSheet.close()
AttendanceConfigDialog(activity, true, null, null) AttendanceConfigDialog(activity, true, null, null).show()
}), }),
BottomSheetSeparatorItem(true), BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true) BottomSheetPrimaryItem(true)

View File

@ -96,7 +96,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
}}) }})
adapter.onAttendanceClick = { adapter.onAttendanceClick = {
AttendanceDetailsDialog(activity, it) AttendanceDetailsDialog(activity, it).show()
} }
}; return true} }; return true}

View File

@ -108,7 +108,7 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
}}) }})
adapter.onAttendanceClick = { adapter.onAttendanceClick = {
AttendanceDetailsDialog(activity, it) AttendanceDetailsDialog(activity, it).show()
} }
b.toggleGroup.check(when (periodSelection) { b.toggleGroup.check(when (periodSelection) {

View File

@ -5,58 +5,34 @@
package pl.szczodrzynski.edziennik.ui.captcha package pl.szczodrzynski.edziennik.ui.captcha
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.View import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.RecaptchaViewBinding import pl.szczodrzynski.edziennik.databinding.RecaptchaViewBinding
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import kotlin.coroutines.CoroutineContext import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
class LibrusCaptchaDialog( class LibrusCaptchaDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val onSuccess: (recaptchaCode: String) -> Unit, private val onSuccess: (recaptchaCode: String) -> Unit,
val onFailure: (() -> Unit)?, private val onFailure: (() -> Unit)?,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<RecaptchaViewBinding>(activity, onShowListener, onDismissListener) {
companion object { override val TAG = "LibrusCaptchaDialog"
private const val TAG = "LibrusCaptchaDialog"
}
private lateinit var app: App override fun getTitleRes(): Int? = null
private lateinit var b: RecaptchaViewBinding override fun inflate(layoutInflater: LayoutInflater) =
private lateinit var dialog: AlertDialog RecaptchaViewBinding.inflate(layoutInflater)
private val job = Job() override fun getNegativeButtonText() = R.string.cancel
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private lateinit var checkboxBackground: Drawable private lateinit var checkboxBackground: Drawable
private lateinit var checkboxForeground: Drawable private lateinit var checkboxForeground: Drawable
private var success = false private var success = false
init { run { override suspend fun onShow() {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
b = RecaptchaViewBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setView(b.root)
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
if (!success)
onFailure?.invoke()
onDismissListener?.invoke(TAG)
}
.show()
checkboxBackground = b.checkbox.background checkboxBackground = b.checkbox.background
checkboxForeground = b.checkbox.foreground checkboxForeground = b.checkbox.foreground
success = false success = false
@ -85,7 +61,12 @@ class LibrusCaptchaDialog(
b.checkbox.foreground = checkboxForeground b.checkbox.foreground = checkboxForeground
b.progress.visibility = View.GONE b.progress.visibility = View.GONE
} }
) ).show()
}
}
override fun onDismiss() {
if (!success)
onFailure?.invoke()
} }
}}
} }

View File

@ -6,129 +6,56 @@ package pl.szczodrzynski.edziennik.ui.captcha
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.os.Handler
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.* import kotlinx.coroutines.*
import okhttp3.* import okhttp3.*
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.RecaptchaDialogBinding import pl.szczodrzynski.edziennik.databinding.RecaptchaDialogBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import java.io.IOException import java.io.IOException
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class RecaptchaDialog( class RecaptchaDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val siteKey: String, private val siteKey: String,
val referer: String, private val referer: String,
val autoRetry: Boolean = true, private val autoRetry: Boolean = true,
val onSuccess: (recaptchaCode: String) -> Unit, private val onSuccess: (recaptchaCode: String) -> Unit,
val onFailure: (() -> Unit)? = null, private val onFailure: (() -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<RecaptchaDialogBinding>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "RecaptchaDialog"
}
private lateinit var app: App override val TAG = "RecaptchaDialog"
private val b by lazy { RecaptchaDialogBinding.inflate(LayoutInflater.from(activity)) }
private var dialog: AlertDialog? = null override fun getTitleRes(): Int? = null
override fun inflate(layoutInflater: LayoutInflater) =
RecaptchaDialogBinding.inflate(layoutInflater)
override fun getPositiveButtonText() = R.string.ok
private val captchaUrl = "https://www.google.com/recaptcha/api/fallback?k=$siteKey" private val captchaUrl = "https://www.google.com/recaptcha/api/fallback?k=$siteKey"
private var success = false private var success = false
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private var code = "" private var code = ""
private var payload = "" private var payload = ""
init { run { override suspend fun onBeforeShow(): Boolean {
if (activity.isFinishing) val (title, text, bitmap) = withContext(Dispatchers.Default) {
return@run val html = loadCaptchaHtml() ?: return@withContext null
app = activity.applicationContext as App return@withContext loadCaptchaData(html)
onShowListener?.invoke(TAG) } ?: run {
success = false
launch { initCaptcha() }
}}
private suspend fun initCaptcha() {
withContext(Dispatchers.Default) {
val request = Request.Builder()
.url(captchaUrl)
.addHeader("Referer", referer)
.addHeader("Accept-Language", "pl")
.build()
app.http.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
val html = response.body()?.string() ?: return
Log.d(TAG, html)
parseHtml(html)
}
override fun onFailure(call: Call, e: IOException) {
}
})
}
}
private fun parseHtml(html: String) {
launch {
"class=\"rc-imageselect-desc(?:-no-canonical)?\">(.+?) <strong>(.+?)</strong>".toRegex().find(html)?.let {
b.descTitle.text = it.groupValues[1]
b.descText.text = it.groupValues[2]
}
code = "name=\"c\" value=\"([A-z0-9-_]+)\"".toRegex().find(html)?.let { it.groupValues[1] } ?: return@launch
payload = "https://www.google.com/recaptcha/api2/payload?c=$code&k=$siteKey"
withContext(Dispatchers.Default) {
val request = Request.Builder()
.url(payload)
.addHeader("Referer", captchaUrl)
.addHeader("Accept-Language", "pl")
.build()
app.http.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
val bitmap: Bitmap? = BitmapFactory.decodeStream(response.body()?.byteStream())
Handler(activity.mainLooper).post {
if (bitmap == null) {
onFailure?.invoke() onFailure?.invoke()
Toast.makeText(activity, "Nie udało się załadować reCAPTCHA.", Toast.LENGTH_SHORT).show() return false
return@post
}
b.payload.setImageBitmap(bitmap)
showDialog()
}
} }
override fun onFailure(call: Call, e: IOException) { initViews(title, text, bitmap)
onFailure?.invoke() return true
}
})
}
}
} }
private fun showDialog() { override suspend fun onShow() {
if (dialog == null) {
dialog = MaterialAlertDialogBuilder(activity)
.setView(b.root)
.setPositiveButton("OK") { _, _ ->
validateAnswer()
}
.setOnDismissListener {
if (!success)
onFailure?.invoke()
onDismissListener?.invoke(TAG)
}
.create()
}
b.image0.isChecked = false b.image0.isChecked = false
b.image1.isChecked = false b.image1.isChecked = false
b.image2.isChecked = false b.image2.isChecked = false
@ -138,11 +65,88 @@ class RecaptchaDialog(
b.image6.isChecked = false b.image6.isChecked = false
b.image7.isChecked = false b.image7.isChecked = false
b.image8.isChecked = false b.image8.isChecked = false
dialog!!.show()
} }
private fun validateAnswer() { override fun onDismiss() {
launch { if (!success)
onFailure?.invoke()
}
private fun initViews(title: String, text: String, bitmap: Bitmap) {
b.descTitle.text = title
b.descText.text = text
b.payload.setImageBitmap(bitmap)
}
private suspend fun loadCaptchaHtml(): String? {
val request = Request.Builder()
.url(captchaUrl)
.addHeader("Referer", referer)
.addHeader("Accept-Language", "pl")
.build()
return suspendCoroutine { cont ->
app.http.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
val html = response.body()?.string()
cont.resume(html)
}
override fun onFailure(call: Call, e: IOException) {
cont.resume(null)
}
})
}
}
private suspend fun loadCaptchaData(html: String): Triple<String, String, Bitmap>? {
var title = ""
var text = ""
"class=\"rc-imageselect-desc(?:-no-canonical)?\">(.+?) <strong>(.+?)</strong>"
.toRegex()
.find(html)
?.let {
title = it.groupValues[1]
text = it.groupValues[2]
}
code = "name=\"c\" value=\"([A-z0-9-_]+)\""
.toRegex()
.find(html)
?.let { it.groupValues[1] }
?: return null
payload = "https://www.google.com/recaptcha/api2/payload?c=$code&k=$siteKey"
val request = Request.Builder()
.url(payload)
.addHeader("Referer", captchaUrl)
.addHeader("Accept-Language", "pl")
.build()
val bitmap = suspendCoroutine<Bitmap?> { cont ->
app.http.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
val bitmap: Bitmap? = BitmapFactory.decodeStream(response.body()?.byteStream())
if (bitmap == null) {
Toast.makeText(
activity,
"Nie udało się załadować reCAPTCHA.",
Toast.LENGTH_SHORT
).show()
}
cont.resume(bitmap)
}
override fun onFailure(call: Call, e: IOException) {
cont.resume(null)
}
})
} ?: return null
return Triple(title, text, bitmap)
}
override suspend fun onPositiveClick(): Boolean {
val list = mutableListOf( val list = mutableListOf(
"c=$code" "c=$code"
) )
@ -155,34 +159,62 @@ class RecaptchaDialog(
if (b.image6.isChecked) list += "response=6" if (b.image6.isChecked) list += "response=6"
if (b.image7.isChecked) list += "response=7" if (b.image7.isChecked) list += "response=7"
if (b.image8.isChecked) list += "response=8" if (b.image8.isChecked) list += "response=8"
val request = Request.Builder() val request = Request.Builder()
.url(captchaUrl) .url(captchaUrl)
.addHeader("Referer", captchaUrl) .addHeader("Referer", captchaUrl)
.addHeader("Accept-Language", "pl") .addHeader("Accept-Language", "pl")
.addHeader("Origin", "https://www.google.com") .addHeader("Origin", "https://www.google.com")
.addHeader("Content-Type", "application/x-www-form-urlencoded") .addHeader("Content-Type", "application/x-www-form-urlencoded")
.post(RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), list.joinToString("&"))) .post(RequestBody.create(
MediaType.parse("application/x-www-form-urlencoded"),
list.joinToString("&"),
))
.build() .build()
withContext(Dispatchers.Default) {
val (code, html) = withContext(Dispatchers.Default) {
return@withContext suspendCoroutine<Pair<String?, String?>> { cont ->
app.http.newCall(request).enqueue(object : Callback { app.http.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) { override fun onResponse(call: Call, response: Response) {
val html = response.body()?.string() ?: return val html = response.body()?.string() ?: run {
val match = "<textarea.+?>([A-z0-9-_]+)</textarea>".toRegex().find(html) cont.resume(null to null)
if (match == null) {
parseHtml(html)
return return
} }
Handler(activity.mainLooper).post { val match = "<textarea.+?>([A-z0-9-_]+)</textarea>".toRegex().find(html)
success = true if (match == null) {
onSuccess(match.groupValues[1]) cont.resume(null to html)
} else {
cont.resume(match.groupValues[1] to null)
} }
} }
override fun onFailure(call: Call, e: IOException) { override fun onFailure(call: Call, e: IOException) {
cont.resume(null to null)
} }
}) })
} }
} }
when {
code != null -> {
success = true
onSuccess(code)
return DISMISS
}
html != null -> {
val (title, text, bitmap) = withContext(Dispatchers.Default) {
return@withContext loadCaptchaData(html)
} ?: run {
onFailure?.invoke()
return DISMISS
}
initViews(title, text, bitmap)
return NO_DISMISS
}
else -> {
onFailure?.invoke()
return DISMISS
}
}
} }
} }

View File

@ -72,7 +72,7 @@ class LabPageFragment : LazyFragment(), CoroutineScope {
} }
b.clearProfile.onClick { b.clearProfile.onClick {
ProfileRemoveDialog(activity, App.profileId, "FAKE", noProfileRemoval = true) ProfileRemoveDialog(activity, App.profileId, "FAKE", noProfileRemoval = true).show()
} }
b.removeHomework.onClick { b.removeHomework.onClick {

View File

@ -4,41 +4,32 @@
package pl.szczodrzynski.edziennik.ui.dialogs package pl.szczodrzynski.edziennik.ui.dialogs
import androidx.appcompat.app.AlertDialog import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.DialogBellSyncBinding import pl.szczodrzynski.edziennik.databinding.DialogBellSyncBinding
import pl.szczodrzynski.edziennik.ext.resolveDrawable import pl.szczodrzynski.edziennik.ext.resolveDrawable
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer import pl.szczodrzynski.edziennik.ext.startCoroutineTimer
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.coroutines.CoroutineContext
class BellSyncDialog( class BellSyncDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
private val bellTime: Time, private val bellTime: Time,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<DialogBellSyncBinding>(activity, onShowListener, onDismissListener) {
companion object { override val TAG = "BellSyncDialog"
const val TAG = "BellSyncDialog"
}
private lateinit var job: Job override fun getTitleRes() = R.string.bell_sync_title
override val coroutineContext: CoroutineContext override fun inflate(layoutInflater: LayoutInflater) =
get() = job + Dispatchers.Main DialogBellSyncBinding.inflate(layoutInflater)
private lateinit var dialog: AlertDialog override fun getNeutralButtonText() = R.string.cancel
private lateinit var b: DialogBellSyncBinding
private val app by lazy { activity.application as App }
private var counterJob: Job? = null private var counterJob: Job? = null
@ -50,25 +41,7 @@ class BellSyncDialog(
return Pair(bellDiff, multiplier) return Pair(bellDiff, multiplier)
} }
init { apply { override suspend fun onShow() {
if (activity.isFinishing)
return@apply
job = Job()
b = DialogBellSyncBinding.inflate(activity.layoutInflater)
onShowListener?.invoke(TAG)
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.bell_sync_title)
.setView(b.root)
.setNeutralButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
.setOnDismissListener {
counterJob?.cancel()
onDismissListener?.invoke(TAG)
}
.show()
initView()
}}
private fun initView() {
b.bellSyncButton.setOnClickListener { b.bellSyncButton.setOnClickListener {
val (bellDiff, multiplier) = actualBellDiff val (bellDiff, multiplier) = actualBellDiff
val bellDiffText = (if (multiplier == -1) '-' else '+') + bellDiff.stringHMS val bellDiffText = (if (multiplier == -1) '-' else '+') + bellDiff.stringHMS
@ -90,12 +63,15 @@ class BellSyncDialog(
b.bellSyncButton.setImageDrawable(R.drawable.ic_bell_wtf.resolveDrawable(app)) // wtf b.bellSyncButton.setImageDrawable(R.drawable.ic_bell_wtf.resolveDrawable(app)) // wtf
} }
launch {
counterJob = startCoroutineTimer(repeatMillis = 500) { counterJob = startCoroutineTimer(repeatMillis = 500) {
val (bellDiff, multiplier) = actualBellDiff val (bellDiff, multiplier) = actualBellDiff
val bellDiffText = (if (multiplier == -1) '-' else '+') + bellDiff.stringHMS val bellDiffText = (if (multiplier == -1) '-' else '+') + bellDiff.stringHMS
b.bellSyncHowto.text = app.getString(R.string.bell_sync_howto, bellTime.stringHM, bellDiffText) b.bellSyncHowto.text =
app.getString(R.string.bell_sync_howto, bellTime.stringHM, bellDiffText)
} }
} }
override fun onDismiss() {
counterJob?.cancel()
} }
} }

View File

@ -4,7 +4,7 @@
package pl.szczodrzynski.edziennik.ui.dialogs package pl.szczodrzynski.edziennik.ui.dialogs
import androidx.appcompat.app.AlertDialog import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.* import kotlinx.coroutines.*
@ -13,66 +13,62 @@ import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Lesson import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.databinding.DialogBellSyncTimeChooseBinding import pl.szczodrzynski.edziennik.databinding.DialogBellSyncTimeChooseBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.utils.TextInputDropDown import pl.szczodrzynski.edziennik.utils.TextInputDropDown
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.coroutines.CoroutineContext
class BellSyncTimeChooseDialog( class BellSyncTimeChooseDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<DialogBellSyncTimeChooseBinding>(activity, onShowListener, onDismissListener) {
companion object { companion object {
const val TAG = "BellSyncTimeChooseDialog"
private const val MAX_DIFF_MINUTES = 10 private const val MAX_DIFF_MINUTES = 10
} }
private lateinit var job: Job override val TAG = "BellSyncTimeChooseDialog"
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private lateinit var dialog: AlertDialog override fun getTitleRes() = R.string.bell_sync_title
private lateinit var b: DialogBellSyncTimeChooseBinding override fun inflate(layoutInflater: LayoutInflater) =
DialogBellSyncTimeChooseBinding.inflate(layoutInflater)
private val app by lazy { activity.application as App } override fun getPositiveButtonText() = R.string.ok
override fun getNeutralButtonText() = R.string.reset
override fun getNegativeButtonText() = R.string.cancel
override suspend fun onShow() = Unit
private val today = Date.getToday() private val today = Date.getToday()
private val selectedTime: Time? private val selectedTime: Time?
get() = b.timeDropdown.selected?.tag as Time? get() = b.timeDropdown.selected?.tag as Time?
init { apply { override suspend fun onPositiveClick(): Boolean {
if (activity.isFinishing)
return@apply
job = Job()
b = DialogBellSyncTimeChooseBinding.inflate(activity.layoutInflater)
onShowListener?.invoke(TAG)
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.bell_sync_title)
.setView(b.root)
.setPositiveButton(R.string.ok) { dialog, _ ->
dialog.dismiss()
selectedTime?.let { selectedTime?.let {
BellSyncDialog(activity, it) BellSyncDialog(activity, it).show()
}
}
.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.create()
.apply {
setButton(AlertDialog.BUTTON_NEUTRAL, app.getString(R.string.reset)) { _, _ ->
showResetDialog()
} }
return DISMISS
} }
initView() override suspend fun onNeutralClick(): Boolean {
}} MaterialAlertDialogBuilder(activity)
.setTitle(R.string.bell_sync_title)
.setMessage(R.string.bell_sync_reset_confirm)
.setPositiveButton(R.string.yes) { dialog, _ ->
app.config.timetable.bellSyncDiff = null
app.config.timetable.bellSyncMultiplier = 0
private fun initView() { dialog.dismiss()
reload()
if (activity is MainActivity)
activity.reloadTarget()
}
.setNegativeButton(R.string.no, null)
.show()
return NO_DISMISS
}
override suspend fun onBeforeShow(): Boolean {
b.bellSyncHowto.text = app.getString(R.string.bell_sync_choose_howto) b.bellSyncHowto.text = app.getString(R.string.bell_sync_choose_howto)
app.config.timetable.bellSyncDiff?.let { bellDiff -> app.config.timetable.bellSyncDiff?.let { bellDiff ->
@ -84,7 +80,7 @@ class BellSyncTimeChooseDialog(
) )
} }
loadTimeList() return loadTimeList()
} }
private fun checkForLessons(timeList: List<Time>): Boolean { private fun checkForLessons(timeList: List<Time>): Boolean {
@ -98,7 +94,7 @@ class BellSyncTimeChooseDialog(
} else false } else false
} }
private fun loadTimeList() { launch { private suspend fun loadTimeList(): Boolean {
val timeItems = withContext(Dispatchers.Default) { val timeItems = withContext(Dispatchers.Default) {
val lessons = app.db.timetableDao().getAllForDateNow(App.profileId, today) val lessons = app.db.timetableDao().getAllForDateNow(App.profileId, today)
val items = mutableListOf<TextInputDropDown.Item>() val items = mutableListOf<TextInputDropDown.Item>()
@ -106,17 +102,21 @@ class BellSyncTimeChooseDialog(
lessons.forEach { lessons.forEach {
if (it.type != Lesson.TYPE_NO_LESSONS && if (it.type != Lesson.TYPE_NO_LESSONS &&
it.type != Lesson.TYPE_CANCELLED && it.type != Lesson.TYPE_CANCELLED &&
it.type != Lesson.TYPE_SHIFTED_SOURCE) { it.type != Lesson.TYPE_SHIFTED_SOURCE
) {
items += TextInputDropDown.Item( items += TextInputDropDown.Item(
it.displayStartTime?.value?.toLong() ?: return@forEach, it.displayStartTime?.value?.toLong() ?: return@forEach,
app.getString(R.string.bell_sync_lesson_item, it.displaySubjectName, it.displayStartTime?.stringHM), app.getString(R.string.bell_sync_lesson_item,
it.displaySubjectName,
it.displayStartTime?.stringHM),
tag = it.displayStartTime tag = it.displayStartTime
) )
items += TextInputDropDown.Item( items += TextInputDropDown.Item(
it.displayEndTime?.value?.toLong() ?: return@forEach, it.displayEndTime?.value?.toLong() ?: return@forEach,
app.getString(R.string.bell_sync_break_item, it.displayEndTime?.stringHM), app.getString(R.string.bell_sync_break_item,
it.displayEndTime?.stringHM),
tag = it.displayEndTime tag = it.displayEndTime
) )
} }
@ -130,8 +130,9 @@ class BellSyncTimeChooseDialog(
MaterialAlertDialogBuilder(activity) MaterialAlertDialogBuilder(activity)
.setTitle(R.string.bell_sync_title) .setTitle(R.string.bell_sync_title)
.setMessage(R.string.bell_sync_cannot_now) .setMessage(R.string.bell_sync_cannot_now)
.setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() } .setPositiveButton(R.string.ok, null)
.show() .show()
return false
} else { } else {
b.timeDropdown.clear() b.timeDropdown.clear()
b.timeDropdown.append(timeItems) b.timeDropdown.append(timeItems)
@ -144,24 +145,7 @@ class BellSyncTimeChooseDialog(
b.timeDropdown.isEnabled = true b.timeDropdown.isEnabled = true
// TODO Fix popup cutting off // TODO Fix popup cutting off
dialog.show()
} }
}} return true
private fun showResetDialog() {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.bell_sync_title)
.setMessage(R.string.bell_sync_reset_confirm)
.setPositiveButton(R.string.yes) { confirmDialog, _ ->
app.config.timetable.bellSyncDiff = null
app.config.timetable.bellSyncMultiplier = 0
confirmDialog.dismiss()
initView()
if (activity is MainActivity) activity.reloadTarget()
}
.setNegativeButton(R.string.no) { dialog, _ -> dialog.dismiss() }
.show()
} }
} }

View File

@ -4,43 +4,29 @@
package pl.szczodrzynski.edziennik.ui.dialogs package pl.szczodrzynski.edziennik.ui.dialogs
import android.os.Build
import android.widget.ScrollView import android.widget.ScrollView
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ext.dp import pl.szczodrzynski.edziennik.ext.dp
import pl.szczodrzynski.edziennik.ui.dialogs.base.ViewDialog
import pl.szczodrzynski.edziennik.utils.BetterLinkMovementMethod import pl.szczodrzynski.edziennik.utils.BetterLinkMovementMethod
import pl.szczodrzynski.edziennik.utils.html.BetterHtml import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import kotlin.coroutines.CoroutineContext
class ChangelogDialog( class ChangelogDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : ViewDialog<ScrollView>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "ChangelogDialog"
}
private lateinit var app: App override val TAG = "ChangelogDialog"
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitleRes() = R.string.whats_new
override val coroutineContext: CoroutineContext override fun getPositiveButtonText() = R.string.close
get() = job + Dispatchers.Main
init { run { override suspend fun onShow() = Unit
if (activity.isFinishing)
return@run override fun getRootView(): ScrollView {
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
val textView = TextView(activity) val textView = TextView(activity)
textView.setPadding(24.dp, 24.dp, 24.dp, 0) textView.setPadding(24.dp, 24.dp, 24.dp, 0)
@ -49,29 +35,22 @@ class ChangelogDialog(
} }
val commitsUrlPrefix = "https://github.com/szkolny-eu/szkolny-android/commits?author=" val commitsUrlPrefix = "https://github.com/szkolny-eu/szkolny-android/commits?author="
text = text.replace("""\[(.+?)]\(@([A-z0-9-]+)\)""".toRegex(), "<a href=\"$commitsUrlPrefix$2\">$1</a>") text = text.replace(
text = text.replace("""\s@([A-z0-9-]+)""".toRegex(), " <a href=\"$commitsUrlPrefix$1\">@$1</a>") regex = """\[(.+?)]\(@([A-z0-9-]+)\)""".toRegex(),
replacement = "<a href=\"$commitsUrlPrefix$2\">$1</a>"
)
text = text.replace(
regex = """\s@([A-z0-9-]+)""".toRegex(),
replacement = " <a href=\"$commitsUrlPrefix$1\">@$1</a>"
)
val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) textView.text = BetterHtml.fromHtml(activity, text)
text
else
text.replace("<li>", "<br><li> - ")
textView.text = BetterHtml.fromHtml(activity, html)
textView.movementMethod = BetterLinkMovementMethod.getInstance() textView.movementMethod = BetterLinkMovementMethod.getInstance()
val scrollView = ScrollView(activity) val scrollView = ScrollView(activity)
scrollView.addView(textView) scrollView.addView(textView)
dialog = MaterialAlertDialogBuilder(activity) return scrollView
.setTitle(R.string.whats_new)
.setView(scrollView)
.setPositiveButton(R.string.close) { dialog, _ ->
dialog.dismiss()
} }
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
} }

View File

@ -5,51 +5,41 @@
package pl.szczodrzynski.edziennik.ui.dialogs package pl.szczodrzynski.edziennik.ui.dialogs
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.* import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
import kotlin.coroutines.CoroutineContext
class ProfileRemoveDialog( class ProfileRemoveDialog(
val activity: MainActivity, activity: AppCompatActivity,
val profileId: Int, val profileId: Int,
val profileName: String, val profileName: String,
val noProfileRemoval: Boolean = false, val noProfileRemoval: Boolean = false,
val onRemove: (() -> Unit)? = null val onRemove: (() -> Unit)? = null,
) : CoroutineScope { onShowListener: ((tag: String) -> Unit)? = null,
companion object { onDismissListener: ((tag: String) -> Unit)? = null,
private const val TAG = "ProfileRemoveDialog" ) : BaseDialog(activity, onShowListener, onDismissListener) {
}
private lateinit var job: Job override val TAG = "ProfileRemoveDialog"
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private val app by lazy { activity.application as App } override fun getTitleRes() = R.string.profile_menu_remove_confirm
private lateinit var b: DialogLessonDetailsBinding override fun getMessageFormat() =
private lateinit var dialog: AlertDialog R.string.profile_menu_remove_confirm_text_format to listOf(
profileName,
profileName
)
init { run { override fun isCancelable() = false
job = Job() override fun getPositiveButtonText() = R.string.remove
override fun getNeutralButtonText() = R.string.cancel
dialog = MaterialAlertDialogBuilder(activity) override suspend fun onShow() = Unit
.setTitle(R.string.profile_menu_remove_confirm)
.setMessage(activity.getString(R.string.profile_menu_remove_confirm_text_format, profileName, profileName))
.setPositiveButton(R.string.remove) { _, _ ->
removeProfile()
}
.setNeutralButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
.setCancelable(false)
.show()
}}
private fun removeProfile() { launch { override suspend fun onPositiveClick(): Boolean {
val deferred = async(Dispatchers.Default) { withContext(Dispatchers.Default) {
val profileObject = app.db.profileDao().getByIdNow(profileId) ?: return@async val profileObject = app.db.profileDao().getByIdNow(profileId) ?: return@withContext
app.db.announcementDao().clear(profileId) app.db.announcementDao().clear(profileId)
app.db.attendanceDao().clear(profileId) app.db.attendanceDao().clear(profileId)
app.db.attendanceTypeDao().clear(profileId) app.db.attendanceTypeDao().clear(profileId)
@ -77,12 +67,13 @@ class ProfileRemoveDialog(
app.db.metadataDao().deleteAll(profileId) app.db.metadataDao().deleteAll(profileId)
if (noProfileRemoval) if (noProfileRemoval)
return@async return@withContext
app.db.configDao().clear(profileId) app.db.configDao().clear(profileId)
val loginStoreId = profileObject.loginStoreId val loginStoreId = profileObject.loginStoreId
val profilesUsingLoginStore = app.db.profileDao().getIdsByLoginStoreIdNow(loginStoreId) val profilesUsingLoginStore =
app.db.profileDao().getIdsByLoginStoreIdNow(loginStoreId)
if (profilesUsingLoginStore.size == 1) { if (profilesUsingLoginStore.size == 1) {
app.db.loginStoreDao().remove(loginStoreId) app.db.loginStoreDao().remove(loginStoreId)
} }
@ -92,10 +83,13 @@ class ProfileRemoveDialog(
app.profileLoadLast { } app.profileLoadLast { }
} }
} }
deferred.await()
dialog.dismiss() if (activity is MainActivity)
activity.reloadTarget() activity.reloadTarget()
Toast.makeText(activity, R.string.dialog_profile_remove_success, Toast.LENGTH_LONG).show() Toast.makeText(activity, R.string.dialog_profile_remove_success, Toast.LENGTH_LONG).show()
onRemove?.invoke() onRemove?.invoke()
}}
return DISMISS
}
} }

View File

@ -4,59 +4,36 @@
package pl.szczodrzynski.edziennik.ui.dialogs package pl.szczodrzynski.edziennik.ui.dialogs
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import me.dm7.barcodescanner.zxing.ZXingScannerView import me.dm7.barcodescanner.zxing.ZXingScannerView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ext.dp import pl.szczodrzynski.edziennik.ext.dp
import kotlin.coroutines.CoroutineContext import pl.szczodrzynski.edziennik.ui.dialogs.base.ViewDialog
class QrScannerDialog( class QrScannerDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val onCodeScanned: (text: String) -> Unit, val onCodeScanned: (text: String) -> Unit,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : ViewDialog<ZXingScannerView>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "QrScannerDialog"
}
private lateinit var app: App override val TAG = "QrScannerDialog"
private lateinit var scannerView: ZXingScannerView
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitleRes() = R.string.qr_scanner_dialog_title
override val coroutineContext: CoroutineContext override fun getPositiveButtonText() = R.string.close
get() = job + Dispatchers.Main
init { run { override fun getRootView(): ZXingScannerView {
if (activity.isFinishing) val scannerView = ZXingScannerView(activity)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
scannerView = ZXingScannerView(activity)
scannerView.setPadding(0, 16.dp, 2.dp, 0) scannerView.setPadding(0, 16.dp, 2.dp, 0)
dialog = MaterialAlertDialogBuilder(activity) return scannerView
.setTitle(R.string.qr_scanner_dialog_title)
.setView(scannerView)
.setPositiveButton(R.string.close) { dialog, _ ->
dialog.dismiss()
} }
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
scannerView.setResultHandler { override suspend fun onShow() {
scannerView.stopCamera() root.setResultHandler {
root.stopCamera()
dialog.dismiss() dialog.dismiss()
onCodeScanned(it.text) onCodeScanned(it.text)
} }
scannerView.startCamera() root.startCamera()
}} }
} }

View File

@ -7,59 +7,45 @@ package pl.szczodrzynski.edziennik.ui.dialogs
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.text.Editable import android.text.Editable
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import androidx.appcompat.app.AlertDialog import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.StyledTextDialogBinding import pl.szczodrzynski.edziennik.databinding.StyledTextDialogBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.utils.DefaultTextStyles import pl.szczodrzynski.edziennik.utils.DefaultTextStyles
import pl.szczodrzynski.edziennik.utils.Themes import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.HtmlMode.SIMPLE import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.HtmlMode.SIMPLE
import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.StylingConfig import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.StylingConfig
class StyledTextDialog( class StyledTextDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val initialText: Editable?, val initialText: Editable?,
val onSuccess: (text: Editable) -> Unit, val onSuccess: (text: Editable) -> Unit,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) { ) : BindingDialog<StyledTextDialogBinding>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "StyledTextDialog" override val TAG = "StyledTextDialog"
}
private lateinit var app: App
private lateinit var b: StyledTextDialogBinding
private lateinit var dialog: AlertDialog
private lateinit var config: StylingConfig private lateinit var config: StylingConfig
private val manager private val manager
get() = app.textStylingManager get() = app.textStylingManager
init { override fun getTitleRes() = R.string.styled_text_dialog_title
show() override fun inflate(layoutInflater: LayoutInflater) =
} StyledTextDialogBinding.inflate(layoutInflater)
fun show() { override fun getPositiveButtonText() = R.string.save
if (activity.isFinishing) override fun getNeutralButtonText() = R.string.cancel
return
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
b = StyledTextDialogBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity) override suspend fun onPositiveClick(): Boolean {
.setTitle(R.string.styled_text_dialog_title)
.setView(b.root)
.setPositiveButton(R.string.save) { _, _ ->
onSuccess(b.editText.text ?: SpannableStringBuilder("")) onSuccess(b.editText.text ?: SpannableStringBuilder(""))
return DISMISS
} }
.setNeutralButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
override suspend fun onShow() {
config = StylingConfig( config = StylingConfig(
editText = b.editText, editText = b.editText,
fontStyleGroup = b.fontStyle.styles, fontStyleGroup = b.fontStyle.styles,

View File

@ -0,0 +1,199 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-10-18.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.base
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AlertDialog.*
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ext.setMessage
import kotlin.coroutines.CoroutineContext
abstract class BaseDialog(
protected val activity: AppCompatActivity,
protected val onShowListener: ((tag: String) -> Unit)? = null,
protected val onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope {
companion object {
const val DISMISS = true
const val NO_DISMISS = false
}
@Suppress("PropertyName")
abstract val TAG: String
protected lateinit var app: App
protected lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private var items = emptyList<Any>()
private var itemSelected: Any? = null
private var itemStates = BooleanArray(0)
protected open fun getTitle(): CharSequence? = null
protected abstract fun getTitleRes(): Int?
protected open fun getMessage(): CharSequence? = null
protected open fun getMessageRes(): Int? = null
protected open fun getMessageFormat(): Pair<Int, List<CharSequence>>? = null
protected open fun getView(): View? = null
open fun isCancelable() = true
open fun getPositiveButtonText(): Int? = null
open fun getNeutralButtonText(): Int? = null
open fun getNegativeButtonText(): Int? = null
protected open fun getSingleChoiceItems(): Map<CharSequence, Any>? = null
protected open fun getMultiChoiceItems(): Map<CharSequence, Any>? = null
protected open fun getDefaultSelectedItem(): Any? = null
protected open fun getDefaultSelectedItems(): Set<Any> = emptySet()
open suspend fun onPositiveClick() = true
open suspend fun onNeutralClick() = true
open suspend fun onNegativeClick() = true
open suspend fun onSingleSelectionChanged(item: Any?) = Unit
open suspend fun onMultiSelectionChanged(items: Set<Any>) = Unit
protected open suspend fun onBeforeShow() = true
protected abstract suspend fun onShow()
protected open fun onDismiss() = Unit
fun show() {
if (activity.isFinishing)
return
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
dialog = MaterialAlertDialogBuilder(activity)
.also(this::configure)
.setCancelable(isCancelable())
.setOnDismissListener {
onDismiss()
onDismissListener?.invoke(TAG)
}
.create()
reload()
}
protected fun reload() {
launch {
if (activity.isFinishing)
return@launch
if (!onBeforeShow()) {
dialog.dismiss()
return@launch
}
dialog.show()
setButtons()
onShow()
}
}
private fun configure(md: MaterialAlertDialogBuilder) {
getTitle()?.let {
md.setTitle(it)
}
getTitleRes()?.let {
md.setTitle(it)
}
getPositiveButtonText()?.let {
md.setPositiveButton(it, null)
}
getNeutralButtonText()?.let {
md.setNeutralButton(it, null)
}
getNegativeButtonText()?.let {
md.setNegativeButton(it, null)
}
getMessage()?.let {
md.setMessage(it)
}
getMessageRes()?.let {
md.setMessage(it)
}
getMessageFormat()?.let { (stringId, formatArgs) ->
md.setMessage(stringId, *formatArgs.toTypedArray())
}
getView()?.let {
md.setView(it)
}
getSingleChoiceItems()?.let { map ->
val default = getDefaultSelectedItem()
val defaultIndex = map.values.indexOf(default)
md.setSingleChoiceItems(map.keys.toTypedArray(), defaultIndex) { _, which ->
launch {
itemSelected = items[which]
onSingleSelectionChanged(getSingleSelection())
}
}
items = map.values.toList()
itemSelected = default
md.setMessage(null)
}
getMultiChoiceItems()?.let { map ->
val default = getDefaultSelectedItems()
val defaultStates = map.values.map {
it in default
}.toBooleanArray()
md.setMultiChoiceItems(map.keys.toTypedArray(),
defaultStates) { _, position, isChecked ->
launch {
itemStates[position] = isChecked
onMultiSelectionChanged(getMultiSelection())
}
}
items = map.values.toList()
itemStates = defaultStates
md.setMessage(null)
}
}
private fun setButtons() {
dialog.getButton(BUTTON_POSITIVE)?.onClick {
launch {
if (onPositiveClick())
dialog.dismiss()
}
}
dialog.getButton(BUTTON_NEUTRAL)?.onClick {
launch {
if (onNeutralClick())
dialog.dismiss()
}
}
dialog.getButton(BUTTON_NEGATIVE)?.onClick {
launch {
if (onNegativeClick())
dialog.dismiss()
}
}
}
protected fun getSingleSelection() = itemSelected
protected fun getMultiSelection(): Set<Any> {
return itemStates.mapIndexed { position, isChecked ->
if (isChecked)
items[position]
else
null
}.filterNotNull().toSet()
}
protected fun dismiss() {
dialog.dismiss()
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-10-18.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.base
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
abstract class BindingDialog<B : ViewBinding>(
activity: AppCompatActivity,
onShowListener: ((tag: String) -> Unit)? = null,
onDismissListener: ((tag: String) -> Unit)? = null,
) : ViewDialog<View>(activity, onShowListener, onDismissListener) {
protected lateinit var b: B
protected abstract fun inflate(layoutInflater: LayoutInflater): B
final override fun getRootView(): View {
b = inflate(activity.layoutInflater)
return b.root
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-10-18.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.base
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
abstract class ConfigDialog<B : ViewBinding>(
activity: AppCompatActivity,
private val reloadOnDismiss: Boolean = true,
onShowListener: ((tag: String) -> Unit)? = null,
onDismissListener: ((tag: String) -> Unit)? = null,
) : BindingDialog<B>(activity, onShowListener, onDismissListener) {
final override fun getPositiveButtonText() = R.string.ok
final override suspend fun onShow() = Unit
protected val config by lazy { app.config.grades }
protected open suspend fun loadConfig() = Unit
protected open suspend fun saveConfig() = Unit
protected open fun initView() = Unit
final override suspend fun onBeforeShow(): Boolean {
initView()
loadConfig()
return true
}
final override fun onDismiss() {
launch {
saveConfig()
}
if (reloadOnDismiss && activity is MainActivity)
activity.reloadTarget()
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-10-18.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.base
import android.view.View
import androidx.appcompat.app.AppCompatActivity
abstract class ViewDialog<V : View>(
activity: AppCompatActivity,
onShowListener: ((tag: String) -> Unit)? = null,
onDismissListener: ((tag: String) -> Unit)? = null
) : BaseDialog(activity, onShowListener, onDismissListener) {
protected lateinit var root: V
protected abstract fun getRootView(): V
final override fun getMessage() = null
final override fun getMessageFormat() = null
final override fun getView(): View {
root = getRootView()
return root
}
}

View File

@ -4,7 +4,7 @@
package pl.szczodrzynski.edziennik.ui.dialogs.settings package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AlertDialog import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
@ -12,57 +12,48 @@ import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.REGISTRATION_ENABLED import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.REGISTRATION_ENABLED
import pl.szczodrzynski.edziennik.databinding.DialogConfigAgendaBinding import pl.szczodrzynski.edziennik.databinding.DialogConfigAgendaBinding
import pl.szczodrzynski.edziennik.ext.onChange import pl.szczodrzynski.edziennik.ext.onChange
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog
import java.util.* import java.util.*
class AgendaConfigDialog( class AgendaConfigDialog(
private val activity: AppCompatActivity, activity: AppCompatActivity,
private val reloadOnDismiss: Boolean = true, reloadOnDismiss: Boolean = true,
private val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
private val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : ConfigDialog<DialogConfigAgendaBinding>(
activity,
reloadOnDismiss,
onShowListener,
onDismissListener,
) { ) {
companion object {
const val TAG = "AgendaConfigDialog"
}
private val app by lazy { activity.application as App } override val TAG = "AgendaConfigDialog"
private val config by lazy { app.config.ui }
override fun getTitleRes() = R.string.menu_agenda_config
override fun inflate(layoutInflater: LayoutInflater) =
DialogConfigAgendaBinding.inflate(layoutInflater)
private val profileConfig by lazy { app.config.forProfile().ui } private val profileConfig by lazy { app.config.forProfile().ui }
private lateinit var b: DialogConfigAgendaBinding override suspend fun loadConfig() {
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.config = profileConfig
b.isAgendaMode = profileConfig.agendaViewType == Profile.AGENDA_DEFAULT b.isAgendaMode = profileConfig.agendaViewType == Profile.AGENDA_DEFAULT
b.eventSharingEnabled.isChecked = app.profile.enableSharedEvents b.eventSharingEnabled.isChecked =
&& app.profile.registration == REGISTRATION_ENABLED app.profile.enableSharedEvents && app.profile.registration == REGISTRATION_ENABLED
b.eventSharingEnabled.onChange { _, isChecked -> b.eventSharingEnabled.onChange { _, isChecked ->
if (isChecked && app.profile.registration != REGISTRATION_ENABLED) { if (isChecked && app.profile.registration != REGISTRATION_ENABLED) {
b.eventSharingEnabled.isChecked = false b.eventSharingEnabled.isChecked = false
val dialog = RegistrationConfigDialog(activity, app.profile, onChangeListener = { enabled -> val dialog = RegistrationConfigDialog(
activity,
app.profile,
onChangeListener = { enabled ->
b.eventSharingEnabled.isChecked = enabled b.eventSharingEnabled.isChecked = enabled
setEventSharingEnabled(enabled) setEventSharingEnabled(enabled)
}, onShowListener, onDismissListener) },
onShowListener,
onDismissListener,
)
dialog.showEnableDialog() dialog.showEnableDialog()
return@onChange return@onChange
} }
@ -86,8 +77,4 @@ class AgendaConfigDialog(
.setPositiveButton(R.string.ok, null) .setPositiveButton(R.string.ok, null)
.show() .show()
} }
private fun saveConfig() {
}
} }

View File

@ -4,68 +4,40 @@
package pl.szczodrzynski.edziennik.ui.dialogs.settings package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import pl.szczodrzynski.edziennik.R
import kotlinx.coroutines.CoroutineScope import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import kotlin.coroutines.CoroutineContext
class AppLanguageDialog( class AppLanguageDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BaseDialog(activity, onShowListener, onDismissListener) {
companion object { override val TAG = "AppLanguageDialog"
private const val TAG = "AppLanguageDialog"
}
private lateinit var app: App override fun getTitleRes() = R.string.app_language_dialog_title
private lateinit var dialog: AlertDialog override fun getMessage() = activity.getString(R.string.app_language_dialog_text)
override fun getPositiveButtonText() = R.string.ok
override fun getNegativeButtonText() = R.string.cancel
private val job = Job() override fun getSingleChoiceItems(): Map<CharSequence, Any> = mapOf(
override val coroutineContext: CoroutineContext R.string.language_system to "",
get() = job + Dispatchers.Main R.string.language_polish to "pl",
R.string.language_english to "en",
R.string.language_german to "de",
).mapKeys { (resId, _) -> activity.getString(resId) }
// local variables go here override fun getDefaultSelectedItem() = app.config.ui.language
init { run { override suspend fun onShow() = Unit
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
val languages = mapOf( override suspend fun onPositiveClick(): Boolean {
null to R.string.language_system, val language = getSingleSelection() as? String ?: return DISMISS
"pl" to R.string.language_polish, if (language.isEmpty())
"en" to R.string.language_english, app.config.ui.language = null
"de" to R.string.language_german else
) app.config.ui.language = language
val languageIds = languages.map { it.key }
val languageNames = languages.map {
activity.getString(it.value)
}
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.app_language_dialog_title)
//.setMessage(R.string.settings_about_language_dialog_text)
.setSingleChoiceItems(
languageNames.toTypedArray(),
languageIds.indexOf(app.config.ui.language),
null
)
.setPositiveButton(R.string.ok) { _, _ ->
val which = dialog.listView.checkedItemPosition
app.config.ui.language = languageIds[which]
activity.recreate() activity.recreate()
return NO_DISMISS
} }
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
} }

View File

@ -4,64 +4,40 @@
package pl.szczodrzynski.edziennik.ui.dialogs.settings package pl.szczodrzynski.edziennik.ui.dialogs.settings
import android.annotation.SuppressLint import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.AttendanceConfigDialogBinding import pl.szczodrzynski.edziennik.databinding.AttendanceConfigDialogBinding
import pl.szczodrzynski.edziennik.ext.onChange import pl.szczodrzynski.edziennik.ext.onChange
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog
class AttendanceConfigDialog( class AttendanceConfigDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
private val reloadOnDismiss: Boolean = true, reloadOnDismiss: Boolean = true,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : ConfigDialog<AttendanceConfigDialogBinding>(
activity,
reloadOnDismiss,
onShowListener,
onDismissListener,
) { ) {
companion object {
const val TAG = "GradesConfigDialog"
}
private val app by lazy { activity.application as App } override val TAG = "AttendanceConfigDialog"
override fun getTitleRes() = R.string.menu_attendance_config
override fun inflate(layoutInflater: LayoutInflater) =
AttendanceConfigDialogBinding.inflate(layoutInflater)
private val profileConfig by lazy { app.config.getFor(app.profileId).attendance } private val profileConfig by lazy { app.config.getFor(app.profileId).attendance }
private lateinit var b: AttendanceConfigDialogBinding override suspend fun loadConfig() {
private lateinit var dialog: AlertDialog
init { run {
if (activity.isFinishing)
return@run
b = AttendanceConfigDialogBinding.inflate(activity.layoutInflater)
onShowListener?.invoke(TAG)
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.menu_attendance_config)
.setView(b.root)
.setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() }
.setOnDismissListener {
saveConfig()
onDismissListener?.invoke(TAG)
if (reloadOnDismiss) (activity as? MainActivity)?.reloadTarget()
}
.create()
initView()
loadConfig()
dialog.show()
}}
@SuppressLint("SetTextI18n")
private fun loadConfig() {
b.useSymbols.isChecked = profileConfig.useSymbols b.useSymbols.isChecked = profileConfig.useSymbols
b.groupConsecutiveDays.isChecked = profileConfig.groupConsecutiveDays b.groupConsecutiveDays.isChecked = profileConfig.groupConsecutiveDays
b.showPresenceInMonth.isChecked = profileConfig.showPresenceInMonth b.showPresenceInMonth.isChecked = profileConfig.showPresenceInMonth
} }
private fun saveConfig() { override fun initView() {
// nothing to do here, yet
}
private fun initView() {
b.useSymbols.onChange { _, isChecked -> b.useSymbols.onChange { _, isChecked ->
profileConfig.useSymbols = isChecked profileConfig.useSymbols = isChecked
} }

View File

@ -4,40 +4,31 @@
package pl.szczodrzynski.edziennik.ui.dialogs.settings package pl.szczodrzynski.edziennik.ui.dialogs.settings
import android.widget.TextView import android.view.LayoutInflater
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import com.google.android.material.dialog.MaterialAlertDialogBuilder import pl.szczodrzynski.edziennik.R
import com.google.android.material.textfield.TextInputEditText import pl.szczodrzynski.edziennik.databinding.DialogEditTextBinding
import com.google.android.material.textfield.TextInputLayout import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.coroutines.CoroutineContext
class BellSyncConfigDialog( class BellSyncConfigDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val onChangeListener: (() -> Unit)? = null, private val onChangeListener: (() -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<DialogEditTextBinding>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "BellSyncConfigDialog"
}
private lateinit var app: App override val TAG = "BellSyncConfigDialog"
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitleRes() = R.string.bell_sync_title
override val coroutineContext: CoroutineContext override fun inflate(layoutInflater: LayoutInflater) =
get() = job + Dispatchers.Main DialogEditTextBinding.inflate(layoutInflater)
// local variables go here override fun getPositiveButtonText() = R.string.ok
override fun getNeutralButtonText() = R.string.reset
override fun getNegativeButtonText() = R.string.cancel
private fun parse(input: String): Pair<Time, Int>? { private fun parse(input: String): Pair<Time, Int>? {
if (input.length < 8) { if (input.length < 8) {
@ -56,51 +47,28 @@ class BellSyncConfigDialog(
return time to multiplier return time to multiplier
} }
init { run { override suspend fun onShow() {
if (activity.isFinishing) b.title.setText(R.string.bell_sync_adjust_content)
return@run b.text1.hint = "±H:MM:SS"
onShowListener?.invoke(TAG) b.text1.setText(app.config.timetable.bellSyncDiff?.let {
app = activity.applicationContext as App
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.bell_sync_title)
.setView(R.layout.dialog_edit_text)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.cancel, null)
.setNeutralButton(R.string.reset) { _, _ ->
app.config.timetable.bellSyncDiff = null
app.config.timetable.bellSyncMultiplier = 0
onChangeListener?.invoke()
}
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
val message = dialog.findViewById<TextView>(android.R.id.title)
val editText = dialog.findViewById<TextInputEditText>(android.R.id.text1)
val textLayout = dialog.findViewById<TextInputLayout>(R.id.text_input_layout)
message?.setText(R.string.bell_sync_adjust_content)
editText?.hint = "±H:MM:SS"
editText?.setText(app.config.timetable.bellSyncDiff?.let {
(if (app.config.timetable.bellSyncMultiplier == -1) "-" else "+") + it.stringHMS (if (app.config.timetable.bellSyncMultiplier == -1) "-" else "+") + it.stringHMS
} ?: "+0:00:00") } ?: "+0:00:00")
editText?.addTextChangedListener { text -> b.text1.addTextChangedListener { text ->
val input = text?.toString() val input = text?.toString()
textLayout?.error = b.textInputLayout.error =
if (input != null && parse(input) == null) if (input != null && parse(input) == null)
activity.getString(R.string.bell_sync_adjust_error) activity.getString(R.string.bell_sync_adjust_error)
else else
null null
} }
}
dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.onClick { override suspend fun onPositiveClick(): Boolean {
val input = editText?.text?.toString() ?: return@onClick val input = b.text1.text?.toString() ?: return NO_DISMISS
val parsed = parse(input) val parsed = parse(input)
if (parsed == null) { if (parsed == null) {
Toast.makeText(activity, R.string.bell_sync_adjust_error, Toast.LENGTH_SHORT).show() Toast.makeText(activity, R.string.bell_sync_adjust_error, Toast.LENGTH_SHORT).show()
return@onClick return NO_DISMISS
} }
val (time, multiplier) = parsed val (time, multiplier) = parsed
@ -112,7 +80,13 @@ class BellSyncConfigDialog(
app.config.timetable.bellSyncMultiplier = multiplier app.config.timetable.bellSyncMultiplier = multiplier
onChangeListener?.invoke() onChangeListener?.invoke()
dialog.dismiss() return DISMISS
}
override suspend fun onNeutralClick(): Boolean {
app.config.timetable.bellSyncDiff = null
app.config.timetable.bellSyncMultiplier = 0
onChangeListener?.invoke()
return DISMISS
} }
}}
} }

View File

@ -5,17 +5,18 @@
package pl.szczodrzynski.edziennik.ui.dialogs.settings package pl.szczodrzynski.edziennik.ui.dialogs.settings
import android.annotation.SuppressLint import android.annotation.SuppressLint
import androidx.appcompat.app.AlertDialog import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import it.sephiroth.android.library.numberpicker.doOnStopTrackingTouch import it.sephiroth.android.library.numberpicker.doOnStopTrackingTouch
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.DialogConfigGradesBinding import pl.szczodrzynski.edziennik.databinding.DialogConfigGradesBinding
import pl.szczodrzynski.edziennik.ext.join import pl.szczodrzynski.edziennik.ext.join
import pl.szczodrzynski.edziennik.ext.onChange import pl.szczodrzynski.edziennik.ext.onChange
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ext.setOnSelectedListener import pl.szczodrzynski.edziennik.ext.setOnSelectedListener
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_DEFAULT import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_DEFAULT
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_DATE_DESC import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_DATE_DESC
@ -25,47 +26,29 @@ import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_AVG import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_AVG
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_SEM import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_1_SEM_2_SEM
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
import java.util.*
class GradesConfigDialog( class GradesConfigDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
private val reloadOnDismiss: Boolean = true, reloadOnDismiss: Boolean = true,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : ConfigDialog<DialogConfigGradesBinding>(
activity,
reloadOnDismiss,
onShowListener,
onDismissListener,
) { ) {
companion object {
const val TAG = "GradesConfigDialog"
}
private val app by lazy { activity.application as App } override val TAG = "GradesConfigDialog"
private val config by lazy { app.config.grades }
override fun getTitleRes() = R.string.menu_grades_config
override fun inflate(layoutInflater: LayoutInflater) =
DialogConfigGradesBinding.inflate(layoutInflater)
private val profileConfig by lazy { app.config.getFor(app.profileId).grades } private val profileConfig by lazy { app.config.getFor(app.profileId).grades }
private lateinit var b: DialogConfigGradesBinding
private lateinit var dialog: AlertDialog
init { run {
if (activity.isFinishing)
return@run
b = DialogConfigGradesBinding.inflate(activity.layoutInflater)
onShowListener?.invoke(TAG)
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.menu_grades_config)
.setView(b.root)
.setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() }
.setOnDismissListener {
saveConfig()
onDismissListener?.invoke(TAG)
if (reloadOnDismiss) (activity as? MainActivity)?.reloadTarget()
}
.create()
initView()
loadConfig()
dialog.show()
}}
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
private fun loadConfig() { override suspend fun loadConfig() {
b.customPlusCheckBox.isChecked = profileConfig.plusValue != null b.customPlusCheckBox.isChecked = profileConfig.plusValue != null
b.customPlusValue.isVisible = b.customPlusCheckBox.isChecked b.customPlusValue.isVisible = b.customPlusCheckBox.isChecked
b.customMinusCheckBox.isChecked = profileConfig.minusValue != null b.customMinusCheckBox.isChecked = profileConfig.minusValue != null
@ -95,21 +78,23 @@ class GradesConfigDialog(
else -> null else -> null
}?.isChecked = true }?.isChecked = true
b.dontCountGrades.isChecked = profileConfig.dontCountEnabled && profileConfig.dontCountGrades.isNotEmpty() b.dontCountGrades.isChecked =
profileConfig.dontCountEnabled && profileConfig.dontCountGrades.isNotEmpty()
b.hideImproved.isChecked = profileConfig.hideImproved b.hideImproved.isChecked = profileConfig.hideImproved
b.averageWithoutWeight.isChecked = profileConfig.averageWithoutWeight b.averageWithoutWeight.isChecked = profileConfig.averageWithoutWeight
if (profileConfig.dontCountGrades.isEmpty()) { if (profileConfig.dontCountGrades.isEmpty()) {
b.dontCountGradesText.setText("nb, 0, bz, bd") b.dontCountGradesText.setText("nb, 0, bz, bd")
} } else {
else {
b.dontCountGradesText.setText(profileConfig.dontCountGrades.join(", ")) b.dontCountGradesText.setText(profileConfig.dontCountGrades.join(", "))
} }
} }
private fun saveConfig() { override suspend fun saveConfig() {
profileConfig.plusValue = if (b.customPlusCheckBox.isChecked) b.customPlusValue.progress else null profileConfig.plusValue =
profileConfig.minusValue = if (b.customMinusCheckBox.isChecked) b.customMinusValue.progress else null if (b.customPlusCheckBox.isChecked) b.customPlusValue.progress else null
profileConfig.minusValue =
if (b.customMinusCheckBox.isChecked) b.customMinusValue.progress else null
b.dontCountGradesText.setText( b.dontCountGradesText.setText(
b.dontCountGradesText b.dontCountGradesText
@ -125,7 +110,7 @@ class GradesConfigDialog(
?: listOf() ?: listOf()
} }
private fun initView() { override fun initView() {
b.customPlusCheckBox.onChange { _, isChecked -> b.customPlusCheckBox.onChange { _, isChecked ->
b.customPlusValue.isVisible = isChecked b.customPlusValue.isVisible = isChecked
} }
@ -145,17 +130,31 @@ class GradesConfigDialog(
b.sortGradesByDateRadio.setOnSelectedListener { config.orderBy = ORDER_BY_DATE_DESC } b.sortGradesByDateRadio.setOnSelectedListener { config.orderBy = ORDER_BY_DATE_DESC }
b.sortGradesBySubjectRadio.setOnSelectedListener { config.orderBy = ORDER_BY_SUBJECT_ASC } b.sortGradesBySubjectRadio.setOnSelectedListener { config.orderBy = ORDER_BY_SUBJECT_ASC }
b.gradeColorFromERegister.setOnSelectedListener { profileConfig.colorMode = COLOR_MODE_DEFAULT } b.gradeColorFromERegister.setOnSelectedListener {
profileConfig.colorMode = COLOR_MODE_DEFAULT
}
b.gradeColorByValue.setOnSelectedListener { profileConfig.colorMode = COLOR_MODE_WEIGHTED } b.gradeColorByValue.setOnSelectedListener { profileConfig.colorMode = COLOR_MODE_WEIGHTED }
b.gradeAverageMode4.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_ALL_GRADES } b.gradeAverageMode4.setOnSelectedListener {
b.gradeAverageMode0.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_AVG_2_AVG } profileConfig.yearAverageMode = YEAR_ALL_GRADES
b.gradeAverageMode1.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_SEM_2_AVG } }
b.gradeAverageMode2.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_AVG_2_SEM } b.gradeAverageMode0.setOnSelectedListener {
b.gradeAverageMode3.setOnSelectedListener { profileConfig.yearAverageMode = YEAR_1_SEM_2_SEM } profileConfig.yearAverageMode = YEAR_1_AVG_2_AVG
}
b.gradeAverageMode1.setOnSelectedListener {
profileConfig.yearAverageMode = YEAR_1_SEM_2_AVG
}
b.gradeAverageMode2.setOnSelectedListener {
profileConfig.yearAverageMode = YEAR_1_AVG_2_SEM
}
b.gradeAverageMode3.setOnSelectedListener {
profileConfig.yearAverageMode = YEAR_1_SEM_2_SEM
}
b.hideImproved.onChange { _, isChecked -> profileConfig.hideImproved = isChecked } b.hideImproved.onChange { _, isChecked -> profileConfig.hideImproved = isChecked }
b.averageWithoutWeight.onChange { _, isChecked -> profileConfig.averageWithoutWeight = isChecked } b.averageWithoutWeight.onChange { _, isChecked ->
profileConfig.averageWithoutWeight = isChecked
}
b.averageWithoutWeightHelp.onClick { b.averageWithoutWeightHelp.onClick {
MaterialAlertDialogBuilder(activity) MaterialAlertDialogBuilder(activity)

View File

@ -4,59 +4,42 @@
package pl.szczodrzynski.edziennik.ui.dialogs.settings package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AlertDialog import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.MessagesConfigDialogBinding import pl.szczodrzynski.edziennik.databinding.MessagesConfigDialogBinding
import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog
class MessagesConfigDialog( class MessagesConfigDialog(
private val activity: AppCompatActivity, activity: AppCompatActivity,
private val reloadOnDismiss: Boolean = true, reloadOnDismiss: Boolean = true,
private val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
private val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : ConfigDialog<MessagesConfigDialogBinding>(
activity,
reloadOnDismiss,
onShowListener,
onDismissListener,
) { ) {
companion object {
const val TAG = "MessagesConfigDialog"
}
private val app by lazy { activity.application as App } override val TAG = "MessagesConfigDialog"
private val config by lazy { app.config.ui }
private val profileConfig by lazy { app.config.forProfile().ui }
private lateinit var b: MessagesConfigDialogBinding override fun getTitleRes() = R.string.menu_messages_config
private lateinit var dialog: AlertDialog override fun inflate(layoutInflater: LayoutInflater) =
MessagesConfigDialogBinding.inflate(layoutInflater)
init { run { private val profileConfig by lazy { app.config.getFor(app.profileId).ui }
if (activity.isFinishing)
return@run
b = MessagesConfigDialogBinding.inflate(activity.layoutInflater)
onShowListener?.invoke(TAG)
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.menu_messages_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() { override suspend fun loadConfig() {
b.config = profileConfig b.config = profileConfig
b.greetingText.setText( b.greetingText.setText(
profileConfig.messagesGreetingText ?: "\n\nZ poważaniem\n${app.profile.accountOwnerName}" profileConfig.messagesGreetingText
?: "\n\nZ poważaniem\n${app.profile.accountOwnerName}"
) )
} }
private fun saveConfig() { override suspend fun saveConfig() {
val greetingText = b.greetingText.text?.toString()?.trim() val greetingText = b.greetingText.text?.toString()?.trim()
if (greetingText.isNullOrEmpty()) if (greetingText.isNullOrEmpty())
profileConfig.messagesGreetingText = null profileConfig.messagesGreetingText = null

View File

@ -4,13 +4,7 @@
package pl.szczodrzynski.edziennik.ui.dialogs.settings package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
@ -24,73 +18,45 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_NOTIFICATIO
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_SETTINGS import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_SETTINGS
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import kotlin.coroutines.CoroutineContext import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
class MiniMenuConfigDialog( class MiniMenuConfigDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BaseDialog(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "MiniMenuConfigDialog"
}
private lateinit var app: App override val TAG = "BellSyncTimeChooseDialog"
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitleRes() = R.string.settings_theme_mini_drawer_buttons_dialog_title
override val coroutineContext: CoroutineContext override fun getMessageRes() = R.string.settings_theme_mini_drawer_buttons_dialog_text
get() = job + Dispatchers.Main override fun getPositiveButtonText() = R.string.ok
override fun getNegativeButtonText() = R.string.cancel
// local variables go here override fun getMultiChoiceItems(): Map<CharSequence, Any> = mapOf(
R.string.menu_home_page to DRAWER_ITEM_HOME,
R.string.menu_timetable to DRAWER_ITEM_TIMETABLE,
R.string.menu_agenda to DRAWER_ITEM_AGENDA,
R.string.menu_grades to DRAWER_ITEM_GRADES,
R.string.menu_messages to DRAWER_ITEM_MESSAGES,
R.string.menu_homework to DRAWER_ITEM_HOMEWORK,
R.string.menu_notices to DRAWER_ITEM_BEHAVIOUR,
R.string.menu_attendance to DRAWER_ITEM_ATTENDANCE,
R.string.menu_announcements to DRAWER_ITEM_ANNOUNCEMENTS,
R.string.menu_notifications to DRAWER_ITEM_NOTIFICATIONS,
R.string.menu_settings to DRAWER_ITEM_SETTINGS,
).mapKeys { (resId, _) -> activity.getString(resId) }
init { run { override fun getDefaultSelectedItems() = app.config.ui.miniMenuButtons.toSet()
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
val buttons = mapOf( override suspend fun onShow() = Unit
DRAWER_ITEM_HOME to R.string.menu_home_page,
DRAWER_ITEM_TIMETABLE to R.string.menu_timetable,
DRAWER_ITEM_AGENDA to R.string.menu_agenda,
DRAWER_ITEM_GRADES to R.string.menu_grades,
DRAWER_ITEM_MESSAGES to R.string.menu_messages,
DRAWER_ITEM_HOMEWORK to R.string.menu_homework,
DRAWER_ITEM_BEHAVIOUR to R.string.menu_notices,
DRAWER_ITEM_ATTENDANCE to R.string.menu_attendance,
DRAWER_ITEM_ANNOUNCEMENTS to R.string.menu_announcements,
DRAWER_ITEM_NOTIFICATIONS to R.string.menu_notifications,
DRAWER_ITEM_SETTINGS to R.string.menu_settings
)
val miniMenuButtons = app.config.ui.miniMenuButtons
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.settings_theme_mini_drawer_buttons_dialog_title)
//.setMessage(R.string.settings_theme_mini_drawer_buttons_dialog_text)
.setMultiChoiceItems(
buttons.map { activity.getString(it.value) }.toTypedArray(),
buttons.map { it.key in miniMenuButtons }.toBooleanArray(),
null
)
.setPositiveButton(R.string.ok) { _, _ ->
app.config.ui.miniMenuButtons =
buttons.keys.mapIndexedNotNull { index, id ->
if (dialog.listView.checkedItemPositions[index])
id
else
null
}
override suspend fun onPositiveClick(): Boolean {
app.config.ui.miniMenuButtons = getMultiSelection().filterIsInstance<Int>()
if (activity is MainActivity) { if (activity is MainActivity) {
activity.setDrawerItems() activity.setDrawerItems()
activity.drawer.updateBadges() activity.drawer.updateBadges()
} }
return DISMISS
} }
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
} }

View File

@ -4,96 +4,71 @@
package pl.szczodrzynski.edziennik.ui.dialogs.settings package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Notification import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
import kotlin.coroutines.CoroutineContext
// TODO refactor dialog to allow configuring other profiles
// than the selected one in UI
class NotificationFilterDialog( class NotificationFilterDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BaseDialog(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "NotificationFilterDialog" override val TAG = "NotificationFilterDialog"
private val notificationTypes = listOf(
Notification.TYPE_TIMETABLE_LESSON_CHANGE to R.string.notification_type_timetable_lesson_change, override fun getTitleRes() = R.string.dialog_notification_filter_title
Notification.TYPE_NEW_GRADE to R.string.notification_type_new_grade, override fun getMessageRes() = R.string.dialog_notification_filter_text
Notification.TYPE_NEW_EVENT to R.string.notification_type_new_event, override fun getPositiveButtonText() = R.string.ok
Notification.TYPE_NEW_HOMEWORK to R.string.notification_type_new_homework, override fun getNegativeButtonText() = R.string.cancel
Notification.TYPE_NEW_MESSAGE to R.string.notification_type_new_message,
Notification.TYPE_LUCKY_NUMBER to R.string.notification_type_lucky_number, override fun getMultiChoiceItems(): Map<CharSequence, Any> {
Notification.TYPE_NEW_NOTICE to R.string.notification_type_notice, notificationTypes = mapOf(
Notification.TYPE_NEW_ATTENDANCE to R.string.notification_type_attendance, R.string.notification_type_timetable_lesson_change to Notification.TYPE_TIMETABLE_LESSON_CHANGE,
Notification.TYPE_NEW_ANNOUNCEMENT to R.string.notification_type_new_announcement, R.string.notification_type_new_grade to Notification.TYPE_NEW_GRADE,
Notification.TYPE_NEW_SHARED_EVENT to R.string.notification_type_new_shared_event, R.string.notification_type_new_event to Notification.TYPE_NEW_EVENT,
Notification.TYPE_NEW_SHARED_HOMEWORK to R.string.notification_type_new_shared_homework, R.string.notification_type_new_homework to Notification.TYPE_NEW_HOMEWORK,
Notification.TYPE_REMOVED_SHARED_EVENT to R.string.notification_type_removed_shared_event, R.string.notification_type_new_message to Notification.TYPE_NEW_MESSAGE,
Notification.TYPE_TEACHER_ABSENCE to R.string.notification_type_new_teacher_absence R.string.notification_type_lucky_number to Notification.TYPE_LUCKY_NUMBER,
) R.string.notification_type_notice to Notification.TYPE_NEW_NOTICE,
R.string.notification_type_attendance to Notification.TYPE_NEW_ATTENDANCE,
R.string.notification_type_new_announcement to Notification.TYPE_NEW_ANNOUNCEMENT,
R.string.notification_type_new_shared_event to Notification.TYPE_NEW_SHARED_EVENT,
R.string.notification_type_new_shared_homework to Notification.TYPE_NEW_SHARED_HOMEWORK,
R.string.notification_type_removed_shared_event to Notification.TYPE_REMOVED_SHARED_EVENT,
R.string.notification_type_new_teacher_absence to Notification.TYPE_TEACHER_ABSENCE,
).mapKeys { (resId, _) -> activity.getString(resId) }
return notificationTypes
} }
private lateinit var app: App override fun getDefaultSelectedItems() =
private lateinit var dialog: AlertDialog notificationTypes.values.subtract(app.config.forProfile().sync.notificationFilter)
private val job = Job() override suspend fun onShow() = Unit
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private val notificationFilter = mutableListOf<Int>() private lateinit var notificationTypes: Map<CharSequence, Int>
init { run { override suspend fun onPositiveClick(): Boolean {
if (activity.isFinishing) val enabledTypes = getMultiSelection().filterIsInstance<Int>()
return@run val disabledTypes = notificationTypes.values.subtract(enabledTypes).toList()
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
notificationFilter.clear() if (disabledTypes.isNotEmpty()) {
notificationFilter += app.config.forProfile().sync.notificationFilter
val items = notificationTypes.map { app.getString(it.second) }.toTypedArray()
val checkedItems = notificationTypes.map { !notificationFilter.contains(it.first) }.toBooleanArray()
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.dialog_notification_filter_title)
//.setMessage(R.string.dialog_notification_filter_text)
.setMultiChoiceItems(items, checkedItems) { _, which, isChecked ->
val type = notificationTypes[which].first
notificationFilter.remove(type)
if (!isChecked)
notificationFilter += type
}
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.onClick {
if (notificationFilter.isEmpty()) {
app.config.forProfile().sync.notificationFilter = notificationFilter
dialog.dismiss()
return@onClick
}
// warn user when he tries to disable some notifications // warn user when he tries to disable some notifications
MaterialAlertDialogBuilder(activity) MaterialAlertDialogBuilder(activity)
.setTitle(R.string.are_you_sure) .setTitle(R.string.are_you_sure)
.setMessage(R.string.notification_filter_warning) .setMessage(R.string.notification_filter_warning)
.setPositiveButton(R.string.ok) { _, _ -> .setPositiveButton(R.string.ok) { _, _ ->
app.config.forProfile().sync.notificationFilter = notificationFilter app.config.forProfile().sync.notificationFilter = disabledTypes
dialog.dismiss() dismiss()
} }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.show() .show()
return NO_DISMISS
}
app.config.forProfile().sync.notificationFilter = disabledTypes
return DISMISS
} }
}}
} }

View File

@ -5,14 +5,10 @@
package pl.szczodrzynski.edziennik.ui.dialogs.settings package pl.szczodrzynski.edziennik.ui.dialogs.settings
import android.content.res.ColorStateList import android.content.res.ColorStateList
import androidx.appcompat.app.AlertDialog import android.view.LayoutInflater
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.MaterialShapeDrawable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.databinding.DialogProfileConfigBinding import pl.szczodrzynski.edziennik.databinding.DialogProfileConfigBinding
@ -20,54 +16,38 @@ import pl.szczodrzynski.edziennik.ext.dp
import pl.szczodrzynski.edziennik.ext.onChange import pl.szczodrzynski.edziennik.ext.onChange
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ui.dialogs.ProfileRemoveDialog import pl.szczodrzynski.edziennik.ui.dialogs.ProfileRemoveDialog
import kotlin.coroutines.CoroutineContext import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
class ProfileConfigDialog( class ProfileConfigDialog(
val activity: MainActivity, activity: MainActivity,
val profile: Profile, private val profile: Profile,
val onProfileSaved: ((profile: Profile) -> Unit)? = null, private val onProfileSaved: ((profile: Profile) -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<DialogProfileConfigBinding>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "ProfileConfigDialog"
}
private lateinit var app: App override val TAG = "ProfileConfigDialog"
private lateinit var b: DialogProfileConfigBinding
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitleRes(): Int? = null
override val coroutineContext: CoroutineContext override fun inflate(layoutInflater: LayoutInflater) =
get() = job + Dispatchers.Main DialogProfileConfigBinding.inflate(layoutInflater)
override fun getPositiveButtonText() = R.string.close
// local variables go here
private var profileChanged = false private var profileChanged = false
private var profileRemoved = false private var profileRemoved = false
init { run { override suspend fun onShow() {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
b = DialogProfileConfigBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setView(b.root)
.setPositiveButton(R.string.close, null)
.setOnDismissListener {
if (!profileRemoved && profileChanged) {
app.profileSave(profile)
onProfileSaved?.invoke(profile)
}
onDismissListener?.invoke(TAG)
}
.show()
b.profile = profile b.profile = profile
profile.applyImageTo(b.image) profile.applyImageTo(b.image)
// I can't believe how simple it is to get the dialog's background color !! // I can't believe how simple it is to get the dialog's background color !!
val shape = MaterialShapeDrawable(activity, null, R.attr.alertDialogStyle, R.style.MaterialAlertDialog_MaterialComponents) val shape = MaterialShapeDrawable(
activity,
null,
R.attr.alertDialogStyle,
R.style.MaterialAlertDialog_MaterialComponents
)
val surface = MaterialColors.getColor(activity, R.attr.colorSurface, TAG) val surface = MaterialColors.getColor(activity, R.attr.colorSurface, TAG)
shape.setCornerSize(18.dp.toFloat()) shape.setCornerSize(18.dp.toFloat())
shape.initializeElevationOverlay(activity) shape.initializeElevationOverlay(activity)
@ -84,6 +64,8 @@ class ProfileConfigDialog(
} }
b.imageButton.onClick { b.imageButton.onClick {
if (activity !is MainActivity)
return@onClick
activity.requestHandler.requestProfileImage(profile) { activity.requestHandler.requestProfileImage(profile) {
val profile = it as? Profile ?: return@requestProfileImage val profile = it as? Profile ?: return@requestProfileImage
if (this@ProfileConfigDialog.profile == profile) { if (this@ProfileConfigDialog.profile == profile) {
@ -98,7 +80,14 @@ class ProfileConfigDialog(
ProfileRemoveDialog(activity, profile.id, profile.name) { ProfileRemoveDialog(activity, profile.id, profile.name) {
profileRemoved = true profileRemoved = true
dialog.dismiss() dialog.dismiss()
}.show()
}
}
override fun onDismiss() {
if (!profileRemoved && profileChanged) {
app.profileSave(profile)
onProfileSaved?.invoke(profile)
} }
} }
}}
} }

View File

@ -4,44 +4,28 @@
package pl.szczodrzynski.edziennik.ui.dialogs.settings package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import pl.szczodrzynski.edziennik.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.ext.HOUR import pl.szczodrzynski.edziennik.ext.HOUR
import pl.szczodrzynski.edziennik.ext.MINUTE import pl.szczodrzynski.edziennik.ext.MINUTE
import pl.szczodrzynski.edziennik.ext.getSyncInterval import pl.szczodrzynski.edziennik.ext.getSyncInterval
import kotlin.coroutines.CoroutineContext import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
class SyncIntervalDialog( class SyncIntervalDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val onChangeListener: (() -> Unit)? = null, private val onChangeListener: (() -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BaseDialog(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "SyncIntervalDialog"
}
private lateinit var app: App override val TAG = "SyncIntervalDialog"
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitleRes() = R.string.settings_sync_sync_interval_dialog_title
override val coroutineContext: CoroutineContext override fun getMessageRes() = R.string.settings_sync_sync_interval_dialog_text
get() = job + Dispatchers.Main override fun getPositiveButtonText() = R.string.ok
override fun getNegativeButtonText() = R.string.cancel
// local variables go here override fun getSingleChoiceItems(): Map<CharSequence, Any> = listOf(
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
val intervals = listOf(
30 * MINUTE, 30 * MINUTE,
45 * MINUTE, 45 * MINUTE,
60 * MINUTE, 60 * MINUTE,
@ -50,31 +34,17 @@ class SyncIntervalDialog(
3 * HOUR, 3 * HOUR,
4 * HOUR, 4 * HOUR,
6 * HOUR, 6 * HOUR,
10 * HOUR 10 * HOUR,
) ).associateBy { activity.getSyncInterval(it.toInt()) }
val intervalNames = intervals.map {
activity.getSyncInterval(it.toInt())
}
dialog = MaterialAlertDialogBuilder(activity) override fun getDefaultSelectedItem() = app.config.sync.interval.toLong()
.setTitle(R.string.settings_sync_sync_interval_dialog_title)
//.setMessage(R.string.settings_sync_sync_interval_dialog_text)
.setSingleChoiceItems(
intervalNames.toTypedArray(),
intervals.indexOf(app.config.sync.interval.toLong()),
null
)
.setPositiveButton(R.string.ok) { _, _ ->
val which = dialog.listView.checkedItemPosition
val interval = intervals[which] override suspend fun onShow() = Unit
override suspend fun onPositiveClick(): Boolean {
val interval = getSingleSelection() as? Long ?: return DISMISS
app.config.sync.interval = interval.toInt() app.config.sync.interval = interval.toInt()
onChangeListener?.invoke() onChangeListener?.invoke()
return DISMISS
} }
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
} }

View File

@ -4,62 +4,38 @@
package pl.szczodrzynski.edziennik.ui.dialogs.settings package pl.szczodrzynski.edziennik.ui.dialogs.settings
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
import pl.szczodrzynski.edziennik.utils.Themes import pl.szczodrzynski.edziennik.utils.Themes
import kotlin.coroutines.CoroutineContext
class ThemeChooserDialog( class ThemeChooserDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BaseDialog(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "ThemeChooserDialog" override val TAG = "ThemeChooserDialog"
override fun getTitleRes() = R.string.settings_theme_theme_text
override fun getPositiveButtonText() = R.string.ok
override fun getNegativeButtonText() = R.string.cancel
override fun getSingleChoiceItems(): Map<CharSequence, Any> = Themes.themeList.associate {
activity.getString(it.name) to it.id
} }
private lateinit var app: App override fun getDefaultSelectedItem() = Themes.theme.id
private lateinit var dialog: AlertDialog
private val job = Job() override suspend fun onShow() = Unit
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here override suspend fun onPositiveClick(): Boolean {
val themeId = getSingleSelection() as? Int ?: return DISMISS
init { run { if (app.config.ui.theme != themeId) {
if (activity.isFinishing) app.config.ui.theme = themeId
return@run Themes.themeInt = themeId
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.settings_theme_theme_text)
.setSingleChoiceItems(
Themes.getThemeNames(activity).toTypedArray(),
Themes.themeIndex,
null
)
.setPositiveButton(R.string.ok) { _, _ ->
val which = dialog.listView.checkedItemPosition
val theme = Themes.themeList[which]
if (app.config.ui.theme == theme.id)
return@setPositiveButton
app.config.ui.theme = theme.id
Themes.themeIndex = which
activity.recreate() activity.recreate()
} }
.setNegativeButton(R.string.cancel, null) return DISMISS
.setOnDismissListener {
onDismissListener?.invoke(TAG)
} }
.show()
}}
} }

View File

@ -6,89 +6,60 @@ package pl.szczodrzynski.edziennik.ui.dialogs.sync
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import coil.load import coil.load
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.BuildConfig import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus
import pl.szczodrzynski.edziennik.databinding.DialogRegisterUnavailableBinding import pl.szczodrzynski.edziennik.databinding.DialogRegisterUnavailableBinding
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import kotlin.coroutines.CoroutineContext
class RegisterUnavailableDialog( class RegisterUnavailableDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val status: RegisterAvailabilityStatus, private val status: RegisterAvailabilityStatus,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<DialogRegisterUnavailableBinding>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "RegisterUnavailableDialog" override val TAG = "RegisterUnavailableDialog"
override fun getTitleRes(): Int? = null
override fun inflate(layoutInflater: LayoutInflater) =
DialogRegisterUnavailableBinding.inflate(layoutInflater)
override fun getPositiveButtonText() = R.string.close
override suspend fun onBeforeShow(): Boolean {
if (!status.available && status.userMessage != null)
return true
if (status.minVersionCode <= BuildConfig.VERSION_CODE)
return false
val update = app.config.update
UpdateAvailableDialog(
activity = activity,
update = update,
mandatory = update != null && update.versionCode >= status.minVersionCode,
onShowListener = onShowListener,
onDismissListener = onDismissListener
).show()
return false
} }
private lateinit var app: App override suspend fun onShow() {
private lateinit var dialog: AlertDialog b.message = status.userMessage ?: return
b.text.movementMethod = LinkMovementMethod.getInstance()
private val job = Job() if (status.userMessage.image != null) {
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
if (!status.available && status.userMessage != null) {
val b = DialogRegisterUnavailableBinding.inflate(LayoutInflater.from(activity), null, false)
b.message = status.userMessage
if (status.userMessage.image != null)
b.image.load(status.userMessage.image) b.image.load(status.userMessage.image)
}
if (status.userMessage.url != null) { if (status.userMessage.url != null) {
b.readMore.onClick { b.readMore.onClick {
Utils.openUrl(activity, status.userMessage.url) Utils.openUrl(activity, status.userMessage.url)
} }
} }
b.text.movementMethod = LinkMovementMethod.getInstance()
dialog = MaterialAlertDialogBuilder(activity)
.setView(b.root)
.setPositiveButton(R.string.close) { dialog, _ ->
dialog.dismiss()
} }
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
return@run
}
val update = app.config.update
if (status.minVersionCode > BuildConfig.VERSION_CODE) {
if (update != null && update.versionCode >= status.minVersionCode) {
UpdateAvailableDialog(activity, update, true, onShowListener, onDismissListener)
}
else {
// this *should* never happen
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.update_available_title)
.setMessage(R.string.update_available_fallback)
.setPositiveButton(R.string.update_available_button) { dialog, _ ->
Utils.openGooglePlay(activity)
dialog.dismiss()
}
.setCancelable(false)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}
return@run
}
}}
} }

View File

@ -4,48 +4,24 @@
package pl.szczodrzynski.edziennik.ui.dialogs.sync package pl.szczodrzynski.edziennik.ui.dialogs.sync
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import kotlin.coroutines.CoroutineContext import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
class ServerMessageDialog( class ServerMessageDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val title: String, private val titleText: String,
val message: CharSequence, private val messageText: CharSequence,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BaseDialog(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "ServerMessageDialog"
}
private lateinit var app: App override val TAG = "ServerMessageDialog"
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitle() = titleText
override val coroutineContext: CoroutineContext override fun getTitleRes(): Int? = null
get() = job + Dispatchers.Main override fun getMessage() = messageText
override fun getPositiveButtonText() = R.string.close
init { run { override suspend fun onShow() = Unit
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(title)
.setMessage(message)
.setPositiveButton(R.string.close) { dialog, _ ->
dialog.dismiss()
}
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
} }

View File

@ -4,113 +4,93 @@
package pl.szczodrzynski.edziennik.ui.dialogs.sync package pl.szczodrzynski.edziennik.ui.dialogs.sync
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ATTENDANCE
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
import pl.szczodrzynski.edziennik.ui.messages.list.MessagesFragment import pl.szczodrzynski.edziennik.ui.messages.list.MessagesFragment
import kotlin.coroutines.CoroutineContext
class SyncViewListDialog( class SyncViewListDialog(
val activity: MainActivity, activity: MainActivity,
val currentViewId: Int? = null private val currentViewId: Int,
) : CoroutineScope { onShowListener: ((tag: String) -> Unit)? = null,
companion object { onDismissListener: ((tag: String) -> Unit)? = null,
private const val TAG = "SyncViewListDialog" ) : BaseDialog(activity, onShowListener, onDismissListener) {
override val TAG = "SyncViewListDialog"
override fun getTitleRes() = R.string.dialog_sync_view_list_title
override fun getPositiveButtonText() = R.string.ok
override fun getNeutralButtonText() = R.string.sync_feature_all
override fun getNegativeButtonText() = R.string.cancel
override fun getMultiChoiceItems(): Map<CharSequence, Any> {
items = mapOf(
R.string.menu_timetable to DRAWER_ITEM_TIMETABLE,
R.string.menu_agenda to DRAWER_ITEM_AGENDA,
R.string.menu_grades to DRAWER_ITEM_GRADES,
R.string.menu_homework to DRAWER_ITEM_HOMEWORK,
R.string.menu_notices to DRAWER_ITEM_BEHAVIOUR,
R.string.menu_attendance to DRAWER_ITEM_ATTENDANCE,
R.string.title_messages_inbox_single to (DRAWER_ITEM_MESSAGES to 0),
R.string.title_messages_sent_single to (DRAWER_ITEM_MESSAGES to 1),
R.string.menu_announcements to DRAWER_ITEM_ANNOUNCEMENTS,
).mapKeys { (resId, _) -> activity.getString(resId) }
return items
} }
private lateinit var job: Job override fun getDefaultSelectedItems(): Set<Any> {
override val coroutineContext: CoroutineContext val everything = currentViewId == DRAWER_ITEM_HOME
get() = job + Dispatchers.Main return when {
everything -> items.values.toSet()
private val app by lazy { activity.application as App } currentViewId == DRAWER_ITEM_MESSAGES -> when (MessagesFragment.pageSelection) {
private lateinit var b: DialogLessonDetailsBinding 1 -> setOf(DRAWER_ITEM_MESSAGES to 1)
private lateinit var dialog: AlertDialog else -> setOf(DRAWER_ITEM_MESSAGES to 0)
init { run {
job = Job()
val viewIds = arrayOf(
MainActivity.DRAWER_ITEM_TIMETABLE,
MainActivity.DRAWER_ITEM_AGENDA,
MainActivity.DRAWER_ITEM_GRADES,
MainActivity.DRAWER_ITEM_HOMEWORK,
MainActivity.DRAWER_ITEM_BEHAVIOUR,
MainActivity.DRAWER_ITEM_ATTENDANCE,
MainActivity.DRAWER_ITEM_MESSAGES,
MainActivity.DRAWER_ITEM_MESSAGES,
MainActivity.DRAWER_ITEM_ANNOUNCEMENTS
)
val items = arrayOf<String>(
app.getString(R.string.menu_timetable),
app.getString(R.string.menu_agenda),
app.getString(R.string.menu_grades),
app.getString(R.string.menu_homework),
app.getString(R.string.menu_notices),
app.getString(R.string.menu_attendance),
app.getString(R.string.title_messages_inbox_single),
app.getString(R.string.title_messages_sent_single),
app.getString(R.string.menu_announcements)
)
val everything = currentViewId == MainActivity.DRAWER_ITEM_HOME
val checkedItems = booleanArrayOf(
everything || currentViewId == MainActivity.DRAWER_ITEM_TIMETABLE,
everything || currentViewId == MainActivity.DRAWER_ITEM_AGENDA,
everything || currentViewId == MainActivity.DRAWER_ITEM_GRADES,
everything || currentViewId == MainActivity.DRAWER_ITEM_HOMEWORK,
everything || currentViewId == MainActivity.DRAWER_ITEM_BEHAVIOUR,
everything || currentViewId == MainActivity.DRAWER_ITEM_ATTENDANCE,
everything || currentViewId == MainActivity.DRAWER_ITEM_MESSAGES && MessagesFragment.pageSelection != 1,
everything || currentViewId == MainActivity.DRAWER_ITEM_MESSAGES && MessagesFragment.pageSelection == 1,
everything || currentViewId == MainActivity.DRAWER_ITEM_ANNOUNCEMENTS
)
val userChooses = checkedItems.toMutableList()
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.dialog_sync_view_list_title)
.setMultiChoiceItems(items, checkedItems) { _, which, isChecked ->
userChooses[which] = isChecked
} }
.setPositiveButton(R.string.ok) { _, _ -> else -> setOf(currentViewId)
dialog.dismiss()
val selectedViewIds = userChooses.mapIndexed { index, it ->
if (it)
viewIds[index] to when (index) {
7 -> 1
else -> 0
} }
else
null
}.let {
listOfNotNull(*it.toTypedArray())
} }
if (selectedViewIds.isNotEmpty()) { override suspend fun onShow() = Unit
private lateinit var items: Map<CharSequence, Any>
@Suppress("UNCHECKED_CAST")
override suspend fun onPositiveClick(): Boolean {
val selected = getMultiSelection().mapNotNull {
when (it) {
is Int -> it to 0
is Pair<*, *> -> it as Pair<Int, Int>
else -> null
}
}
if (selected.isEmpty())
return DISMISS
if (activity is MainActivity)
activity.swipeRefreshLayout.isRefreshing = true activity.swipeRefreshLayout.isRefreshing = true
EdziennikTask.syncProfile( EdziennikTask.syncProfile(
App.profileId, App.profileId,
selectedViewIds selected
).enqueue(activity) ).enqueue(activity)
return DISMISS
} }
}
.setNeutralButton(R.string.sync_feature_all) { _, _ ->
dialog.dismiss()
override suspend fun onNeutralClick(): Boolean {
if (activity is MainActivity)
activity.swipeRefreshLayout.isRefreshing = true activity.swipeRefreshLayout.isRefreshing = true
EdziennikTask.syncProfile(App.profileId).enqueue(activity) EdziennikTask.syncProfile(App.profileId).enqueue(activity)
return DISMISS
} }
.setNegativeButton(R.string.cancel) { _, _ ->
dialog.dismiss()
}
.show()
}}
} }

View File

@ -4,66 +4,54 @@
package pl.szczodrzynski.edziennik.ui.dialogs.sync package pl.szczodrzynski.edziennik.ui.dialogs.sync
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import pl.szczodrzynski.edziennik.BuildConfig
import kotlinx.coroutines.CoroutineScope import pl.szczodrzynski.edziennik.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
import pl.szczodrzynski.edziennik.ext.Intent import pl.szczodrzynski.edziennik.ext.Intent
import pl.szczodrzynski.edziennik.ext.setMessage
import pl.szczodrzynski.edziennik.sync.UpdateDownloaderService import pl.szczodrzynski.edziennik.sync.UpdateDownloaderService
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.html.BetterHtml import pl.szczodrzynski.edziennik.utils.html.BetterHtml
import kotlin.coroutines.CoroutineContext
class UpdateAvailableDialog( class UpdateAvailableDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val update: Update, private val update: Update?,
val mandatory: Boolean = update.updateMandatory, private val mandatory: Boolean = update?.updateMandatory ?: false,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BaseDialog(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "UpdateAvailableDialog"
}
private lateinit var app: App override val TAG = "UpdateAvailableDialog"
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitleRes() = R.string.update_available_title
override val coroutineContext: CoroutineContext override fun getMessageFormat(): Pair<Int, List<CharSequence>> {
get() = job + Dispatchers.Main if (update != null) {
return R.string.update_available_format to listOf(
init { run {
if (activity.isFinishing)
return@run
if (update.versionCode <= BuildConfig.VERSION_CODE)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.update_available_title)
.setMessage(
R.string.update_available_format,
BuildConfig.VERSION_NAME, BuildConfig.VERSION_NAME,
update.versionName, update.versionName,
update.releaseNotes?.let { BetterHtml.fromHtml(activity, it) } ?: "---" update.releaseNotes?.let { BetterHtml.fromHtml(activity, it) } ?: "---",
) )
.setPositiveButton(R.string.update_available_button) { dialog, _ -> }
return R.string.update_available_fallback to emptyList()
}
override fun isCancelable() = !mandatory
override fun getPositiveButtonText() = R.string.update_available_button
override fun getNeutralButtonText() = if (mandatory) null else R.string.update_available_later
override suspend fun onShow() = Unit
override suspend fun onPositiveClick(): Boolean {
if (update == null)
Utils.openGooglePlay(activity)
else
activity.startService(Intent(app, UpdateDownloaderService::class.java)) activity.startService(Intent(app, UpdateDownloaderService::class.java))
dialog.dismiss() return NO_DISMISS
} }
.also {
if (!mandatory) override suspend fun onBeforeShow(): Boolean {
it.setNeutralButton(R.string.update_available_later, null) // show only if app is older than available
return update == null || update.versionCode > BuildConfig.VERSION_CODE
} }
.setCancelable(!mandatory)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
} }

View File

@ -5,46 +5,32 @@
package pl.szczodrzynski.edziennik.ui.error package pl.szczodrzynski.edziennik.ui.error
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.* import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.*
import kotlin.coroutines.CoroutineContext import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
class ErrorDetailsDialog( class ErrorDetailsDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val errors: List<ApiError>, private val errors: List<ApiError>,
val titleRes: Int = R.string.dialog_error_details_title, private val titleRes: Int = R.string.dialog_error_details_title,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BaseDialog(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "ApiErrorDialog"
}
private lateinit var app: App override val TAG = "ErrorDetailsDialog"
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitleRes() = titleRes
override val coroutineContext: CoroutineContext override fun getMessage() = errors.map {
get() = job + Dispatchers.Main
private val api by lazy { SzkolnyApi(activity.applicationContext as App) }
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
if (errors.isNotEmpty()) {
val message = errors.map {
listOf( listOf(
it.getStringReason(activity).asBoldSpannable().asColoredSpannable(R.attr.colorOnBackground.resolveAttr(activity)), it.getStringReason(activity)
.asBoldSpannable()
.asColoredSpannable(R.attr.colorOnBackground.resolveAttr(activity)),
activity.getString(R.string.error_unknown_format, it.errorCode, it.tag), activity.getString(R.string.error_unknown_format, it.errorCode, it.tag),
if (App.devMode) if (App.devMode)
it.throwable?.stackTraceString ?: it.throwable?.localizedMessage it.throwable?.stackTraceString ?: it.throwable?.localizedMessage
@ -53,30 +39,30 @@ class ErrorDetailsDialog(
).concat("\n") ).concat("\n")
}.concat("\n\n") }.concat("\n\n")
dialog = MaterialAlertDialogBuilder(activity) override fun isCancelable() = false
.setTitle(titleRes) override fun getPositiveButtonText() = R.string.close
.setMessage(message) override fun getNeutralButtonText() = R.string.report
.setPositiveButton(R.string.ok) { _, _ ->
dialog.dismiss() override suspend fun onShow() = Unit
private val api by lazy { SzkolnyApi(activity.applicationContext as App) }
override suspend fun onBeforeShow(): Boolean {
return errors.isNotEmpty()
} }
.setNeutralButton(R.string.report) { _, _ ->
launch { override suspend fun onNeutralClick(): Boolean {
api.runCatching({ api.runCatching({
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
errorReport(errors.map { it.toReportableError(activity) }) errorReport(errors.map { it.toReportableError(activity) })
} }
}, { }, {
Toast.makeText(activity, activity.getString(R.string.crash_report_cannot_send) + it, Toast.LENGTH_LONG).show() Toast.makeText(
}) ?: return@launch activity,
activity.getString(R.string.crash_report_cannot_send) + it,
dialog.dismiss() Toast.LENGTH_LONG
).show()
})
return DISMISS
} }
} }
.setCancelable(false)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}
}}
}

View File

@ -34,7 +34,7 @@ class ErrorSnackbar(val activity: AppCompatActivity) : CoroutineScope {
this.coordinator = coordinatorLayout this.coordinator = coordinatorLayout
snackbar = Snackbar.make(coordinator, R.string.snackbar_error_text, Snackbar.LENGTH_INDEFINITE) snackbar = Snackbar.make(coordinator, R.string.snackbar_error_text, Snackbar.LENGTH_INDEFINITE)
snackbar?.setAction(R.string.more) { snackbar?.setAction(R.string.more) {
ErrorDetailsDialog(activity, errors) ErrorDetailsDialog(activity, errors).show()
errors = mutableListOf() errors = mutableListOf()
} }
val bgColor = ColorUtils.compositeColors( val bgColor = ColorUtils.compositeColors(

View File

@ -8,6 +8,7 @@ import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.provider.CalendarContract import android.provider.CalendarContract
import android.provider.CalendarContract.Events import android.provider.CalendarContract.Events
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
@ -25,74 +26,63 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.db.full.EventFull import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.databinding.DialogEventDetailsBinding import pl.szczodrzynski.edziennik.databinding.DialogEventDetailsBinding
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment
import pl.szczodrzynski.edziennik.utils.BetterLink import pl.szczodrzynski.edziennik.utils.BetterLink
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import kotlin.coroutines.CoroutineContext
// TODO: 2021-10-19 rewrite to the new dialog style
class EventDetailsDialog( class EventDetailsDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
// this event is observed for changes // this event is observed for changes
var event: EventFull, private var event: EventFull,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<DialogEventDetailsBinding>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "EventDetailsDialog" override val TAG = "EventDetailsDialog"
}
override fun getTitleRes(): Int? = null
override fun inflate(layoutInflater: LayoutInflater) =
DialogEventDetailsBinding.inflate(layoutInflater)
override fun getPositiveButtonText() = R.string.close
override fun getNeutralButtonText() = if (event.addedManually) R.string.remove else null
private lateinit var app: App
private lateinit var b: DialogEventDetailsBinding
private lateinit var dialog: AlertDialog
private var removeEventDialog: AlertDialog? = null private var removeEventDialog: AlertDialog? = null
private val eventShared = event.sharedBy != null private val eventShared = event.sharedBy != null
private val eventOwn = event.sharedBy == "self" private val eventOwn = event.sharedBy == "self"
private val manager private val manager
get() = app.eventManager get() = app.eventManager
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private val api by lazy { private val api by lazy {
SzkolnyApi(app) SzkolnyApi(app)
} }
private var progressDialog: AlertDialog? = null private var progressDialog: AlertDialog? = null
init { run { override suspend fun onNeutralClick(): Boolean {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
EventBus.getDefault().register(this)
app = activity.applicationContext as App
b = DialogEventDetailsBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setView(b.root)
.setPositiveButton(R.string.close) { dialog, _ ->
dialog.dismiss()
}
.apply {
if (event.addedManually)
setNeutralButton(R.string.remove, null)
}
.setOnDismissListener {
onDismissListener?.invoke(TAG)
EventBus.getDefault().unregister(this@EventDetailsDialog)
progressDialog?.dismiss()
}
.show()
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)?.onClick {
showRemoveEventDialog() showRemoveEventDialog()
return NO_DISMISS
} }
override suspend fun onBeforeShow(): Boolean {
EventBus.getDefault().register(this)
return true
}
override suspend fun onShow() {
// watch the event for changes // watch the event for changes
app.db.eventDao().getById(event.profileId, event.id).observe(activity) { app.db.eventDao().getById(event.profileId, event.id).observe(activity) {
event = it ?: return@observe event = it ?: return@observe
update() update()
} }
}} }
override fun onDismiss() {
EventBus.getDefault().unregister(this@EventDetailsDialog)
progressDialog?.dismiss()
}
private fun update() { private fun update() {
b.event = event b.event = event
@ -185,7 +175,7 @@ class EventDetailsDialog(
}, },
onShowListener = onShowListener, onShowListener = onShowListener,
onDismissListener = onDismissListener onDismissListener = onDismissListener
) ).show()
} }
b.editButton.attachToastHint(R.string.hint_edit_event) b.editButton.attachToastHint(R.string.hint_edit_event)

View File

@ -4,10 +4,10 @@
package pl.szczodrzynski.edziennik.ui.event package pl.szczodrzynski.edziennik.ui.event
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AlertDialog.BUTTON_NEUTRAL
import androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE import androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -32,6 +32,7 @@ import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.ui.dialogs.StyledTextDialog import pl.szczodrzynski.edziennik.ui.dialogs.StyledTextDialog
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.RegistrationConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.RegistrationConfigDialog
import pl.szczodrzynski.edziennik.ui.views.TimeDropdown.Companion.DISPLAY_LESSONS import pl.szczodrzynski.edziennik.ui.views.TimeDropdown.Companion.DISPLAY_LESSONS
import pl.szczodrzynski.edziennik.utils.Anim import pl.szczodrzynski.edziennik.utils.Anim
@ -40,32 +41,32 @@ import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.HtmlMode.SIM
import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.StylingConfigBase import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.StylingConfigBase
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.coroutines.CoroutineContext
// TODO: 2021-10-19 rewrite to the new dialog style
class EventManualDialog( class EventManualDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val profileId: Int, private val profileId: Int,
val defaultLesson: LessonFull? = null, private val defaultLesson: LessonFull? = null,
val defaultDate: Date? = null, private val defaultDate: Date? = null,
val defaultTime: Time? = null, private val defaultTime: Time? = null,
val defaultType: Long? = null, private val defaultType: Long? = null,
val editingEvent: EventFull? = null, private val editingEvent: EventFull? = null,
val onSaveListener: ((event: EventFull?) -> Unit)? = null, private val onSaveListener: ((event: EventFull?) -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<DialogEventManualV2Binding>(activity, onShowListener, onDismissListener) {
companion object { override val TAG = "EventManualDialog"
private const val TAG = "EventManualDialog"
}
private val job: Job = Job() override fun getTitleRes() = R.string.dialog_event_manual_title
override val coroutineContext: CoroutineContext override fun inflate(layoutInflater: LayoutInflater) =
get() = job + Dispatchers.Main DialogEventManualV2Binding.inflate(layoutInflater)
override fun isCancelable() = false
override fun getPositiveButtonText() = R.string.save
override fun getNeutralButtonText() = if (editingEvent != null) R.string.remove else null
override fun getNegativeButtonText() = R.string.cancel
private val app by lazy { activity.application as App }
private lateinit var b: DialogEventManualV2Binding
private lateinit var dialog: AlertDialog
private lateinit var profile: Profile private lateinit var profile: Profile
private lateinit var stylingConfig: StylingConfigBase private lateinit var stylingConfig: StylingConfigBase
@ -86,46 +87,28 @@ class EventManualDialog(
private var progressDialog: AlertDialog? = null private var progressDialog: AlertDialog? = null
init { launch { override suspend fun onPositiveClick(): Boolean {
if (activity.isFinishing) saveEvent()
return@launch return NO_DISMISS
onShowListener?.invoke(TAG) }
override suspend fun onNeutralClick(): Boolean {
showRemoveEventDialog()
return NO_DISMISS
}
override suspend fun onBeforeShow(): Boolean {
EventBus.getDefault().register(this@EventManualDialog) EventBus.getDefault().register(this@EventManualDialog)
b = DialogEventManualV2Binding.inflate(activity.layoutInflater) return true
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.dialog_event_manual_title)
.setView(b.root)
.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
.setPositiveButton(R.string.save, null)
.apply {
if (editingEvent != null) {
setNeutralButton(R.string.remove, null)
} }
}
.setOnDismissListener { override fun onDismiss() {
onDismissListener?.invoke(TAG)
EventBus.getDefault().unregister(this@EventManualDialog) EventBus.getDefault().unregister(this@EventManualDialog)
enqueuedWeekDialog?.dismiss() enqueuedWeekDialog?.dismiss()
progressDialog?.dismiss() progressDialog?.dismiss()
} }
.setCancelable(false)
.create()
.apply {
setOnShowListener { dialog ->
val positiveButton = (dialog as AlertDialog).getButton(BUTTON_POSITIVE)
positiveButton?.setOnClickListener {
saveEvent()
}
val neutralButton = dialog.getButton(BUTTON_NEUTRAL)
neutralButton?.setOnClickListener {
showRemoveEventDialog()
}
}
show()
}
override suspend fun onShow() {
b.shareSwitch.isChecked = editingShared b.shareSwitch.isChecked = editingShared
b.shareSwitch.isEnabled = !editingShared || (editingShared && editingOwn) b.shareSwitch.isEnabled = !editingShared || (editingShared && editingOwn)
@ -152,7 +135,7 @@ class EventManualDialog(
}, },
onShowListener, onShowListener,
onDismissListener onDismissListener
) ).show()
} }
stylingConfig = StylingConfigBase(editText = b.topic, htmlMode = SIMPLE) stylingConfig = StylingConfigBase(editText = b.topic, htmlMode = SIMPLE)
@ -163,7 +146,7 @@ class EventManualDialog(
} }
loadLists() loadLists()
}} }
private fun updateShareText(checked: Boolean = b.shareSwitch.isChecked) { private fun updateShareText(checked: Boolean = b.shareSwitch.isChecked) {
b.shareDetails.visibility = if (checked || editingShared) b.shareDetails.visibility = if (checked || editingShared)
@ -259,13 +242,13 @@ class EventManualDialog(
progressDialog?.dismiss() progressDialog?.dismiss()
} }
private fun loadLists() = launch { private suspend fun loadLists() {
val profile = withContext(Dispatchers.Default) { val profile = withContext(Dispatchers.Default) {
app.db.profileDao().getByIdNow(profileId) app.db.profileDao().getByIdNow(profileId)
} }
if (profile == null) { if (profile == null) {
Toast.makeText(activity, R.string.event_manual_no_profile, Toast.LENGTH_SHORT).show() Toast.makeText(activity, R.string.event_manual_no_profile, Toast.LENGTH_SHORT).show()
return@launch return
} }
this@EventManualDialog.profile = profile this@EventManualDialog.profile = profile
@ -611,7 +594,7 @@ class EventManualDialog(
it.teamName = b.teamDropdown.getSelected()?.name it.teamName = b.teamDropdown.getSelected()?.name
it.typeName = b.typeDropdown.getSelected()?.name it.typeName = b.typeDropdown.getSelected()?.name
}) })
dialog.dismiss() dismiss()
Toast.makeText(activity, R.string.saved, Toast.LENGTH_SHORT).show() Toast.makeText(activity, R.string.saved, Toast.LENGTH_SHORT).show()
} }
private fun finishRemoving() { private fun finishRemoving() {
@ -624,7 +607,7 @@ class EventManualDialog(
removeEventDialog?.dismiss() removeEventDialog?.dismiss()
onSaveListener?.invoke(null) onSaveListener?.invoke(null)
dialog.dismiss() dismiss()
Toast.makeText(activity, R.string.removed, Toast.LENGTH_SHORT).show() Toast.makeText(activity, R.string.removed, Toast.LENGTH_SHORT).show()
} }
} }

View File

@ -1,56 +1,39 @@
package pl.szczodrzynski.edziennik.ui.grades package pl.szczodrzynski.edziennik.ui.grades
import androidx.appcompat.app.AlertDialog import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.* import kotlinx.coroutines.withContext
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.full.GradeFull import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ext.setTintColor import pl.szczodrzynski.edziennik.ext.setTintColor
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
import pl.szczodrzynski.edziennik.utils.BetterLink import pl.szczodrzynski.edziennik.utils.BetterLink
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext
class GradeDetailsDialog( class GradeDetailsDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val grade: GradeFull, private val grade: GradeFull,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<DialogGradeDetailsBinding>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "GradeDetailsDialog"
}
private lateinit var app: App override val TAG = "GradeDetailsDialog"
private lateinit var b: DialogGradeDetailsBinding
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitleRes(): Int? = null
override val coroutineContext: CoroutineContext override fun inflate(layoutInflater: LayoutInflater) =
get() = job + Dispatchers.Main DialogGradeDetailsBinding.inflate(layoutInflater)
// local variables go here override fun getPositiveButtonText() = R.string.close
init { run { override suspend fun onShow() {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
b = DialogGradeDetailsBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setView(b.root)
.setPositiveButton(R.string.close, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
val manager = app.gradesManager val manager = app.gradesManager
val gradeColor = manager.getGradeColor(grade) val gradeColor = manager.getGradeColor(grade)
@ -58,15 +41,23 @@ class GradeDetailsDialog(
b.weightText = manager.getWeightString(app, grade) b.weightText = manager.getWeightString(app, grade)
b.commentVisible = false b.commentVisible = false
b.devMode = App.devMode b.devMode = App.devMode
b.gradeName.setTextColor(if (ColorUtils.calculateLuminance(gradeColor) > 0.3) 0xaa000000.toInt() else 0xccffffff.toInt()) b.gradeName.setTextColor(
if (ColorUtils.calculateLuminance(gradeColor) > 0.3)
0xaa000000.toInt()
else
0xccffffff.toInt()
)
b.gradeName.background.setTintColor(gradeColor) b.gradeName.background.setTintColor(gradeColor)
b.gradeValue = if (grade.weight == 0f || grade.value < 0f) -1f else manager.getGradeValue(grade) b.gradeValue = if (grade.weight == 0f || grade.value < 0f)
-1f
else
manager.getGradeValue(grade)
b.customValueDivider.isVisible = manager.plusValue != null || manager.minusValue != null b.customValueDivider.isVisible = manager.plusValue != null || manager.minusValue != null
b.customValueLayout.isVisible = b.customValueDivider.isVisible b.customValueLayout.isVisible = b.customValueDivider.isVisible
b.customValueButton.onClick { b.customValueButton.onClick {
GradesConfigDialog(activity, reloadOnDismiss = true) GradesConfigDialog(activity, reloadOnDismiss = true).show()
} }
grade.teacherName?.let { name -> grade.teacherName?.let { name ->
@ -77,24 +68,26 @@ class GradeDetailsDialog(
) )
} }
launch {
val historyList = withContext(Dispatchers.Default) { val historyList = withContext(Dispatchers.Default) {
app.db.gradeDao().getByParentIdNow(App.profileId, grade.id) app.db.gradeDao().getByParentIdNow(App.profileId, grade.id)
} }
if (historyList.isEmpty()) { if (historyList.isEmpty()) {
b.historyVisible = false b.historyVisible = false
return@launch return
} }
b.historyVisible = true b.historyVisible = true
//b.gradeHistoryNest.isNestedScrollingEnabled = false //b.gradeHistoryNest.isNestedScrollingEnabled = false
b.gradeHistoryList.adapter = GradesAdapter(activity, { b.gradeHistoryList.adapter = GradesAdapter(activity, {
GradeDetailsDialog(activity, it) GradeDetailsDialog(activity, it).show()
}).also { it.items = historyList.toMutableList() } }).also {
it.items = historyList.toMutableList()
}
b.gradeHistoryList.apply { b.gradeHistoryList.apply {
setHasFixedSize(true) setHasFixedSize(true)
layoutManager = LinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
addItemDecoration(SimpleDividerItemDecoration(context)) addItemDecoration(SimpleDividerItemDecoration(context))
} }
} }
}}
} }

View File

@ -110,7 +110,7 @@ class GradesListFragment : Fragment(), CoroutineScope {
}}) }})
adapter.onGradeClick = { adapter.onGradeClick = {
GradeDetailsDialog(activity, it) GradeDetailsDialog(activity, it).show()
} }
adapter.onGradesEditorClick = { subject, semester -> adapter.onGradesEditorClick = { subject, semester ->
@ -140,7 +140,7 @@ class GradesListFragment : Fragment(), CoroutineScope {
.withIcon(CommunityMaterial.Icon.cmd_cog_outline) .withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener(View.OnClickListener { .withOnClickListener(View.OnClickListener {
activity.bottomSheet.close() activity.bottomSheet.close()
GradesConfigDialog(activity, true, null, null) GradesConfigDialog(activity, true, null, null).show()
}), }),
BottomSheetSeparatorItem(true), BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true) BottomSheetPrimaryItem(true)

View File

@ -105,7 +105,7 @@ class StatsViewHolder(
b.customValueDivider.isVisible = manager.dontCountEnabled || manager.plusValue != null || manager.minusValue != null b.customValueDivider.isVisible = manager.dontCountEnabled || manager.plusValue != null || manager.minusValue != null
b.customValueLayout.isVisible = b.customValueDivider.isVisible b.customValueLayout.isVisible = b.customValueDivider.isVisible
b.customValueButton.onClick { b.customValueButton.onClick {
GradesConfigDialog(activity, reloadOnDismiss = true) GradesConfigDialog(activity, reloadOnDismiss = true).show()
} }
} }

View File

@ -70,7 +70,7 @@ class CounterActivity : AppCompatActivity(), CoroutineScope {
} }
) )
b.bellSync.onClick { b.bellSync.onClick {
BellSyncTimeChooseDialog(activity = this@CounterActivity) BellSyncTimeChooseDialog(activity = this@CounterActivity).show()
} }
app.config.timetable.bellSyncDiff?.let { app.config.timetable.bellSyncDiff?.let {

View File

@ -4,77 +4,66 @@
package pl.szczodrzynski.edziennik.ui.home package pl.szczodrzynski.edziennik.ui.home
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_EVENTS import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_EVENTS
import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_GRADES import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_GRADES
import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_LUCKY_NUMBER import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_LUCKY_NUMBER
import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_TIMETABLE import pl.szczodrzynski.edziennik.ui.home.HomeCard.Companion.CARD_TIMETABLE
import kotlin.collections.set
class HomeConfigDialog( class HomeConfigDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
private val reloadOnDismiss: Boolean = true, private val reloadOnDismiss: Boolean = true,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) { ) : BaseDialog(activity, onShowListener, onDismissListener) {
companion object {
const val TAG = "HomeConfigDialog" override val TAG = "HomeConfigDialog"
}
override fun getTitleRes() = R.string.home_configure_add_remove
override fun getPositiveButtonText() = R.string.ok
override fun getNegativeButtonText() = R.string.cancel
override fun getMultiChoiceItems(): Map<CharSequence, Any> = mapOf(
R.string.card_type_lucky_number to CARD_LUCKY_NUMBER,
R.string.card_type_timetable to CARD_TIMETABLE,
R.string.card_type_grades to CARD_GRADES,
R.string.card_type_events to CARD_EVENTS,
).mapKeys { (resId, _) -> activity.getString(resId) }
override fun getDefaultSelectedItems() =
profileConfig.homeCards
.filter { it.profileId == App.profileId }
.map { it.cardId }
.toSet()
override suspend fun onShow() = Unit
private val app by lazy { activity.application as App }
private val profileConfig by lazy { app.config.getFor(app.profileId).ui } private val profileConfig by lazy { app.config.getFor(app.profileId).ui }
private var configChanged = false
private lateinit var dialog: AlertDialog override suspend fun onPositiveClick(): Boolean {
val homeCards = profileConfig.homeCards.toMutableList()
init { run { homeCards.removeAll { it.profileId == App.profileId }
if (activity.isFinishing) homeCards += getMultiSelection().mapNotNull {
return@run HomeCardModel(
onShowListener?.invoke(TAG) profileId = App.profileId,
cardId = it as? Int ?: return@mapNotNull null
val ids = listOf(
CARD_LUCKY_NUMBER,
CARD_TIMETABLE,
CARD_GRADES,
CARD_EVENTS
) )
val items = listOf(
app.getString(R.string.card_type_lucky_number),
app.getString(R.string.card_type_timetable),
app.getString(R.string.card_type_grades),
app.getString(R.string.card_type_events)
)
val checkedItems = ids.map { it to false }.toMap().toMutableMap()
val profileId = App.profileId
val homeCards = profileConfig.homeCards
.filter { it.profileId == profileId }
.toMutableList()
homeCards.forEach {
checkedItems[it.cardId] = true
} }
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.home_configure_add_remove)
.setMultiChoiceItems(items.toTypedArray(), checkedItems.values.toBooleanArray()) { _, which, isChecked ->
if (isChecked) {
homeCards += HomeCardModel(profileId, ids[which])
}
else {
homeCards.removeAll { it.profileId == profileId && it.cardId == ids[which] }
}
}
.setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() }
.setOnDismissListener {
profileConfig.homeCards = homeCards profileConfig.homeCards = homeCards
onDismissListener?.invoke(TAG) return DISMISS
if (reloadOnDismiss) (activity as? MainActivity)?.reloadTarget() }
override suspend fun onMultiSelectionChanged(items: Set<Any>) {
configChanged = true
}
override fun onDismiss() {
if (configChanged && reloadOnDismiss && activity is MainActivity)
activity.reloadTarget()
} }
.show()
}}
} }

View File

@ -102,7 +102,7 @@ class HomeFragment : Fragment(), CoroutineScope {
.withIcon(Icon.cmd_card_bulleted_settings_outline) .withIcon(Icon.cmd_card_bulleted_settings_outline)
.withOnClickListener(OnClickListener { .withOnClickListener(OnClickListener {
activity.bottomSheet.close() activity.bottomSheet.close()
HomeConfigDialog(activity, reloadOnDismiss = true) HomeConfigDialog(activity, reloadOnDismiss = true).show()
}), }),
BottomSheetPrimaryItem(true) BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_set_student_number) .withTitle(R.string.menu_set_student_number)
@ -131,7 +131,7 @@ class HomeFragment : Fragment(), CoroutineScope {
}) })
) )
b.configureCards.onClick { b.configureCards.onClick {
HomeConfigDialog(activity, reloadOnDismiss = true) HomeConfigDialog(activity, reloadOnDismiss = true).show()
} }
b.scrollView.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, _: Int -> b.scrollView.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, _: Int ->

View File

@ -72,7 +72,7 @@ class HomeAvailabilityCard(
if (status.userMessage.icon != null) if (status.userMessage.icon != null)
b.homeAvailabilityIcon.load(status.userMessage.icon) b.homeAvailabilityIcon.load(status.userMessage.icon)
onInfoClick = { onInfoClick = {
RegisterUnavailableDialog(activity, status) RegisterUnavailableDialog(activity, status).show()
} }
} }
// show "update available" when available OR version too old for the register // show "update available" when available OR version too old for the register
@ -82,7 +82,7 @@ class HomeAvailabilityCard(
b.homeAvailabilityUpdate.isVisible = true b.homeAvailabilityUpdate.isVisible = true
b.homeAvailabilityIcon.setImageResource(R.drawable.ic_update) b.homeAvailabilityIcon.setImageResource(R.drawable.ic_update)
onInfoClick = { onInfoClick = {
UpdateAvailableDialog(activity, update) UpdateAvailableDialog(activity, update).show()
} }
} }
else { else {

View File

@ -87,7 +87,7 @@ class HomeDebugCard(
b.librusCaptchaButton.onClick { b.librusCaptchaButton.onClick {
//app.startActivity(Intent(activity, LoginLibrusCaptchaActivity::class.java)) //app.startActivity(Intent(activity, LoginLibrusCaptchaActivity::class.java))
LibrusCaptchaDialog(activity, onSuccess = {}, onFailure = {}) LibrusCaptchaDialog(activity, onSuccess = {}, onFailure = {}).show()
} }
b.getLogs.onClick { b.getLogs.onClick {

View File

@ -70,14 +70,14 @@ class HomeEventsCard(
EventDetailsDialog( EventDetailsDialog(
activity, activity,
it it
) ).show()
}, },
onEventEditClick = { onEventEditClick = {
EventManualDialog( EventManualDialog(
activity, activity,
it.profileId, it.profileId,
editingEvent = it editingEvent = it
) ).show()
} }
) )

View File

@ -112,7 +112,7 @@ class HomeTimetableCard(
b.bellSync.setOnClickListener { b.bellSync.setOnClickListener {
BellSyncTimeChooseDialog( BellSyncTimeChooseDialog(
activity activity
) ).show()
} }
b.showCounter.setOnClickListener { b.showCounter.setOnClickListener {

View File

@ -65,7 +65,7 @@ class HomeworkFragment : Fragment(), CoroutineScope {
.withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline) .withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
.withOnClickListener(View.OnClickListener { .withOnClickListener(View.OnClickListener {
activity.bottomSheet.close() activity.bottomSheet.close()
EventManualDialog(activity, App.profileId, defaultType = Event.TYPE_HOMEWORK) EventManualDialog(activity, App.profileId, defaultType = Event.TYPE_HOMEWORK).show()
}), }),
BottomSheetSeparatorItem(true), BottomSheetSeparatorItem(true),
BottomSheetPrimaryItem(true) BottomSheetPrimaryItem(true)
@ -108,7 +108,7 @@ class HomeworkFragment : Fragment(), CoroutineScope {
} }
setFabOnClickListener(View.OnClickListener { setFabOnClickListener(View.OnClickListener {
EventManualDialog(activity, App.profileId, defaultType = Event.TYPE_HOMEWORK) EventManualDialog(activity, App.profileId, defaultType = Event.TYPE_HOMEWORK).show()
}) })
} }

View File

@ -68,14 +68,14 @@ class HomeworkListFragment : LazyFragment(), CoroutineScope {
EventDetailsDialog( EventDetailsDialog(
activity, activity,
it it
) ).show()
}, },
onEventEditClick = { onEventEditClick = {
EventManualDialog( EventManualDialog(
activity, activity,
it.profileId, it.profileId,
editingEvent = it editingEvent = it
) ).show()
} }
) )

View File

@ -275,7 +275,7 @@ class LoginChooserFragment : Fragment(), CoroutineScope {
return when (error.type) { return when (error.type) {
Type.NOT_AVAILABLE -> { Type.NOT_AVAILABLE -> {
RegisterUnavailableDialog(activity, error.status!!) RegisterUnavailableDialog(activity, error.status!!).show()
false false
} }
Type.API_ERROR -> { Type.API_ERROR -> {

View File

@ -141,7 +141,7 @@ class MessagesComposeFragment : Fragment(), CoroutineScope {
.withIcon(CommunityMaterial.Icon.cmd_cog_outline) .withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener { .withOnClickListener {
activity.bottomSheet.close() activity.bottomSheet.close()
MessagesConfigDialog(activity, false, null, null) MessagesConfigDialog(activity, false, null, null).show()
} }
) )

View File

@ -118,7 +118,7 @@ class MessagesFragment : Fragment(), CoroutineScope {
.withIcon(CommunityMaterial.Icon.cmd_cog_outline) .withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener { .withOnClickListener {
activity.bottomSheet.close() activity.bottomSheet.close()
MessagesConfigDialog(activity, false, null, null) MessagesConfigDialog(activity, false, null, null).show()
} }
) )

View File

@ -74,7 +74,7 @@ class MessageFragment : Fragment(), CoroutineScope {
.withIcon(CommunityMaterial.Icon.cmd_cog_outline) .withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.withOnClickListener { .withOnClickListener {
activity.bottomSheet.close() activity.bottomSheet.close()
MessagesConfigDialog(activity, false, null, null) MessagesConfigDialog(activity, false, null, null).show()
} }
) )

View File

@ -104,7 +104,7 @@ class SettingsAboutCard(util: SettingsUtil) : SettingsCard(util), CoroutineScope
text = R.string.settings_about_changelog_text, text = R.string.settings_about_changelog_text,
icon = CommunityMaterial.Icon3.cmd_radar icon = CommunityMaterial.Icon3.cmd_radar
) { ) {
ChangelogDialog(activity) ChangelogDialog(activity).show()
}, },
util.createActionItem( util.createActionItem(

View File

@ -31,7 +31,7 @@ class SettingsProfileCard(util: SettingsUtil) : SettingsCard(util) {
card.items.remove(item) card.items.remove(item)
card.items.add(index, getProfileItem()) card.items.add(index, getProfileItem())
util.refresh() util.refresh()
}) }).show()
} }
override fun getItems() = listOf( override fun getItems() = listOf(

View File

@ -59,28 +59,28 @@ class SettingsRegisterCard(util: SettingsUtil) : SettingsCard(util) {
text = R.string.menu_agenda_config, text = R.string.menu_agenda_config,
icon = CommunityMaterial.Icon.cmd_calendar_outline icon = CommunityMaterial.Icon.cmd_calendar_outline
) { ) {
AgendaConfigDialog(activity, reloadOnDismiss = false) AgendaConfigDialog(activity, reloadOnDismiss = false).show()
}, },
util.createActionItem( util.createActionItem(
text = R.string.menu_grades_config, text = R.string.menu_grades_config,
icon = CommunityMaterial.Icon3.cmd_numeric_5_box_outline icon = CommunityMaterial.Icon3.cmd_numeric_5_box_outline
) { ) {
GradesConfigDialog(activity, reloadOnDismiss = false) GradesConfigDialog(activity, reloadOnDismiss = false).show()
}, },
util.createActionItem( util.createActionItem(
text = R.string.menu_messages_config, text = R.string.menu_messages_config,
icon = CommunityMaterial.Icon.cmd_calendar_outline icon = CommunityMaterial.Icon.cmd_calendar_outline
) { ) {
MessagesConfigDialog(activity, reloadOnDismiss = false) MessagesConfigDialog(activity, reloadOnDismiss = false).show()
}, },
util.createActionItem( util.createActionItem(
text = R.string.menu_attendance_config, text = R.string.menu_attendance_config,
icon = CommunityMaterial.Icon.cmd_calendar_remove_outline icon = CommunityMaterial.Icon.cmd_calendar_remove_outline
) { ) {
AttendanceConfigDialog(activity, reloadOnDismiss = false) AttendanceConfigDialog(activity, reloadOnDismiss = false).show()
}, },
util.createPropertyItem( util.createPropertyItem(
@ -126,7 +126,7 @@ class SettingsRegisterCard(util: SettingsUtil) : SettingsCard(util) {
BellSyncConfigDialog(activity, onChangeListener = { BellSyncConfigDialog(activity, onChangeListener = {
it.subText = getBellSync() it.subText = getBellSync()
util.refresh() util.refresh()
}) }).show()
} }
).also { ).also {
it.subText = getBellSync() it.subText = getBellSync()

View File

@ -108,7 +108,7 @@ class SettingsSyncCard(util: SettingsUtil) : SettingsCard(util) {
item.onCheckedChangedAction.onCheckedChanged(item, true) item.onCheckedChangedAction.onCheckedChanged(item, true)
if (configGlobal.sync.enabled) if (configGlobal.sync.enabled)
util.refresh() util.refresh()
}) }).show()
} }
).also { ).also {
it.subTextChecked = activity.getSyncInterval(configGlobal.sync.interval) it.subTextChecked = activity.getSyncInterval(configGlobal.sync.interval)
@ -124,7 +124,7 @@ class SettingsSyncCard(util: SettingsUtil) : SettingsCard(util) {
subText = R.string.settings_profile_notifications_subtext, subText = R.string.settings_profile_notifications_subtext,
icon = CommunityMaterial.Icon2.cmd_filter_outline icon = CommunityMaterial.Icon2.cmd_filter_outline
) { ) {
NotificationFilterDialog(activity) NotificationFilterDialog(activity).show()
}, },
util.createPropertyActionItem( util.createPropertyActionItem(

View File

@ -54,7 +54,7 @@ class SettingsThemeCard(util: SettingsUtil) : SettingsCard(util) {
subText = Themes.getThemeNameRes(), subText = Themes.getThemeNameRes(),
icon = CommunityMaterial.Icon3.cmd_palette_outline icon = CommunityMaterial.Icon3.cmd_palette_outline
) { ) {
ThemeChooserDialog(activity) ThemeChooserDialog(activity).show()
}, },
util.createActionItem( util.createActionItem(
@ -62,7 +62,7 @@ class SettingsThemeCard(util: SettingsUtil) : SettingsCard(util) {
subText = R.string.settings_about_language_subtext, subText = R.string.settings_about_language_subtext,
icon = CommunityMaterial.Icon3.cmd_translate icon = CommunityMaterial.Icon3.cmd_translate
) { ) {
AppLanguageDialog(activity) AppLanguageDialog(activity).show()
}, },
util.createPropertyItem( util.createPropertyItem(
@ -81,7 +81,7 @@ class SettingsThemeCard(util: SettingsUtil) : SettingsCard(util) {
text = R.string.settings_theme_mini_drawer_buttons_text, text = R.string.settings_theme_mini_drawer_buttons_text,
icon = CommunityMaterial.Icon2.cmd_format_list_checks icon = CommunityMaterial.Icon2.cmd_format_list_checks
) { ) {
MiniMenuConfigDialog(activity) MiniMenuConfigDialog(activity).show()
}, },
util.createActionItem( util.createActionItem(

View File

@ -4,63 +4,77 @@
package pl.szczodrzynski.edziennik.ui.template package pl.szczodrzynski.edziennik.ui.template
import androidx.appcompat.app.AlertDialog import android.view.LayoutInflater
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.DialogTemplateBinding import pl.szczodrzynski.edziennik.databinding.DialogTemplateBinding
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ui.dialogs.base.BaseDialog
import kotlin.coroutines.CoroutineContext import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.ui.dialogs.base.ViewDialog
/**
* This class represents a sample dialog using the new style.
*
* A dialog may subclass the [BaseDialog], [ViewDialog] or [BindingDialog].
*
* Fields and methods have the preferred order which should be used when writing new code.
* The position of the first occurrence of duplicated methods should be used.
* Multi-line methods should be followed by a blank line, one-liners may be just joined together.
*
* Constructor properties should be private.
*
* [onShow], when not used, should be placed just before the local variables, as a one-liner.
* All other multi-line methods go below the local variables part.
*/
class TemplateDialog( class TemplateDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val onActionPerformed: (() -> Unit)? = null, private val onActionPerformed: (() -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<DialogTemplateBinding>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "TemplateDialog"
}
private lateinit var app: App override val TAG = "TemplateDialog"
private lateinit var b: DialogTemplateBinding
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitle(): CharSequence = "Template"
override val coroutineContext: CoroutineContext override fun getTitleRes() = R.string.menu_template
get() = job + Dispatchers.Main override fun inflate(layoutInflater: LayoutInflater) =
DialogTemplateBinding.inflate(layoutInflater)
// override fun getTitle(): CharSequence = "Template"
// override fun getTitleRes() = R.string.menu_template
// override fun getMessage() = ""
// override fun getMessageRes() = R.string.edziennik_progress_login_template_api
// override fun getMessageFormat() =
// R.string.card_update_text_format to listOf(
// BuildConfig.VERSION_BASE,
// "5.0",
// )
// override fun getTitleRes() = R.string.menu_template
override fun isCancelable() = true
override fun getPositiveButtonText() = R.string.ok
override fun getNeutralButtonText() = R.string.reset
override fun getNegativeButtonText() = R.string.cancel
// getSingleChoiceItem / getMultiChoiceItems
// getDefaultSelectedItem / getDefaultSelectedItems
// to convert a map of StringIDs to CharSequences
// .mapKeys { (resId, _) -> activity.getString(resId) }
override suspend fun onShow() = Unit
// local variables go here // local variables go here
init { run { // onPositiveClick
if (activity.isFinishing) // onNeutralClick
return@run // onNegativeClick
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
b = DialogTemplateBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setView(b.root)
.setPositiveButton(R.string.close) { dialog, _ ->
dialog.dismiss()
}
.setNeutralButton(R.string.add, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)?.onClick { // onSingleSelectionChanged
// do custom action on neutral button click // onMultiSelectionChanged
// (does not dismiss the dialog)
}
b.clickMe.onClick { // getRootView
onActionPerformed?.invoke() // onBeforeShow
dialog.dismiss() // onShow
} // onDismiss
}}
} }

View File

@ -5,19 +5,14 @@
package pl.szczodrzynski.edziennik.ui.timetable package pl.szczodrzynski.edziennik.ui.timetable
import android.content.Intent import android.content.Intent
import android.view.LayoutInflater
import android.view.View import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp import com.mikepenz.iconics.utils.sizeDp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Lesson import pl.szczodrzynski.edziennik.data.db.entity.Lesson
@ -27,6 +22,7 @@ import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding
import pl.szczodrzynski.edziennik.ext.onClick import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ext.setText import pl.szczodrzynski.edziennik.ext.setText
import pl.szczodrzynski.edziennik.ui.attendance.AttendanceDetailsDialog import pl.szczodrzynski.edziennik.ui.attendance.AttendanceDetailsDialog
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
import pl.szczodrzynski.edziennik.ui.event.EventDetailsDialog import pl.szczodrzynski.edziennik.ui.event.EventDetailsDialog
import pl.szczodrzynski.edziennik.ui.event.EventListAdapter import pl.szczodrzynski.edziennik.ui.event.EventListAdapter
import pl.szczodrzynski.edziennik.ui.event.EventManualDialog import pl.szczodrzynski.edziennik.ui.event.EventManualDialog
@ -34,26 +30,23 @@ import pl.szczodrzynski.edziennik.utils.BetterLink
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Week import pl.szczodrzynski.edziennik.utils.models.Week
import kotlin.coroutines.CoroutineContext
class LessonDetailsDialog( class LessonDetailsDialog(
val activity: AppCompatActivity, activity: AppCompatActivity,
val lesson: LessonFull, private val lesson: LessonFull,
val attendance: AttendanceFull? = null, private val attendance: AttendanceFull? = null,
val onShowListener: ((tag: String) -> Unit)? = null, onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null onDismissListener: ((tag: String) -> Unit)? = null,
) : CoroutineScope { ) : BindingDialog<DialogLessonDetailsBinding>(activity, onShowListener, onDismissListener) {
companion object {
private const val TAG = "LessonDetailsDialog"
}
private lateinit var app: App override val TAG = "LessonDetailsDialog"
private lateinit var b: DialogLessonDetailsBinding
private lateinit var dialog: AlertDialog
private val job = Job() override fun getTitleRes(): Int? = null
override val coroutineContext: CoroutineContext override fun inflate(layoutInflater: LayoutInflater) =
get() = job + Dispatchers.Main DialogLessonDetailsBinding.inflate(layoutInflater)
override fun getPositiveButtonText() = R.string.close
override fun getNeutralButtonText() = R.string.add
private lateinit var adapter: EventListAdapter private lateinit var adapter: EventListAdapter
private val manager private val manager
@ -61,44 +54,26 @@ class LessonDetailsDialog(
private val attendanceManager private val attendanceManager
get() = app.attendanceManager get() = app.attendanceManager
init { run { override suspend fun onNeutralClick(): Boolean {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
b = DialogLessonDetailsBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setView(b.root)
.setPositiveButton(R.string.close) { dialog, _ ->
dialog.dismiss()
}
.setNeutralButton(R.string.add, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)?.onClick {
EventManualDialog( EventManualDialog(
activity, activity,
lesson.profileId, lesson.profileId,
defaultLesson = lesson, defaultLesson = lesson,
onShowListener = onShowListener, onShowListener = onShowListener,
onDismissListener = onDismissListener onDismissListener = onDismissListener
) ).show()
return NO_DISMISS
} }
override suspend fun onShow() {
if (App.devMode) if (App.devMode)
b.lessonId.visibility = View.VISIBLE b.lessonId.visibility = View.VISIBLE
update()
}}
private fun update() {
b.lesson = lesson b.lesson = lesson
val lessonDate = lesson.displayDate ?: return val lessonDate = lesson.displayDate ?: return
val lessonTime = lesson.displayStartTime ?: return val lessonTime = lesson.displayStartTime ?: return
b.lessonDate.text = Week.getFullDayName(lessonDate.weekDay) + ", " + lessonDate.formattedString b.lessonDate.text =
Week.getFullDayName(lessonDate.weekDay) + ", " + lessonDate.formattedString
b.annotationVisible = manager.getAnnotation(activity, lesson, b.annotation) b.annotationVisible = manager.getAnnotation(activity, lesson, b.annotation)
@ -145,8 +120,7 @@ class LessonDetailsDialog(
} }
activity.sendBroadcast(intent) activity.sendBroadcast(intent)
} }
} } else {
else {
b.shiftedLayout.visibility = View.GONE b.shiftedLayout.visibility = View.GONE
} }
@ -195,7 +169,12 @@ class LessonDetailsDialog(
true true
} }
b.attendanceDetails.onClick { b.attendanceDetails.onClick {
AttendanceDetailsDialog(activity, attendance, onShowListener, onDismissListener) AttendanceDetailsDialog(
activity = activity,
attendance = attendance,
onShowListener = onShowListener,
onDismissListener = onDismissListener,
).show()
} }
} }
@ -213,7 +192,7 @@ class LessonDetailsDialog(
it, it,
onShowListener = onShowListener, onShowListener = onShowListener,
onDismissListener = onDismissListener onDismissListener = onDismissListener
) ).show()
}, },
onEventEditClick = { onEventEditClick = {
EventManualDialog( EventManualDialog(
@ -222,11 +201,15 @@ class LessonDetailsDialog(
editingEvent = it, editingEvent = it,
onShowListener = onShowListener, onShowListener = onShowListener,
onDismissListener = onDismissListener onDismissListener = onDismissListener
) ).show()
} }
) )
app.db.eventDao().getAllByDateTime(lesson.profileId, lessonDate, lessonTime).observe(activity, Observer { events -> app.db.eventDao().getAllByDateTime(
lesson.profileId,
lessonDate,
lessonTime
).observe(activity) { events ->
adapter.setAllItems(events) adapter.setAllItems(events)
if (b.eventsView.adapter == null) { if (b.eventsView.adapter == null) {
b.eventsView.adapter = adapter b.eventsView.adapter = adapter
@ -246,7 +229,7 @@ class LessonDetailsDialog(
b.eventsView.visibility = View.GONE b.eventsView.visibility = View.GONE
b.eventsNoData.visibility = View.VISIBLE b.eventsNoData.visibility = View.VISIBLE
} }
}) }
lesson.displayTeacherName?.let { name -> lesson.displayTeacherName?.let { name ->
lesson.displayTeacherId ?: return@let lesson.displayTeacherId ?: return@let

View File

@ -225,7 +225,7 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope {
activity = activity, activity = activity,
lesson = lessonObj as LessonFull, lesson = lessonObj as LessonFull,
attendance = attendanceObj as AttendanceFull? attendance = attendanceObj as AttendanceFull?
) ).show()
} }
} }

View File

@ -189,7 +189,7 @@ class TimetableFragment : Fragment(), CoroutineScope {
.withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline) .withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
.withOnClickListener(View.OnClickListener { .withOnClickListener(View.OnClickListener {
activity.bottomSheet.close() activity.bottomSheet.close()
EventManualDialog(activity, App.profileId, defaultDate = pageSelection) EventManualDialog(activity, App.profileId, defaultDate = pageSelection).show()
}), }),
BottomSheetPrimaryItem(true) BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_generate_block_timetable) .withTitle(R.string.menu_generate_block_timetable)

View File

@ -65,7 +65,7 @@ class WebPushFragment : Fragment(), CoroutineScope {
QrScannerDialog(activity, { QrScannerDialog(activity, {
b.tokenEditText.setText(it.crc32().toString(36).uppercase()) b.tokenEditText.setText(it.crc32().toString(36).uppercase())
pairBrowser(browserId = it) pairBrowser(browserId = it)
}) }).show()
} }
} }

View File

@ -71,7 +71,7 @@ class LessonDialogActivity : AppCompatActivity(), CoroutineScope {
if (shownDialogs.isEmpty()) if (shownDialogs.isEmpty())
finish() finish()
} }
) ).show()
} }
} }
} }

View File

@ -87,11 +87,15 @@ class UserActionManager(val app: App) {
return return
// show captcha dialog // show captcha dialog
// use passed onSuccess listener, else sync profile // use passed onSuccess listener, else sync profile
LibrusCaptchaDialog(activity, onSuccess = onSuccess ?: { code -> LibrusCaptchaDialog(
activity = activity,
onSuccess = onSuccess ?: { code ->
EdziennikTask.syncProfile(profileId, arguments = JsonObject( EdziennikTask.syncProfile(profileId, arguments = JsonObject(
"recaptchaCode" to code, "recaptchaCode" to code,
"recaptchaTime" to System.currentTimeMillis() "recaptchaTime" to System.currentTimeMillis()
)).enqueue(activity) )).enqueue(activity)
}, onFailure = onFailure) },
onFailure = onFailure
).show()
} }
} }