Settings revamp (#1160)

This commit is contained in:
Damian Czupryn 2021-03-06 18:18:42 +01:00 committed by GitHub
parent 1afa7ecf3c
commit 47b0f1b527
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1001 additions and 413 deletions

View File

@ -52,7 +52,6 @@ class WulkanowyApp : Application(), Configuration.Provider {
override fun onCreate() {
super.onCreate()
FragmentManager.enableNewStateManager(false)
initializeAppLanguage()
themeManager.applyDefaultTheme()
initLogging()

View File

@ -18,26 +18,43 @@ class PreferencesRepository @Inject constructor(
get() = getString(R.string.pref_key_start_menu, R.string.pref_default_startup).toInt()
val isShowPresent: Boolean
get() = getBoolean(R.string.pref_key_attendance_present, R.bool.pref_default_attendance_present)
get() = getBoolean(
R.string.pref_key_attendance_present,
R.bool.pref_default_attendance_present
)
val gradeAverageMode: GradeAverageMode
get() = GradeAverageMode.getByValue(getString(R.string.pref_key_grade_average_mode, R.string.pref_default_grade_average_mode))
get() = GradeAverageMode.getByValue(
getString(
R.string.pref_key_grade_average_mode,
R.string.pref_default_grade_average_mode
)
)
val gradeAverageForceCalc: Boolean
get() = getBoolean(R.string.pref_key_grade_average_force_calc, R.bool.pref_default_grade_average_force_calc)
get() = getBoolean(
R.string.pref_key_grade_average_force_calc,
R.bool.pref_default_grade_average_force_calc
)
val isGradeExpandable: Boolean
get() = !getBoolean(R.string.pref_key_expand_grade, R.bool.pref_default_expand_grade)
val showAllSubjectsOnStatisticsList: Boolean
get() = getBoolean(R.string.pref_key_grade_statistics_list, R.bool.pref_default_grade_statistics_list)
get() = getBoolean(
R.string.pref_key_grade_statistics_list,
R.bool.pref_default_grade_statistics_list
)
val appThemeKey = context.getString(R.string.pref_key_app_theme)
val appTheme: String
get() = getString(appThemeKey, R.string.pref_default_app_theme)
val gradeColorTheme: String
get() = getString(R.string.pref_key_grade_color_scheme, R.string.pref_default_grade_color_scheme)
get() = getString(
R.string.pref_key_grade_color_scheme,
R.string.pref_default_grade_color_scheme
)
val appLanguageKey = context.getString(R.string.pref_key_app_language)
val appLanguage
@ -55,50 +72,86 @@ class PreferencesRepository @Inject constructor(
val isServicesOnlyWifi: Boolean
get() = getBoolean(servicesOnlyWifiKey, R.bool.pref_default_services_wifi_only)
val notificationsEnableKey = context.getString(R.string.pref_key_notifications_enable)
val isNotificationsEnable: Boolean
get() = getBoolean(R.string.pref_key_notifications_enable, R.bool.pref_default_notifications_enable)
get() = getBoolean(notificationsEnableKey, R.bool.pref_default_notifications_enable)
val isUpcomingLessonsNotificationsEnableKey = context.getString(R.string.pref_key_notifications_upcoming_lessons_enable)
val isUpcomingLessonsNotificationsEnableKey =
context.getString(R.string.pref_key_notifications_upcoming_lessons_enable)
val isUpcomingLessonsNotificationsEnable: Boolean
get() = getBoolean(isUpcomingLessonsNotificationsEnableKey, R.bool.pref_default_notification_upcoming_lessons_enable)
get() = getBoolean(
isUpcomingLessonsNotificationsEnableKey,
R.bool.pref_default_notification_upcoming_lessons_enable
)
val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug)
val isDebugNotificationEnable: Boolean
get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug)
val gradePlusModifier: Double
get() = getString(R.string.pref_key_grade_modifier_plus, R.string.pref_default_grade_modifier_plus).toDouble()
get() = getString(
R.string.pref_key_grade_modifier_plus,
R.string.pref_default_grade_modifier_plus
).toDouble()
val gradeMinusModifier: Double
get() = getString(R.string.pref_key_grade_modifier_minus, R.string.pref_default_grade_modifier_minus).toDouble()
get() = getString(
R.string.pref_key_grade_modifier_minus,
R.string.pref_default_grade_modifier_minus
).toDouble()
val fillMessageContent: Boolean
get() = getBoolean(R.string.pref_key_fill_message_content, R.bool.pref_default_fill_message_content)
get() = getBoolean(
R.string.pref_key_fill_message_content,
R.bool.pref_default_fill_message_content
)
val showGroupsInPlan: Boolean
get() = getBoolean(R.string.pref_key_timetable_show_groups, R.bool.pref_default_timetable_show_groups)
get() = getBoolean(
R.string.pref_key_timetable_show_groups,
R.bool.pref_default_timetable_show_groups
)
val showWholeClassPlan: String
get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class)
get() = getString(
R.string.pref_key_timetable_show_whole_class,
R.string.pref_default_timetable_show_whole_class
)
val gradeSortingMode: GradeSortingMode
get() = GradeSortingMode.getByValue(getString(R.string.pref_key_grade_sorting_mode, R.string.pref_default_grade_sorting_mode))
get() = GradeSortingMode.getByValue(
getString(
R.string.pref_key_grade_sorting_mode,
R.string.pref_default_grade_sorting_mode
)
)
val showTimetableTimers: Boolean
get() = getBoolean(R.string.pref_key_timetable_show_timers, R.bool.pref_default_timetable_show_timers)
get() = getBoolean(
R.string.pref_key_timetable_show_timers,
R.bool.pref_default_timetable_show_timers
)
var isHomeworkFullscreen: Boolean
get() = getBoolean(R.string.pref_key_homework_fullscreen, R.bool.pref_default_homework_fullscreen)
get() = getBoolean(
R.string.pref_key_homework_fullscreen,
R.bool.pref_default_homework_fullscreen
)
set(value) = sharedPref.edit().putBoolean("homework_fullscreen", value).apply()
val showSubjectsWithoutGrades: Boolean
get() = getBoolean(R.string.pref_key_subjects_without_grades, R.bool.pref_default_subjects_without_grades)
get() = getBoolean(
R.string.pref_key_subjects_without_grades,
R.bool.pref_default_subjects_without_grades
)
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
private fun getString(id: String, default: Int) = sharedPref.getString(id, context.getString(default)) ?: context.getString(default)
private fun getString(id: String, default: Int) =
sharedPref.getString(id, context.getString(default)) ?: context.getString(default)
private fun getBoolean(id: Int, default: Int) = getBoolean(context.getString(id), default)
private fun getBoolean(id: String, default: Int) = sharedPref.getBoolean(id, context.resources.getBoolean(default))
private fun getBoolean(id: String, default: Int) =
sharedPref.getBoolean(id, context.resources.getBoolean(default))
}

View File

@ -15,12 +15,17 @@ import android.os.Build.VERSION_CODES.P
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updateMargins
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.aurelhubert.ahbottomnavigation.AHBottomNavigation.TitleState.ALWAYS_SHOW
import com.aurelhubert.ahbottomnavigation.AHBottomNavigationItem
import com.google.android.material.elevation.ElevationOverlayProvider
@ -55,7 +60,8 @@ import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainView {
class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainView,
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
@Inject
override lateinit var presenter: MainPresenter
@ -245,12 +251,20 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
with(navController) {
setOnViewChangeListener { section, name ->
if (section == MainView.Section.ACCOUNT || section == MainView.Section.STUDENT_INFO) {
binding.mainBottomNav.visibility = View.GONE
binding.mainBottomNav.isVisible = false
binding.mainFragmentContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
updateMargins(bottom = 0)
}
if (appInfo.systemVersion >= P) {
window.navigationBarColor = getThemeAttrColor(R.attr.colorSurface)
}
} else {
binding.mainBottomNav.visibility = View.VISIBLE
binding.mainBottomNav.isVisible = true
binding.mainFragmentContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
updateMargins(bottom = dpToPx(56f).toInt())
}
if (appInfo.systemVersion >= P) {
window.navigationBarColor =
getThemeAttrColor(android.R.attr.navigationBarColor)
@ -271,6 +285,16 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
}
}
override fun onPreferenceStartFragment(
caller: PreferenceFragmentCompat,
pref: Preference
): Boolean {
val fragment =
supportFragmentManager.fragmentFactory.instantiate(classLoader, pref.fragment)
navController.pushFragment(fragment)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item.itemId == R.id.mainMenuAccount) presenter.onAccountManagerSelected()
else false

View File

@ -63,9 +63,6 @@ class MoreFragment : BaseFragment<FragmentMoreBinding>(R.layout.fragment_more),
override val settingsRes: Pair<String, Drawable?>?
get() = context?.run { getString(R.string.settings_title) to getCompatDrawable(R.drawable.ic_more_settings) }
override val aboutRes: Pair<String, Drawable?>?
get() = context?.run { getString(R.string.about_title) to getCompatDrawable(R.drawable.ic_all_about) }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentMoreBinding.bind(view)
@ -124,10 +121,6 @@ class MoreFragment : BaseFragment<FragmentMoreBinding>(R.layout.fragment_more),
(activity as? MainActivity)?.pushView(SettingsFragment.newInstance())
}
override fun openAboutView() {
(activity as? MainActivity)?.pushView(AboutFragment.newInstance())
}
override fun popView(depth: Int) {
(activity as? MainActivity)?.popView(depth)
}

View File

@ -30,7 +30,6 @@ class MorePresenter @Inject constructor(
conferencesRes?.first -> openConferencesView()
schoolAndTeachersRes?.first -> openSchoolAndTeachersView()
settingsRes?.first -> openSettingsView()
aboutRes?.first -> openAboutView()
}
}
}
@ -51,8 +50,7 @@ class MorePresenter @Inject constructor(
mobileDevicesRes,
conferencesRes,
schoolAndTeachersRes,
settingsRes,
aboutRes
settingsRes
))
}
}

