diff:
@@ -80,6 +80,9 @@ class PreferencesRepository @Inject constructor(
val showTimetableTimers: Boolean
get() = getBoolean(R.string.pref_key_timetable_show_timers, R.bool.pref_default_timetable_show_timers)
+ val showSubjectsWithoutGrades: Boolean
+ 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)

diff:
@@ -186,7 +186,7 @@ class GradeDetailsPresenter @Inject constructor(
private fun createGradeItems(items: List<GradeDetailsWithAverage>): List<GradeDetailsItem> {
return items
- .filter { it.grades.isNotEmpty() }
+ .let { if (!preferencesRepository.showSubjectsWithoutGrades) it.filter { it.grades.isNotEmpty() } else it.filter { true }}
.sortedBy { it.subject }
.map { (subject, average, points, _, grades) ->
val subItems = grades

diff:
<bool name="pref_default_fill_message_content">true</bool>
<string name="pref_default_timetable_show_whole_class">no</string>
<bool name="pref_default_timetable_show_timers">false</bool>
+ <bool name="pref_default_subjects_without_grades">false</bool>
</resources>

diff:
<string name="pref_key_fill_message_content">fill_message_content</string>
<string name="pref_key_timetable_show_whole_class">show_whole_class_plan</string>
<string name="pref_key_timetable_show_timers">timetable_show_timers</string>
+ <string name="pref_key_subjects_without_grades">subjects_without_grades</string>
</resources>

diff:
<string name="pref_view_timetable_show_timers">Mark current lesson in timetable</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_grade_color_scheme">Grades color scheme</string>
<string name="pref_view_app_language">App language</string>

diff:
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_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"

original_message: Show subjects without grades in "Grades" (#947)


change_type: MODIFY

diff:
@@ -14,6 +14,9 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.note.NoteRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
+import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory
+import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory.NEUTRAL
+import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory.POSITIVE
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
@@ -41,8 +44,20 @@ class NoteWork @Inject constructor(
private fun notify(notes: List<Note>) {
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewNotesChannel.CHANNEL_ID)
- .setContentTitle(context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size))
- .setContentText(context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size))
+ .setContentTitle(
+ when (NoteCategory.getByValue(notes.first().categoryType)) {
+ POSITIVE -> context.resources.getQuantityString(R.plurals.praise_new_items, notes.size, notes.size)
+ NEUTRAL -> context.resources.getQuantityString(R.plurals.neutral_note_new_items, notes.size, notes.size)
+ else -> context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size)
+ }
+ )
+ .setContentText(
+ when (NoteCategory.getByValue(notes.first().categoryType)) {
+ POSITIVE -> context.resources.getQuantityString(R.plurals.praise_notify_new_items, notes.size, notes.size)
+ NEUTRAL -> context.resources.getQuantityString(R.plurals.neutral_note_notify_new_items, notes.size, notes.size)
+ else -> context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size)
+ }
+ )
.setSmallIcon(R.drawable.ic_stat_note)
.setAutoCancel(true)
.setDefaults(DEFAULT_ALL)
@@ -52,7 +67,13 @@ class NoteWork @Inject constructor(
PendingIntent.getActivity(context, MainView.Section.NOTE.id,
MainActivity.getStartIntent(context, MainView.Section.NOTE, true), FLAG_UPDATE_CURRENT))
.setStyle(NotificationCompat.InboxStyle().run {
- setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size))
+ setSummaryText(
+ when (NoteCategory.getByValue(notes.first().categoryType)) {
+ POSITIVE -> context.resources.getQuantityString(R.plurals.praise_number_item, notes.size, notes.size)
+ NEUTRAL -> context.resources.getQuantityString(R.plurals.neutral_note_number_item, notes.size, notes.size)
+ else -> context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size)
+ }
+ )
notes.forEach { addLine("${it.teacher}: ${it.category}") }
this
})

diff:
<item quantity="other">You received %1$d notes</item>
</plurals>
+ <!--Praise-->
+ <plurals name="praise_number_item">
+ <item quantity="one">%d praise</item>
+ <item quantity="other">%d praises</item>
+ </plurals>
+ <plurals name="praise_new_items">
+ <item quantity="one">New praise</item>
+ <item quantity="other">New praises</item>
+ </plurals>
+ <plurals name="praise_notify_new_items">
+ <item quantity="one">You received %1$d praise</item>
+ <item quantity="other">You received %1$d praises</item>
+ </plurals>
+
+ <!--Neutral notes-->
+ <plurals name="neutral_note_number_item">
+ <item quantity="one">%d neutral note</item>
+ <item quantity="other">%d neutral notes</item>
+ </plurals>
+ <plurals name="neutral_note_new_items">
+ <item quantity="one">New neutral note</item>
+ <item quantity="other">New neutral notes</item>
+ </plurals>
+ <plurals name="neutral_note_notify_new_items">
+ <item quantity="one">You received %1$d neutral note</item>
+ <item quantity="other">You received %1$d neutral notes</item>
+ </plurals>
+
<!--Homework-->
<string name="homework_no_items">No info about homework</string>

license: Apache License 2.0

repo: wulkanowy/wulkanowy

original_message: Different notification for notes and praises (#952)


diff:
@@ -80,6 +80,10 @@ class PreferencesRepository @Inject constructor(
val showTimetableTimers: Boolean
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)
+ 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)

diff:
@@ -29,6 +29,8 @@ class HomeworkDetailsAdapter @Inject constructor() :
attachments = value?.attachments.orEmpty()
}
+ var isHomeworkFullscreen = false
+
var onAttachmentClickListener: (url: String) -> Unit = {}
var onFullScreenClickListener = {}
@@ -67,6 +69,8 @@ class HomeworkDetailsAdapter @Inject constructor() :
homeworkDialogSubject.text = homework?.subject
homeworkDialogTeacher.text = homework?.teacher
homeworkDialogContent.text = homework?.content
+ homeworkDialogFullScreen.visibility = if (isHomeworkFullscreen) GONE else VISIBLE
+ homeworkDialogFullScreenExit.visibility = if (isHomeworkFullscreen) VISIBLE else GONE
homeworkDialogFullScreen.setOnClickListener {
homeworkDialogFullScreen.visibility = GONE
homeworkDialogFullScreenExit.visibility = VISIBLE

diff:
@@ -62,12 +62,25 @@ class HomeworkDetailsDialog : BaseDialogFragment<DialogHomeworkBinding>(), Homew
homeworkDialogClose.setOnClickListener { dismiss() }
}
+ if (presenter.isHomeworkFullscreen) {
+ dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT)
+ } else {
+ dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT)
+ }
+
with(binding.homeworkDialogRecycler) {
layoutManager = LinearLayoutManager(context)
adapter = detailsAdapter.apply {
onAttachmentClickListener = { context.openInternetBrowser(it, ::showMessage) }
- onFullScreenClickListener = { dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT) }
- onFullScreenExitClickListener = { dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT) }
+ onFullScreenClickListener = {
+ dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT)
+ presenter.isHomeworkFullscreen = true
+ }
+ onFullScreenExitClickListener = {
+ dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT)
+ presenter.isHomeworkFullscreen = false
+ }
+ isHomeworkFullscreen = presenter.isHomeworkFullscreen
homework = this@HomeworkDetailsDialog.homework
}
}

