[UI] Add new Settings fragment.

This commit is contained in:
Kuba Szczodrzyński 2021-03-22 21:01:04 +01:00
parent 07561c6484
commit 208d097986
33 changed files with 1920 additions and 177 deletions

View File

@ -115,7 +115,7 @@ dependencies {
implementation "eu.szkolny:agendacalendarview:1799f8ef47"
implementation "eu.szkolny:cafebar:5bf0c618de"
implementation "eu.szkolny.fslogin:lib:2.0.0"
implementation "eu.szkolny:material-about-library:0534abf316"
implementation "eu.szkolny:material-about-library:fe4a5cd6f1"
implementation "eu.szkolny:mhttp:af4b62e6e9"
implementation "eu.szkolny:nachos:0e5dfcaceb"
implementation "eu.szkolny.selective-dao:annotation:27f8f3f194"
@ -129,7 +129,7 @@ dependencies {
implementation "com.mikepenz:iconics-core:5.2.8"
implementation "com.mikepenz:iconics-views:5.2.8"
implementation "com.mikepenz:community-material-typeface:5.8.55.0-kotlin@aar"
implementation "eu.szkolny:szkolny-font:1dab7d64ed"
implementation "eu.szkolny:szkolny-font:1.2"
// Other dependencies
implementation "cat.ereza:customactivityoncrash:2.3.0"

View File

@ -1278,3 +1278,41 @@ operator fun <K, V> Iterable<Pair<K, V>>.get(key: K): V? {
}
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
fun <E> MutableList<E>.after(what: E, insert: E) {
val index = indexOf(what)
if (index != -1)
add(index + 1, insert)
}
fun <E> MutableList<E>.before(what: E, insert: E) {
val index = indexOf(what)
if (index != -1)
add(index, insert)
}
fun <E> MutableList<E>.after(what: E, insert: Collection<E>) {
val index = indexOf(what)
if (index != -1)
addAll(index + 1, insert)
}
fun <E> MutableList<E>.before(what: E, insert: Collection<E>) {
val index = indexOf(what)
if (index != -1)
addAll(index, insert)
}
fun Context.getSyncInterval(interval: Int): String {
val hours = interval / 60 / 60
val minutes = interval / 60 % 60
val hoursText = if (hours > 0)
plural(R.plurals.time_till_hours, hours)
else
null
val minutesText = if (minutes > 0)
plural(R.plurals.time_till_minutes, minutes)
else
""
return hoursText?.plus(" $minutesText") ?: minutesText
}

View File

@ -79,6 +79,7 @@ import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.compose.MessagesComposeFragment
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsListFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
import pl.szczodrzynski.edziennik.ui.modules.webpush.WebPushFragment
@ -199,12 +200,18 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
.isStatic(true)
.isBelowSeparator(true)
list += NavTarget(DRAWER_ITEM_SETTINGS, R.string.menu_settings, SettingsNewFragment::class)
list += NavTarget(DRAWER_ITEM_SETTINGS, R.string.menu_settings, SettingsFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_cog_outline)
.isInDrawer(true)
.isStatic(true)
.isBelowSeparator(true)
list += NavTarget(DRAWER_ITEM_SETTINGS+1, R.string.menu_settings, SettingsNewFragment::class)
.withIcon(CommunityMaterial.Icon3.cmd_seal)
.isInDrawer(true)
.isStatic(true)
.isBelowSeparator(true)
// profile settings items
list += NavTarget(DRAWER_PROFILE_ADD_NEW, R.string.menu_add_new_profile, null)

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-20.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.bell
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.addTextChangedListener
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.coroutines.CoroutineContext
class BellSyncConfigDialog(
val activity: AppCompatActivity,
val onChangeListener: (() -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "BellSyncConfigDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
private fun parse(input: String): Pair<Time, Int>? {
if (input.length < 8) {
return null
}
if (input[2] != ':' || input[5] != ':') {
return null
}
val multiplier = when {
input[0] == '+' -> 1
input[0] == '-' -> -1
else -> return null
}
val time = Time.fromH_m_s("0" + input.substring(1))
return time to multiplier
}
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
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
} ?: "+0:00:00")
editText?.addTextChangedListener { text ->
val input = text?.toString()
textLayout?.error =
if (input != null && parse(input) == null)
activity.getString(R.string.bell_sync_adjust_error)
else
null
}
dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.onClick {
val input = editText?.text?.toString() ?: return@onClick
val parsed = parse(input)
if (parsed == null) {
Toast.makeText(activity, R.string.bell_sync_adjust_error, Toast.LENGTH_SHORT).show()
return@onClick
}
val (time, multiplier) = parsed
app.config.timetable.bellSyncDiff =
if (time.value == 0)
null
else
time
app.config.timetable.bellSyncMultiplier = multiplier
onChangeListener?.invoke()
dialog.dismiss()
}
}}
}

View File