View File

@ -21,16 +21,12 @@ interface MoreView : BaseView {
val settingsRes: Pair<String, Drawable?>?
val aboutRes: Pair<String, Drawable?>?
fun initView()
fun updateData(data: List<Pair<String, Drawable?>>)
fun openSettingsView()
fun openAboutView()
fun popView(depth: Int)
fun openMessagesView()

View File

@ -1,153 +1,22 @@
package io.github.wulkanowy.ui.modules.settings
import android.content.SharedPreferences
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.thelittlefireman.appkillermanager.AppKillerManager
import com.thelittlefireman.appkillermanager.exceptions.NoActionFoundException
import com.yariksoffice.lingver.Lingver
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.openInternetBrowser
import javax.inject.Inject
import timber.log.Timber
@AndroidEntryPoint
class SettingsFragment : PreferenceFragmentCompat(),
SharedPreferences.OnSharedPreferenceChangeListener,
MainView.TitledView, SettingsView {
@Inject
lateinit var presenter: SettingsPresenter
@Inject
lateinit var appInfo: AppInfo
@Inject
lateinit var lingver: Lingver
class SettingsFragment : PreferenceFragmentCompat(), MainView.TitledView {
companion object {
fun newInstance() = SettingsFragment()
}
override val titleStringId get() = R.string.settings_title
override val syncSuccessString get() = getString(R.string.pref_services_message_sync_success)
override val syncFailedString get() = getString(R.string.pref_services_message_sync_failed)
override fun initView() {
findPreference<Preference>(getString(R.string.pref_key_services_force_sync))?.run {
onPreferenceClickListener = Preference.OnPreferenceClickListener {
presenter.onSyncNowClicked()
true
}
}
findPreference<Preference>(getString(R.string.pref_key_notifications_fix_issues))?.run {
isVisible =
AppKillerManager.isDeviceSupported() && AppKillerManager.isAnyActionAvailable(
requireContext()
)
setOnPreferenceClickListener {
presenter.onFixSyncIssuesClicked()
true
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter.onAttachView(this)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.scheme_preferences, rootKey)
findPreference<Preference>(getString(R.string.pref_key_notification_debug))?.isVisible =
appInfo.isDebug
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
presenter.onSharedPreferenceChanged(key)
}
override fun recreateView() {
activity?.recreate()
}
override fun updateLanguage(langCode: String) {
lingver.setLocale(requireContext(), langCode)
}
override fun updateLanguageToFollowSystem() {
lingver.setFollowSystemLocale(requireContext())
}
override fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) {
findPreference<Preference>(serviceEnablesKey)?.run {
summary = if (isHolidays) getString(R.string.pref_services_suspended) else ""
isEnabled = !isHolidays
}
}
override fun setSyncInProgress(inProgress: Boolean) {
if (activity == null || !isAdded) return
findPreference<Preference>(getString(R.string.pref_key_services_force_sync))?.run {
isEnabled = !inProgress
summary = if (inProgress) getString(R.string.pref_services_sync_in_progress) else ""
}
}
override fun showError(text: String, error: Throwable) {
(activity as? BaseActivity<*, *>)?.showError(text, error)
}
override fun showMessage(text: String) {
(activity as? BaseActivity<*, *>)?.showMessage(text)
}
override fun showExpiredDialog() {
(activity as? BaseActivity<*, *>)?.showExpiredDialog()
}
override fun openClearLoginView() {
(activity as? BaseActivity<*, *>)?.openClearLoginView()
}
override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
override fun showFixSyncDialog() {
AlertDialog.Builder(requireContext())
.setTitle(R.string.pref_notify_fix_sync_issues)
.setMessage(R.string.pref_notify_fix_sync_issues_message)
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.setPositiveButton(R.string.pref_notify_fix_sync_issues_settings_button) { _, _ ->
try {
AppKillerManager.doActionPowerSaving(requireContext())
AppKillerManager.doActionAutoStart(requireContext())
AppKillerManager.doActionNotification(requireContext())
} catch (e: NoActionFoundException) {
requireContext().openInternetBrowser("https://dontkillmyapp.com/${AppKillerManager.getDevice()?.manufacturer}", ::showMessage)
}
}
.show()
}
override fun onResume() {
super.onResume()
preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
Timber.i("Settings view was initialized")
}
}

View File

@ -0,0 +1,78 @@
package io.github.wulkanowy.ui.modules.settings.advanced
import android.content.SharedPreferences
import android.os.Bundle
import android.view.View
import androidx.preference.PreferenceFragmentCompat
import com.yariksoffice.lingver.Lingver
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.AppInfo
import javax.inject.Inject
@AndroidEntryPoint
class AdvancedFragment : PreferenceFragmentCompat(),
SharedPreferences.OnSharedPreferenceChangeListener,
MainView.TitledView, AdvancedView {
@Inject
lateinit var presenter: AdvancedPresenter
@Inject
lateinit var appInfo: AppInfo
@Inject
lateinit var lingver: Lingver
companion object {
fun newInstance() = AdvancedFragment()
}
override val titleStringId get() = R.string.pref_settings_advanced_title
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter.onAttachView(this)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.scheme_preferences_advanced, rootKey)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
presenter.onSharedPreferenceChanged(key)
}
override fun showError(text: String, error: Throwable) {
(activity as? BaseActivity<*, *>)?.showError(text, error)
}
override fun showMessage(text: String) {
(activity as? BaseActivity<*, *>)?.showMessage(text)
}
override fun showExpiredDialog() {
(activity as? BaseActivity<*, *>)?.showExpiredDialog()
}
override fun openClearLoginView() {
(activity as? BaseActivity<*, *>)?.openClearLoginView()
}
override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
override fun onResume() {
super.onResume()
preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
}
}