diff:
@@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.modules.homework.details
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.repositories.homework.HomeworkRepository
+import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
@@ -16,9 +17,16 @@ class HomeworkDetailsPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val homeworkRepository: HomeworkRepository,
- private val analytics: FirebaseAnalyticsHelper
+ private val analytics: FirebaseAnalyticsHelper,
+ private val preferencesRepository: PreferencesRepository
) : BasePresenter<HomeworkDetailsView>(errorHandler, studentRepository) {
+ var isHomeworkFullscreen
+ get() = preferencesRepository.isHomeworkFullscreen
+ set(value) {
+ preferencesRepository.isHomeworkFullscreen = value
+ }
+
override fun onAttachView(view: HomeworkDetailsView) {
super.onAttachView(view)
view.initView()

diff:
<bool name="pref_default_fill_message_content">true</bool>
<string name="pref_default_timetable_show_whole_class">no</string>
<bool name="pref_default_timetable_show_timers">false</bool>
+ <bool name="pref_default_homework_fullscreen">false</bool>
<bool name="pref_default_subjects_without_grades">false</bool>
</resources>

diff:
<string name="pref_key_fill_message_content">fill_message_content</string>
<string name="pref_key_timetable_show_whole_class">show_whole_class_plan</string>
<string name="pref_key_timetable_show_timers">timetable_show_timers</string>
+ <string name="pref_key_homework_fullscreen">homework_fullscreen</string>
<string name="pref_key_subjects_without_grades">subjects_without_grades</string>
</resources>

original_message:
Add remembering the full screen mode in homework (#956)

Co-authored-by: Faierbel <RafalBO99@outlook.com>


message: Add subjects sorting in grades

diff:
@@ -5,6 +5,7 @@ import android.content.SharedPreferences
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
+import io.github.wulkanowy.ui.modules.grade.GradeSortingMode
import javax.inject.Inject
import javax.inject.Singleton
@@ -77,6 +78,9 @@ class PreferencesRepository @Inject constructor(
val showWholeClassPlan: String
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))
+
val showTimetableTimers: Boolean
get() = getBoolean(R.string.pref_key_timetable_show_timers, R.bool.pref_default_timetable_show_timers)

change_type: ADD

diff:
+package io.github.wulkanowy.ui.modules.grade
+
+enum class GradeSortingMode(val value: String) {
+ ALPHABETIC("alphabetic"),
+ DATE("date");
+
+ companion object {
+ fun getByValue(value: String) = values().firstOrNull { it.value == value } ?: ALPHABETIC
+ }
+}
\ No newline at end of file

diff:
package io.github.wulkanowy.ui.modules.grade.details
+import android.annotation.SuppressLint
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.repositories.grade.GradeRepository
@@ -10,6 +11,8 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage
+import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.ALPHABETIC
+import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.DATE
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
@@ -184,10 +187,20 @@ class GradeDetailsPresenter @Inject constructor(
}
}
+ @SuppressLint("DefaultLocale")
private fun createGradeItems(items: List<GradeDetailsWithAverage>): List<GradeDetailsItem> {
return items
- .let { if (!preferencesRepository.showSubjectsWithoutGrades) it.filter { it.grades.isNotEmpty() } else it.filter { true }}
- .sortedBy { it.subject }
+ .let { gradesWithAverages ->
+ if (!preferencesRepository.showSubjectsWithoutGrades) {
+ gradesWithAverages.filter { it.grades.isNotEmpty() }
+ } else gradesWithAverages
+ }
+ .let {
+ when (preferencesRepository.gradeSortingMode) {
+ DATE -> it.sortedByDescending { gradeDetailsWithAverage -> gradeDetailsWithAverage.grades.firstOrNull()?.date }
+ ALPHABETIC -> it.sortedBy { gradeDetailsWithAverage -> gradeDetailsWithAverage.subject.toLowerCase() }
+ }
+ }
.map { (subject, average, points, _, grades) ->
val subItems = grades
.sortedByDescending { it.date }

diff:
<string name="pref_default_grade_modifier_minus">0.33</string>
<bool name="pref_default_fill_message_content">true</bool>
<string name="pref_default_timetable_show_whole_class">no</string>
+ <string name="pref_default_grade_sorting_mode">alphabetic</string>
<bool name="pref_default_timetable_show_timers">false</bool>
<bool name="pref_default_homework_fullscreen">false</bool>
<bool name="pref_default_subjects_without_grades">false</bool>

diff:
<string name="pref_key_grade_modifier_plus">grade_modifier_plus</string>
<string name="pref_key_grade_modifier_minus">grade_modifier_minus</string>
<string name="pref_key_fill_message_content">fill_message_content</string>
+ <string name="pref_key_grade_sorting_mode">grade_sorting_mode</string>
<string name="pref_key_timetable_show_whole_class">show_whole_class_plan</string>
<string name="pref_key_timetable_show_timers">timetable_show_timers</string>
<string name="pref_key_homework_fullscreen">homework_fullscreen</string>

diff:
<item>0.75</item>
</string-array>
+ <string-array name="grade_sorting_mode_entries">
+ <item>Alphabetic</item>
+ <item>By date</item>
+ </string-array>
+ <string-array name="grade_sorting_mode_values" translatable="false">
+ <item>alphabetic</item>
+ <item>date</item>
+ </string-array>
+
<string-array name="grade_color_scheme_entries">
<item>Dzienniczek+</item>
<item>Wulkanowy</item>

diff:
<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_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_notify_header">Notifications</string>

diff:
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"

original_message: Add subjects sorting in grades (#946)


diff:
@@ -75,6 +75,9 @@ class PreferencesRepository @Inject constructor(
val fillMessageContent: Boolean
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)
+
val showWholeClassPlan: String
get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class)

diff:
@@ -41,6 +41,8 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
var showWholeClassPlan: String = "no"
+ var showGroupsInPlan: Boolean = false
+
var showTimers: Boolean = false
private val timers = mutableMapOf<Int, Timer>()
@@ -99,6 +101,7 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
with(binding) {
timetableItemNumber.text = lesson.number.toString()
timetableItemSubject.text = lesson.subject
+ timetableItemGroup.text = lesson.group
timetableItemRoom.text = lesson.room
timetableItemTeacher.text = lesson.teacher
timetableItemTimeStart.text = lesson.start.toFormattedString("HH:mm")
@@ -218,6 +221,7 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
timetableItemDescription.text = lesson.info
timetableItemRoom.visibility = GONE
+ timetableItemGroup.visibility = GONE
timetableItemTeacher.visibility = GONE
timetableItemDescription.setTextColor(root.context.getThemeAttrColor(
@@ -227,6 +231,7 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
} else {
timetableItemDescription.visibility = GONE
timetableItemRoom.visibility = VISIBLE
+ timetableItemGroup.visibility = if (showGroupsInPlan && lesson.group.isNotBlank()) VISIBLE else GONE
timetableItemTeacher.visibility = VISIBLE
}
}

diff:
@@ -88,11 +88,12 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
else false
}
- override fun updateData(data: List<Timetable>, showWholeClassPlanType: String, showTimetableTimers: Boolean) {
+ override fun updateData(data: List<Timetable>, showWholeClassPlanType: String, showGroupsInPlanType: Boolean, showTimetableTimers: Boolean) {
with(timetableAdapter) {
items = data.toMutableList()
showTimers = showTimetableTimers
showWholeClassPlan = showWholeClassPlanType
+ showGroupsInPlan = showGroupsInPlanType
notifyDataSetChanged()
}
}

diff:
@@ -143,6 +143,7 @@ class TimetablePresenter @Inject constructor(
view?.apply {
updateData(
showWholeClassPlanType = prefRepository.showWholeClassPlan,
+ showGroupsInPlanType = prefRepository.showGroupsInPlan,
showTimetableTimers = prefRepository.showTimetableTimers,
data = it.data!!
.filter { item -> if (prefRepository.showWholeClassPlan == "no") item.isStudentPlan else true }

diff:
@@ -12,7 +12,7 @@ interface TimetableView : BaseView {
fun initView()
- fun updateData(data: List<Timetable>, showWholeClassPlanType: String, showTimetableTimers: Boolean)
+ fun updateData(data: List<Timetable>, showWholeClassPlanType: String, showGroupsInPlanType: Boolean, showTimetableTimers: Boolean)
fun updateNavigationDay(date: String)

diff:
<string name="pref_default_grade_modifier_plus">0.33</string>
<string name="pref_default_grade_modifier_minus">0.33</string>
<bool name="pref_default_fill_message_content">true</bool>
+ <bool name="pref_default_timetable_show_groups">false</bool>
<string name="pref_default_timetable_show_whole_class">no</string>
<string name="pref_default_grade_sorting_mode">alphabetic</string>
<bool name="pref_default_timetable_show_timers">false</bool>

diff:
<string name="pref_key_fill_message_content">fill_message_content</string>
<string name="pref_key_grade_sorting_mode">grade_sorting_mode</string>
<string name="pref_key_timetable_show_whole_class">show_whole_class_plan</string>
+ <string name="pref_key_timetable_show_groups">show_groups_in_plan</string>
<string name="pref_key_timetable_show_timers">timetable_show_timers</string>
<string name="pref_key_homework_fullscreen">homework_fullscreen</string>
<string name="pref_key_subjects_without_grades">subjects_without_grades</string>

diff:
<string name="pref_view_app_theme">Application 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_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>

diff:
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"

original_message: Show groups next to subjects in timetable (#953)


diff:
<item name="android:windowBackground">@drawable/layer_splash_background</item>
</style>
- <style name="WulkanowyTheme.WidgetAccountSwitcher" parent="Theme.MaterialComponents.Light.Dialog">
+ <style name="WulkanowyTheme.WidgetAccountSwitcher" parent="Theme.MaterialComponents.DayNight.Dialog">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorSecondary">@color/colorPrimary</item>
<item name="windowActionBar">false</item>

original_message: Add support for night mode in account switcher dialog (#988)


message: Add support for BIG font

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

diff:
<TextView
android:id="@+id/aboutItemSummary"
android:layout_width="wrap_content"
- android:layout_height="20dp"
+ android:layout_height="wrap_content"
+ android:minHeight="20dp"
android:layout_below="@id/aboutItemTitle"
android:layout_marginEnd="16dp"
android:layout_toEndOf="@id/aboutItemImage"

diff:
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/attendanceItemContainer"
<LinearLayout
android:id="@+id/attendanceItemNumberContainer"
- android:layout_width="40dp"
- android:layout_height="40dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="40dp"
+ android:minHeight="40dp"
android:gravity="center"
- android:orientation="vertical">
+ android:orientation="vertical"
+ app:layout_constraintBottom_toBottomOf="@+id/attendanceItemDetailsContainer"
+ app:layout_constraintEnd_toStartOf="@+id/attendanceItemDetailsContainer"
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/attendanceItemDetailsContainer">
<TextView
android:id="@+id/attendanceItemNumber"
app:tint="?attr/colorOnSurface" />
</LinearLayout>
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/attendanceItemDetailsContainer"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="10dp"
+ app:layout_constraintEnd_toStartOf="@+id/attendanceItemAlert"
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintStart_toEndOf="@+id/attendanceItemNumberContainer"
+ app:layout_constraintTop_toTopOf="parent">
+
<TextView
android:id="@+id/attendanceItemSubject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
- android:layout_marginStart="10dp"
- android:layout_marginEnd="40dp"
- android:layout_toStartOf="@id/attendanceItemAlert"
- android:layout_toEndOf="@+id/attendanceItemNumberContainer"
android:ellipsize="end"
android:maxLines="1"
android:textSize="17sp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
tools:text="Matematyka" />
<TextView
android:id="@+id/attendanceItemDescription"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignStart="@id/attendanceItemSubject"
- android:layout_alignBottom="@+id/attendanceItemNumberContainer"
android:maxLines="1"
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
+ app:layout_constraintBottom_toBottomOf="@id/attendanceItemDetailsContainer"
+ app:layout_constraintStart_toStartOf="@id/attendanceItemDetailsContainer"
+ app:layout_constraintTop_toBottomOf="@id/attendanceItemSubject"
tools:text="Present" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="@+id/attendanceItemAlert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_marginTop="10dp"
+ app:layout_constraintBottom_toBottomOf="@+id/attendanceItemDetailsContainer"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintStart_toEndOf="@+id/attendanceItemDetailsContainer"
+ app:layout_constraintTop_toTopOf="@+id/attendanceItemDetailsContainer"
app:srcCompat="@drawable/ic_all_mark"
app:tint="?colorPrimary"
tools:ignore="contentDescription" />
-</RelativeLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>

diff:
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/attendanceSummaryItemSubject"
android:layout_width="match_parent"
- android:layout_height="48dp"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp"
android:gravity="start"
android:maxLines="1"
android:paddingLeft="16dp"

diff:
<TextView
android:id="@+id/completedLessonItemNumber"
- android:layout_width="40dp"
- android:layout_height="40dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:minWidth="40dp"
+ android:minHeight="40dp"
android:layout_centerVertical="true"
android:gravity="center"
android:includeFontPadding="false"
android:id="@+id/completedLessonItemTopic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_below="@+id/completedLessonItemSubject"
android:layout_alignStart="@id/completedLessonItemSubject"
android:layout_alignEnd="@+id/completedLessonItemAlert"
- android:layout_alignBottom="@+id/completedLessonItemNumber"
android:layout_marginEnd="40dp"
android:ellipsize="end"
android:maxLines="1"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
- android:tint="?colorTimetableChange"
app:srcCompat="@drawable/ic_timetable_swap"
+ app:tint="?colorTimetableChange"
tools:ignore="contentDescription" />
</RelativeLayout>

diff:
<TextView
android:id="@+id/gradeItemValue"
- android:layout_width="45dp"
- android:layout_height="40dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:background="@color/grade_material_default"
android:gravity="center"
android:maxLength="5"
+ android:minWidth="45dp"
+ android:minHeight="40dp"
android:textColor="@android:color/white"
android:textSize="16sp"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toBottomOf="@+id/gradeDetailsContainer"
+ app:layout_constraintEnd_toStartOf="@+id/gradeDetailsContainer"
+ app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/gradeDetailsContainer"
tools:text="6" />
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/gradeDetailsContainer"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/gradeItemNote"
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintStart_toEndOf="@+id/gradeItemValue"
+ app:layout_constraintTop_toTopOf="parent">
+
<TextView
android:id="@+id/gradeItemDescription"
android:layout_width="0dp"
android:ellipsize="end"
android:maxLines="1"
android:textSize="14sp"
- app:layout_constraintEnd_toStartOf="@id/gradeItemNote"
- app:layout_constraintStart_toEndOf="@+id/gradeItemValue"
+ app:layout_constraintEnd_toEndOf="@id/gradeDetailsContainer"
+ app:layout_constraintStart_toStartOf="@id/gradeDetailsContainer"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/lorem" />
android:layout_height="wrap_content"
android:maxLines="1"
android:textSize="12sp"
- app:layout_constraintBottom_toBottomOf="@+id/gradeItemValue"
+ app:layout_constraintBottom_toBottomOf="@+id/gradeDetailsContainer"
app:layout_constraintStart_toStartOf="@+id/gradeItemDescription"
+ app:layout_constraintTop_toBottomOf="@+id/gradeItemDescription"
tools:text="@tools:sample/date/ddmmyy" />
<TextView
android:layout_marginEnd="20dp"
android:maxLines="1"
android:textSize="12sp"
- app:layout_constraintBottom_toBottomOf="@+id/gradeItemValue"
- app:layout_constraintEnd_toStartOf="@id/gradeItemNote"
+ app:layout_constraintBottom_toBottomOf="@+id/gradeDetailsContainer"
+ app:layout_constraintEnd_toEndOf="@id/gradeDetailsContainer"
app:layout_constraintStart_toEndOf="@+id/gradeItemDate"
+ app:layout_constraintTop_toBottomOf="@+id/gradeItemDescription"
tools:text="@tools:sample/lorem" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
<ImageView
android:id="@+id/gradeItemNote"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
+ app:layout_constraintBottom_toBottomOf="@+id/gradeDetailsContainer"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintStart_toEndOf="@+id/gradeDetailsContainer"
+ app:layout_constraintTop_toTopOf="@+id/gradeDetailsContainer"
app:srcCompat="@drawable/ic_all_round_mark"
app:tint="?colorPrimary"
tools:ignore="contentDescription" />

diff:
<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="64dp"
+ android:layout_height="wrap_content"
+ android:minHeight="64dp"
android:background="?selectableItemBackground"
android:orientation="vertical"
android:paddingStart="16dp"
<TextView
android:id="@+id/licenseItemName"
android:layout_width="match_parent"
- android:layout_height="28dp"
+ android:layout_height="wrap_content"
+ android:minHeight="28dp"
android:ellipsize="end"
android:gravity="bottom"
android:singleLine="true"
<TextView
android:id="@+id/licenseItemSummary"
android:layout_width="match_parent"
- android:layout_height="20dp"
+ android:layout_height="wrap_content"
+ android:minHeight="20dp"
android:gravity="bottom"
android:textColor="?android:textColorSecondary"
android:textSize="14sp"

diff:
<TextView
android:id="@+id/loginItemSignedIn"
android:layout_width="wrap_content"
- android:layout_height="20dp"
+ android:layout_height="wrap_content"
+ android:minHeight="20dp"
android:layout_below="@id/loginItemSchool"
android:layout_marginEnd="16dp"
android:layout_toEndOf="@id/loginItemCheck"

diff:
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mobileDevice_subitem_container"
android:layout_width="match_parent"
- android:layout_height="64dp"
+ android:layout_height="wrap_content"
+ android:minHeight="64dp"
tools:context=".ui.modules.mobiledevice.MobileDeviceAdapter">
<TextView
android:id="@+id/mobileDeviceItemName"
android:layout_width="match_parent"
- android:layout_height="32dp"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
android:layout_marginStart="16dp"
+ android:layout_marginTop="0dp"
android:layout_toStartOf="@id/mobileDeviceItemUnregister"
android:ellipsize="end"
android:gravity="bottom"
android:maxLines="1"
+ android:minHeight="32dp"
android:textSize="16sp"
tools:text="@tools:sample/lorem/random" />
<TextView
android:id="@+id/mobileDeviceItemDate"
android:layout_width="match_parent"
- android:layout_height="20dp"
+ android:layout_height="wrap_content"
+ android:minHeight="20dp"
android:layout_below="@id/mobileDeviceItemName"
android:layout_marginStart="16dp"
android:layout_toStartOf="@id/mobileDeviceItemUnregister"

diff:
<TextView
android:id="@+id/timetableItemNumber"
- android:layout_width="40dp"
- android:layout_height="40dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="40dp"
+ android:minHeight="40dp"
android:gravity="center"
android:includeFontPadding="false"
android:maxLength="2"

diff:
<TextView
android:id="@+id/timetableSmallItemNumber"
- android:layout_width="40dp"
+ android:layout_width="wrap_content"
+ android:minWidth="40dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:gravity="center"

diff:
<TextView
android:id="@+id/timetableWidgetItemNumber"
- android:layout_width="40dp"
- android:layout_height="40dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="40dp"
+ android:minHeight="40dp"
android:gravity="center"
android:includeFontPadding="false"
android:maxLength="2"

diff:
<TextView
android:id="@+id/timetableWidgetItemNumber"
- android:layout_width="40dp"
+ android:layout_width="wrap_content"
+ android:minWidth="40dp"
android:layout_height="wrap_content"
android:gravity="center"
android:maxLength="2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
- android:layout_marginLeft="6dp"
android:maxLines="1"
android:textColor="@android:color/white"
android:textSize="13sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
- android:layout_marginLeft="10dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@android:color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
- android:layout_marginLeft="10dp"
android:maxLines="1"
android:textColor="@android:color/white"
android:textSize="15sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
- android:layout_marginLeft="10dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@android:color/white"

diff:
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="104dp"
+ android:layout_height="wrap_content"
+ android:minHeight="104dp"
android:orientation="vertical"
tools:context=".ui.modules.about.AboutAdapter">

original_message:
Add support for BIG font (#989)

Co-authored-by: Faierbel <RafalBO99@outlook.com>


diff:
@@ -65,6 +65,9 @@ class MessagePreviewFragment :
override val printHTML: String
get() = requireContext().assets.open("message-print-page.html").bufferedReader().use { it.readText() }
+ override val messageNotExists: String
+ get() = getString(R.string.message_not_exists)
+
companion object {
const val MESSAGE_ID_KEY = "message_id"

diff:
@@ -10,8 +10,8 @@ import io.github.wulkanowy.data.repositories.MessageRepository
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.AppInfo
import io.github.wulkanowy.utils.AnalyticsHelper
+import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.flowWithResourceIn
@@ -65,7 +65,7 @@ class MessagePreviewPresenter @Inject constructor(
Status.LOADING -> Timber.i("Loading message ${message.messageId} preview started")
Status.SUCCESS -> {
Timber.i("Loading message ${message.messageId} preview result: Success ")
- checkNotNull(it.data, { "Can't find message in local db! Probably no longer exist in this folder" })
+ if (it.data != null) {
this@MessagePreviewPresenter.message = it.data.message
this@MessagePreviewPresenter.attachments = it.data.attachments
view?.apply {
@@ -77,6 +77,12 @@ class MessagePreviewPresenter @Inject constructor(
"type" to "message_preview",
"length" to it.data.message.content.length
)
+ } else {
+ view?.run {
+ showMessage(messageNotExists)
+ popView()
+ }
+ }
}
Status.ERROR -> {
Timber.i("Loading message ${message.messageId} preview result: An exception occurred ")

diff:
@@ -14,6 +14,8 @@ interface MessagePreviewView : BaseView {
val printHTML: String
+ val messageNotExists: String
+
fun initView()
fun setMessageWithAttachment(item: MessageWithAttachment)

diff:
<string name="message_subject">Subject</string>
<string name="message_content">Content</string>
<string name="message_send_successful">Message sent successfully</string>
+ <string name="message_not_exists">Message does not exist</string>
<string name="message_required_recipients">You need to choose at least 1 recipient</string>
<string name="message_content_min_length">The message content must be at least 3 characters</string>
<plurals name="message_number_item">

original_message: Show appropriate message when message does not exist (#1083)


message: Disable vibrations in data picker

diff:
@@ -222,6 +222,7 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
setDateRangeLimiter(SchooldaysRangeLimiter())
version = DatePickerDialog.Version.VERSION_2
scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
+ vibrate(false)
show(this@AttendanceFragment.parentFragmentManager, null)
}
}

diff:
@@ -176,6 +176,7 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
setDateRangeLimiter(SchooldaysRangeLimiter())
version = DatePickerDialog.Version.VERSION_2
scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
+ vibrate(false)
show(this@TimetableFragment.parentFragmentManager, null)
}
}

diff:
@@ -128,6 +128,7 @@ class AdditionalLessonsFragment :
setDateRangeLimiter(SchooldaysRangeLimiter())
version = DatePickerDialog.Version.VERSION_2
scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
+ vibrate(false)
show(this@AdditionalLessonsFragment.parentFragmentManager, null)
}
}

diff:
@@ -147,6 +147,7 @@ class CompletedLessonsFragment :
setDateRangeLimiter(SchooldaysRangeLimiter())
version = DatePickerDialog.Version.VERSION_2
scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
+ vibrate(false)
show(this@CompletedLessonsFragment.parentFragmentManager, null)
}
}

original_message: Disable vibrations in data picker (#1093)


message: Fix empty message bug

diff:
@@ -70,6 +70,7 @@ class MessagePreviewPresenter @Inject constructor(
this@MessagePreviewPresenter.attachments = it.data.attachments
view?.apply {
setMessageWithAttachment(it.data)
+ showContent(true)
initOptions()
}
analytics.logEvent(

original_message: Fix empty message bug (#1122)


diff:
android:name=".ui.modules.login.LoginActivity"
android:configChanges="orientation|screenSize"
android:label="@string/login_title"
- android:theme="@style/WulkanowyTheme.NoActionBar"
+ android:theme="@style/WulkanowyTheme.Login"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".ui.modules.main.MainActivity"
android:name=".ui.modules.message.send.SendMessageActivity"
android:configChanges="orientation|screenSize"
android:label="@string/send_message_title"
- android:theme="@style/WulkanowyTheme.NoActionBar"
+ android:theme="@style/WulkanowyTheme.MessageSend"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"

diff:
@@ -8,6 +8,9 @@ import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import io.github.wulkanowy.R
import io.github.wulkanowy.data.repositories.PreferencesRepository
+import io.github.wulkanowy.ui.modules.login.LoginActivity
+import io.github.wulkanowy.ui.modules.main.MainActivity
+import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import javax.inject.Inject
import javax.inject.Singleton
@@ -17,7 +20,13 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer
fun applyActivityTheme(activity: AppCompatActivity) {
if (isThemeApplicable(activity)) {
applyDefaultTheme()
- if (preferencesRepository.appTheme == "black") activity.setTheme(R.style.WulkanowyTheme_Black)
+ if (preferencesRepository.appTheme == "black") {
+ when (activity) {
+ is MainActivity -> activity.setTheme(R.style.WulkanowyTheme_Black)
+ is LoginActivity -> activity.setTheme(R.style.WulkanowyTheme_Login_Black)
+ is SendMessageActivity -> activity.setTheme(R.style.WulkanowyTheme_MessageSend_Black)
+ }
+ }
}
}
@@ -33,8 +42,13 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer
}
private fun isThemeApplicable(activity: AppCompatActivity): Boolean {
- return activity.packageManager.getPackageInfo(activity.packageName, GET_ACTIVITIES)
- .activities.singleOrNull { it.name == activity::class.java.canonicalName }?.theme
- .let { it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar }
+ return activity.packageManager
+ .getPackageInfo(activity.packageName, GET_ACTIVITIES)
+ .activities.singleOrNull { it.name == activity::class.java.canonicalName }
+ ?.theme.let {
+ it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar
+ || it == R.style.WulkanowyTheme_Login || it == R.style.WulkanowyTheme_Login_Black
+ || it == R.style.WulkanowyTheme_MessageSend || it == R.style.WulkanowyTheme_MessageSend_Black
+ }
}
}

diff:
@@ -11,6 +11,7 @@ import android.graphics.drawable.Icon
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.LOLLIPOP
+import android.os.Build.VERSION_CODES.P
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
@@ -194,6 +195,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
return true
}
+ @SuppressLint("NewApi")
override fun initView() {
with(binding.mainToolbar) {
if (SDK_INT >= LOLLIPOP) stateListAnimator = null
@@ -233,11 +235,17 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
with(navController) {
setOnViewChangeListener { section, name ->
- binding.mainBottomNav.visibility =
if (section == MainView.Section.ACCOUNT || section == MainView.Section.STUDENT_INFO) {
- View.GONE
+ binding.mainBottomNav.visibility = View.GONE
+ if (appInfo.systemVersion >= P) {
+ window.navigationBarColor = getThemeAttrColor(R.attr.colorSurface)
+ }
} else {
- View.VISIBLE
+ binding.mainBottomNav.visibility = View.VISIBLE
+ if (appInfo.systemVersion >= P) {
+ window.navigationBarColor =
+ getThemeAttrColor(android.R.attr.navigationBarColor)
+ }
}
analytics.setCurrentScreen(this@MainActivity, name)

diff:
<item name="colorSwipeRefresh">@color/colorSwipeRefreshDark</item>
<item name="android:windowBackground">?colorSurface</item>
<item name="android:textColor">?android:textColorPrimary</item>
- <item name="android:navigationBarColor" tools:targetApi="lollipop">@android:color/black
+ <item name="android:navigationBarColor" tools:targetApi="lollipop">
+ @color/colorNavigationBarLight
+ </item>
+ <item name="android:statusBarColor" tools:targetApi="lollipop">
+ @color/colorStatusBarLight
</item>
- <item name="android:statusBarColor" tools:targetApi="lollipop">@android:color/black</item>
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">false</item>
<item name="mdtp_theme_dark">true</item>
+ <item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
</style>
<style name="WulkanowyTheme.Black" parent="WulkanowyTheme.NoActionBar">
<item name="colorSurface">@android:color/black</item>
+ <item name="android:navigationBarColor" tools:targetApi="lollipop">
+ @color/colorNavigationBarBlack
+ </item>
+ <item name="android:statusBarColor" tools:targetApi="lollipop">@android:color/black</item>
+ <item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
+ </style>
+
+ <style name="WulkanowyTheme.Login" parent="WulkanowyTheme.NoActionBar">
+ <item name="android:navigationBarColor" tools:targetApi="lollipop">?colorSurface</item>
+ <item name="android:statusBarColor" tools:targetApi="lollipop">?colorSurface</item>
+ </style>
+
+ <style name="WulkanowyTheme.Login.Black" parent="WulkanowyTheme.Black">
+ <item name="android:navigationBarColor" tools:targetApi="lollipop">?colorSurface</item>
+ <item name="android:statusBarColor" tools:targetApi="lollipop">?colorSurface</item>
+ </style>
+
+ <style name="WulkanowyTheme.MessageSend" parent="WulkanowyTheme.NoActionBar">
+ <item name="android:navigationBarColor" tools:targetApi="lollipop">?colorSurface</item>
+ <item name="android:statusBarColor" tools:targetApi="lollipop">#1C1C1C</item>
+ </style>
+
+ <style name="WulkanowyTheme.MessageSend.Black" parent="WulkanowyTheme.Black">
+ <item name="android:navigationBarColor" tools:targetApi="lollipop">?colorSurface</item>
+ <item name="android:statusBarColor" tools:targetApi="lollipop">@android:color/black</item>
</style>
</resources>

diff:
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="WulkanowyTheme" parent="BaseWulkanowyTheme">
+ <item name="android:windowLightStatusBar">true</item>
+ <item name="android:statusBarColor">@android:color/white</item>
+ </style>
+
+ <style name="WulkanowyTheme.SplashScreen" parent="WulkanowyTheme.NoActionBar">
+ <item name="android:windowBackground">@drawable/layer_splash_background</item>
+ <item name="android:statusBarColor">@color/colorPrimaryDark</item>
+ <item name="android:navigationBarColor">@color/colorPrimaryDark</item>
+ <item name="android:windowLightStatusBar">false</item>
+ </style>
+</resources>
\ No newline at end of file

diff:
+<resources>
+
+ <style name="WulkanowyTheme" parent="BaseWulkanowyTheme">
+ <item name="android:navigationBarColor">@android:color/black</item>
+ <item name="android:windowLightStatusBar">false</item>
+ <item name="android:statusBarColor">@android:color/darker_gray</item>
+ </style>
+
+ <style name="WulkanowyTheme.SplashScreen" parent="WulkanowyTheme.NoActionBar">
+ <item name="android:windowBackground">@drawable/layer_splash_background</item>
+ <item name="android:statusBarColor">@color/colorPrimaryDark</item>
+ <item name="android:navigationBarColor">@color/colorPrimaryDark</item>
+ <item name="android:windowLightStatusBar">false</item>
+ </style>
+</resources>

change_type: RENAME

diff:
<resources>
<style name="WulkanowyTheme" parent="BaseWulkanowyTheme">
- <item name="android:statusBarColor">@android:color/darker_gray</item>
<item name="android:navigationBarColor">@android:color/white</item>
<item name="android:windowLightNavigationBar">true</item>
+ <item name="android:windowLightStatusBar">true</item>
+ <item name="android:statusBarColor">@android:color/white</item>
</style>
<style name="WulkanowyTheme.SplashScreen" parent="WulkanowyTheme.NoActionBar">
<item name="android:statusBarColor">@color/colorPrimaryDark</item>
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
<item name="android:windowLightNavigationBar">false</item>
+ <item name="android:windowLightStatusBar">false</item>
</style>
</resources>
\ No newline at end of file

diff:
+<resources>
+
+ <style name="WulkanowyTheme" parent="BaseWulkanowyTheme">
+ <item name="android:navigationBarColor">@android:color/white</item>
+ <item name="android:windowLightNavigationBar">true</item>
+ <item name="android:windowLightStatusBar">true</item>
+ <item name="android:statusBarColor">@android:color/white</item>
+ </style>
+
+ <style name="WulkanowyTheme.SplashScreen" parent="WulkanowyTheme.NoActionBar">
+ <item name="android:windowBackground">@drawable/layer_splash_background</item>
+ <item name="android:statusBarColor">@color/colorPrimaryDark</item>
+ <item name="android:navigationBarColor">@color/colorPrimaryDark</item>
+ <item name="android:windowLightNavigationBar">false</item>
+ <item name="android:windowLightStatusBar">false</item>
+ </style>
+</resources>
\ No newline at end of file

diff:
<color name="colorError">#ff5722</color>
<color name="colorErrorLight">#e84853</color>
+ <color name="colorNavigationBarLight">#2E2E2E</color>
+ <color name="colorNavigationBarBlack">#1E1E1E</color>
+
+ <color name="colorStatusBarLight">#1C1C1C</color>
+
<color name="timetable_change_light">#ffd54f</color>
<color name="timetable_change_dark">#ff8f00</color>

diff:
</style>
<style name="mdtp_ActionButton.Text" parent="Widget.MaterialComponents.Button.TextButton.Dialog" />
+
+ <style name="WulkanowyTheme.Login" parent="WulkanowyTheme.NoActionBar" />
+
+ <style name="WulkanowyTheme.MessageSend" parent="WulkanowyTheme.NoActionBar" />
</resources>

original_message: Change background color of navigation and notification bars (#1120)


message: Fix notification and status bars colors

diff:
<item name="android:navigationBarColor" tools:targetApi="lollipop">
@color/colorNavigationBarBlack
</item>
- <item name="android:statusBarColor" tools:targetApi="lollipop">@android:color/black</item>
+ <item name="android:statusBarColor" tools:targetApi="lollipop">@color/colorStatusBarBlack</item>
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
</style>

diff:
<color name="colorError">#ff5722</color>
<color name="colorErrorLight">#e84853</color>
- <color name="colorNavigationBarLight">#2E2E2E</color>
+ <color name="colorNavigationBarLight">#2D2D2D</color>
<color name="colorNavigationBarBlack">#1E1E1E</color>
<color name="colorStatusBarLight">#1C1C1C</color>
+ <color name="colorStatusBarBlack">#0D0D0D</color>
<color name="timetable_change_light">#ffd54f</color>
<color name="timetable_change_dark">#ff8f00</color>

original_message: Fix notification and status bars colors (#1196)


diff:
@@ -53,13 +53,14 @@ class SchoolFragment : BaseFragment<FragmentSchoolBinding>(R.layout.fragment_sch
override fun updateData(data: School) {
with(binding) {
- schoolName.text = data.name
- schoolAddress.text = data.address.ifBlank { "-" }
+ val noDataString = getString(R.string.all_no_data)
+ schoolName.text = data.name.ifBlank { noDataString }
+ schoolAddress.text = data.address.ifBlank { noDataString }
schoolAddressButton.visibility = if (data.address.isNotBlank()) VISIBLE else GONE
- schoolTelephone.text = data.contact.ifBlank { "-" }
+ schoolTelephone.text = data.contact.ifBlank { noDataString }
schoolTelephoneButton.visibility = if (data.contact.isNotBlank()) VISIBLE else GONE
- schoolHeadmaster.text = data.headmaster
- schoolPedagogue.text = data.pedagogue
+ schoolHeadmaster.text = data.headmaster.ifBlank { noDataString }
+ schoolPedagogue.text = data.pedagogue.ifBlank { noDataString }
}
}

original_message: Replace dash mark with no data string in SchoolFragment (#1203)


message: Add system settings shortcut

diff:
@@ -21,6 +21,10 @@ import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.toFormattedString
import io.github.wulkanowy.utils.toLocalDateTime
import javax.inject.Inject
+import android.content.Intent
+import android.net.Uri
+import android.provider.Settings
+import android.util.Log
@AndroidEntryPoint
class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about), AboutView,

diff:
package io.github.wulkanowy.ui.modules.settings.notifications
+import android.annotation.SuppressLint
+import android.content.Intent
import android.content.SharedPreferences
+import android.net.Uri
+import android.os.Build
import android.os.Bundle
+import android.provider.Settings
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -16,7 +21,9 @@ 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 timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
@@ -27,6 +34,9 @@ class NotificationsFragment : PreferenceFragmentCompat(),
@Inject
lateinit var presenter: NotificationsPresenter
+ @Inject
+ lateinit var appInfo: AppInfo
+
companion object {
fun newInstance() = NotificationsFragment()
}
@@ -46,6 +56,13 @@ class NotificationsFragment : PreferenceFragmentCompat(),
true
}
}
+
+ findPreference<Preference>(getString(R.string.pref_key_notifications_system_settings))?.run {
+ setOnPreferenceClickListener {
+ presenter.onOpenSystemSettingsClicked()
+ true
+ }
+ }
}
override fun onCreateRecyclerView(
@@ -118,6 +135,24 @@ class NotificationsFragment : PreferenceFragmentCompat(),
.show()
}
+ @SuppressLint("InlinedApi")
+ override fun openSystemSettings() {
+ val intent = if (appInfo.systemVersion >= Build.VERSION_CODES.O) {
+ Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
+ putExtra("android.provider.extra.APP_PACKAGE", requireActivity().packageName)
+ }
+ } else {
+ Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
+ data = Uri.fromParts("package", requireActivity().packageName, null)
+ }
+ }
+ try {
+ requireActivity().startActivity(intent)
+ } catch (e: Exception) {
+ Timber.e(e)
+ }
+ }
+
override fun onResume() {
super.onResume()
preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)

diff:
@@ -55,4 +55,8 @@ class NotificationsPresenter @Inject constructor(
fun onFixSyncIssuesClicked() {
view?.showFixSyncDialog()
}
+
+ fun onOpenSystemSettingsClicked() {
+ view?.openSystemSettings()
+ }
}

diff:
@@ -8,5 +8,7 @@ interface NotificationsView : BaseView {
fun showFixSyncDialog()
+ fun openSystemSettings()
+
fun enableNotification(notificationKey: String, enable: Boolean)
}

diff:
<string name="about_facebook_summary">Like our facebook fanpage</string>
<string name="about_privacy">Privacy policy</string>
<string name="about_privacy_summary">Rules for collecting personal data</string>
+ <string name="about_system">System settings</string>
+ <string name="about_system_summary">Open system settings</string>
<string name="about_homepage">Homepage</string>
<string name="about_homepage_summary">Visit the website and help develop the application</string>
<string name="about_licenses">Licenses</string>
<string name="pref_notify_header">Notifications</string>
<string name="pref_notify_switch">Show notifications</string>
<string name="pref_notify_upcoming_lessons_switch">Show upcoming lesson notifications</string>
+ <string name="pref_notify_open_system_settings">Open system notification settings</string>
<string name="pref_notify_fix_sync_issues">Fix synchronization & notifications issues</string>
<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>

diff:
app:key="@string/pref_key_notification_debug"
app:singleLineTitle="false"
app:title="@string/pref_notify_debug_switch" />
+ <Preference
+ app:iconSpaceReserved="false"
+ app:key="@string/pref_key_notifications_system_settings"
+ app:title="@string/pref_notify_open_system_settings" />
<Preference
app:iconSpaceReserved="false"
app:isPreferenceVisible="false"

original_message: Add system settings shortcut (#1271)


message: Migrate material date picker

diff:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'com.google.firebase.crashlytics'
@@ -209,7 +210,6 @@ dependencies {
implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation "fr.bipi.treessence:treessence:0.3.2"
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
- implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
implementation "io.coil-kt:coil:1.1.1"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
implementation 'me.xdrop:fuzzywuzzy:1.3.1'

diff:
@@ -13,7 +13,8 @@ import android.view.View.VISIBLE
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.view.ActionMode
import androidx.recyclerview.widget.LinearLayoutManager
-import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
+import com.google.android.material.datepicker.CalendarConstraints
+import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Attendance
@@ -24,9 +25,12 @@ import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragme
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
-import io.github.wulkanowy.utils.SchooldaysRangeLimiter
+import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.getThemeAttrColor
+import io.github.wulkanowy.utils.schoolYearStart
+import io.github.wulkanowy.utils.toLocalDateTime
+import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate
import javax.inject.Inject
@@ -216,19 +220,27 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
}
override fun showDatePickerDialog(currentDate: LocalDate) {
- val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth ->
- presenter.onDateSet(year, month + 1, dayOfMonth)
+ val now = LocalDate.now()
+ val startOfSchoolYear = now.schoolYearStart.toTimestamp()
+ val endWeek = now.plusWeeks(1).toTimestamp()
+
+ val constraintsBuilder = CalendarConstraints.Builder().apply {
+ setValidator(SchoolDaysValidator(startOfSchoolYear, endWeek))
+ setStart(startOfSchoolYear)
+ setEnd(endWeek)
}
- val datePickerDialog = DatePickerDialog.newInstance(dateSetListener,
- currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth)
+ val datePicker =
+ MaterialDatePicker.Builder.datePicker()
+ .setCalendarConstraints(constraintsBuilder.build())
+ .setSelection(currentDate.toTimestamp())
+ .build()
- with(datePickerDialog) {
- setDateRangeLimiter(SchooldaysRangeLimiter())
- version = DatePickerDialog.Version.VERSION_2
- scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
- vibrate(false)
- show(this@AttendanceFragment.parentFragmentManager, null)
+ datePicker.addOnPositiveButtonClickListener {
+ val date = it.toLocalDateTime()
+ presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth)
}
+
+ datePicker.show(this@AttendanceFragment.parentFragmentManager, null)
}
override fun showExcuseDialog() {

diff:
@@ -5,7 +5,8 @@ import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.recyclerview.widget.LinearLayoutManager
-import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
+import com.google.android.material.datepicker.CalendarConstraints
+import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.LuckyNumber
@@ -13,14 +14,18 @@ import io.github.wulkanowy.databinding.FragmentLuckyNumberHistoryBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
-import io.github.wulkanowy.utils.SchooldaysRangeLimiter
+import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
+import io.github.wulkanowy.utils.schoolYearStart
+import io.github.wulkanowy.utils.toLocalDateTime
+import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate
import javax.inject.Inject
@AndroidEntryPoint
class LuckyNumberHistoryFragment :
- BaseFragment<FragmentLuckyNumberHistoryBinding>(R.layout.fragment_lucky_number_history), LuckyNumberHistoryView,
+ BaseFragment<FragmentLuckyNumberHistoryBinding>(R.layout.fragment_lucky_number_history),
+ LuckyNumberHistoryView,
MainView.TitledView {
@Inject
@@ -107,19 +112,26 @@ class LuckyNumberHistoryFragment :
}
override fun showDatePickerDialog(currentDate: LocalDate) {
- val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth ->
- presenter.onDateSet(year, month + 1, dayOfMonth)
+ val now = LocalDate.now()
+ val startOfSchoolYear = now.schoolYearStart.toTimestamp()
+
+ val constraintsBuilder = CalendarConstraints.Builder().apply {
+ setValidator(SchoolDaysValidator(startOfSchoolYear, now.toTimestamp()))
+ setStart(startOfSchoolYear)
+ setEnd(now.toTimestamp())
}
- val datePickerDialog = DatePickerDialog.newInstance(dateSetListener,
- currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth)
+ val datePicker =
+ MaterialDatePicker.Builder.datePicker()
+ .setCalendarConstraints(constraintsBuilder.build())
+ .setSelection(currentDate.toTimestamp())
+ .build()
- with(datePickerDialog) {
- setDateRangeLimiter(SchooldaysRangeLimiter())
- version = DatePickerDialog.Version.VERSION_2
- scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
- vibrate(false)
- show(this@LuckyNumberHistoryFragment.parentFragmentManager, null)
+ datePicker.addOnPositiveButtonClickListener {
+ val date = it.toLocalDateTime()
+ presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth)
}
+
+ datePicker.show(this@LuckyNumberHistoryFragment.parentFragmentManager, null)
}
override fun showContent(show: Boolean) {

diff:
@@ -9,7 +9,8 @@ import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.core.text.HtmlCompat
import androidx.recyclerview.widget.LinearLayoutManager
-import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
+import com.google.android.material.datepicker.CalendarConstraints
+import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Timetable
@@ -20,9 +21,13 @@ import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.timetable.additional.AdditionalLessonsFragment
import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
-import io.github.wulkanowy.utils.SchooldaysRangeLimiter
+import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.getThemeAttrColor
+import io.github.wulkanowy.utils.schoolYearEnd
+import io.github.wulkanowy.utils.schoolYearStart
+import io.github.wulkanowy.utils.toLocalDateTime
+import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate
import javax.inject.Inject
@@ -184,21 +189,27 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
}
override fun showDatePickerDialog(currentDate: LocalDate) {
- val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth ->
- presenter.onDateSet(year, month + 1, dayOfMonth)
+ val now = LocalDate.now()
+ val startOfSchoolYear = now.schoolYearStart.toTimestamp()
+ val endOfSchoolYear = now.schoolYearEnd.toTimestamp()
+
+ val constraintsBuilder = CalendarConstraints.Builder().apply {
+ setValidator(SchoolDaysValidator(startOfSchoolYear, endOfSchoolYear))
+ setStart(startOfSchoolYear)
+ setEnd(endOfSchoolYear)
}
- val datePickerDialog = DatePickerDialog.newInstance(
- dateSetListener,
- currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth
- )
+ val datePicker =
+ MaterialDatePicker.Builder.datePicker()
+ .setCalendarConstraints(constraintsBuilder.build())
+ .setSelection(currentDate.toTimestamp())
+ .build()
- with(datePickerDialog) {
- setDateRangeLimiter(SchooldaysRangeLimiter())
- version = DatePickerDialog.Version.VERSION_2
- scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
- vibrate(false)
- show(this@TimetableFragment.parentFragmentManager, null)
+ datePicker.addOnPositiveButtonClickListener {
+ val date = it.toLocalDateTime()
+ presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth)
}
+
+ datePicker.show(this@TimetableFragment.parentFragmentManager, null)
}
override fun openAdditionalLessonsView() {

diff:
@@ -3,7 +3,8 @@ package io.github.wulkanowy.ui.modules.timetable.additional
import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
-import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
+import com.google.android.material.datepicker.CalendarConstraints
+import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.TimetableAdditional
@@ -11,9 +12,13 @@ import io.github.wulkanowy.databinding.FragmentTimetableAdditionalBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
-import io.github.wulkanowy.utils.SchooldaysRangeLimiter
+import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.getThemeAttrColor
+import io.github.wulkanowy.utils.schoolYearEnd
+import io.github.wulkanowy.utils.schoolYearStart
+import io.github.wulkanowy.utils.toLocalDateTime
+import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate
import javax.inject.Inject
@@ -55,7 +60,11 @@ class AdditionalLessonsFragment :
with(binding) {
additionalLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
additionalLessonsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
- additionalLessonsSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
+ additionalLessonsSwipe.setProgressBackgroundColorSchemeColor(
+ requireContext().getThemeAttrColor(
+ R.attr.colorSwipeRefresh
+ )
+ )
additionalLessonsErrorRetry.setOnClickListener { presenter.onRetry() }
additionalLessonsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
@@ -114,7 +123,8 @@ class AdditionalLessonsFragment :
}
override fun showPreButton(show: Boolean) {
- binding.additionalLessonsPreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
+ binding.additionalLessonsPreviousButton.visibility =
+ if (show) View.VISIBLE else View.INVISIBLE
}
override fun showNextButton(show: Boolean) {
@@ -122,19 +132,27 @@ class AdditionalLessonsFragment :
}
override fun showDatePickerDialog(currentDate: LocalDate) {
- val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth ->
- presenter.onDateSet(year, month + 1, dayOfMonth)
+ val now = LocalDate.now()
+ val startOfSchoolYear = now.schoolYearStart.toTimestamp()
+ val endOfSchoolYear = now.schoolYearEnd.toTimestamp()
+
+ val constraintsBuilder = CalendarConstraints.Builder().apply {
+ setValidator(SchoolDaysValidator(startOfSchoolYear, endOfSchoolYear))
+ setStart(startOfSchoolYear)
+ setEnd(endOfSchoolYear)
}
- val datePickerDialog = DatePickerDialog.newInstance(dateSetListener,
- currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth)
+ val datePicker =
+ MaterialDatePicker.Builder.datePicker()
+ .setCalendarConstraints(constraintsBuilder.build())
+ .setSelection(currentDate.toTimestamp())
+ .build()
- with(datePickerDialog) {
- setDateRangeLimiter(SchooldaysRangeLimiter())
- version = DatePickerDialog.Version.VERSION_2
- scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
- vibrate(false)
- show(this@AdditionalLessonsFragment.parentFragmentManager, null)
+ datePicker.addOnPositiveButtonClickListener {
+ val date = it.toLocalDateTime()
+ presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth)
}
+
+ datePicker.show(this@AdditionalLessonsFragment.parentFragmentManager, null)
}
override fun onSaveInstanceState(outState: Bundle) {

diff:
@@ -6,7 +6,8 @@ import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import androidx.recyclerview.widget.LinearLayoutManager
-import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
+import com.google.android.material.datepicker.CalendarConstraints
+import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.CompletedLesson
@@ -15,10 +16,14 @@ import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
-import io.github.wulkanowy.utils.SchooldaysRangeLimiter
+import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.getCompatDrawable
import io.github.wulkanowy.utils.getThemeAttrColor
+import io.github.wulkanowy.utils.schoolYearEnd
+import io.github.wulkanowy.utils.schoolYearStart
+import io.github.wulkanowy.utils.toLocalDateTime
+import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate
import javax.inject.Inject
@@ -62,7 +67,11 @@ class CompletedLessonsFragment :
with(binding) {
completedLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
completedLessonsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
- completedLessonsSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
+ completedLessonsSwipe.setProgressBackgroundColorSchemeColor(
+ requireContext().getThemeAttrColor(
+ R.attr.colorSwipeRefresh
+ )
+ )
completedLessonErrorRetry.setOnClickListener { presenter.onRetry() }
completedLessonErrorDetails.setOnClickListener { presenter.onDetailsClick() }
@@ -136,23 +145,35 @@ class CompletedLessonsFragment :
}
override fun showCompletedLessonDialog(completedLesson: CompletedLesson) {
- (activity as? MainActivity)?.showDialogFragment(CompletedLessonDialog.newInstance(completedLesson))
+ (activity as? MainActivity)?.showDialogFragment(
+ CompletedLessonDialog.newInstance(
+ completedLesson
+ )
+ )
}
override fun showDatePickerDialog(currentDate: LocalDate) {
- val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth ->
- presenter.onDateSet(year, month + 1, dayOfMonth)
+ val now = LocalDate.now()
+ val startOfSchoolYear = now.schoolYearStart.toTimestamp()
+ val endOfSchoolYear = now.schoolYearEnd.toTimestamp()
+
+ val constraintsBuilder = CalendarConstraints.Builder().apply {
+ setValidator(SchoolDaysValidator(startOfSchoolYear, endOfSchoolYear))
+ setStart(startOfSchoolYear)
+ setEnd(endOfSchoolYear)
}
- val datePickerDialog = DatePickerDialog.newInstance(dateSetListener,
- currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth)
+ val datePicker =
+ MaterialDatePicker.Builder.datePicker()
+ .setCalendarConstraints(constraintsBuilder.build())
+ .setSelection(currentDate.toTimestamp())
+ .build()
- with(datePickerDialog) {
- setDateRangeLimiter(SchooldaysRangeLimiter())
- version = DatePickerDialog.Version.VERSION_2
- scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
- vibrate(false)
- show(this@CompletedLessonsFragment.parentFragmentManager, null)
+ datePicker.addOnPositiveButtonClickListener {
+ val date = it.toLocalDateTime()
+ presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth)
}
+
+ datePicker.show(this@CompletedLessonsFragment.parentFragmentManager, null)
}
override fun onSaveInstanceState(outState: Bundle) {

change_type: DELETE

diff:
-package io.github.wulkanowy.utils
-
-import android.os.Parcel
-import android.os.Parcelable
-import com.wdullaer.materialdatetimepicker.date.DateRangeLimiter
-import java.time.DayOfWeek
-import java.time.LocalDate
-import java.util.Calendar
-
-@Suppress("UNUSED_PARAMETER")
-class SchooldaysRangeLimiter : DateRangeLimiter {
-
- private val now = LocalDate.now()
-
- override fun setToNearestDate(day: Calendar): Calendar = day
-
- override fun isOutOfRange(year: Int, month: Int, day: Int): Boolean {
- val date = LocalDate.of(year, month + 1, day)
- val dayOfWeek = date.dayOfWeek
- return dayOfWeek == DayOfWeek.SUNDAY || date.isHolidays
- }
-
- override fun getStartDate(): Calendar {
- val startYear = if (now.monthValue <= 6) now.year - 1 else now.year
- val startOfSchoolYear = now.withYear(startYear).firstSchoolDay
-
- val calendar = Calendar.getInstance()
- calendar.set(startOfSchoolYear.year, startOfSchoolYear.monthValue - 1, startOfSchoolYear.dayOfMonth)
- return calendar
- }
-
- override fun getEndDate(): Calendar {
- val endYear = if (now.monthValue > 6) now.year + 1 else now.year
- val endOfSchoolYear = now.withYear(endYear).lastSchoolDay
-
- val calendar = Calendar.getInstance()
- calendar.set(endOfSchoolYear.year, endOfSchoolYear.monthValue - 1, endOfSchoolYear.dayOfMonth)
- return calendar
- }
-
- override fun writeToParcel(parcel: Parcel, flags: Int) {}
-
- override fun describeContents() = 0
-
- companion object CREATOR : Parcelable.Creator<SchooldaysRangeLimiter> {
-
- override fun createFromParcel(parcel: Parcel): SchooldaysRangeLimiter = SchooldaysRangeLimiter()
-
- override fun newArray(size: Int): Array<SchooldaysRangeLimiter?> = arrayOfNulls(size)
- }
-}

diff:
+package io.github.wulkanowy.utils
+
+import com.google.android.material.datepicker.CalendarConstraints
+import kotlinx.parcelize.Parcelize
+import java.time.DayOfWeek
+import java.time.temporal.ChronoUnit
+
+@Parcelize
+class SchoolDaysValidator(val start: Long, val end: Long) : CalendarConstraints.DateValidator {
+
+ override fun isValid(dateLong: Long): Boolean {
+ val date = dateLong.toLocalDateTime()
+
+ return date.until(end.toLocalDateTime(), ChronoUnit.DAYS) >= 0 &&
+ date.until(start.toLocalDateTime(), ChronoUnit.DAYS) <= 0 &&
+ date.dayOfWeek != DayOfWeek.SUNDAY
+ }
+}
\ No newline at end of file

diff:
@@ -11,6 +11,7 @@ import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalDateTime.now
import java.time.LocalDateTime.ofInstant
+import java.time.LocalTime
import java.time.Month
import java.time.ZoneId
import java.time.ZoneOffset
@@ -22,15 +23,20 @@ import java.util.Locale
private const val DATE_PATTERN = "dd.MM.yyyy"
-fun String.toLocalDate(format: String = DATE_PATTERN): LocalDate = LocalDate.parse(this, ofPattern(format))
+fun String.toLocalDate(format: String = DATE_PATTERN): LocalDate =
+ LocalDate.parse(this, ofPattern(format))
-fun LocalDateTime.toTimestamp() = atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).toInstant().toEpochMilli()
+fun LocalDateTime.toTimestamp() =
+ atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).toInstant().toEpochMilli()
fun Long.toLocalDateTime(): LocalDateTime = ofInstant(ofEpochMilli(this), ZoneId.systemDefault())
+fun LocalDate.toTimestamp() = atTime(LocalTime.now()).toTimestamp()
+
fun LocalDate.toFormattedString(format: String = DATE_PATTERN): String = format(ofPattern(format))
-fun LocalDateTime.toFormattedString(format: String = DATE_PATTERN): String = format(ofPattern(format))
+fun LocalDateTime.toFormattedString(format: String = DATE_PATTERN): String =
+ format(ofPattern(format))
@SuppressLint("DefaultLocale")
fun Month.getFormattedName(): String {
@@ -105,6 +111,12 @@ inline val LocalDate.lastSchoolDay: LocalDate
get() = LocalDate.of(year, 6, 20)
.with(next(FRIDAY))
+inline val LocalDate.schoolYearStart: LocalDate
+ get() = withYear(if (this.monthValue <= 6) this.year - 1 else this.year).firstSchoolDay
+
+inline val LocalDate.schoolYearEnd: LocalDate
+ get() = withYear(if (this.monthValue > 6) this.year + 1 else this.year).lastSchoolDay
+
private fun Int.getSchoolYearByMonth(monthValue: Int): Int {
return when (monthValue) {
in 9..12 -> this

diff:
<item name="android:statusBarColor">@color/colorStatusBarLight</item>
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">false</item>
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
- <item name="mdtp_theme_dark">true</item>
</style>
<style name="WulkanowyTheme.Black" parent="WulkanowyTheme.NoActionBar">

diff:
<item name="android:statusBarColor">@android:color/darker_gray</item>
<item name="android:textColor">?android:textColorPrimary</item>
<item name="android:preferenceStyle">@style/PreferenceThemeOverlay</item>
- <item name="mdtp_theme_dark">false</item>
</style>
<style name="WulkanowyTheme.NoActionBar" parent="WulkanowyTheme">

original_message: Migrate material date picker (#1277)


diff:
<TextView
android:id="@+id/student_info_item_title"
android:layout_width="match_parent"
- android:layout_height="28dp"
+ android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:layout_marginTop="5dp"
+ android:layout_marginTop="13dp"
android:layout_marginEnd="16dp"
android:gravity="bottom"
android:maxLines="1"
- android:textColor="?android:textColorPrimary"
- android:textSize="16sp"
+ android:textColor="?android:textColorSecondary"
+ android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
<TextView
android:id="@+id/student_info_item_subtitle"
android:layout_width="match_parent"
- android:layout_height="18dp"
+ android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:gravity="bottom"
android:maxLines="1"
- android:textColor="?android:textColorSecondary"
- android:textSize="12sp"
+ android:textColor="?android:textColorPrimary"
+ android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/student_info_item_title"
tools:text="@tools:sample/lorem/random" />
+
</androidx.constraintlayout.widget.ConstraintLayout>

diff:
tools:context=".ui.modules.schoolandteachers.teacher.TeacherAdapter">
<TextView
- android:id="@+id/teacherItemName"
+ android:id="@+id/teacherItemSubject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginStart="10dp"
android:ellipsize="end"
android:maxLines="1"
- android:textSize="17sp"
+ android:textColor="?android:textColorSecondary"
+ android:textSize="12sp"
+ tools:text="11:11-12:00" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_below="@+id/teacherItemSubject"
+ android:layout_marginStart="10dp">
+
+ <TextView
+ android:id="@+id/teacherItemName"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textSize="16sp"
tools:text="@tools:sample/full_names" />
<TextView
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="40dp"
- android:layout_toEndOf="@+id/teacherItemName"
android:maxLines="1"
android:textColor="?android:textColorSecondary"
- android:textSize="17sp"
+ android:textSize="16sp"
tools:text="[LI]" />
- <TextView
- android:id="@+id/teacherItemSubject"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@+id/teacherItemName"
- android:layout_alignStart="@id/teacherItemName"
- android:maxLines="1"
- android:textColor="?android:textColorSecondary"
- android:textSize="12sp"
- tools:text="11:11-12:00" />
+ </LinearLayout>
</RelativeLayout>

original_message: Student info and Teachers screens unification (#1279)


message: Fix Homework dialog buttons layout

diff:
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ic_all_divider"
- android:paddingHorizontal="10dp"
- android:paddingVertical="5dp">
+ android:gravity="end"
+ android:minHeight="52dp"
+ android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/homeworkDialogRead"
- style="@style/Widget.MaterialComponents.Button.TextButton"
+ style="@style/Widget.MaterialComponents.Button.TextButton.Dialog"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:layout_marginTop="8dp"
android:insetRight="0dp"
android:insetBottom="0dp"
android:minWidth="88dp"
+ android:layout_gravity="center_vertical"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:text="@string/homework_mark_as_done"
<com.google.android.material.button.MaterialButton
android:id="@+id/homeworkDialogClose"
- style="@style/Widget.MaterialComponents.Button.TextButton"
+ style="@style/Widget.MaterialComponents.Button.TextButton.Dialog"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:layout_marginTop="8dp"
android:insetRight="0dp"
android:insetBottom="0dp"
android:minWidth="88dp"
+ android:layout_gravity="center_vertical"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:text="@string/all_close"

diff:
<View
android:layout_width="292dp"
- android:layout_height="416dp"
+ android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"

diff:
@@ -520,6 +520,7 @@ class GradeAverageProviderTest {
fun `calc all year average`() {
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
every { preferencesRepository.gradeAverageForceCalc } returns false
+ every { preferencesRepository.isOptionalArithmeticAverage } returns false
coEvery {
gradeRepository.getGrades(
student,

original_message: Fix Homework dialog buttons layout (#1297)


message: Change titles in Student Info

diff:
@@ -38,7 +38,15 @@ class StudentInfoFragment :
lateinit var studentInfoAdapter: StudentInfoAdapter
override val titleStringId: Int
- get() = R.string.student_info_title
+ get() = when (requireArguments().getSerializable(INFO_TYPE_ARGUMENT_KEY) as? StudentInfoView.Type) {
+ StudentInfoView.Type.PERSONAL -> R.string.account_personal_data
+ StudentInfoView.Type.CONTACT -> R.string.account_contact
+ StudentInfoView.Type.ADDRESS -> R.string.account_address
+ StudentInfoView.Type.FAMILY -> R.string.account_family
+ StudentInfoView.Type.SECOND_GUARDIAN -> R.string.student_info_guardian
+ StudentInfoView.Type.FIRST_GUARDIAN -> R.string.student_info_guardian
+ else -> R.string.student_info_title
+ }
override val isViewEmpty get() = studentInfoAdapter.items.isEmpty()

diff:
<string name="student_info_male">Male</string>
<string name="student_info_female">Female</string>
<string name="student_info_last_name">Last name</string>
+ <string name="student_info_guardian">Guardian</string>
<!--Account edit-->

original_message: Change titles in Student Info (#1121)


message: Fix homework dialog attachments margin

diff:
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="24dp"
- android:paddingStart="0dp"
+ android:paddingStart="24dp"
android:paddingEnd="16dp"
+ android:paddingTop="3dp"
+ android:layout_marginBottom="3dp"
+ android:paddingBottom="3dp"
android:drawablePadding="10dp"
android:textSize="16sp"
app:drawableStartCompat="@drawable/ic_attachment"

diff:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="0dp"
+ android:layout_marginStart="4dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:text="@string/homework_attachments"

original_message: Fix homework dialog attachments margin (#1322)


diff:
android:visibility="gone"
tools:ignore="UseCompoundDrawables">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:gravity="center"
- android:text="@string/school_announcement_no_items"
- android:textSize="20sp" />
-
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_all_about"
app:tint="?android:attr/textColorPrimary"
tools:ignore="contentDescription" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dp"
+ android:gravity="center"
+ android:text="@string/school_announcement_no_items"
+ android:textSize="20sp" />
</LinearLayout>
<LinearLayout

original_message: Fix no info text position in school announcements (#1327)


message: Change dashboard no data strings

diff:
<item quantity="other">%1$d more lessons</item>
</plurals>
<string name="dashboard_timetable_third_time">until %1$s</string>
- <string name="dashboard_timetable_no_lessons">No lessons</string>
+ <string name="dashboard_timetable_no_lessons">No upcoming lessons</string>
<string name="dashboard_timetable_error">An error occurred while loading the lesson</string>
<string name="dashboard_homework_title">Homework</string>
- <string name="dashboard_homework_no_homework">No homework</string>
+ <string name="dashboard_homework_no_homework">No homework to do</string>
<string name="dashboard_homework_error">An error occurred while loading the homework</string>
<plurals name="dashboard_homework_more">
<item quantity="one">%1$d more homework</item>
<string name="dashboard_grade_error">An error occurred while loading the grades</string>
<string name="dashboard_announcements_title">School announcements</string>
- <string name="dashboard_announcements_no_announcements">No announcements</string>
+ <string name="dashboard_announcements_no_announcements">No current announcements</string>
<string name="dashboard_announcements_error">An error occurred while loading the announcements</string>
<plurals name="dashboard_announcements_more">
<item quantity="one">%1$d more announcement</item>
</plurals>
<string name="dashboard_exams_title">Exams</string>
- <string name="dashboard_exams_no_exams">No exams</string>
+ <string name="dashboard_exams_no_exams">No upcoming exams</string>
<string name="dashboard_exams_error">An error occurred while loading the exams</string>
<plurals name="dashboard_exams_more">
<item quantity="one">%1$d more exam</item>
</plurals>
<string name="dashboard_conferences_title">Conferences</string>
- <string name="dashboard_conferences_no_conferences">No conferences</string>
+ <string name="dashboard_conferences_no_conferences">No upcoming conferences</string>
<string name="dashboard_conferences_error">An error occurred while loading the conferences</string>
<plurals name="dashboard_conference_more">
<item quantity="one">%1$d more conference</item>
</plurals>
<string name="dashboard_horizontal_group_error">An error occurred while loading data</string>
- <string name="dashboard_horizontal_group_no_lukcy_number">No</string>
+ <string name="dashboard_horizontal_group_no_lukcy_number">None</string>
<!--Error dialog-->

original_message: Change dashboard no data strings (#1426)