@ -33,7 +33,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
import pl.szczodrzynski.edziennik.ui.dialogs.sync.RegistrationEnableDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.RegistrationConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.views.TimeDropdown.Companion.DISPLAY_LESSONS
import pl.szczodrzynski.edziennik.utils.Anim
import pl.szczodrzynski.edziennik.utils.TextInputDropDown
@ -64,7 +64,7 @@ class EventManualDialog(
private val app by lazy { activity.application as App }
private lateinit var b: DialogEventManualV2Binding
private lateinit var dialog: AlertDialog
private var profile: Profile? = null
private lateinit var profile: Profile
private var customColor: Int? = null
private val editingShared = editingEvent?.sharedBy != null
@ -80,9 +80,9 @@ class EventManualDialog(
private var progressDialog: AlertDialog? = null
init { run {
init { launch {
if (activity.isFinishing)
return@run
return@launch
onShowListener?.invoke(TAG)
EventBus.getDefault().register(this)
b = DialogEventManualV2Binding.inflate(activity.layoutInflater)
@ -236,8 +236,15 @@ class EventManualDialog(
progressDialog?.dismiss()
}
private fun loadLists() { launch {
profile = withContext(Dispatchers.Default) { app.db.profileDao().getByIdNow(profileId) }
private fun loadLists() = launch {
val profile = withContext(Dispatchers.Default) {
app.db.profileDao().getByIdNow(profileId)
}
if (profile == null) {
Toast.makeText(activity, R.string.event_manual_no_profile, Toast.LENGTH_SHORT).show()
return@launch
}
this@EventManualDialog.profile = profile
with (b.dateDropdown) {
db = app.db
@ -380,7 +387,7 @@ class EventManualDialog(
})
colorPickerDialog.show(activity.supportFragmentManager, "color-picker-dialog")
}
}}
}
private fun showRemoveEventDialog() {
val shareNotice = when {
@ -417,12 +424,11 @@ class EventManualDialog(
val share = b.shareSwitch.isChecked
if (share && profile?.registration != Profile.REGISTRATION_ENABLED) {
RegistrationEnableDialog(activity, profileId).showEventShareDialog {
if (it != null)
profile = it
if (share && profile.registration != Profile.REGISTRATION_ENABLED) {
RegistrationConfigDialog(activity, profile, onChangeListener = { enabled ->
if (enabled)
saveEvent()
}
}).showEventShareDialog()
return
}

View File

@ -13,7 +13,6 @@ import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.databinding.DialogGradeDetailsBinding
import pl.szczodrzynski.edziennik.onClick
import pl.szczodrzynski.edziennik.setTintColor
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration
import kotlin.coroutines.CoroutineContext

View File

@ -2,7 +2,7 @@
* Copyright (c) Kacper Ziubryniewicz 2020-1-16
*/
package pl.szczodrzynski.edziennik.ui.dialogs.settings
package pl.szczodrzynski.edziennik.ui.dialogs.grade
import android.annotation.SuppressLint
import androidx.appcompat.app.AlertDialog

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-19.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.settings
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 pl.szczodrzynski.edziennik.*
import kotlin.coroutines.CoroutineContext
class AppLanguageDialog(
val activity: AppCompatActivity,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "AppLanguageDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
val languages = mapOf(
null to R.string.language_system,
"pl" to R.string.language_polish,
"en" to R.string.language_english,
"de" to R.string.language_german
)
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()
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.settings
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 pl.szczodrzynski.edziennik.App
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_NOTIFICATIONS
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_SETTINGS
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.R
import kotlin.coroutines.CoroutineContext
class MiniMenuConfigDialog(
val activity: AppCompatActivity,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "MiniMenuConfigDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
val buttons = mapOf(
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
}
if (activity is MainActivity) {
activity.setDrawerItems()
activity.drawer.updateBadges()
}
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.settings
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 pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.utils.Themes
import kotlin.coroutines.CoroutineContext
class ThemeChooserDialog(
val activity: AppCompatActivity,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "ThemeChooserDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
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()
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
}

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-20.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.sync
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
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.utils.models.Time
import kotlin.coroutines.CoroutineContext
class QuietHoursConfigDialog(
val activity: AppCompatActivity,
val onChangeListener: (() -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "QuietHoursConfigDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.settings_sync_quiet_hours_dialog_title)
.setItems(arrayOf(
activity.getString(R.string.settings_sync_quiet_hours_set_beginning),
activity.getString(R.string.settings_sync_quiet_hours_set_end)
)) { dialog, which ->
when (which) {
0 -> configStartTime()
1 -> configEndTime()
}
dialog.dismiss()
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
private fun configStartTime() {
onShowListener?.invoke(TAG + "Start")
val time = app.config.sync.quietHoursStart ?: return
val picker = MaterialTimePicker.Builder()
.setTitleText(R.string.settings_sync_quiet_hours_set_beginning)
.setTimeFormat(TimeFormat.CLOCK_24H)
.setHour(time.hour)
.setMinute(time.minute)
.build()
picker.show(activity.supportFragmentManager, TAG)
picker.addOnPositiveButtonClickListener {
app.config.sync.quietHoursEnabled = true
app.config.sync.quietHoursStart = Time(picker.hour, picker.minute, 0)
onChangeListener?.invoke()
}
picker.addOnDismissListener {
onDismissListener?.invoke(TAG + "Start")
}
}
private fun configEndTime() {
onShowListener?.invoke(TAG + "End")
val time = app.config.sync.quietHoursEnd ?: return
val picker = MaterialTimePicker.Builder()
.setTitleText(R.string.settings_sync_quiet_hours_set_end)
.setTimeFormat(TimeFormat.CLOCK_24H)
.setHour(time.hour)
.setMinute(time.minute)
.build()
picker.show(activity.supportFragmentManager, TAG)
picker.addOnPositiveButtonClickListener {
app.config.sync.quietHoursEnabled = true
app.config.sync.quietHoursEnd = Time(picker.hour, picker.minute, 0)
onChangeListener?.invoke()
}
picker.addOnDismissListener {
onDismissListener?.invoke(TAG + "End")
}
}
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-15.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.sync
import android.text.Html
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.task.AppSync
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import kotlin.coroutines.CoroutineContext
class RegistrationConfigDialog(
val activity: AppCompatActivity,
val profile: Profile,
val onChangeListener: ((enabled: Boolean) -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "RegistrationEnableDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
app = activity.applicationContext as App
}}
fun showEventShareDialog() {
onShowListener?.invoke(TAG + "EventShare")
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.registration_config_event_sharing_title)
.setMessage(R.string.registration_config_event_sharing_text)
.setPositiveButton(R.string.i_agree) { _, _ ->
enableRegistration()
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG + "EventShare")
}
.show()
}
fun showEnableDialog() {
onShowListener?.invoke(TAG + "Enable")
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.registration_config_title)
.setMessage(Html.fromHtml(app.getString(R.string.registration_config_enable_text)))
.setPositiveButton(R.string.i_agree) { _, _ ->
enableRegistration()
}
.setNegativeButton(R.string.i_disagree, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG + "Enable")
}
.show()
}
fun showDisableDialog() {
onShowListener?.invoke(TAG + "Disable")
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.registration_config_title)
.setMessage(Html.fromHtml(app.getString(R.string.registration_config_disable_text)))
.setPositiveButton(R.string.ok) { _, _ ->
disableRegistration()
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG + "Disable")
}
.show()
}
private fun enableRegistration() = launch {
onShowListener?.invoke(TAG + "Enabling")
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.please_wait)
.setMessage(R.string.registration_config_enable_progress_text)
.setCancelable(false)
.setOnDismissListener {
onDismissListener?.invoke(TAG + "Enabling")
}
.show()
withContext(Dispatchers.Default) {
profile.registration = Profile.REGISTRATION_ENABLED
// force full registration of the user
App.config.getFor(profile.id).hash = ""
AppSync(app, mutableListOf(), listOf(profile), SzkolnyApi(app)).run(
0L,
markAsSeen = true
)
app.db.profileDao().add(profile)
if (profile.id == App.profileId) {
App.profile.registration = profile.registration
}
}
dialog.dismiss()
onChangeListener?.invoke(true)
}
private fun disableRegistration() = launch {
onShowListener?.invoke(TAG + "Disabling")
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.please_wait)
.setMessage(R.string.registration_config_disable_progress_text)
.setCancelable(false)
.setOnDismissListener {
onDismissListener?.invoke(TAG + "Disabling")
}
.show()
withContext(Dispatchers.Default) {
profile.registration = Profile.REGISTRATION_DISABLED
SzkolnyApi(app).runCatching(activity) {
unregisterAppUser(profile.userCode)
}
app.db.profileDao().add(profile)
if (profile.id == App.profileId) {
App.profile.registration = profile.registration
}
}
dialog.dismiss()
onChangeListener?.invoke(false)
}
}

View File

@ -1,89 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-15.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.sync
import android.text.Html
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.task.AppSync
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import kotlin.coroutines.CoroutineContext
class RegistrationEnableDialog(
val activity: AppCompatActivity,
val profileId: Int
) : CoroutineScope {
companion object {
private const val TAG = "RegistrationEnableDialog"
}
private lateinit var app: App
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
private var progressDialog: AlertDialog? = null
init { run {
if (activity.isFinishing)
return@run
app = activity.applicationContext as App
}}
fun showEventShareDialog(onSuccess: (profile: Profile?) -> Unit) {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.event_manual_need_registration_title)
.setMessage(R.string.event_manual_need_registration_text)
.setPositiveButton(R.string.ok) { dialog, which ->
enableRegistration(onSuccess)
}
.setNegativeButton(R.string.cancel, null)
.show()
}
fun showEnableDialog(onSuccess: (profile: Profile?) -> Unit) {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.registration_enable_dialog_title)
.setMessage(Html.fromHtml(app.getString(R.string.registration_enable_dialog_text)))
.setPositiveButton(R.string.ok) { dialog, which ->
enableRegistration(onSuccess)
}
.setNegativeButton(R.string.cancel, null)
.show()
}
private fun enableRegistration(onSuccess: (profile: Profile?) -> Unit) { launch {
progressDialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.please_wait)
.setMessage(R.string.registration_enable_progress_text)
.setCancelable(false)
.show()
val profile = withContext(Dispatchers.Default) {
val profile = app.db.profileDao().getByIdNow(profileId) ?: return@withContext null
profile.registration = Profile.REGISTRATION_ENABLED
// force full registration of the user
App.config.getFor(profile.id).hash = ""
AppSync(app, mutableListOf(), listOf(profile), SzkolnyApi(app)).run(0L, markAsSeen = true)
app.db.profileDao().add(profile)
if (profile.id == App.profileId) {
App.profile.registration = profile.registration
}
return@withContext profile
}
progressDialog?.dismiss()
onSuccess(profile)
}}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-20.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.sync
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 pl.szczodrzynski.edziennik.*
import kotlin.coroutines.CoroutineContext
class SyncIntervalDialog(
val activity: AppCompatActivity,
val onChangeListener: (() -> Unit)? = null,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
private const val TAG = "SyncIntervalDialog"
}
private lateinit var app: App
private lateinit var dialog: AlertDialog
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
// local variables go here
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
app = activity.applicationContext as App
val intervals = listOf(
30 * MINUTE,
45 * MINUTE,
60 * MINUTE,
90 * MINUTE,
2 * HOUR,
3 * HOUR,
4 * HOUR,
6 * HOUR,
10 * HOUR
)
val intervalNames = intervals.map {
activity.getSyncInterval(it.toInt())
}
dialog = MaterialAlertDialogBuilder(activity)
.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]
app.config.sync.interval = interval.toInt()
onChangeListener?.invoke()
}
.setNegativeButton(R.string.cancel, null)
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
}}
}

View File

@ -23,7 +23,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_GRADE
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.databinding.GradesListFragmentBinding
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradeDetailsDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesAverages
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSemester
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesStats

View File