View File

@ -0,0 +1,25 @@
package io.github.wulkanowy.ui.modules.settings.advanced
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
import timber.log.Timber
import javax.inject.Inject
class AdvancedPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val analytics: AnalyticsHelper,
) : BasePresenter<AdvancedView>(errorHandler, studentRepository) {
override fun onAttachView(view: AdvancedView) {
super.onAttachView(view)
Timber.i("Settings advanced view was initialized")
}
fun onSharedPreferenceChanged(key: String) {
Timber.i("Change settings $key")
analytics.logEvent("setting_changed", "name" to key)
}
}

View File

@ -0,0 +1,5 @@
package io.github.wulkanowy.ui.modules.settings.advanced
import io.github.wulkanowy.ui.base.BaseView
interface AdvancedView : BaseView {}

View File

@ -0,0 +1,90 @@
package io.github.wulkanowy.ui.modules.settings.appearance
import android.content.SharedPreferences
import android.os.Bundle
import android.view.View
import androidx.preference.PreferenceFragmentCompat
import com.yariksoffice.lingver.Lingver
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.AppInfo
import javax.inject.Inject
@AndroidEntryPoint
class AppearanceFragment : PreferenceFragmentCompat(),
SharedPreferences.OnSharedPreferenceChangeListener,
MainView.TitledView, AppearanceView {
@Inject
lateinit var presenter: AppearancePresenter
@Inject
lateinit var appInfo: AppInfo
@Inject
lateinit var lingver: Lingver
companion object {
fun newInstance() = AppearanceFragment()
}
override val titleStringId get() = R.string.pref_settings_appearance_title
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter.onAttachView(this)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.scheme_preferences_appearance, rootKey)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
presenter.onSharedPreferenceChanged(key)
}
override fun recreateView() {
activity?.recreate()
}
override fun updateLanguage(langCode: String) {
lingver.setLocale(requireContext(), langCode)
}
override fun updateLanguageToFollowSystem() {
lingver.setFollowSystemLocale(requireContext())
}
override fun showError(text: String, error: Throwable) {
(activity as? BaseActivity<*, *>)?.showError(text, error)
}
override fun showMessage(text: String) {
(activity as? BaseActivity<*, *>)?.showMessage(text)
}
override fun showExpiredDialog() {
(activity as? BaseActivity<*, *>)?.showExpiredDialog()
}
override fun openClearLoginView() {
(activity as? BaseActivity<*, *>)?.openClearLoginView()
}
override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
override fun onResume() {
super.onResume()
preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
}
}

View File

@ -0,0 +1,45 @@
package io.github.wulkanowy.ui.modules.settings.appearance
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.AppInfo
import timber.log.Timber
import javax.inject.Inject
class AppearancePresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val preferencesRepository: PreferencesRepository,
private val analytics: AnalyticsHelper,
private val appInfo: AppInfo
) : BasePresenter<AppearanceView>(errorHandler, studentRepository) {
override fun onAttachView(view: AppearanceView) {
super.onAttachView(view)
Timber.i("Settings appearance view was initialized")
}
fun onSharedPreferenceChanged(key: String) {
Timber.i("Change settings $key")
preferencesRepository.apply {
when (key) {
appThemeKey -> view?.recreateView()
appLanguageKey -> view?.run {
if (appLanguage == "system") {
updateLanguageToFollowSystem()
analytics.logEvent("language", "setting_changed" to appInfo.systemLanguage)
} else {
updateLanguage(appLanguage)
analytics.logEvent("language", "setting_changed" to appLanguage)
}
recreateView()
}
}
}
analytics.logEvent("setting_changed", "name" to key)
}
}

View File

@ -0,0 +1,12 @@
package io.github.wulkanowy.ui.modules.settings.appearance
import io.github.wulkanowy.ui.base.BaseView
interface AppearanceView : BaseView {
fun recreateView()
fun updateLanguage(langCode: String)
fun updateLanguageToFollowSystem()
}

View File

