[Timetable/Generate] Add automatic timetable sync when no timetable for the selected week.

This commit is contained in:
Kuba Szczodrzyński 2020-03-09 14:57:14 +01:00
parent 0b186a754a
commit 098beb14fe
6 changed files with 244 additions and 161 deletions

View File

@ -30,7 +30,6 @@
Staramy się usuwać takie przypadki, jednak na chwilę obecną mogą występować błędy w: Staramy się usuwać takie przypadki, jednak na chwilę obecną mogą występować błędy w:
<ul> <ul>
<li>Wysyłanie wiadomości może nie działać w pełni prawidłowo - proszę o zgłaszanie wszystkich błędów na naszym serwerze Discord</li> <li>Wysyłanie wiadomości może nie działać w pełni prawidłowo - proszę o zgłaszanie wszystkich błędów na naszym serwerze Discord</li>
<li>Cisza nocna w powiadomieniach jeszcze nie działa.</li>
</ul> </ul>
<br> <br>
<br> <br>

View File

@ -21,7 +21,14 @@ import androidx.core.content.FileProvider
import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskAllFinishedEvent
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskFinishedEvent
import pl.szczodrzynski.edziennik.data.db.entity.Lesson import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.data.db.full.LessonFull import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.DialogGenerateBlockTimetableBinding import pl.szczodrzynski.edziennik.databinding.DialogGenerateBlockTimetableBinding
@ -64,11 +71,16 @@ class GenerateBlockTimetableDialog(
private var showTeachersNames: Boolean = true private var showTeachersNames: Boolean = true
private var noColors: Boolean = false private var noColors: Boolean = false
private var enqueuedWeekDialog: AlertDialog? = null
private var enqueuedWeekStart = Date.getToday()
private var enqueuedWeekEnd = Date.getToday()
init { run { init { run {
if (activity.isFinishing) if (activity.isFinishing)
return@run return@run
job = Job() job = Job()
onShowListener?.invoke(TAG) onShowListener?.invoke(TAG)
EventBus.getDefault().register(this)
val weekCurrentStart = Week.getWeekStart() val weekCurrentStart = Week.getWeekStart()
val weekCurrentEnd = Week.getWeekEnd() val weekCurrentEnd = Week.getWeekEnd()
@ -88,16 +100,20 @@ class GenerateBlockTimetableDialog(
.setTitle(R.string.timetable_generate_range) .setTitle(R.string.timetable_generate_range)
.setView(b.root) .setView(b.root)
.setNeutralButton(R.string.cancel) { dialog, _ -> dialog.dismiss() } .setNeutralButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
.setPositiveButton(R.string.save) { dialog, _ -> .setPositiveButton(R.string.save, null)
dialog.dismiss() .setOnDismissListener {
when (b.weekSelectionRadioGroup.checkedRadioButtonId) { onDismissListener?.invoke(TAG)
R.id.withChangesCurrentWeekRadio -> generateBlockTimetable(weekCurrentStart, weekCurrentEnd) EventBus.getDefault().unregister(this@GenerateBlockTimetableDialog)
R.id.withChangesNextWeekRadio -> generateBlockTimetable(weekNextStart, weekNextEnd)
R.id.forSelectedWeekRadio -> selectDate()
}
} }
.setOnDismissListener { onDismissListener?.invoke(TAG) }
.show() .show()
dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.onClick {
when (b.weekSelectionRadioGroup.checkedRadioButtonId) {
R.id.withChangesCurrentWeekRadio -> generateBlockTimetable(weekCurrentStart, weekCurrentEnd)
R.id.withChangesNextWeekRadio -> generateBlockTimetable(weekNextStart, weekNextEnd)
R.id.forSelectedWeekRadio -> selectDate()
}
}
}} }}
private fun selectDate() { private fun selectDate() {
@ -115,12 +131,26 @@ class GenerateBlockTimetableDialog(
.show(activity.supportFragmentManager, "MaterialDatePicker") .show(activity.supportFragmentManager, "MaterialDatePicker")
} }
private fun generateBlockTimetable(weekStart: Date, weekEnd: Date) { launch { @Subscribe(threadMode = ThreadMode.MAIN)
val progressDialog = MaterialAlertDialogBuilder(activity) fun onApiTaskFinishedEvent(event: ApiTaskFinishedEvent) {
.setTitle(R.string.timetable_generate_progress_title) if (event.profileId == App.profileId) {
.setMessage(R.string.timetable_generate_progress_text) enqueuedWeekDialog?.dismiss()
.show() generateBlockTimetable(enqueuedWeekStart, enqueuedWeekEnd)
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onApiTaskAllFinishedEvent(event: ApiTaskAllFinishedEvent) {
enqueuedWeekDialog?.dismiss()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onApiTaskErrorEvent(event: ApiTaskErrorEvent) {
dialog.dismiss()
enqueuedWeekDialog?.dismiss()
}
private fun generateBlockTimetable(weekStart: Date, weekEnd: Date) { launch {
val weekDays = mutableListOf<MutableList<Lesson>>() val weekDays = mutableListOf<MutableList<Lesson>>()
for (i in weekStart.weekDay..weekEnd.weekDay) { for (i in weekStart.weekDay..weekEnd.weekDay) {
weekDays.add(mutableListOf()) weekDays.add(mutableListOf())
@ -157,174 +187,212 @@ class GenerateBlockTimetableDialog(
return@mapNotNull lesson return@mapNotNull lesson
} }
if (lessons.isEmpty()) {
if (enqueuedWeekDialog != null) {
return@launch
}
enqueuedWeekDialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.please_wait)
.setMessage(R.string.timetable_generate_syncing_text)
.setCancelable(false)
.show()
enqueuedWeekStart = weekStart
enqueuedWeekEnd = weekEnd
EdziennikTask.syncProfile(
profileId = App.profileId,
viewIds = listOf(
MainActivity.DRAWER_ITEM_TIMETABLE to 0
),
arguments = JsonObject(
"weekStart" to weekStart.stringY_m_d
)
).enqueue(activity)
return@launch
}
val progressDialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.timetable_generate_progress_title)
.setMessage(R.string.timetable_generate_progress_text)
.show()
if (minTime == null) { if (minTime == null) {
progressDialog.dismiss() progressDialog.dismiss()
// TODO: Toast // TODO: Toast
return@launch return@launch
} }
val diff = Time.diff(maxTime, minTime) dialog.dismiss()
val imageWidth = WIDTH_CONSTANT + maxWeekDay * (WIDTH_WEEKDAY + WIDTH_SPACING) - WIDTH_SPACING val uri = withContext(Dispatchers.Default) {
val imageHeight = heightProfileName + HEIGHT_CONSTANT + diff.inMinutes * HEIGHT_MINUTE + HEIGHT_FOOTER
val bitmap = Bitmap.createBitmap(imageWidth + 20, imageHeight + 30, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
if (noColors) canvas.drawARGB(255, 255, 255, 255) val diff = Time.diff(maxTime, minTime)
else canvas.drawARGB(255, 225, 225, 225)
val paint = Paint().apply { val imageWidth = WIDTH_CONSTANT + maxWeekDay * (WIDTH_WEEKDAY + WIDTH_SPACING) - WIDTH_SPACING
isAntiAlias = true val imageHeight = heightProfileName + HEIGHT_CONSTANT + diff.inMinutes * HEIGHT_MINUTE + HEIGHT_FOOTER
isFilterBitmap = true val bitmap = Bitmap.createBitmap(imageWidth + 20, imageHeight + 30, Bitmap.Config.ARGB_8888)
isDither = true val canvas = Canvas(bitmap)
}
lessons.forEach { lesson -> if (noColors) canvas.drawARGB(255, 255, 255, 255)
val lessonLength = Time.diff(lesson.displayEndTime, lesson.displayStartTime) else canvas.drawARGB(255, 225, 225, 225)
val firstOffset = Time.diff(lesson.displayStartTime, minTime)
val lessonWeekDay = lesson.displayDate!!.weekDay
val left = WIDTH_CONSTANT + lessonWeekDay * (WIDTH_WEEKDAY + WIDTH_SPACING) val paint = Paint().apply {
val top = heightProfileName + HEIGHT_CONSTANT + firstOffset.inMinutes * HEIGHT_MINUTE isAntiAlias = true
isFilterBitmap = true
val blockWidth = WIDTH_WEEKDAY isDither = true
val blockHeight = lessonLength.inMinutes * HEIGHT_MINUTE
val viewWidth = 380.dp
val viewHeight = lessonLength.inMinutes * 4.dp
val layout = activity.layoutInflater.inflate(R.layout.row_timetable_block_item, null) as LinearLayout
val item: LinearLayout = layout.findViewById(R.id.timetableItemLayout)
val card: CardView = layout.findViewById(R.id.timetableItemCard)
val subjectName: TextView = layout.findViewById(R.id.timetableItemSubjectName)
val classroomName: TextView = layout.findViewById(R.id.timetableItemClassroomName)
val teacherName: TextView = layout.findViewById(R.id.timetableItemTeacherName)
val teamName: TextView = layout.findViewById(R.id.timetableItemTeamName)
if (noColors) {
card.setCardBackgroundColor(Color.WHITE)
card.cardElevation = 0f
item.setBackgroundResource(R.drawable.bg_rounded_16dp_outline)
subjectName.setTextColor(Color.BLACK)
classroomName.setTextColor(0xffaaaaaa.toInt())
teacherName.setTextColor(0xffaaaaaa.toInt())
teamName.setTextColor(0xffaaaaaa.toInt())
} }
subjectName.text = lesson.displaySubjectName ?: "" lessons.forEach { lesson ->
classroomName.text = lesson.displayClassroom ?: "" val lessonLength = Time.diff(lesson.displayEndTime, lesson.displayStartTime)
teacherName.text = lesson.displayTeacherName ?: "" val firstOffset = Time.diff(lesson.displayStartTime, minTime)
teamName.text = lesson.displayTeamName ?: "" val lessonWeekDay = lesson.displayDate!!.weekDay
if (!showTeachersNames) teacherName.visibility = View.GONE val left = WIDTH_CONSTANT + lessonWeekDay * (WIDTH_WEEKDAY + WIDTH_SPACING)
val top = heightProfileName + HEIGHT_CONSTANT + firstOffset.inMinutes * HEIGHT_MINUTE
when (lesson.type) { val blockWidth = WIDTH_WEEKDAY
Lesson.TYPE_NORMAL -> {} val blockHeight = lessonLength.inMinutes * HEIGHT_MINUTE
Lesson.TYPE_CANCELLED, Lesson.TYPE_SHIFTED_SOURCE -> {
card.setCardBackgroundColor(Color.BLACK) val viewWidth = 380.dp
subjectName.setTextColor(Color.WHITE) val viewHeight = lessonLength.inMinutes * 4.dp
subjectName.text = lesson.displaySubjectName?.asStrikethroughSpannable() ?: ""
val layout = activity.layoutInflater.inflate(R.layout.row_timetable_block_item, null) as LinearLayout
val item: LinearLayout = layout.findViewById(R.id.timetableItemLayout)
val card: CardView = layout.findViewById(R.id.timetableItemCard)
val subjectName: TextView = layout.findViewById(R.id.timetableItemSubjectName)
val classroomName: TextView = layout.findViewById(R.id.timetableItemClassroomName)
val teacherName: TextView = layout.findViewById(R.id.timetableItemTeacherName)
val teamName: TextView = layout.findViewById(R.id.timetableItemTeamName)
if (noColors) {
card.setCardBackgroundColor(Color.WHITE)
card.cardElevation = 0f
item.setBackgroundResource(R.drawable.bg_rounded_16dp_outline)
subjectName.setTextColor(Color.BLACK)
classroomName.setTextColor(0xffaaaaaa.toInt())
teacherName.setTextColor(0xffaaaaaa.toInt())
teamName.setTextColor(0xffaaaaaa.toInt())
} }
else -> {
card.setCardBackgroundColor(0xff234158.toInt()) subjectName.text = lesson.displaySubjectName ?: ""
subjectName.setTextColor(Color.WHITE) classroomName.text = lesson.displayClassroom ?: ""
subjectName.setTypeface(null, Typeface.BOLD_ITALIC) teacherName.text = lesson.displayTeacherName ?: ""
teamName.text = lesson.displayTeamName ?: ""
if (!showTeachersNames) teacherName.visibility = View.GONE
when (lesson.type) {
Lesson.TYPE_NORMAL -> {
}
Lesson.TYPE_CANCELLED, Lesson.TYPE_SHIFTED_SOURCE -> {
card.setCardBackgroundColor(Color.BLACK)
subjectName.setTextColor(Color.WHITE)
subjectName.text = lesson.displaySubjectName?.asStrikethroughSpannable()
?: ""
}
else -> {
card.setCardBackgroundColor(0xff234158.toInt())
subjectName.setTextColor(Color.WHITE)
subjectName.setTypeface(null, Typeface.BOLD_ITALIC)
}
}
layout.isDrawingCacheEnabled = true
layout.measure(MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY))
layout.layout(0, 0, layout.measuredWidth, layout.measuredHeight)
layout.buildDrawingCache(true)
val itemBitmap = layout.drawingCache
canvas.drawBitmap(itemBitmap, null, Rect(left, top, left + blockWidth, top + blockHeight), paint)
}
val textPaint = Paint().apply {
setARGB(255, 0, 0, 0)
textAlign = Paint.Align.CENTER
textSize = 30f
isAntiAlias = true
isFilterBitmap = true
isDither = true
}
for (w in 0..maxWeekDay) {
val x = WIDTH_CONSTANT + w * WIDTH_WEEKDAY + w * WIDTH_SPACING
canvas.drawText(Week.getFullDayName(w), x + (WIDTH_WEEKDAY / 2f), heightProfileName + HEIGHT_CONSTANT / 2 + 10f, textPaint)
}
if (showProfileName) {
textPaint.textSize = 50f
canvas.drawText("${app.profile.name} - plan lekcji, ${weekStart.formattedStringShort} - ${weekEnd.formattedStringShort}", (imageWidth + 20) / 2f, 80f, textPaint)
}
textPaint.apply {
setARGB(128, 0, 0, 0)
textAlign = Paint.Align.RIGHT
textSize = 26f
typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC)
}
val footerTextPaintCenter = ((textPaint.descent() + textPaint.ascent()) / 2).roundToInt()
canvas.drawText("Wygenerowano w aplikacji Szkolny.eu", imageWidth - 10f, imageHeight - footerTextPaintCenter - 10f, textPaint)
textPaint.apply {
setARGB(255, 127, 127, 127)
textAlign = Paint.Align.CENTER
textSize = 16f
typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
}
val textPaintCenter = ((textPaint.descent() + textPaint.ascent()) / 2).roundToInt()
val linePaint = Paint().apply {
setARGB(255, 100, 100, 100)
style = Paint.Style.STROKE
pathEffect = DashPathEffect(floatArrayOf(10f, 10f), 0f)
isAntiAlias = true
isFilterBitmap = true
isDither = true
}
val minTimeInt = ((minTime!!.value / 10000) * 60) + ((minTime!!.value / 100) % 100)
lessonRanges.forEach { (startTime, endTime) ->
listOf(startTime, endTime).forEach { value ->
val hour = value / 10000
val minute = (value / 100) % 100
val time = Time(hour, minute, 0)
val firstOffset = time.inMinutes - minTimeInt // offset in minutes
val top = (heightProfileName + HEIGHT_CONSTANT + firstOffset * HEIGHT_MINUTE).toFloat()
canvas.drawText(time.stringHM, WIDTH_CONSTANT / 2f, top - textPaintCenter, textPaint)
canvas.drawLine(WIDTH_CONSTANT.toFloat(), top, imageWidth.toFloat(), top, linePaint)
} }
} }
layout.isDrawingCacheEnabled = true val today = Date.getToday().stringY_m_d
layout.measure(MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY), val now = Time.getNow().stringH_M_S
MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY))
layout.layout(0, 0, layout.measuredWidth, layout.measuredHeight)
layout.buildDrawingCache(true)
val itemBitmap = layout.drawingCache val outputDir = Environment.getExternalStoragePublicDirectory("Szkolny.eu").apply { mkdirs() }
canvas.drawBitmap(itemBitmap, null, Rect(left, top, left + blockWidth, top + blockHeight), paint) val outputFile = File(outputDir, "plan_lekcji_${app.profile.name}_${today}_${now}.png")
}
val textPaint = Paint().apply { try {
setARGB(255, 0, 0, 0) val fos = FileOutputStream(outputFile)
textAlign = Paint.Align.CENTER bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
textSize = 30f fos.close()
isAntiAlias = true } catch (e: Exception) {
isFilterBitmap = true Log.e("SAVE_IMAGE", e.message, e)
isDither = true return@withContext null
}
for (w in 0..maxWeekDay) {
val x = WIDTH_CONSTANT + w * WIDTH_WEEKDAY + w * WIDTH_SPACING
canvas.drawText(Week.getFullDayName(w), x + (WIDTH_WEEKDAY / 2f), heightProfileName + HEIGHT_CONSTANT / 2 + 10f, textPaint)
}
if (showProfileName) {
textPaint.textSize = 50f
canvas.drawText("${app.profile.name} - plan lekcji, ${weekStart.formattedStringShort} - ${weekEnd.formattedStringShort}", (imageWidth + 20) / 2f, 80f, textPaint)
}
textPaint.apply {
setARGB(128, 0, 0, 0)
textAlign = Paint.Align.RIGHT
textSize = 26f
typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC)
}
val footerTextPaintCenter = ((textPaint.descent() + textPaint.ascent()) / 2).roundToInt()
canvas.drawText("Wygenerowano w aplikacji Szkolny.eu", imageWidth - 10f, imageHeight - footerTextPaintCenter - 10f, textPaint)
textPaint.apply {
setARGB(255, 127, 127, 127)
textAlign = Paint.Align.CENTER
textSize = 16f
typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
}
val textPaintCenter = ((textPaint.descent() + textPaint.ascent()) / 2).roundToInt()
val linePaint = Paint().apply {
setARGB(255, 100, 100, 100)
style = Paint.Style.STROKE
pathEffect = DashPathEffect(floatArrayOf(10f, 10f), 0f)
isAntiAlias = true
isFilterBitmap = true
isDither = true
}
val minTimeInt = ((minTime!!.value / 10000) * 60) + ((minTime!!.value / 100) % 100)
lessonRanges.forEach { (startTime, endTime) ->
listOf(startTime, endTime).forEach { value ->
val hour = value / 10000
val minute = (value / 100) % 100
val time = Time(hour, minute, 0)
val firstOffset = time.inMinutes - minTimeInt // offset in minutes
val top = (heightProfileName + HEIGHT_CONSTANT + firstOffset * HEIGHT_MINUTE).toFloat()
canvas.drawText(time.stringHM, WIDTH_CONSTANT / 2f, top - textPaintCenter, textPaint)
canvas.drawLine(WIDTH_CONSTANT.toFloat(), top, imageWidth.toFloat(), top, linePaint)
} }
}
val today = Date.getToday().stringY_m_d val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val now = Time.getNow().stringH_M_S FileProvider.getUriForFile(activity, app.packageName + ".provider", outputFile)
} else {
val outputDir = Environment.getExternalStoragePublicDirectory("Szkolny.eu").apply { mkdirs() } Uri.parse("file://" + outputFile.absolutePath)
val outputFile = File(outputDir, "plan_lekcji_${app.profile.name}_${today}_${now}.png") }
uri
try {
val fos = FileOutputStream(outputFile)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
fos.close()
} catch (e: Exception) {
Log.e("SAVE_IMAGE", e.message, e)
return@launch
}
val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
FileProvider.getUriForFile(activity, app.packageName + ".provider", outputFile)
} else {
Uri.parse("file://" + outputFile.absolutePath)
} }
progressDialog.dismiss() progressDialog.dismiss()

View File

@ -48,6 +48,7 @@ class ErrorSnackbar(val activity: AppCompatActivity) : CoroutineScope {
fun addError(apiError: ApiError): ErrorSnackbar { fun addError(apiError: ApiError): ErrorSnackbar {
errors.add(apiError) errors.add(apiError)
snackbar?.setText(apiError.getStringReason(activity)) snackbar?.setText(apiError.getStringReason(activity))
snackbar?.duration = 15000
return this return this
} }

View File

@ -55,7 +55,8 @@ class TimeDropdown : TextInputDropDown {
isEnabled = false isEnabled = false
} }
suspend fun loadItems() { suspend fun loadItems(): Boolean {
var noLessons = false
val hours = withContext(Dispatchers.Default) { val hours = withContext(Dispatchers.Default) {
val hours = mutableListOf<Item>() val hours = mutableListOf<Item>()
@ -90,6 +91,7 @@ class TimeDropdown : TextInputDropDown {
hours += lessons.map { lesson -> hours += lessons.map { lesson ->
if (lesson.type == Lesson.TYPE_NO_LESSONS) { if (lesson.type == Lesson.TYPE_NO_LESSONS) {
// indicate there are no lessons this day // indicate there are no lessons this day
noLessons = true
return@map Item( return@map Item(
-2L, -2L,
context.getString(R.string.dialog_event_manual_no_lessons), context.getString(R.string.dialog_event_manual_no_lessons),
@ -158,6 +160,8 @@ class TimeDropdown : TextInputDropDown {
else -> false else -> false
} }
} }
return !noLessons
} }
fun pickerDialog() { fun pickerDialog() {

View File

@ -9,7 +9,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:paddingHorizontal="20dp"> android:paddingHorizontal="20dp"
android:paddingTop="16dp">
<RadioGroup <RadioGroup
android:id="@+id/weekSelectionRadioGroup" android:id="@+id/weekSelectionRadioGroup"
@ -36,10 +37,17 @@
android:text="@string/timetable_generate_selected_week" /> android:text="@string/timetable_generate_selected_week" />
</RadioGroup> </RadioGroup>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="8dp"
android:background="@drawable/divider"/>
<CheckBox <CheckBox
android:id="@+id/showProfileNameCheckbox" android:id="@+id/showProfileNameCheckbox"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minHeight="0dp"
android:text="@string/timetable_generate_show_profile_name" /> android:text="@string/timetable_generate_show_profile_name" />
<CheckBox <CheckBox
@ -47,12 +55,14 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:checked="true" android:checked="true"
android:minHeight="0dp"
android:text="@string/timetable_generate_show_teachers_names" /> android:text="@string/timetable_generate_show_teachers_names" />
<CheckBox <CheckBox
android:id="@+id/noColorsCheckbox" android:id="@+id/noColorsCheckbox"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minHeight="0dp"
android:text="@string/timetable_generate_no_colors" /> android:text="@string/timetable_generate_no_colors" />
</LinearLayout> </LinearLayout>
</layout> </layout>

View File

@ -1255,4 +1255,5 @@
<string name="grades_config_average_without_weight_message">Pozwala na liczenie średniej arytmetycznej z przedmiotów, w których wszystkie wystawione oceny mają wagę 0 (nie są liczone do średniej).\n\nJeśli taki przedmiot celowo nie powinien być liczony, odznacz okienko przy tym ustawieniu.</string> <string name="grades_config_average_without_weight_message">Pozwala na liczenie średniej arytmetycznej z przedmiotów, w których wszystkie wystawione oceny mają wagę 0 (nie są liczone do średniej).\n\nJeśli taki przedmiot celowo nie powinien być liczony, odznacz okienko przy tym ustawieniu.</string>
<string name="grades_config_minus_value">Własna wartość minusa</string> <string name="grades_config_minus_value">Własna wartość minusa</string>
<string name="grades_config_plus_value">Własna wartość plusa</string> <string name="grades_config_plus_value">Własna wartość plusa</string>
<string name="timetable_generate_syncing_text">Pobieranie planu lekcji na wybrany tydzień...</string>
</resources> </resources>