@ -15,7 +15,7 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.GradesItemStatsBinding
import pl.szczodrzynski.edziennik.onClick
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesStats
import java.text.DecimalFormat

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-17.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings
import android.content.Context
import android.view.View
import com.danielstone.materialaboutlibrary.holders.MaterialAboutItemViewHolder
import com.danielstone.materialaboutlibrary.items.MaterialAboutTitleItem
class MaterialAboutProfileItem(item: MaterialAboutTitleItem) : MaterialAboutTitleItem(item) {
companion object {
fun getViewHolder(view: View): MaterialAboutItemViewHolder =
MaterialAboutTitleItem.getViewHolder(view)
fun setupItem(
holder: MaterialAboutTitleItemViewHolder,
item: MaterialAboutProfileItem,
context: Context
) = MaterialAboutTitleItem.setupItem(holder, item, context)
}
override fun getType(): Int {
return SettingsViewTypeManager.ItemType.PROFILE_ITEM
}
override fun getDetailString() = "MaterialAboutProfileItem{" +
"text=" + text +
", textRes=" + textRes +
", desc=" + desc +
", descRes=" + descRes +
", icon=" + icon +
", iconRes=" + iconRes +
", onClickAction=" + onClickAction +
", onLongClickAction=" + onLongClickAction +
'}'
override fun clone() = MaterialAboutProfileItem(this)
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-17.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings
import com.danielstone.materialaboutlibrary.items.MaterialAboutItem
import com.danielstone.materialaboutlibrary.model.MaterialAboutCard
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
abstract class SettingsCard(
protected val util: SettingsUtil,
) {
protected val app: App = util.activity.application as App
protected val activity: MainActivity = util.activity
protected val configGlobal by lazy { app.config }
protected val configProfile by lazy { app.config.forProfile() }
val card by lazy {
buildCard()
}
protected abstract fun buildCard(): MaterialAboutCard
protected abstract fun getItems(): List<MaterialAboutItem>
protected abstract fun getItemsMore(): List<MaterialAboutItem>
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-16.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.danielstone.materialaboutlibrary.MaterialAboutFragment
import com.danielstone.materialaboutlibrary.model.MaterialAboutList
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.ui.modules.settings.cards.*
import kotlin.coroutines.CoroutineContext
class SettingsFragment : MaterialAboutFragment(), CoroutineScope {
companion object {
private const val TAG = "SettingsFragment"
}
private lateinit var app: App
private lateinit var activity: MainActivity
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private val util by lazy {
SettingsUtil(activity) {
refreshMaterialAboutList()
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
activity = (getActivity() as MainActivity?) ?: return null
app = activity.application as App
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun getViewTypeManager() =
SettingsViewTypeManager()
override fun getMaterialAboutList(activityContext: Context?): MaterialAboutList {
return MaterialAboutList(
SettingsProfileCard(util).card,
SettingsThemeCard(util).card,
SettingsSyncCard(util).card,
SettingsRegisterCard(util).card,
SettingsAboutCard(util).card,
)
}
}

View File

@ -53,8 +53,8 @@ import pl.szczodrzynski.edziennik.network.NetworkUtils;
import pl.szczodrzynski.edziennik.sync.SyncWorker;
import pl.szczodrzynski.edziennik.sync.UpdateWorker;
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog;
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradesConfigDialog;
import pl.szczodrzynski.edziennik.ui.dialogs.settings.AttendanceConfigDialog;
import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog;
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog;
import pl.szczodrzynski.edziennik.ui.dialogs.sync.NotificationFilterDialog;
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity;
@ -641,6 +641,9 @@ public class SettingsNewFragment extends MaterialAboutFragment {
Time time = app.getConfig().getSync().getQuietHoursStart();
if (time == null)
time = new Time(22, 30, 0);
Time timeEnd = app.getConfig().getSync().getQuietHoursEnd();
if (timeEnd == null)
app.getConfig().getSync().setQuietHoursEnd(new Time(5, 30, 0));
TimePickerDialog.newInstance((v2, hourOfDay, minute, second) -> {
app.getConfig().getSync().setQuietHoursEnabled(true);
app.getConfig().getSync().setQuietHoursStart(new Time(hourOfDay, minute, second));
@ -654,6 +657,9 @@ public class SettingsNewFragment extends MaterialAboutFragment {
Time time = app.getConfig().getSync().getQuietHoursEnd();
if (time == null)
time = new Time(5, 30, 0);
Time timeStart = app.getConfig().getSync().getQuietHoursStart();
if (timeStart == null)
app.getConfig().getSync().setQuietHoursStart(new Time(22, 30, 0));
TimePickerDialog.newInstance((v2, hourOfDay, minute, second) -> {
app.getConfig().getSync().setQuietHoursEnabled(true);
app.getConfig().getSync().setQuietHoursEnd(new Time(hourOfDay, minute, second));
@ -847,7 +853,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
registerCardAllowRegistrationItem.setChecked(true);
app.getProfile().setRegistration(REGISTRATION_ENABLED);
app.profileSave();
addCardItem(CARD_REGISTER, 2, getRegisterCardSharedEventsItem());
addCardItem(CARD_REGISTER, 3, getRegisterCardSharedEventsItem());
refreshMaterialAboutList();
}))
.show();
@ -860,7 +866,7 @@ public class SettingsNewFragment extends MaterialAboutFragment {
registerCardAllowRegistrationItem.setChecked(false);
app.getProfile().setRegistration(REGISTRATION_DISABLED);
app.profileSave();
removeCardItem(CARD_REGISTER, 2);
removeCardItem(CARD_REGISTER, 3);
refreshMaterialAboutList();
MaterialDialog progressDialog = new MaterialDialog.Builder(activity)
.title(getString(R.string.settings_register_allow_registration_dialog_disabling_title))
@ -1058,8 +1064,8 @@ public class SettingsNewFragment extends MaterialAboutFragment {
.icon(icon(CommunityMaterial.Icon3.cmd_translate, iconSizeDp, primaryTextOnPrimaryBg))
.setOnClickAction(() -> {
new MaterialDialog.Builder(activity)
.title(getString(R.string.settings_about_language_dialog_title))
.content(getString(R.string.settings_about_language_dialog_text))
.title(getString(R.string.app_language_dialog_title))
.content(getString(R.string.app_language_dialog_text))
.items(getString(R.string.language_system), getString(R.string.language_polish), getString(R.string.language_english), getString(R.string.language_german))
.itemsCallbackSingleChoice(app.getConfig().getUi().getLanguage() == null ? 0 : app.getConfig().getUi().getLanguage().equals("pl") ? 1 : app.getConfig().getUi().getLanguage().equals("en") ? 2 : 3, (dialog, itemView, which, text) -> {
switch (which) {
@ -1168,9 +1174,9 @@ public class SettingsNewFragment extends MaterialAboutFragment {
MaterialAboutList materialAboutList = new MaterialAboutList();
materialAboutList.addCard(getCardWithItems(null, getProfileCard(false)));
materialAboutList.addCard(getCardWithItems(getString(R.string.settings_theme_title_text), getThemeCard(false)));
materialAboutList.addCard(getCardWithItems(getString(R.string.settings_sync_title_text), getSyncCard(false)));
materialAboutList.addCard(getCardWithItems(getString(R.string.settings_about_register_title_text), getRegisterCard(false)));
materialAboutList.addCard(getCardWithItems(getString(R.string.settings_card_theme_title), getThemeCard(false)));
materialAboutList.addCard(getCardWithItems(getString(R.string.settings_card_sync_title), getSyncCard(false)));
materialAboutList.addCard(getCardWithItems(getString(R.string.settings_card_register_title), getRegisterCard(false)));
//if (configurableEndpoints != null)
// materialAboutList.addCard(getCardWithItems(getString(R.string.settings_sync_customize_title_text), getSyncCustomizeCard(false)));
materialAboutList.addCard(getCardWithItems(null, getAboutCard(false), true));

View File

@ -0,0 +1,188 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-17.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import androidx.core.graphics.drawable.toBitmap
import com.danielstone.materialaboutlibrary.items.*
import com.danielstone.materialaboutlibrary.model.MaterialAboutCard
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.IIcon
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.after
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.utils.Colors
import pl.szczodrzynski.edziennik.utils.Themes
class SettingsUtil(
val activity: MainActivity,
private val onRefresh: () -> Unit
) {
fun refresh() = onRefresh()
private fun IIcon.asDrawable(color: Int? = null, size: Int = 20) = IconicsDrawable(activity).apply {
icon = this@asDrawable
sizeDp = size
colorInt = color ?: Themes.getPrimaryTextColor(activity)
}
fun createCard(
titleRes: Int?,
items: List<MaterialAboutItem>,
itemsMore: List<MaterialAboutItem>,
backgroundColor: Int? = null,
theme: Int? = null
): MaterialAboutCard {
val card = MaterialAboutCard.Builder()
.title(titleRes ?: 0)
.cardColor(backgroundColor ?: 0)
.theme(theme ?: 0)
.build()
card.items.addAll(items)
if (itemsMore.isNotEmpty()) {
card.items.add(createMoreItem(card, itemsMore))
}
return card
}
fun createMoreItem(
card: MaterialAboutCard,
items: List<MaterialAboutItem>
): MaterialAboutActionItem {
val iconColor = card.cardColor.let {
if (it == 0)
null
else
Colors.legibleTextColor(it)
}
val moreItem = MaterialAboutActionItem.Builder()
.text(R.string.settings_more_text)
.icon(CommunityMaterial.Icon.cmd_chevron_down.asDrawable(iconColor, size = 14))
.build()
moreItem.setOnClickAction {
card.items.after(moreItem, items)
card.items.remove(moreItem)
onRefresh()
}
return moreItem
}
fun createSectionItem(text: Int) = MaterialAboutSectionItem(text)
fun createActionItem(
text: Int,
subText: Int? = null,
icon: IIcon,
backgroundColor: Int? = null,
onClick: (item: MaterialAboutActionItem) -> Unit
): MaterialAboutActionItem {
val iconColor = backgroundColor?.let { Colors.legibleTextColor(it) }
val item = MaterialAboutActionItem.Builder()
.text(text)
.subText(subText ?: 0)
.icon(icon.asDrawable(iconColor))
.build()
item.setOnClickAction {
onClick(item)
}
return item
}
fun createPropertyItem(
text: Int,
subText: Int? = null,
subTextChecked: Int? = null,
icon: IIcon,
backgroundColor: Int? = null,
value: Boolean,
beforeChange: ((item: MaterialAboutSwitchItem, value: Boolean) -> Boolean)? = null,
onChange: (item: MaterialAboutSwitchItem, value: Boolean) -> Unit
): MaterialAboutSwitchItem {
val iconColor = backgroundColor?.let { Colors.legibleTextColor(it) }
val item = MaterialAboutSwitchItem.Builder()
.text(text)
.subText(subText ?: 0)
.subTextChecked(subTextChecked ?: 0)
.icon(icon.asDrawable(iconColor))
.setChecked(value)
.build()
item.setOnCheckedChangedAction { item, isChecked ->
if (beforeChange?.invoke(item as MaterialAboutSwitchItem, isChecked) == false)
return@setOnCheckedChangedAction false
onChange(item as MaterialAboutSwitchItem, isChecked)
true
}
return item
}
fun createPropertyActionItem(
text: Int,
subText: Int? = null,
subTextChecked: Int? = null,
icon: IIcon,
backgroundColor: Int? = null,
value: Boolean,
onChange: (item: MaterialAboutActionSwitchItem, value: Boolean) -> Unit,
onClick: (item: MaterialAboutActionSwitchItem) -> Unit
): MaterialAboutSwitchItem {
val iconColor = backgroundColor?.let { Colors.legibleTextColor(it) }
val item = MaterialAboutActionSwitchItem.Builder()
.text(text)
.subText(subText ?: 0)
.subTextChecked(subTextChecked ?: 0)
.icon(icon.asDrawable(iconColor))
.setChecked(value)
.build()
item.setOnClickAction {
onClick(item)
}
item.setOnCheckedChangedAction { item, isChecked ->
onChange(item as MaterialAboutActionSwitchItem, isChecked)
true
}
return item
}
fun createTitleItem(): MaterialAboutTitleItem =
MaterialAboutTitleItem.Builder()
.text(R.string.app_name)
.desc(R.string.settings_about_title_subtext)
.icon(R.mipmap.ic_splash)
.build()
fun createProfileItem(profile: Profile, onClick: (profile: Profile) -> Unit) =
MaterialAboutProfileItem(
MaterialAboutTitleItem.Builder()
.text(profile.name)
.desc(profile.subname)
.icon(RoundedBitmapDrawableFactory.create(
activity.resources,
profile.getImageDrawable(activity).toBitmap()
).also {
it.isCircular = true
})
.setOnClickAction { onClick(profile) }
.build()
)
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-17.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings
import android.content.Context
import android.view.View
import com.danielstone.materialaboutlibrary.holders.MaterialAboutItemViewHolder
import com.danielstone.materialaboutlibrary.items.MaterialAboutItem
import com.danielstone.materialaboutlibrary.items.MaterialAboutTitleItem.MaterialAboutTitleItemViewHolder
import com.danielstone.materialaboutlibrary.util.DefaultViewTypeManager
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsViewTypeManager.ItemType.Companion.PROFILE_ITEM
class SettingsViewTypeManager : DefaultViewTypeManager() {
class ItemType {
companion object {
const val PROFILE_ITEM = 10
}
}
override fun getLayout(itemType: Int) = when (itemType) {
PROFILE_ITEM -> R.layout.mal_material_about_profile_item
else -> super.getLayout(itemType)
}
override fun getViewHolder(itemType: Int, view: View): MaterialAboutItemViewHolder =
when (itemType) {
PROFILE_ITEM -> MaterialAboutProfileItem.getViewHolder(view)
else -> super.getViewHolder(itemType, view)
}
override fun setupItem(
itemType: Int,
holder: MaterialAboutItemViewHolder,
item: MaterialAboutItem,
context: Context
) = when (itemType) {
PROFILE_ITEM -> MaterialAboutProfileItem.setupItem(
holder as MaterialAboutTitleItemViewHolder,
item as MaterialAboutProfileItem, context
)
else -> super.setupItem(itemType, holder, item, context)
}
}

View File

@ -0,0 +1,150 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings.cards
import android.content.Intent
import android.media.MediaPlayer
import android.widget.Toast
import com.danielstone.materialaboutlibrary.items.MaterialAboutItem
import com.danielstone.materialaboutlibrary.model.MaterialAboutCard
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import eu.szkolny.font.SzkolnyFont
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.BuildConfig
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.sync.UpdateWorker
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsCard
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsLicenseActivity
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsUtil
import pl.szczodrzynski.edziennik.utils.Utils
import kotlin.coroutines.CoroutineContext
class SettingsAboutCard(util: SettingsUtil) : SettingsCard(util), CoroutineScope {
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private var clickCounter = 0
private val mediaPlayer by lazy {
MediaPlayer.create(activity, R.raw.ogarnij_sie)
}
override fun buildCard(): MaterialAboutCard =
util.createCard(
null,
items = listOf(),
itemsMore = listOf(),
backgroundColor = 0xff1976d2.toInt(),
theme = R.style.AppTheme_Dark
).also {
it.items.addAll(getItems(it))
}
override fun getItems() = listOf<MaterialAboutItem>()
override fun getItemsMore() = listOf<MaterialAboutItem>()
private fun getItems(card: MaterialAboutCard) = listOf(
util.createTitleItem(),
util.createActionItem(
text = R.string.settings_about_version_text,
icon = CommunityMaterial.Icon2.cmd_information_outline,
onClick = { item ->
clickCounter++
if (clickCounter < 7)
Toast.makeText(activity, "\uD83D\uDE02", Toast.LENGTH_SHORT).show()
item.subText =
BuildConfig.VERSION_NAME + ", " + BuildConfig.BUILD_TYPE + " \uD83D\uDCA3"
util.refresh()
if (clickCounter >= 7) {
mediaPlayer.start()
clickCounter = 0
}
}
).also {
it.subText = BuildConfig.VERSION_NAME + ", " + BuildConfig.BUILD_TYPE
},
util.createMoreItem(card, items = listOf(
util.createActionItem(
text = R.string.settings_about_changelog_text,
icon = CommunityMaterial.Icon3.cmd_radar
) {
ChangelogDialog(activity)
},
util.createActionItem(
text = R.string.settings_about_update_text,
subText = R.string.settings_about_update_subtext,
icon = CommunityMaterial.Icon3.cmd_update
) {
launch {
UpdateWorker.runNow(app)
}
}
)),
util.createSectionItem(
text = R.string.see_also
),
util.createActionItem(
text = R.string.settings_about_privacy_policy_text,
icon = CommunityMaterial.Icon3.cmd_shield_outline
) {
Utils.openUrl(activity, "https://szkolny.eu/privacy-policy")
},
util.createActionItem(
text = R.string.settings_about_discord_text,
subText = R.string.settings_about_discord_subtext,
icon = SzkolnyFont.Icon.szf_discord_outline
) {
Utils.openUrl(activity, "https://szkolny.eu/discord")
},
util.createActionItem(
text = R.string.settings_about_github_text,
subText = R.string.settings_about_github_subtext,
icon = SzkolnyFont.Icon.szf_github_face
) {
Utils.openUrl(activity, "https://szkolny.eu/github/android")
},
util.createMoreItem(card, items = listOfNotNull(
util.createActionItem(
text = R.string.settings_about_homepage_text,
subText = R.string.settings_about_homepage_subtext,
icon = CommunityMaterial.Icon.cmd_earth
) {
Utils.openUrl(activity, "https://szkolny.eu/")
},
util.createActionItem(
text = R.string.settings_about_licenses_text,
icon = CommunityMaterial.Icon.cmd_code_braces
) {
activity.startActivity(Intent(activity, SettingsLicenseActivity::class.java))
},
if (App.devMode)
util.createActionItem(
text = R.string.settings_about_crash_text,
subText = R.string.settings_about_crash_subtext,
icon = CommunityMaterial.Icon.cmd_bug_outline
) {
throw RuntimeException("MANUAL CRASH")
}
else
null
))
)
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings.cards
import android.content.Intent
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import eu.szkolny.font.SzkolnyFont
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsCard
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsUtil
class SettingsProfileCard(util: SettingsUtil) : SettingsCard(util) {
override fun buildCard() = util.createCard(
null,
items = getItems(),
itemsMore = getItemsMore()
)
override fun getItems() = listOf(
util.createProfileItem(
profile = app.profile
) {
},
util.createActionItem(
text = R.string.settings_add_student_text,
subText = R.string.settings_add_student_subtext,
icon = CommunityMaterial.Icon.cmd_account_plus_outline
) {
activity.startActivity(Intent(activity, LoginActivity::class.java))
},
util.createActionItem(
text = R.string.settings_profile_remove_text,
subText = R.string.settings_profile_remove_subtext,
icon = SzkolnyFont.Icon.szf_delete_empty_outline
) {
ProfileRemoveDialog(activity, app.profile.id, app.profile.name, false)
}
)
override fun getItemsMore() = listOf(
util.createPropertyItem(
text = R.string.settings_profile_sync_text,
subText = R.string.settings_profile_sync_subtext,
icon = CommunityMaterial.Icon.cmd_account_convert,
value = app.profile.syncEnabled,
) { _, it ->
app.profile.syncEnabled = it
app.profileSave()
}
)
}

View File

@ -0,0 +1,152 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings.cards
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import eu.szkolny.font.SzkolnyFont
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.after
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.REGISTRATION_ENABLED
import pl.szczodrzynski.edziennik.ui.dialogs.bell.BellSyncConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.grade.GradesConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.AttendanceConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.RegistrationConfigDialog
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsCard
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsUtil
class SettingsRegisterCard(util: SettingsUtil) : SettingsCard(util) {
override fun buildCard() = util.createCard(
R.string.settings_card_register_title,
items = getItems(),
itemsMore = getItemsMore()
)
private fun getBellSync() =
configGlobal.timetable.bellSyncDiff?.let {
activity.getString(
R.string.settings_register_bell_sync_subtext_format,
(if (configGlobal.timetable.bellSyncMultiplier == -1) "-" else "+") + it.stringHMS
)
} ?: activity.getString(R.string.settings_register_bell_sync_subtext_disabled)
private val sharedEventsItem by lazy {
util.createPropertyItem(
text = R.string.settings_register_shared_events_text,
subText = R.string.settings_register_shared_events_subtext,
icon = CommunityMaterial.Icon3.cmd_share_outline,
value = app.profile.enableSharedEvents
) { _, value ->
app.profile.enableSharedEvents = value
app.profileSave()
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.event_sharing)
.setMessage(
if (value)
R.string.settings_register_shared_events_dialog_enabled_text
else
R.string.settings_register_shared_events_dialog_disabled_text
)
.setPositiveButton(R.string.ok, null)
.show()
}
}
override fun getItems() = listOfNotNull(
util.createActionItem(
text = R.string.menu_grades_config,
icon = CommunityMaterial.Icon3.cmd_numeric_5_box_outline
) {
GradesConfigDialog(activity, reloadOnDismiss = false)
},
util.createActionItem(
text = R.string.menu_attendance_config,
icon = CommunityMaterial.Icon.cmd_calendar_remove_outline
) {
AttendanceConfigDialog(activity, reloadOnDismiss = false)
},
util.createPropertyItem(
text = R.string.settings_register_allow_registration_text,
subText = R.string.settings_register_allow_registration_subtext,
icon = CommunityMaterial.Icon.cmd_account_circle_outline,
value = app.profile.registration == REGISTRATION_ENABLED,
beforeChange = { item, value ->
if (app.profile.registration == REGISTRATION_ENABLED == value)
// allow the switch to change - needed for util.refresh() to change the visual state
return@createPropertyItem true
val dialog =
RegistrationConfigDialog(activity, app.profile, onChangeListener = { enabled ->
if (item.isChecked == enabled)
return@RegistrationConfigDialog
item.isChecked = enabled
if (value) {
card.items.after(item, sharedEventsItem)
} else {
card.items.remove(sharedEventsItem)
}
util.refresh()
})
if (value)
dialog.showEnableDialog()
else
dialog.showDisableDialog()
false
}
) { _, _ -> },
if (app.profile.registration == REGISTRATION_ENABLED)
sharedEventsItem
else
null
)
override fun getItemsMore() = listOfNotNull(
util.createActionItem(
text = R.string.settings_register_bell_sync_text,
icon = SzkolnyFont.Icon.szf_alarm_bell_outline,
onClick = {
BellSyncConfigDialog(activity, onChangeListener = {
it.subText = getBellSync()
util.refresh()
})
}
).also {
it.subText = getBellSync()
},
util.createPropertyItem(
text = R.string.settings_register_count_in_seconds_text,
subText = R.string.settings_register_count_in_seconds_subtext,
icon = CommunityMaterial.Icon3.cmd_timer_outline,
value = configGlobal.timetable.countInSeconds
) { _, it ->
configGlobal.timetable.countInSeconds = it
},
if (app.profile.loginStoreType == LOGIN_TYPE_LIBRUS)
util.createPropertyItem(
text = R.string.settings_register_show_teacher_absences_text,
icon = CommunityMaterial.Icon.cmd_account_arrow_right_outline,
value = app.profile.getStudentData("showTeacherAbsences", true)
) { _, it ->
app.profile.putStudentData("showTeacherAbsences", it)
app.profileSave()
}
else
null,
util.createPropertyItem(
text = R.string.settings_register_hide_sticks_from_old,
icon = CommunityMaterial.Icon3.cmd_numeric_1_box_outline,
value = configProfile.grades.hideSticksFromOld
) { _, it ->
configProfile.grades.hideSticksFromOld = it
}
)
}

View File

@ -0,0 +1,200 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings.cards
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.net.Uri
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES
import android.provider.Settings
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.after
import pl.szczodrzynski.edziennik.getSyncInterval
import pl.szczodrzynski.edziennik.sync.SyncWorker
import pl.szczodrzynski.edziennik.sync.UpdateWorker
import pl.szczodrzynski.edziennik.ui.dialogs.sync.NotificationFilterDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.QuietHoursConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncIntervalDialog
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsCard
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsUtil
import pl.szczodrzynski.edziennik.utils.models.Time
class SettingsSyncCard(util: SettingsUtil) : SettingsCard(util) {
override fun buildCard() = util.createCard(
R.string.settings_card_sync_title,
items = getItems(),
itemsMore = getItemsMore()
)
private fun getQuietHours(): String {
if (configGlobal.sync.quietHoursStart == null) {
configGlobal.sync.quietHoursStart = Time(22, 30, 0)
}
if (configGlobal.sync.quietHoursEnd == null) {
configGlobal.sync.quietHoursEnd = Time(6, 30, 0)
}
return activity.getString(
if (configGlobal.sync.quietHoursStart!! > configGlobal.sync.quietHoursEnd!!)
R.string.settings_sync_quiet_hours_subtext_next_day_format
else
R.string.settings_sync_quiet_hours_subtext_format,
configGlobal.sync.quietHoursStart?.stringHM,
configGlobal.sync.quietHoursEnd?.stringHM
)
}
private val syncWifiItem by lazy {
util.createPropertyItem(
text = R.string.settings_sync_wifi_text,
subText = R.string.settings_sync_wifi_subtext,
icon = CommunityMaterial.Icon3.cmd_wifi_strength_2,
value = configGlobal.sync.onlyWifi
) { _, it ->
configGlobal.sync.onlyWifi = it
SyncWorker.rescheduleNext(app)
}
}
override fun getItems() = listOfNotNull(
util.createPropertyActionItem(
text = R.string.settings_sync_sync_interval_text,
subText = R.string.settings_sync_sync_interval_subtext_disabled,
icon = CommunityMaterial.Icon.cmd_download_outline,
value = configGlobal.sync.enabled,
onChange = { item, value ->
// When calling onChange from the onClick listener below
// a list refresh is requested, the adapter refreshes
// all view holders, changing the state of the switch
// view, thus calling onChange again, causing it to
// try to recursively refresh the list, therefore
// crashing the app. To avoid this, the method will
// continue only if the checked state is different
// from the saved value, which should only happen
// when clicking the switch manually or when called
// by onClick, **once** (because onClick doesn't
// update the config value - we let the switch
// listener do it). Then there comes a different problem,
// when onClick changes the subText and the onChange
// listener returns because the boolean value
// is unchanged, leaving the list not refreshed.
// To solve this, a list refresh is also requested
// in onClick, when the config value is the same
// as the new switch value, which would normally
// cause the onChange method to exit here.
if (value == configGlobal.sync.enabled)
return@createPropertyActionItem
if (value) {
card.items.after(item, syncWifiItem)
} else {
card.items.remove(syncWifiItem)
}
util.refresh()
configGlobal.sync.enabled = value
SyncWorker.rescheduleNext(app)
},
onClick = { item ->
SyncIntervalDialog(activity, onChangeListener = {
item.subTextChecked = activity.getSyncInterval(configGlobal.sync.interval)
item.isChecked = true
item.onCheckedChangedAction.onCheckedChanged(item, true)
if (configGlobal.sync.enabled)
util.refresh()
})
}
).also {
it.subTextChecked = activity.getSyncInterval(configGlobal.sync.interval)
},
if (configGlobal.sync.enabled)
syncWifiItem
else
null,
util.createActionItem(
text = R.string.settings_profile_notifications_text,
subText = R.string.settings_profile_notifications_subtext,
icon = CommunityMaterial.Icon2.cmd_filter_outline
) {
NotificationFilterDialog(activity)
},
util.createPropertyActionItem(
text = R.string.settings_sync_quiet_hours_text,
subText = R.string.settings_sync_quiet_hours_subtext_disabled,
icon = CommunityMaterial.Icon.cmd_bell_sleep_outline,
value = configGlobal.sync.quietHoursEnabled,
onChange = { _, value ->
configGlobal.sync.quietHoursEnabled = value
},
onClick = { item ->
QuietHoursConfigDialog(activity, onChangeListener = {
item.subTextChecked = getQuietHours()
item.isChecked = configGlobal.sync.quietHoursEnabled
util.refresh()
})
}
).also {
it.subTextChecked = getQuietHours()
},
util.createActionItem(
text = R.string.settings_sync_web_push_text,
subText = R.string.settings_sync_web_push_subtext,
icon = CommunityMaterial.Icon2.cmd_laptop
) {
activity.loadTarget(MainActivity.TARGET_WEB_PUSH)
}
)
override fun getItemsMore() = listOfNotNull(
util.createPropertyItem(
text = R.string.settings_sync_updates_text,
icon = CommunityMaterial.Icon.cmd_cellphone_arrow_down,
value = configGlobal.sync.notifyAboutUpdates
) { _, it ->
configGlobal.sync.notifyAboutUpdates = it
UpdateWorker.rescheduleNext(app)
},
if (SDK_INT >= VERSION_CODES.KITKAT)
util.createActionItem(
text = R.string.settings_sync_notifications_settings_text,
subText = R.string.settings_sync_notifications_settings_subtext,
icon = CommunityMaterial.Icon.cmd_cog_outline
) {
val channel = app.notificationChannelsManager.data.key
val intent = Intent().apply {
when {
SDK_INT >= VERSION_CODES.O -> {
action = Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS
putExtra(Settings.EXTRA_APP_PACKAGE, app.packageName)
putExtra(Settings.EXTRA_CHANNEL_ID, channel)
addFlags(FLAG_ACTIVITY_NEW_TASK)
}
SDK_INT >= VERSION_CODES.LOLLIPOP -> {
action = "android.settings.APP_NOTIFICATION_SETTINGS"
putExtra("app_package", app.packageName)
putExtra("app_uid", app.applicationInfo.uid)
}
else -> {
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
addCategory(Intent.CATEGORY_DEFAULT)
data = Uri.parse("package:" + app.packageName)
}
}
}
activity.startActivity(intent)
}
else
null
)
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-3-18.
*/
package pl.szczodrzynski.edziennik.ui.modules.settings.cards
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ui.dialogs.settings.AppLanguageDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.MiniMenuConfigDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ThemeChooserDialog
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsCard
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsUtil
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.utils.models.Date
class SettingsThemeCard(util: SettingsUtil) : SettingsCard(util) {
override fun buildCard() = util.createCard(
R.string.settings_card_theme_title,
items = getItems(),
itemsMore = getItemsMore()
)
override fun getItems() = listOfNotNull(
if (Date.getToday().month % 11 == 1) // cool math games
util.createPropertyItem(
text = R.string.settings_theme_snowfall_text,
subText = R.string.settings_theme_snowfall_subtext,
icon = CommunityMaterial.Icon3.cmd_snowflake,
value = configGlobal.ui.snowfall
) { _, it ->
configGlobal.ui.snowfall = it
activity.recreate()
}
else null,
util.createActionItem(
text = R.string.settings_theme_theme_text,
subText = Themes.getThemeNameRes(),
icon = CommunityMaterial.Icon3.cmd_palette_outline
) {
ThemeChooserDialog(activity)
},
util.createActionItem(
text = R.string.settings_about_language_text,
subText = R.string.settings_about_language_subtext,
icon = CommunityMaterial.Icon3.cmd_translate
) {
AppLanguageDialog(activity)
},
util.createPropertyItem(
text = R.string.settings_theme_mini_drawer_text,
subText = R.string.settings_theme_mini_drawer_subtext,
icon = CommunityMaterial.Icon.cmd_dots_vertical,
value = configGlobal.ui.miniMenuVisible
) { _, it ->
configGlobal.ui.miniMenuVisible = it
activity.navView.drawer.miniDrawerVisiblePortrait = it
}
)
override fun getItemsMore() = listOf(
util.createActionItem(
text = R.string.settings_theme_mini_drawer_buttons_text,
icon = CommunityMaterial.Icon2.cmd_format_list_checks
) {
MiniMenuConfigDialog(activity)
},
util.createActionItem(
text = R.string.settings_theme_drawer_header_text,
icon = CommunityMaterial.Icon2.cmd_image_outline
) {
// TODO: 2021-03-17
},
util.createActionItem(
text = R.string.settings_theme_app_background_text,
subText = R.string.settings_theme_app_background_subtext,
icon = CommunityMaterial.Icon2.cmd_image_filter_hdr
) {
// TODO: 2021-03-17
},
util.createPropertyItem(
text = R.string.settings_theme_open_drawer_on_back_pressed_text,
icon = CommunityMaterial.Icon3.cmd_menu_open,
value = configGlobal.ui.openDrawerOnBackPressed
) { _, it ->
configGlobal.ui.openDrawerOnBackPressed = it
}
)
}

View File

@ -42,6 +42,12 @@ object Themes {
theme = themeList[value]
}
var themeIndex
get() = themeList.indexOf(theme)
set(value) {
theme = themeList[value]
}
val appThemeNoDisplay: Int
get() = if (theme.isDark) R.style.AppTheme_Dark_NoDisplay else R.style.AppTheme_Light_NoDisplay
@ -61,38 +67,15 @@ object Themes {
return getColorFromAttr(context, android.R.attr.textColorSecondary)
}
/*public static int getChipColorRes() {
switch (themeInt) {
case THEME_LIGHT:
return R.color.chipBackgroundLight;
default:
case THEME_DARK:
return R.color.chipBackgroundDark;
case THEME_BLACK:
return R.color.chipBackgroundBlack;
case THEME_CHOCOLATE:
return R.color.chipBackgroundChocolate;
case THEME_BLUE:
return R.color.chipBackgroundBlue;
case THEME_PURPLE:
return R.color.chipBackgroundPurple;
case THEME_GREEN:
return R.color.chipBackgroundGreen;
case THEME_AMBER:
return R.color.chipBackgroundAmber;
case THEME_RED:
return R.color.chipBackgroundRed;
}
}*/
fun getThemeName(context: Context): String {
return context.getString(theme.name)
}
fun getThemeNames(context: Context): List<String> {
val list = mutableListOf<String>()
for (theme in themeList) {
list += context.getString(theme.name)
}
return list
@StringRes
fun getThemeNameRes() = theme.name
fun getThemeNames(context: Context) =
themeList.map {
context.getString(it.name)
}
}

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) Kuba Szczodrzyński 2021-3-19.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?attr/dialogPreferredPadding"
android:paddingTop="8dp"
android:paddingEnd="?attr/dialogPreferredPadding">
<TextView
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="4dp"
tools:text="This is a custom message of the dialog." />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_input_layout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="@dimen/mal_baseline_half">
<ImageView
android:id="@+id/mal_item_image"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="@dimen/mal_baseline"
android:adjustViewBounds="false"
android:contentDescription="@null"
android:cropToPadding="false"
android:scaleType="centerCrop"
tools:background="@tools:sample/backgrounds/scenic" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginVertical="@dimen/mal_baseline"
android:layout_marginEnd="@dimen/mal_baseline"
android:layout_marginRight="@dimen/mal_baseline"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/mal_item_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
android:textColor="?android:textColorPrimary"
android:textSize="24sp"
tools:text="Władca Androida" />
<TextView
android:id="@+id/mal_item_desc"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="?android:textColorSecondary"
android:textSize="14sp"
tools:text="kubasz" />
</LinearLayout>
</LinearLayout>

View File

@ -351,8 +351,6 @@
<string name="event_list_added_by_unknown_format">Hinzugefügt %1$s%3$s</string>
<string name="event_list_shared_by_format">{cmd-share-variant} %1$s von %2$s%3$s</string>
<string name="event_list_shared_by_self_format">{cmd-share-variant} %1$s von Ihnen%3$s</string>
<string name="event_manual_need_registration_text">Um das Ereignis freigeben zu können, müssen Sie die Serverregistrierungsoption aktivieren. Auf diese Weise können Sie Ereignisse erstellen und empfangen, die in Ihrer Klasse geteilt werden.\n\nWenn Sie auf OK klicken, wird diese automatisch aktiviert.\n\nStellen Sie sicher, dass Sie die Bedingungen lesen und akzeptieren.</string>
<string name="event_manual_need_registration_title">Ereignisse teilen</string>
<string name="event_manual_remove">Ereignis entfernen…</string>
<string name="event_manual_saving">Ereignis speichern…</string>
<string name="event_manual_share">Ereignis teilen…</string>
@ -830,9 +828,13 @@
<string name="rate_snackbar_positive">etzt bewerten</string>
<string name="rate_snackbar_text">Zeigen Sie, dass Ihnen Szkolny.eu gefällt - bewerten Sie die App und machen Sie sie noch besser!</string>
<string name="refresh">Aktualisieren</string>
<string name="registration_enable_dialog_text">Die Registrierung erfolgt automatisch, wenn diese Option aktiviert ist. Sie können Ereignisse erstellen und empfangen, die mit anderen Schülern in Ihrer Klasse geteilt werden. Dank dessen können Sie Elemente, die nicht vom Lehrer gespeichert wurden, zum Klassenbuch hinzufügen.\n\nStellen Sie sicher, dass Sie die Bestimmungen der <a href="http://szkolny.eu/privacy-policy"> Datenschutzrichtlinie </a> lesen und die Bestimmungen akzeptieren.</string>
<string name="registration_enable_dialog_title">Server-Registrierung</string>
<string name="registration_enable_progress_text">Gemeinsame Ereignisse herunterladen…</string>
<string name="registration_config_disable_progress_text">Benutzerregistrierung aufheben…</string>
<string name="registration_config_disable_text">Einige Funktionen wie Ereignisfreigabe oder Benachrichtigungsweiterleitung können nicht mehr verwendet werden.\n\nDie Funktion kann je nach ausgewähltem Profil aktiviert / deaktiviert werden.</string>
<string name="registration_config_enable_progress_text">Gemeinsame Ereignisse herunterladen…</string>
<string name="registration_config_enable_text">Die Registrierung erfolgt automatisch, wenn diese Option aktiviert ist. Sie können Ereignisse erstellen und empfangen, die mit anderen Schülern in Ihrer Klasse geteilt werden. Dank dessen können Sie Elemente, die nicht vom Lehrer gespeichert wurden, zum Klassenbuch hinzufügen.\n\nStellen Sie sicher, dass Sie die Bestimmungen der <a href="http://szkolny.eu/privacy-policy"> Datenschutzrichtlinie </a> lesen und die Bestimmungen akzeptieren.</string>
<string name="registration_config_event_sharing_text">Um das Ereignis freigeben zu können, müssen Sie die Serverregistrierungsoption aktivieren. Auf diese Weise können Sie Ereignisse erstellen und empfangen, die in Ihrer Klasse geteilt werden.\n\nWenn Sie auf OK klicken, wird diese automatisch aktiviert.\n\nStellen Sie sicher, dass Sie die Bedingungen lesen und akzeptieren.</string>
<string name="registration_config_event_sharing_title">Ereignisse teilen</string>
<string name="registration_config_title">Szkolny.eu Server-Registrierung</string>
<string name="remove">Löschen</string>
<string name="removed">gelöschtet</string>
<string name="report">Melden</string>
@ -847,13 +849,13 @@
<string name="settings_about_crash_text">Klicken Sie hier, um eine unerwartete Ausnahme auszulösen</string>
<string name="settings_about_discord_subtext">Treten Sie unserem Discord-Server bei!</string>
<string name="settings_about_discord_text">Discord Server</string>
<string name="settings_about_language_dialog_text">Hinweis. Diese Option funktioniert möglicherweise auf einigen Geräten und in einigen Teilen der App nicht.</string>
<string name="settings_about_language_dialog_title">App-Sprache ändern</string>
<string name="app_language_dialog_text">Hinweis. Diese Option funktioniert möglicherweise auf einigen Geräten und in einigen Teilen der App nicht.</string>
<string name="app_language_dialog_title">App-Sprache ändern</string>
<string name="settings_about_language_subtext">Deutsch</string>
<string name="settings_about_language_text">Sprache der App</string>
<string name="settings_about_licenses_text">Open-Source-Lizenzen</string>
<string name="settings_about_privacy_policy_text">Datenschutzrichtlinie</string>
<string name="settings_about_register_title_text">E-Klassenbuch</string>
<string name="settings_card_register_title">E-Klassenbuch</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nSeptember 2018 - Februar 2021</string>
<string name="settings_about_update_subtext">Klicken Sie hier, um nach Aktualisierungen zu suchen</string>
<string name="settings_about_update_text">Aktualisierung</string>
@ -944,7 +946,7 @@
<string name="settings_sync_sync_interval_subtext_disabled">Deaktiviert</string>
<string name="settings_sync_sync_interval_subtext_format">Daten alle %s herunterladen</string>
<string name="settings_sync_sync_interval_text">Automatische Synchronisierung</string>
<string name="settings_sync_title_text">Synchronisation und Benachrichtigungen</string>
<string name="settings_card_sync_title">Synchronisation und Benachrichtigungen</string>
<string name="settings_sync_updates_text">Über App-Aktualisierungen benachrichtigen</string>
<string name="settings_sync_web_push_subtext">Benachrichtigungen auf Ihrem PC anzeigen</string>
<string name="settings_sync_web_push_text">Benachrichtigungsweiterleitung</string>
@ -968,7 +970,7 @@
<string name="settings_theme_theme_pink">Rosa</string>
<string name="settings_theme_theme_system">System</string>
<string name="settings_theme_theme_text">Thema</string>
<string name="settings_theme_title_text">Aussehen</string>
<string name="settings_card_theme_title">Aussehen</string>
<string name="share">Teilen</string>
<string name="share_intent">Teilen über…</string>
<string name="sharing_event">Daten Teilen…</string>

View File

@ -351,8 +351,6 @@
<string name="event_list_added_by_unknown_format">Adde %1$s%3$s</string>
<string name="event_list_shared_by_format">{cmd-share-variant} %1$s by %2$s%3$s</string>
<string name="event_list_shared_by_self_format">{cmd-share-variant} %1$s by you%3$s</string>
<string name="event_manual_need_registration_text">You need to turn on server registration, in order to share an event. This option lets you create and receive shared events in your class.\n\nIt will be turned on automatically by pressing OK.\n\nBefore turning it on, make sure you\'ve read the terms and accept them</string>
<string name="event_manual_need_registration_title">Sharing events</string>
<string name="event_manual_remove">Removing event…</string>
<string name="event_manual_saving">Saving event…</string>
<string name="event_manual_share">Sharing event…</string>
@ -832,9 +830,13 @@
<string name="rate_snackbar_positive">Rate now</string>
<string name="rate_snackbar_text">Show that you like Szkolny.eu - rate the app and make it even better!</string>
<string name="refresh">Refresh</string>
<string name="registration_enable_dialog_text">Registration is automatic if this option is enabled. It allows you to create and receive events shared with other students in your class. Thanks to this, you can add items not saved by the teacher to the journal.\n\nMake sure you read the terms and accept them.</string>
<string name="registration_enable_dialog_title">Server registration</string>
<string name="registration_enable_progress_text">Syncing shared events…</string>
<string name="registration_config_disable_progress_text">Unregistering the user…</string>
<string name="registration_config_disable_text">You won\'t be able to use some functions anymore, like Event sharing and Notification forwarding.\n\nThis feature may be enabled/disabled depending on the active profile.</string>
<string name="registration_config_enable_progress_text">Syncing shared events…</string>
<string name="registration_config_enable_text">Registration is automatic if this option is enabled. It allows you to create and receive events shared with other students in your class. Thanks to this, you can add items not saved by the teacher to the journal.\n\nContinuing means reading and accepting the <a href="https://szkolny.eu/privacy-policy">Privacy policy</a>.</string>
<string name="registration_config_event_sharing_text">You need to turn on server registration, in order to share an event. This option lets you create and receive shared events in your class.\n\nIt will be turned on automatically by pressing OK.\n\nContinuing means reading and accepting the <a href="https://szkolny.eu/privacy-policy">Privacy policy</a>.</string>
<string name="registration_config_event_sharing_title">Sharing events</string>
<string name="registration_config_title">Szkolny.eu server registration</string>
<string name="remove">Remove</string>
<string name="removed">Removed</string>
<string name="report">Report</string>
@ -849,13 +851,13 @@
<string name="settings_about_crash_text">Click to throw an unexpected exception</string>
<string name="settings_about_discord_subtext">Join our Discord community!</string>
<string name="settings_about_discord_text">Discord server</string>
<string name="settings_about_language_dialog_text">Notice. This feature may not work on some devices or in some parts of the app.</string>
<string name="settings_about_language_dialog_title">Change app language</string>
<string name="settings_about_language_subtext">"English "</string>
<string name="app_language_dialog_text">Notice. This feature may not work on some devices or in some parts of the app.</string>
<string name="app_language_dialog_title">Change app language</string>
<string name="settings_about_language_subtext">English</string>
<string name="settings_about_language_text">App language</string>
<string name="settings_about_licenses_text">Open-source licenses</string>
<string name="settings_about_privacy_policy_text">Privacy policy</string>
<string name="settings_about_register_title_text">E-register</string>
<string name="settings_card_register_title">E-register</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nSeptember 2018 - February 2021</string>
<string name="settings_about_update_subtext">Click to check for updates</string>
<string name="settings_about_update_text">Update</string>
@ -946,7 +948,7 @@
<string name="settings_sync_sync_interval_subtext_disabled">Disabled</string>
<string name="settings_sync_sync_interval_subtext_format">Download data every %s</string>
<string name="settings_sync_sync_interval_text">Automatic sync</string>
<string name="settings_sync_title_text">Sync &amp; notifications</string>
<string name="settings_card_sync_title">Sync &amp; notifications</string>
<string name="settings_sync_updates_text">Notify about app updates</string>
<string name="settings_sync_web_push_subtext">Show notifications on your PC</string>
<string name="settings_sync_web_push_text">Notification forwarding</string>
@ -970,7 +972,7 @@
<string name="settings_theme_theme_pink">Pink</string>
<string name="settings_theme_theme_system">System</string>
<string name="settings_theme_theme_text">Theme</string>
<string name="settings_theme_title_text">Appearance</string>
<string name="settings_card_theme_title">Appearance</string>
<string name="share">Share</string>
<string name="share_intent">Share…</string>
<string name="sharing_event">Sharing events…</string>

View File

@ -386,8 +386,6 @@
<string name="event_list_added_by_unknown_format">Dodano %1$s%3$s</string>
<string name="event_list_shared_by_format">{cmd-share-variant} %1$s przez %2$s%3$s</string>
<string name="event_list_shared_by_self_format">{cmd-share-variant} %1$s przez Ciebie%3$s</string>
<string name="event_manual_need_registration_text">Aby móc udostępnić wydarzenie, należy włączyć opcję rejestracji na serwerze. Pozwala to na tworzenie i odbieranie wydarzeń udostępnionych w Twojej klasie.\n\nPo kliknięciu OK zostanie ona automatycznie włączona.\n\nUpewnij się, że zapoznałeś się z warunkami i akceptujesz jej postanowienia.</string>
<string name="event_manual_need_registration_title">Udostępnianie wydarzeń</string>
<string name="event_manual_remove">Usuwam wydarzenie…</string>
<string name="event_manual_saving">Zapisuję wydarzenie…</string>
<string name="event_manual_share">Udostępniam wydarzenie…</string>
@ -895,9 +893,13 @@
<string name="rate_snackbar_positive">Oceń teraz</string>
<string name="rate_snackbar_text">Pokaż, że podoba ci się Szkolny.eu - oceń aplikację i spraw, by była jeszcze lepsza!</string>
<string name="refresh">Odśwież</string>
<string name="registration_enable_dialog_text">Rejestracja jest automatyczna, jeśli ta opcja jest włączona. Pozwala na tworzenie i odbieranie wydarzeń udostępnionych innym uczniom z Twojej klasy. Dzięki temu, można dodawać do dziennika pozycje nie zapisane przez nauczyciela.\n\nUpewnij się, że zapoznałeś się z warunkami <a href="http://szkolny.eu/privacy-policy">Polityki prywatności</a> i akceptujesz jej postanowienia.</string>
<string name="registration_enable_dialog_title">Rejestracja na serwerze</string>
<string name="registration_enable_progress_text">Pobieranie udostępnionych wydarzeń…</string>
<string name="registration_config_disable_progress_text">Trwa wyrejestrowywanie użytkownika…</string>
<string name="registration_config_disable_text">Stracisz możliwość korzystania z niektórych funkcji, takich jak Udostępnianie wydarzeń czy Przekazywanie powiadomień.\n\nFunkcja może być włączona/wyłączona w zależności od wybranego profilu.</string>
<string name="registration_config_enable_progress_text">Pobieranie udostępnionych wydarzeń…</string>
<string name="registration_config_enable_text">Rejestracja jest automatyczna, jeśli ta opcja jest włączona. Pozwala na tworzenie i odbieranie wydarzeń udostępnionych innym uczniom z Twojej klasy. Dzięki temu, można dodawać do dziennika pozycje nie zapisane przez nauczyciela.\n\nKontynuując, oświadczasz przeczytanie i akceptację postanowień <a href="https://szkolny.eu/privacy-policy">Polityki prywatności</a>.</string>
<string name="registration_config_event_sharing_text">Aby móc udostępnić wydarzenie, należy włączyć opcję rejestracji na serwerze. Pozwala to na tworzenie i odbieranie wydarzeń udostępnionych w Twojej klasie.\n\nPo kliknięciu OK zostanie ona automatycznie włączona.\n\nKontynuując, oświadczasz przeczytanie i akceptację postanowień <a href="https://szkolny.eu/privacy-policy">Polityki prywatności</a>.</string>
<string name="registration_config_event_sharing_title">Udostępnianie wydarzeń</string>
<string name="registration_config_title">Rejestracja aplikacji Szkolny.eu</string>
<string name="remove">Usuń</string>
<string name="removed">Usunięto</string>
<string name="report">Zgłoś</string>
@ -912,13 +914,13 @@
<string name="settings_about_crash_text">Kliknij, aby wywołać nieoczekiwany wyjątek</string>
<string name="settings_about_discord_subtext">Dołącz do naszego serwera Discord!</string>
<string name="settings_about_discord_text">Serwer Discord</string>
<string name="settings_about_language_dialog_text">Uwaga. Ta opcja może nie działać na niektórych urządzeniach oraz w niektórych fragmentach aplikacji.</string>
<string name="settings_about_language_dialog_title">Zmień język aplikacji</string>
<string name="app_language_dialog_text">Uwaga. Ta opcja może nie działać na niektórych urządzeniach oraz w niektórych fragmentach aplikacji.</string>
<string name="app_language_dialog_title">Zmień język aplikacji</string>
<string name="settings_about_language_subtext">Polski</string>
<string name="settings_about_language_text">Język aplikacji</string>
<string name="settings_about_licenses_text">Licencje open-source</string>
<string name="settings_about_privacy_policy_text">Polityka prywatności</string>
<string name="settings_about_register_title_text">E-dziennik</string>
<string name="settings_card_register_title">E-dziennik</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nwrzesień 2018 - luty 2021</string>
<string name="settings_about_update_subtext">Kliknij, aby sprawdzić aktualizacje</string>
<string name="settings_about_update_text">Aktualizacja</string>
@ -1011,7 +1013,7 @@
<string name="settings_sync_sync_interval_subtext_disabled">Wyłączona</string>
<string name="settings_sync_sync_interval_subtext_format">Pobieraj dane co %s</string>
<string name="settings_sync_sync_interval_text">Synchronizacja automatyczna</string>
<string name="settings_sync_title_text">Synchronizacja i powiadomienia</string>
<string name="settings_card_sync_title">Synchronizacja i powiadomienia</string>
<string name="settings_sync_updates_text">Powiadamiaj o aktualizacjach aplikacji</string>
<string name="settings_sync_web_push_subtext">Pokazuj powiadomienia na swoim komputerze</string>
<string name="settings_sync_web_push_text">Przekazywanie powiadomień</string>
@ -1035,7 +1037,7 @@
<string name="settings_theme_theme_pink">Różowy</string>
<string name="settings_theme_theme_system">Systemowy</string>
<string name="settings_theme_theme_text">Motyw</string>
<string name="settings_theme_title_text">Wygląd</string>
<string name="settings_card_theme_title">Wygląd</string>
<string name="share">Udostępnij</string>
<string name="share_intent">Udostępnij przez…</string>
<string name="sharing_event">Udostępnianie danych…</string>
@ -1387,4 +1389,10 @@
<string name="login_chooser_mode_dev_only">{cmd-android-studio} Wersja deweloperska</string>
<string name="eggs">\???</string>
<string name="rate_snackbar_negative_message">Szkoda, opinie innych pomagają mi rozwijać aplikację.</string>
<string name="event_manual_no_profile">Nie znaleziono profilu ucznia.</string>
<string name="see_also">Zobacz także</string>
<string name="settings_about_homepage_text">Wejdź na stronę aplikacji</string>
<string name="settings_about_homepage_subtext">Uzyskaj pomoc lub wesprzyj autorów</string>
<string name="settings_about_github_text">Kod źródłowy</string>
<string name="settings_about_github_subtext">Pomóż w rozwoju aplikacji na GitHubie</string>
</resources>