@ -0,0 +1,130 @@
package io.github.wulkanowy.ui.modules.settings.notifications
import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.recyclerview.widget.RecyclerView
import com.thelittlefireman.appkillermanager.AppKillerManager
import com.thelittlefireman.appkillermanager.exceptions.NoActionFoundException
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.openInternetBrowser
import javax.inject.Inject
@AndroidEntryPoint
class NotificationsFragment : PreferenceFragmentCompat(),
SharedPreferences.OnSharedPreferenceChangeListener,
MainView.TitledView, NotificationsView {
@Inject
lateinit var presenter: NotificationsPresenter
companion object {
fun newInstance() = NotificationsFragment()
}
override val titleStringId get() = R.string.pref_settings_notifications_title
override fun initView(showDebugNotificationSwitch: Boolean) {
findPreference<Preference>(getString(R.string.pref_key_notification_debug))?.isVisible =
showDebugNotificationSwitch
findPreference<Preference>(getString(R.string.pref_key_notifications_fix_issues))?.run {
isVisible = AppKillerManager.isDeviceSupported()
&& AppKillerManager.isAnyActionAvailable(requireContext())
setOnPreferenceClickListener {
presenter.onFixSyncIssuesClicked()
true
}
}
}
override fun onCreateRecyclerView(
inflater: LayoutInflater?,
parent: ViewGroup?,
state: Bundle?
): RecyclerView? = super.onCreateRecyclerView(inflater, parent, state)
.also {
it.itemAnimator = null
it.layoutAnimation = null
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter.onAttachView(this)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.scheme_preferences_notifications, rootKey)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
presenter.onSharedPreferenceChanged(key)
}
override fun enableNotification(notificationKey: String, enable: Boolean) {
findPreference<Preference>(notificationKey)?.run {
isEnabled = enable
summary = if (enable) null else getString(R.string.pref_notify_disabled_summary)
}
}
override fun showError(text: String, error: Throwable) {
(activity as? BaseActivity<*, *>)?.showError(text, error)
}
override fun showMessage(text: String) {
(activity as? BaseActivity<*, *>)?.showMessage(text)
}
override fun showExpiredDialog() {
(activity as? BaseActivity<*, *>)?.showExpiredDialog()
}
override fun openClearLoginView() {
(activity as? BaseActivity<*, *>)?.openClearLoginView()
}
override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
override fun showFixSyncDialog() {
AlertDialog.Builder(requireContext())
.setTitle(R.string.pref_notify_fix_sync_issues)
.setMessage(R.string.pref_notify_fix_sync_issues_message)
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.setPositiveButton(R.string.pref_notify_fix_sync_issues_settings_button) { _, _ ->
try {
AppKillerManager.doActionPowerSaving(requireContext())
AppKillerManager.doActionAutoStart(requireContext())
AppKillerManager.doActionNotification(requireContext())
} catch (e: NoActionFoundException) {
requireContext().openInternetBrowser(
"https://dontkillmyapp.com/${AppKillerManager.getDevice()?.manufacturer}",
::showMessage
)
}
}
.show()
}
override fun onResume() {
super.onResume()
preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
}
}

View File

@ -0,0 +1,58 @@
package io.github.wulkanowy.ui.modules.settings.notifications
import com.chuckerteam.chucker.api.ChuckerCollector
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.AppInfo
import timber.log.Timber
import javax.inject.Inject
class NotificationsPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val preferencesRepository: PreferencesRepository,
private val timetableNotificationHelper: TimetableNotificationSchedulerHelper,
private val appInfo: AppInfo,
private val analytics: AnalyticsHelper,
private val chuckerCollector: ChuckerCollector
) : BasePresenter<NotificationsView>(errorHandler, studentRepository) {
override fun onAttachView(view: NotificationsView) {
super.onAttachView(view)
with(view) {
enableNotification(
preferencesRepository.notificationsEnableKey,
preferencesRepository.isServiceEnabled
)
initView(appInfo.isDebug)
}
Timber.i("Settings notifications view was initialized")
}
fun onSharedPreferenceChanged(key: String) {
Timber.i("Change settings $key")
preferencesRepository.apply {
when (key) {
isUpcomingLessonsNotificationsEnableKey -> {
if (!isUpcomingLessonsNotificationsEnable) {
timetableNotificationHelper.cancelNotification()
}
}
isDebugNotificationEnableKey -> {
chuckerCollector.showNotification = isDebugNotificationEnable
}
}
}
analytics.logEvent("setting_changed", "name" to key)
}
fun onFixSyncIssuesClicked() {
view?.showFixSyncDialog()
}
}

View File

@ -0,0 +1,12 @@
package io.github.wulkanowy.ui.modules.settings.notifications
import io.github.wulkanowy.ui.base.BaseView
interface NotificationsView : BaseView {
fun initView(showDebugNotificationSwitch: Boolean)
fun showFixSyncDialog()
fun enableNotification(notificationKey: String, enable: Boolean)
}

View File

@ -0,0 +1,100 @@
package io.github.wulkanowy.ui.modules.settings.sync
import android.content.SharedPreferences
import android.os.Bundle
import android.view.View
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.ui.modules.main.MainView
import javax.inject.Inject
@AndroidEntryPoint
class SyncFragment : PreferenceFragmentCompat(),
SharedPreferences.OnSharedPreferenceChangeListener,
MainView.TitledView, SyncView {
@Inject
lateinit var presenter: SyncPresenter
companion object {
fun newInstance() = SyncFragment()
}
override val titleStringId get() = R.string.pref_settings_sync_title
override val syncSuccessString get() = getString(R.string.pref_services_message_sync_success)
override val syncFailedString get() = getString(R.string.pref_services_message_sync_failed)
override fun initView() {
findPreference<Preference>(getString(R.string.pref_key_services_force_sync))?.run {
onPreferenceClickListener = Preference.OnPreferenceClickListener {
presenter.onSyncNowClicked()
true
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter.onAttachView(this)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.scheme_preferences_sync, rootKey)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
presenter.onSharedPreferenceChanged(key)
}
override fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) {
findPreference<Preference>(serviceEnablesKey)?.run {
summary = if (isHolidays) getString(R.string.pref_services_suspended) else ""
isEnabled = !isHolidays
}
}
override fun setSyncInProgress(inProgress: Boolean) {
if (activity == null || !isAdded) return
findPreference<Preference>(getString(R.string.pref_key_services_force_sync))?.run {
isEnabled = !inProgress
summary = if (inProgress) getString(R.string.pref_services_sync_in_progress) else ""
}
}
override fun showError(text: String, error: Throwable) {
(activity as? BaseActivity<*, *>)?.showError(text, error)
}
override fun showMessage(text: String) {
(activity as? BaseActivity<*, *>)?.showMessage(text)
}
override fun showExpiredDialog() {
(activity as? BaseActivity<*, *>)?.showExpiredDialog()
}
override fun openClearLoginView() {
(activity as? BaseActivity<*, *>)?.openClearLoginView()
}
override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
override fun onResume() {
super.onResume()
preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
}
}

View File

@ -1,15 +1,12 @@
package io.github.wulkanowy.ui.modules.settings
package io.github.wulkanowy.ui.modules.settings.sync
import androidx.work.WorkInfo
import com.chuckerteam.chucker.api.ChuckerCollector
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.isHolidays
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.onEach
@ -17,20 +14,17 @@ import timber.log.Timber
import java.time.LocalDate.now
import javax.inject.Inject
class SettingsPresenter @Inject constructor(
class SyncPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val preferencesRepository: PreferencesRepository,
private val timetableNotificationHelper: TimetableNotificationSchedulerHelper,
private val analytics: AnalyticsHelper,
private val syncManager: SyncManager,
private val chuckerCollector: ChuckerCollector,
private val appInfo: AppInfo
) : BasePresenter<SettingsView>(errorHandler, studentRepository) {
) : BasePresenter<SyncView>(errorHandler, studentRepository) {
override fun onAttachView(view: SettingsView) {
override fun onAttachView(view: SyncView) {
super.onAttachView(view)
Timber.i("Settings view was initialized")
Timber.i("Settings sync view was initialized")
view.setServicesSuspended(preferencesRepository.serviceEnableKey, now().isHolidays)
view.initView()
}
@ -42,20 +36,6 @@ class SettingsPresenter @Inject constructor(
when (key) {
serviceEnableKey -> with(syncManager) { if (isServiceEnabled) startPeriodicSyncWorker() else stopSyncWorker() }
servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startPeriodicSyncWorker(true)
isDebugNotificationEnableKey -> chuckerCollector.showNotification =
isDebugNotificationEnable
appThemeKey -> view?.recreateView()
isUpcomingLessonsNotificationsEnableKey -> if (!isUpcomingLessonsNotificationsEnable) timetableNotificationHelper.cancelNotification()
appLanguageKey -> view?.run {
if (appLanguage == "system") {
updateLanguageToFollowSystem()
analytics.logEvent("language", "setting_changed" to appInfo.systemLanguage)
} else {
updateLanguage(appLanguage)
analytics.logEvent("language", "setting_changed" to appLanguage)
}
recreateView()
}
}
}
analytics.logEvent("setting_changed", "name" to key)
@ -89,8 +69,4 @@ class SettingsPresenter @Inject constructor(
}.launch("sync")
}
}
fun onFixSyncIssuesClicked() {
view?.showFixSyncDialog()
}
}

View File

@ -1,8 +1,8 @@
package io.github.wulkanowy.ui.modules.settings
package io.github.wulkanowy.ui.modules.settings.sync
import io.github.wulkanowy.ui.base.BaseView
interface SettingsView : BaseView {
interface SyncView : BaseView {
val syncSuccessString: String
@ -10,15 +10,7 @@ interface SettingsView : BaseView {
fun initView()
fun recreateView()
fun updateLanguage(langCode: String)
fun updateLanguageToFollowSystem()
fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean)
fun setSyncInProgress(inProgress: Boolean)
fun showFixSyncDialog()
}

View File

@ -2,6 +2,7 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:tint="?colorOnSurface"
android:viewportHeight="24">
<path
android:fillColor="#FFF"

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorOnSurface"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M14.17,13.71l1.4,-2.42c0.09,-0.15 0.05,-0.34 -0.08,-0.45l-1.48,-1.16c0.03,-0.22 0.05,-0.45 0.05,-0.68s-0.02,-0.46 -0.05,-0.69l1.48,-1.16c0.13,-0.11 0.17,-0.3 0.08,-0.45l-1.4,-2.42c-0.09,-0.15 -0.27,-0.21 -0.43,-0.15L12,4.83c-0.36,-0.28 -0.75,-0.51 -1.18,-0.69l-0.26,-1.85C10.53,2.13 10.38,2 10.21,2h-2.8C7.24,2 7.09,2.13 7.06,2.3L6.8,4.15C6.38,4.33 5.98,4.56 5.62,4.84l-1.74,-0.7c-0.16,-0.06 -0.34,0 -0.43,0.15l-1.4,2.42C1.96,6.86 2,7.05 2.13,7.16l1.48,1.16C3.58,8.54 3.56,8.77 3.56,9s0.02,0.46 0.05,0.69l-1.48,1.16C2,10.96 1.96,11.15 2.05,11.3l1.4,2.42c0.09,0.15 0.27,0.21 0.43,0.15l1.74,-0.7c0.36,0.28 0.75,0.51 1.18,0.69l0.26,1.85C7.09,15.87 7.24,16 7.41,16h2.8c0.17,0 0.32,-0.13 0.35,-0.3l0.26,-1.85c0.42,-0.18 0.82,-0.41 1.18,-0.69l1.74,0.7C13.9,13.92 14.08,13.86 14.17,13.71zM8.81,11c-1.1,0 -2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2C10.81,10.1 9.91,11 8.81,11z" />
<path
android:fillColor="@android:color/white"
android:pathData="M21.92,18.67l-0.96,-0.74c0.02,-0.14 0.04,-0.29 0.04,-0.44c0,-0.15 -0.01,-0.3 -0.04,-0.44l0.95,-0.74c0.08,-0.07 0.11,-0.19 0.05,-0.29l-0.9,-1.55c-0.05,-0.1 -0.17,-0.13 -0.28,-0.1l-1.11,0.45c-0.23,-0.18 -0.48,-0.33 -0.76,-0.44l-0.17,-1.18C18.73,13.08 18.63,13 18.53,13h-1.79c-0.11,0 -0.21,0.08 -0.22,0.19l-0.17,1.18c-0.27,0.12 -0.53,0.26 -0.76,0.44l-1.11,-0.45c-0.1,-0.04 -0.22,0 -0.28,0.1l-0.9,1.55c-0.05,0.1 -0.04,0.22 0.05,0.29l0.95,0.74c-0.02,0.14 -0.03,0.29 -0.03,0.44c0,0.15 0.01,0.3 0.03,0.44l-0.95,0.74c-0.08,0.07 -0.11,0.19 -0.05,0.29l0.9,1.55c0.05,0.1 0.17,0.13 0.28,0.1l1.11,-0.45c0.23,0.18 0.48,0.33 0.76,0.44l0.17,1.18c0.02,0.11 0.11,0.19 0.22,0.19h1.79c0.11,0 0.21,-0.08 0.22,-0.19l0.17,-1.18c0.27,-0.12 0.53,-0.26 0.75,-0.44l1.12,0.45c0.1,0.04 0.22,0 0.28,-0.1l0.9,-1.55C22.03,18.86 22,18.74 21.92,18.67zM17.63,18.83c-0.74,0 -1.35,-0.6 -1.35,-1.35s0.6,-1.35 1.35,-1.35s1.35,0.6 1.35,1.35S18.37,18.83 17.63,18.83z" />
</vector>

View File

@ -0,0 +1,22 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorOnSurface"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,22C6.49,22 2,17.51 2,12S6.49,2 12,2s10,4.04 10,9c0,3.31 -2.69,6 -6,6h-1.77c-0.28,0 -0.5,0.22 -0.5,0.5 0,0.12 0.05,0.23 0.13,0.33 0.41,0.47 0.64,1.06 0.64,1.67 0,1.38 -1.12,2.5 -2.5,2.5zM12,4c-4.41,0 -8,3.59 -8,8s3.59,8 8,8c0.28,0 0.5,-0.22 0.5,-0.5 0,-0.16 -0.08,-0.28 -0.14,-0.35 -0.41,-0.46 -0.63,-1.05 -0.63,-1.65 0,-1.38 1.12,-2.5 2.5,-2.5L16,15c2.21,0 4,-1.79 4,-4 0,-3.86 -3.59,-7 -8,-7z" />
<path
android:fillColor="@android:color/white"
android:pathData="M6.5,11.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0" />
<path
android:fillColor="@android:color/white"
android:pathData="M9.5,7.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0" />
<path
android:fillColor="@android:color/white"
android:pathData="M14.5,7.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0" />
<path
android:fillColor="@android:color/white"
android:pathData="M17.5,11.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorOnSurface"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zM18,16v-5c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.64,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2zM16,17L8,17v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5v6z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?colorOnSurface"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z" />
</vector>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/mainContainer"
android:layout_width="match_parent"
@ -10,19 +10,18 @@
style="@style/Widget.MaterialComponents.Toolbar.Surface"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:contentInsetStartWithNavigation="0dp"
app:layout_constraintTop_toTopOf="parent" />
app:contentInsetStartWithNavigation="0dp" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/mainFragmentContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/mainBottomNav"
app:layout_constraintTop_toBottomOf="@id/mainToolbar" />
android:layout_height="match_parent"
android:layout_marginTop="?actionBarSize"
android:layout_marginBottom="@dimen/bottom_navigation_height" />
<com.aurelhubert.ahbottomnavigation.AHBottomNavigation
android:id="@+id/mainBottomNav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
android:layout_gravity="bottom" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -464,21 +464,21 @@
<!--Preferences-->
<string name="pref_view_header">Appearance</string>
<string name="pref_view_header">App apearance &amp; behavior</string>
<string name="pref_view_list">Default view</string>
<string name="pref_view_grade_average_mode">Calculation of the end-of-year average</string>
<string name="pref_view_grade_average_force_calc">Force average calculation by app</string>
<string name="pref_view_present">Show presence in attendance</string>
<string name="pref_view_app_theme">Application theme</string>
<string name="pref_view_present">Show presence</string>
<string name="pref_view_app_theme">Theme</string>
<string name="pref_view_expand_grade">Expand grades</string>
<string name="pref_view_timetable_show_timers">Mark current lesson in timetable</string>
<string name="pref_view_timetable_show_groups">Show groups next to subjects in timetable</string>
<string name="pref_view_timetable_show_timers">Mark current lesson</string>
<string name="pref_view_timetable_show_groups">Show groups next to subjects</string>
<string name="pref_view_grade_statistics_list">Show chart list in class grades</string>
<string name="pref_view_timetable_show_whole_class">Show whole class lessons</string>
<string name="pref_view_subjects_without_grades">Show subjects without grades in Grades</string>
<string name="pref_view_subjects_without_grades">Show subjects without grades</string>
<string name="pref_view_grade_color_scheme">Grades color scheme</string>
<string name="pref_view_grade_sorting_mode">Subjects sorting in "Grades"</string>
<string name="pref_view_app_language">App language</string>
<string name="pref_view_grade_sorting_mode">Subjects sorting</string>
<string name="pref_view_app_language">Language</string>
<string name="pref_notify_header">Notifications</string>
<string name="pref_notify_switch">Show notifications</string>
@ -487,6 +487,7 @@
<string name="pref_notify_fix_sync_issues_message">Your device may have data synchronization issues and with notifications.\n\nTo fix them, you need to add Wulkanowy to the autostart and turn off battery optimization/saving in the phone settings.</string>
<string name="pref_notify_fix_sync_issues_settings_button">Go to settings</string>
<string name="pref_notify_debug_switch">Show debug notifications</string>
<string name="pref_notify_disabled_summary">Synchronization is disabled</string>
<string name="pref_services_header">Synchronization</string>
<string name="pref_services_switch">Automatic update</string>
@ -497,17 +498,32 @@
<string name="pref_services_message_sync_success">Synced!</string>
<string name="pref_services_message_sync_failed">Sync failed</string>
<string name="pref_services_sync_in_progress">Sync in progress</string>
<string name="pref_services_dialog_force_sync_title">Synchronization</string>
<string name="pref_services_dialog_force_sync_summary">
Manual sync doesn\'t refresh app views.
\nTo see the synced data relaunch the app after syncing.
</string>
<string name="pref_other_header">Other</string>
<string name="pref_other_grade_modifier_plus">Value of the plus</string>
<string name="pref_other_grade_modifier_minus">Value of the minus</string>
<string name="pref_other_fill_message_content">Reply with message history</string>
<string name="pref_settings_advanced_title">Advanced</string>
<string name="pref_settings_appearance_title">Appearance &amp; Behavior</string>
<string name="pref_settings_notifications_title">Notifications</string>
<string name="pref_settings_sync_title">Synchronization</string>
<string name="pref_grades_appearance_header">Grades</string>
<string name="pref_attendance_appearance_view">Attendance</string>
<string name="pref_timetable_appearance_view">Timetable</string>
<string name="pref_grades_advanced_header">Grades</string>
<string name="pref_messages_advanced_header">Messages</string>
<string name="pref_appearance_category">Appearance &amp; Behavior</string>
<string name="pref_appearance_category_summary">Languages, themes, subjects sorting</string>
<string name="pref_notifications_category_summary">App notifications, fix problems</string>
<string name="pref_notifications_category">Notifications</string>
<string name="pref_sync_category">Synchronization</string>
<string name="pref_sync_category_summary">Automatic update, synchronization interval</string>
<string name="pref_advanced_category_summary">Plus and minus values, average calculation</string>
<string name="pref_advanced_category">Advanced</string>
<string name="pref_about_category_summary">App version, contributors, social portals, licenses</string>
<!--Notification Channels-->
<string name="channel_new_entries">New entries in register</string>

View File

@ -1,182 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
app:iconSpaceReserved="false"
app:title="@string/pref_view_header">
<ListPreference
app:defaultValue="@string/pref_default_startup"
app:entries="@array/startup_tab_entries"
app:entryValues="@array/startup_tab_value"
app:iconSpaceReserved="false"
app:key="@string/pref_key_start_menu"
app:title="@string/pref_view_list"
app:useSimpleSummaryProvider="true" />
<ListPreference
app:defaultValue="@string/pref_default_app_theme"
app:entries="@array/app_theme_entries"
app:entryValues="@array/app_theme_values"
app:iconSpaceReserved="false"
app:key="@string/pref_key_app_theme"
app:title="@string/pref_view_app_theme"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_attendance_present"
app:iconSpaceReserved="false"
app:key="@string/pref_key_attendance_present"
app:title="@string/pref_view_present" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_expand_grade"
app:iconSpaceReserved="false"
app:key="@string/pref_key_expand_grade"
app:title="@string/pref_view_expand_grade" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_timetable_show_timers"
app:iconSpaceReserved="false"
app:key="@string/pref_key_timetable_show_timers"
app:title="@string/pref_view_timetable_show_timers" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_grade_statistics_list"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_statistics_list"
app:singleLineTitle="false"
app:title="@string/pref_view_grade_statistics_list" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_timetable_show_groups"
app:iconSpaceReserved="false"
app:key="@string/pref_key_timetable_show_groups"
app:singleLineTitle="false"
app:title="@string/pref_view_timetable_show_groups" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_subjects_without_grades"
app:iconSpaceReserved="false"
app:key="@string/pref_key_subjects_without_grades"
app:singleLineTitle="false"
app:title="@string/pref_view_subjects_without_grades" />
<ListPreference
app:defaultValue="@string/pref_default_timetable_show_whole_class"
app:entries="@array/timetable_show_whole_class_entries"
app:entryValues="@array/timetable_show_whole_class_values"
app:iconSpaceReserved="false"
app:key="@string/pref_key_timetable_show_whole_class"
app:title="@string/pref_view_timetable_show_whole_class"
app:useSimpleSummaryProvider="true" />
<ListPreference
app:defaultValue="@string/pref_default_grade_color_scheme"
app:entries="@array/grade_color_scheme_entries"
app:entryValues="@array/grade_color_scheme_values"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_color_scheme"
app:title="@string/pref_view_grade_color_scheme"
app:useSimpleSummaryProvider="true" />
<ListPreference
app:defaultValue="@string/pref_default_grade_sorting_mode"
app:entries="@array/grade_sorting_mode_entries"
app:entryValues="@array/grade_sorting_mode_values"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_sorting_mode"
app:title="@string/pref_view_grade_sorting_mode"
app:useSimpleSummaryProvider="true" />
<ListPreference
app:defaultValue="@string/pref_default_app_language"
app:entries="@array/app_language_entries"
app:entryValues="@array/app_language_values"
app:iconSpaceReserved="false"
app:key="@string/pref_key_app_language"
app:title="@string/pref_view_app_language"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory>
<PreferenceCategory
app:iconSpaceReserved="false"
app:title="@string/pref_services_header">
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_services_enable"
app:iconSpaceReserved="false"
app:key="@string/pref_key_services_enable"
app:title="@string/pref_services_switch" />
<ListPreference
app:defaultValue="@string/pref_default_services_interval"
app:dependency="services_enable"
app:entries="@array/services_interval_entries"
app:entryValues="@array/services_interval_value"
app:iconSpaceReserved="false"
app:key="@string/pref_key_services_interval"
app:title="@string/pref_services_interval"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_services_wifi_only"
app:dependency="services_enable"
app:iconSpaceReserved="false"
app:key="@string/pref_key_services_wifi_only"
app:title="@string/pref_services_wifi" />
<Preference
app:iconSpaceReserved="false"
app:key="@string/pref_key_services_force_sync"
app:title="@string/pref_services_force_sync" />
</PreferenceCategory>
<PreferenceCategory
app:iconSpaceReserved="false"
app:title="@string/pref_notify_header">
app:key="appearance"
app:icon="@drawable/ic_settings_appearance"
app:summary="@string/pref_appearance_category_summary"
app:title="@string/pref_appearance_category"
app:fragment="io.github.wulkanowy.ui.modules.settings.appearance.AppearanceFragment" />
<Preference
app:iconSpaceReserved="false"
app:key="@string/pref_key_notifications_fix_issues"
app:title="@string/pref_notify_fix_sync_issues" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_notifications_enable"
app:dependency="services_enable"
app:iconSpaceReserved="false"
app:key="@string/pref_key_notifications_enable"
app:title="@string/pref_notify_switch" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_notification_upcoming_lessons_enable"
app:iconSpaceReserved="false"
app:key="@string/pref_key_notifications_upcoming_lessons_enable"
app:singleLineTitle="false"
app:title="@string/pref_notify_upcoming_lessons_switch" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_notification_debug"
app:iconSpaceReserved="false"
app:key="@string/pref_key_notification_debug"
app:singleLineTitle="false"
app:title="@string/pref_notify_debug_switch" />
</PreferenceCategory>
<PreferenceCategory
app:iconSpaceReserved="false"
app:title="@string/pref_other_header">
<ListPreference
app:defaultValue="@string/pref_default_grade_modifier_plus"
app:entries="@array/grade_modifier_entries"
app:entryValues="@array/grade_modifier_value"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_modifier_plus"
app:title="@string/pref_other_grade_modifier_plus"
app:useSimpleSummaryProvider="true" />
<ListPreference
app:defaultValue="@string/pref_default_grade_modifier_minus"
app:entries="@array/grade_modifier_entries"
app:entryValues="@array/grade_modifier_value"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_modifier_minus"
app:title="@string/pref_other_grade_modifier_minus"
app:useSimpleSummaryProvider="true" />
<ListPreference
app:defaultValue="@string/pref_default_grade_average_mode"
app:entries="@array/grade_average_mode_entries"
app:entryValues="@array/grade_average_mode_values"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_average_mode"
app:title="@string/pref_view_grade_average_mode"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_grade_average_force_calc"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_average_force_calc"
app:singleLineTitle="false"
app:title="@string/pref_view_grade_average_force_calc" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_fill_message_content"
app:iconSpaceReserved="false"
app:key="@string/pref_key_fill_message_content"
app:singleLineTitle="false"
app:title="@string/pref_other_fill_message_content" />
</PreferenceCategory>
app:key="notifications"
app:icon="@drawable/ic_settings_notifications"
app:summary="@string/pref_notifications_category_summary"
app:title="@string/pref_notifications_category"
app:fragment="io.github.wulkanowy.ui.modules.settings.notifications.NotificationsFragment" />
<Preference
app:key="sync"
app:icon="@drawable/ic_settings_sync"
app:summary="@string/pref_sync_category_summary"
app:title="@string/pref_sync_category"
app:fragment="io.github.wulkanowy.ui.modules.settings.sync.SyncFragment" />
<Preference
app:key="advanced"
app:icon="@drawable/ic_settings_advanced"
app:summary="@string/pref_advanced_category_summary"
app:title="@string/pref_advanced_category"
app:fragment="io.github.wulkanowy.ui.modules.settings.advanced.AdvancedFragment" />
<Preference
app:key="about"
app:icon="@drawable/ic_all_about"
app:summary="@string/pref_about_category_summary"
app:title="@string/about_title"
app:fragment="io.github.wulkanowy.ui.modules.about.AboutFragment" />
</PreferenceScreen>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
app:iconSpaceReserved="false"
app:title="@string/pref_grades_advanced_header">
<ListPreference
app:defaultValue="@string/pref_default_grade_modifier_plus"
app:entries="@array/grade_modifier_entries"
app:entryValues="@array/grade_modifier_value"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_modifier_plus"
app:title="@string/pref_other_grade_modifier_plus"
app:useSimpleSummaryProvider="true" />
<ListPreference
app:defaultValue="@string/pref_default_grade_modifier_minus"
app:entries="@array/grade_modifier_entries"
app:entryValues="@array/grade_modifier_value"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_modifier_minus"
app:title="@string/pref_other_grade_modifier_minus"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_grade_average_force_calc"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_average_force_calc"
app:singleLineTitle="false"
app:title="@string/pref_view_grade_average_force_calc" />
<ListPreference
app:defaultValue="@string/pref_default_grade_average_mode"
app:entries="@array/grade_average_mode_entries"
app:entryValues="@array/grade_average_mode_values"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_average_mode"
app:title="@string/pref_view_grade_average_mode"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory>
<PreferenceCategory
app:iconSpaceReserved="false"
app:title="@string/pref_messages_advanced_header">
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_fill_message_content"
app:iconSpaceReserved="false"
app:key="@string/pref_key_fill_message_content"
app:singleLineTitle="false"
app:title="@string/pref_other_fill_message_content" />
</PreferenceCategory>
</PreferenceScreen>

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
app:iconSpaceReserved="false"
app:title="@string/pref_view_header">
<ListPreference
app:defaultValue="@string/pref_default_app_language"
app:entries="@array/app_language_entries"
app:entryValues="@array/app_language_values"
app:iconSpaceReserved="false"
app:key="@string/pref_key_app_language"
app:title="@string/pref_view_app_language"
app:useSimpleSummaryProvider="true" />
<ListPreference
app:defaultValue="@string/pref_default_app_theme"
app:entries="@array/app_theme_entries"
app:entryValues="@array/app_theme_values"
app:iconSpaceReserved="false"
app:key="@string/pref_key_app_theme"
app:title="@string/pref_view_app_theme"
app:useSimpleSummaryProvider="true" />
<ListPreference
app:defaultValue="@string/pref_default_startup"
app:entries="@array/startup_tab_entries"
app:entryValues="@array/startup_tab_value"
app:iconSpaceReserved="false"
app:key="@string/pref_key_start_menu"
app:title="@string/pref_view_list"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory>
<PreferenceCategory
app:iconSpaceReserved="false"
app:title="@string/pref_grades_appearance_header">
<ListPreference
app:defaultValue="@string/pref_default_grade_color_scheme"
app:entries="@array/grade_color_scheme_entries"
app:entryValues="@array/grade_color_scheme_values"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_color_scheme"
app:title="@string/pref_view_grade_color_scheme"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_expand_grade"
app:iconSpaceReserved="false"
app:key="@string/pref_key_expand_grade"
app:title="@string/pref_view_expand_grade" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_subjects_without_grades"
app:iconSpaceReserved="false"
app:key="@string/pref_key_subjects_without_grades"
app:singleLineTitle="false"
app:title="@string/pref_view_subjects_without_grades" />
<ListPreference
app:defaultValue="@string/pref_default_grade_sorting_mode"
app:entries="@array/grade_sorting_mode_entries"
app:entryValues="@array/grade_sorting_mode_values"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_sorting_mode"
app:title="@string/pref_view_grade_sorting_mode"
app:useSimpleSummaryProvider="true" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_grade_statistics_list"
app:iconSpaceReserved="false"
app:key="@string/pref_key_grade_statistics_list"
app:singleLineTitle="false"
app:title="@string/pref_view_grade_statistics_list" />
</PreferenceCategory>
<PreferenceCategory
app:iconSpaceReserved="false"
app:title="@string/pref_attendance_appearance_view">
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_attendance_present"
app:iconSpaceReserved="false"
app:key="@string/pref_key_attendance_present"
app:title="@string/pref_view_present" />
</PreferenceCategory>
<PreferenceCategory
android:layout_height="wrap_content"
app:iconSpaceReserved="false"
app:title="@string/pref_timetable_appearance_view">
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_timetable_show_timers"
app:iconSpaceReserved="false"
app:key="@string/pref_key_timetable_show_timers"
app:title="@string/pref_view_timetable_show_timers" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_timetable_show_groups"
app:iconSpaceReserved="false"
app:key="@string/pref_key_timetable_show_groups"
app:singleLineTitle="false"
app:title="@string/pref_view_timetable_show_groups" />
<!--Hidden due to not functional hybrid/mobile api modes
<ListPreference
app:defaultValue="@string/pref_default_timetable_show_whole_class"
app:entries="@array/timetable_show_whole_class_entries"
app:entryValues="@array/timetable_show_whole_class_values"
app:iconSpaceReserved="false"
app:key="@string/pref_key_timetable_show_whole_class"
app:title="@string/pref_view_timetable_show_whole_class"
app:useSimpleSummaryProvider="true" />
-->
</PreferenceCategory>
</PreferenceScreen>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
app:iconSpaceReserved="false"
app:title="@string/pref_notify_header">
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_notifications_enable"
app:iconSpaceReserved="false"
app:key="@string/pref_key_notifications_enable"
app:title="@string/pref_notify_switch" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_notification_upcoming_lessons_enable"
app:iconSpaceReserved="false"
app:key="@string/pref_key_notifications_upcoming_lessons_enable"
app:singleLineTitle="false"
app:title="@string/pref_notify_upcoming_lessons_switch" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_notification_debug"
app:iconSpaceReserved="false"
app:key="@string/pref_key_notification_debug"
app:singleLineTitle="false"
app:title="@string/pref_notify_debug_switch" />
<Preference
app:iconSpaceReserved="false"
app:isPreferenceVisible="false"
app:key="@string/pref_key_notifications_fix_issues"
app:title="@string/pref_notify_fix_sync_issues" />
</PreferenceCategory>
</PreferenceScreen>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
app:iconSpaceReserved="false"
app:title="@string/pref_services_header">
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_services_enable"
app:iconSpaceReserved="false"
app:key="@string/pref_key_services_enable"
app:title="@string/pref_services_switch" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_services_wifi_only"
app:dependency="services_enable"
app:iconSpaceReserved="false"
app:key="@string/pref_key_services_wifi_only"
app:title="@string/pref_services_wifi" />
<ListPreference
app:defaultValue="@string/pref_default_services_interval"
app:dependency="services_enable"
app:entries="@array/services_interval_entries"
app:entryValues="@array/services_interval_value"
app:iconSpaceReserved="false"
app:key="@string/pref_key_services_interval"
app:title="@string/pref_services_interval"
app:useSimpleSummaryProvider="true" />
<Preference
app:iconSpaceReserved="false"
app:key="@string/pref_key_services_force_sync"
app:title="@string/pref_services_force_sync" />
</PreferenceCategory>
</PreferenceScreen>