Compare commits

...

6 Commits

27 changed files with 139 additions and 119 deletions

View File

@ -113,10 +113,11 @@ jobs:
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
packageName: pl.szczodrzynski.edziennik
releaseFile: ${{ needs.sign.outputs.signedReleaseFile }}
releaseFiles: ${{ needs.sign.outputs.signedReleaseFile }}
releaseName: ${{ steps.changelog.outputs.appVersionName }}
track: ${{ secrets.PLAY_RELEASE_TRACK }}
whatsNewDirectory: ${{ steps.changelog.outputs.changelogDir }}
status: completed
- name: Upload workflow artifact
uses: actions/upload-artifact@v2

View File

@ -156,6 +156,7 @@ dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:2.5.2"
implementation "androidx.recyclerview:recyclerview:1.2.1"
implementation "androidx.room:room-runtime:2.4.3"
implementation "androidx.room:room-ktx:2.4.3"
implementation "androidx.work:work-runtime-ktx:2.7.1"
kapt "androidx.room:room-compiler:2.4.3"

View File

@ -1,12 +1,8 @@
<h3>Wersja 4.13.3, 2022-12-06</h3>
<h3>Wersja 4.13.5, 2023-03-22</h3>
<ul>
<li>Notatki zastępujące treść lekcji są teraz wyświetlane wszędzie.</li>
<li>Opcja wyświetlania nazwy przedmiotu w miejscu rodzaju wydarzenia.</li>
<li>Na ekranie odliczania czasu lekcji również pada śnieg.</li>
<li>Poprawiono synchronizację w Mobidzienniku bez ustawionego adresu e-mail.</li>
<li>Poprawiono błąd synchronizacji w Vulcanie.</li>
<li>Naprawiono pobieranie załączników na Androidzie 13 i nowszym.</li>
</ul>
<br>
<br>
Dzięki za korzystanie ze Szkolnego!<br>
<i>&copy; [Kuba Szczodrzyński](@kuba2k2) 2022</i>
<i>&copy; [Kuba Szczodrzyński](@kuba2k2) 2023</i>

View File

@ -9,7 +9,7 @@
/*secret password - removed for source code publication*/
static toys AES_IV[16] = {
0xff, 0xf1, 0x81, 0xc5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
0x92, 0xc2, 0xe7, 0x13, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);

View File

@ -235,6 +235,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
}
Signing.getCert(this)
Utils.initializeStorageDir(this)
launch {
withContext(Dispatchers.Default) {

View File

@ -59,10 +59,11 @@ data class AppData(
val lessonHeight: Int,
val enableMarkAsReadAnnouncements: Boolean,
val enableNoticePoints: Boolean,
val eventManualShowSubjectDropdown: Boolean,
)
data class EventType(
val id: Int,
val id: Long,
val color: String,
val name: String,
)

View File

@ -15,7 +15,7 @@ class ProfileConfig(
entries: List<ConfigEntry>?,
) : BaseConfig(db, profileId, entries) {
companion object {
const val DATA_VERSION = 4
const val DATA_VERSION = 5
}
val grades by lazy { ProfileConfigGrades(this) }

View File

@ -16,6 +16,8 @@ import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_AL
class ProfileConfigMigration(config: ProfileConfig) {
init { config.apply {
val profile = db.profileDao().getByIdNow(profileId ?: -1)
if (dataVersion < 2) {
sync.notificationFilter = sync.notificationFilter + NotificationType.TEACHER_ABSENCE
@ -37,11 +39,23 @@ class ProfileConfigMigration(config: ProfileConfig) {
// switch to new event types (USOS)
dataVersion = 4
val profile = db.profileDao().getByIdNow(profileId ?: -1)
if (profile?.loginStoreType?.schoolType == SchoolType.UNIVERSITY) {
db.eventTypeDao().clear(profileId ?: -1)
db.eventTypeDao().addDefaultTypes(profile)
}
}
if (dataVersion < 5) {
// update USOS event types and the appropriate events (2022-12-25)
dataVersion = 5
if (profile?.loginStoreType?.schoolType == SchoolType.UNIVERSITY) {
db.eventTypeDao().getAllWithDefaults(profile)
// wejściówka (4) -> kartkówka (3)
db.eventDao().getRawNow("UPDATE events SET eventType = 3 WHERE profileId = $profileId AND eventType = 4;")
// zadanie (6) -> zadanie domowe (-1)
db.eventDao().getRawNow("UPDATE events SET eventType = -1 WHERE profileId = $profileId AND eventType = 6;")
}
}
}}
}

View File

@ -46,6 +46,6 @@ object Signing {
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
return "$param1.MTIzNDU2Nzg5MDgyAWPEgk===.$param2".sha256()
return "$param1.MTIzNDU2Nzg5MD3uL2uE3E===.$param2".sha256()
}
}

View File

@ -25,6 +25,9 @@ abstract class EventTypeDao {
@Query("DELETE FROM eventTypes WHERE profileId = :profileId")
abstract fun clear(profileId: Int)
@Query("DELETE FROM eventTypes WHERE profileId = :profileId AND eventTypeSource = :source")
abstract fun clearBySource(profileId: Int, source: Int)
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId AND eventType = :typeId")
abstract fun getByIdNow(profileId: Int, typeId: Long): EventType?
@ -43,7 +46,7 @@ abstract class EventTypeDao {
val typeList = data.eventTypes.map {
EventType(
profileId = profile.id,
id = it.id.toLong(),
id = it.id,
name = it.name,
color = Color.parseColor(it.color),
order = order++,
@ -53,4 +56,21 @@ abstract class EventTypeDao {
addAll(typeList)
return typeList
}
fun getAllWithDefaults(profile: Profile): List<EventType> {
val eventTypes = getAllNow(profile.id)
val defaultIdsExpected = AppData.get(profile.loginStoreType).eventTypes
.map { it.id }
val defaultIdsFound = eventTypes.filter { it.source == SOURCE_DEFAULT }
.sortedBy { it.order }
.map { it.id }
if (defaultIdsExpected == defaultIdsFound)
return eventTypes
clearBySource(profile.id, SOURCE_DEFAULT)
addDefaultTypes(profile)
return eventTypes
}
}

View File

@ -28,6 +28,9 @@ interface ProfileDao {
@Query("SELECT * FROM profiles WHERE profileId = :profileId")
fun getByIdNow(profileId: Int): Profile?
@Query("SELECT * FROM profiles WHERE profileId = :profileId")
suspend fun getByIdSuspend(profileId: Int): Profile?
@get:Query("SELECT * FROM profiles WHERE profileId >= 0 ORDER BY profileId")
val all: LiveData<List<Profile>>

View File

@ -5,31 +5,6 @@ package pl.szczodrzynski.edziennik.data.db.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_CLASS_EVENT
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_DEFAULT
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_ELEARNING
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_ESSAY
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_EXAM
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_EXCURSION
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_HOMEWORK
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_INFORMATION
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_PROJECT
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_PT_MEETING
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_READING
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.COLOR_SHORT_QUIZ
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_CLASS_EVENT
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_DEFAULT
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_ELEARNING
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_ESSAY
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_EXAM
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_EXCURSION
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_HOMEWORK
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_INFORMATION
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_PROJECT
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_PT_MEETING
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_READING
import pl.szczodrzynski.edziennik.data.db.entity.Event.Companion.TYPE_SHORT_QUIZ
@Entity(
tableName = "eventTypes",
@ -55,35 +30,5 @@ class EventType(
const val SOURCE_REGISTER = 1
const val SOURCE_CUSTOM = 2
const val SOURCE_SHARED = 3
fun getTypeColorMap() = mapOf(
TYPE_ELEARNING to COLOR_ELEARNING,
TYPE_HOMEWORK to COLOR_HOMEWORK,
TYPE_DEFAULT to COLOR_DEFAULT,
TYPE_EXAM to COLOR_EXAM,
TYPE_SHORT_QUIZ to COLOR_SHORT_QUIZ,
TYPE_ESSAY to COLOR_ESSAY,
TYPE_PROJECT to COLOR_PROJECT,
TYPE_PT_MEETING to COLOR_PT_MEETING,
TYPE_EXCURSION to COLOR_EXCURSION,
TYPE_READING to COLOR_READING,
TYPE_CLASS_EVENT to COLOR_CLASS_EVENT,
TYPE_INFORMATION to COLOR_INFORMATION
)
fun getTypeNameMap() = mapOf(
TYPE_ELEARNING to R.string.event_type_elearning,
TYPE_HOMEWORK to R.string.event_type_homework,
TYPE_DEFAULT to R.string.event_other,
TYPE_EXAM to R.string.event_exam,
TYPE_SHORT_QUIZ to R.string.event_short_quiz,
TYPE_ESSAY to R.string.event_essay,
TYPE_PROJECT to R.string.event_project,
TYPE_PT_MEETING to R.string.event_pt_meeting,
TYPE_EXCURSION to R.string.event_excursion,
TYPE_READING to R.string.event_reading,
TYPE_CLASS_EVENT to R.string.event_class_event,
TYPE_INFORMATION to R.string.event_information
)
}
}

View File

@ -8,6 +8,7 @@ import android.content.Context
import android.content.res.Resources
import android.graphics.Rect
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.*
import androidx.annotation.StringRes
@ -161,3 +162,12 @@ val SwipeRefreshLayout.onScrollListener: RecyclerView.OnScrollListener
}
}
fun View.removeFromParent() {
(parent as? ViewGroup)?.removeView(this)
}
fun View.appendView(child: View) {
val parent = parent as? ViewGroup ?: return
val index = parent.indexOfChild(this)
parent.addView(child, index + 1)
}

View File

@ -142,13 +142,7 @@ class AgendaFragment : Fragment(), CoroutineScope {
private suspend fun checkEventTypes() {
withContext(Dispatchers.Default) {
val eventTypes = app.db.eventTypeDao().getAllNow(app.profileId).map {
it.id
}
val defaultEventTypes = EventType.getTypeColorMap().keys
if (!eventTypes.containsAll(defaultEventTypes)) {
app.db.eventTypeDao().addDefaultTypes(app.profile)
}
app.db.eventTypeDao().getAllWithDefaults(app.profile)
}
}

View File

@ -23,6 +23,7 @@ import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.config.Config
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.SignatureInterceptor
import pl.szczodrzynski.edziennik.data.db.entity.EventType.Companion.SOURCE_DEFAULT
import pl.szczodrzynski.edziennik.databinding.LabFragmentBinding
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.ui.base.lazypager.LazyFragment
@ -65,6 +66,7 @@ class LabPageFragment : LazyFragment(), CoroutineScope {
b.clearEndpointTimers.isVisible = false
b.rodo.isVisible = false
b.removeHomework.isVisible = false
b.resetEventTypes.isVisible = false
b.unarchive.isVisible = false
b.profile.isVisible = false
}
@ -100,6 +102,11 @@ class LabPageFragment : LazyFragment(), CoroutineScope {
app.db.eventDao().getRawNow("UPDATE events SET homeworkBody = NULL WHERE profileId = ${App.profileId}")
}
b.resetEventTypes.onClick {
app.db.eventTypeDao().clearBySource(App.profileId, SOURCE_DEFAULT)
app.db.eventTypeDao().getAllWithDefaults(App.profile)
}
b.chucker.isChecked = App.enableChucker
b.chucker.onChange { _, isChecked ->
app.config.enableChucker = isChecked

View File

@ -19,7 +19,9 @@ import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.config.AppData
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskAllFinishedEvent
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskErrorEvent
@ -35,9 +37,11 @@ import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
import pl.szczodrzynski.edziennik.ext.JsonObject
import pl.szczodrzynski.edziennik.ext.appendView
import pl.szczodrzynski.edziennik.ext.getStudentData
import pl.szczodrzynski.edziennik.ext.onChange
import pl.szczodrzynski.edziennik.ext.onClick
import pl.szczodrzynski.edziennik.ext.removeFromParent
import pl.szczodrzynski.edziennik.ext.setText
import pl.szczodrzynski.edziennik.ext.setTintColor
import pl.szczodrzynski.edziennik.ui.dialogs.base.BindingDialog
@ -117,6 +121,15 @@ class EventManualDialog(
}
override suspend fun onShow() {
val data = withContext(Dispatchers.IO) {
val profile = app.db.profileDao().getByIdSuspend(profileId) ?: return@withContext null
AppData.get(profile.loginStoreType)
}
if (data?.uiConfig?.eventManualShowSubjectDropdown == true) {
b.subjectDropdownLayout.removeFromParent()
b.timeDropdownLayout.appendView(b.subjectDropdownLayout)
}
b.showMore.onClick { // TODO iconics is broken
it.apply {
refreshDrawableState()

View File

@ -109,12 +109,10 @@ class GenerateBlockTimetableDialog(
.show()
dialog.getButton(AlertDialog.BUTTON_POSITIVE)?.onClick {
app.permissionManager.requestStoragePermission(activity, permissionMessage = R.string.permissions_generate_timetable) {
when (b.weekSelectionRadioGroup.checkedRadioButtonId) {
R.id.withChangesCurrentWeekRadio -> generateBlockTimetable(weekCurrentStart, weekCurrentEnd)
R.id.withChangesNextWeekRadio -> generateBlockTimetable(weekNextStart, weekNextEnd)
R.id.forSelectedWeekRadio -> selectDate()
}
when (b.weekSelectionRadioGroup.checkedRadioButtonId) {
R.id.withChangesCurrentWeekRadio -> generateBlockTimetable(weekCurrentStart, weekCurrentEnd)
R.id.withChangesNextWeekRadio -> generateBlockTimetable(weekNextStart, weekNextEnd)
R.id.forSelectedWeekRadio -> selectDate()
}
}
}}

View File

@ -5,6 +5,7 @@
package pl.szczodrzynski.edziennik.ui.views
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.util.AttributeSet
import androidx.appcompat.app.AppCompatActivity
@ -50,6 +51,10 @@ class AttachmentsView @JvmOverloads constructor(
val attachmentSizes = arguments.getLongArray("attachmentSizes")
val adapter = AttachmentAdapter(context, onAttachmentClick = { item ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
downloadAttachment(item)
return@AttachmentAdapter
}
app.permissionManager.requestStoragePermission(activity, R.string.permissions_attachment) {
downloadAttachment(item)
}
@ -57,6 +62,10 @@ class AttachmentsView @JvmOverloads constructor(
val popupMenu = PopupMenu(chip.context, chip)
popupMenu.menu.add(0, 1, 0, R.string.messages_attachment_download_again)
popupMenu.setOnMenuItemClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
downloadAttachment(item)
return@setOnMenuItemClickListener true
}
app.permissionManager.requestStoragePermission(activity, R.string.permissions_attachment) {
downloadAttachment(item, forceDownload = true)
}

View File

@ -33,13 +33,8 @@ class EventTypeDropdown : TextInputDropDown {
suspend fun loadItems() {
val types = withContext(Dispatchers.Default) {
val list = mutableListOf<Item>()
var types = db.eventTypeDao().getAllNow(profileId)
if (types.none { it.id in -1L..10L }) {
val profile = db.profileDao().getByIdNow(profileId) ?: return@withContext listOf()
types = db.eventTypeDao().addDefaultTypes(profile)
}
val types = db.eventTypeDao().getAllNow(profileId)
.sortedBy { it.order }
list += types.map {
Item(it.id, it.name, tag = it, icon = IconicsDrawable(context).apply {

View File

@ -774,14 +774,21 @@ public class Utils {
private static File storageDir = null;
public static File getStorageDir() {
if (storageDir != null)
return storageDir;
storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
storageDir = new File(storageDir, "Szkolny.eu");
storageDir.mkdirs();
return storageDir;
}
public static void initializeStorageDir(Context context) {
if (storageDir != null)
return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
storageDir = context.getExternalFilesDir(null);
} else {
storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
storageDir = new File(storageDir, "Szkolny.eu");
}
storageDir.mkdirs();
}
public static void writeStringToFile(File file, String data) throws IOException {
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file));
outputStreamWriter.write(data);

View File

@ -13,6 +13,7 @@
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/mainLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -34,6 +35,7 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/timeDropdownLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -163,6 +165,7 @@
tools:visibility="visible">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/subjectDropdownLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -100,6 +100,13 @@
android:text="Remove all homework body (null)"
android:textAllCaps="false" />
<Button
android:id="@+id/resetEventTypes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Reset event types"
android:textAllCaps="false" />
<TextView
android:id="@+id/cookies"
android:layout_width="match_parent"

View File

@ -12,7 +12,8 @@
"uiConfig": {
"lessonHeight": 60,
"enableMarkAsReadAnnouncements": true,
"enableNoticePoints": false
"enableNoticePoints": false,
"eventManualShowSubjectDropdown": false
},
"eventTypes": [
{
@ -89,38 +90,34 @@
"timetableTrimHourRange": true
},
"uiConfig": {
"lessonHeight": 45
"lessonHeight": 45,
"eventManualShowSubjectDropdown": true
},
"eventTypes": [
{
"id": 1,
"color": "#f44336",
"name": "egzamin"
"id": 3,
"color": "#76ff03",
"name": "kartkówka"
},
{
"id": 2,
"color": "#e91e63",
"name": "kolokwium"
},
{
"id": 3,
"color": "#76ff03",
"name": "kartkówka"
},
{
"id": 4,
"color": "#ffeb3b",
"name": "wejściówka"
},
{
"id": 5,
"color": "#90caf9",
"name": "zaliczenie"
},
{
"id": 6,
"color": "#4050b5",
"name": "zadanie"
"id": 1,
"color": "#f44336",
"name": "egzamin"
},
{
"id": 11,
"color": "#3d5afe",
"name": "poprawka"
},
{
"id": 7,

View File

@ -847,7 +847,7 @@
<string name="settings_about_licenses_text">Open-Source-Lizenzen</string>
<string name="settings_about_privacy_policy_text">Datenschutzrichtlinie</string>
<string name="settings_card_register_title">E-Klassenbuch</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński, September 2018 - 2022</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński, September 2018 - 2023</string>
<string name="settings_about_update_subtext">Klicken Sie hier, um nach Aktualisierungen zu suchen</string>
<string name="settings_about_update_text">Aktualisierung</string>
<string name="settings_about_version_text">Version</string>

View File

@ -849,7 +849,7 @@
<string name="settings_about_licenses_text">Open-source licenses</string>
<string name="settings_about_privacy_policy_text">Privacy policy</string>
<string name="settings_card_register_title">E-register</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński, September 2018 2022</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński, September 2018 2023</string>
<string name="settings_about_update_subtext">Click to check for updates</string>
<string name="settings_about_update_text">Update</string>
<string name="settings_about_version_text">Version</string>
@ -1353,7 +1353,6 @@
<string name="home_archive_close_no_target_text">Child %s does not have a profile on this account in the current school year. Probably this profile has been deleted or the student no longer attends this class.\n\nTo go to the current profile, select a student from the list or log in to their account with the Add student button.</string>
<string name="build_invalid_no_remote_repo">A reference to a remote repository was not found. Make sure you are using the official repository fork and verify your Gradle configuration.</string>
<string name="login_mode_mobidziennik_web_guide">"Enter the data you use to log in to the MobiDziennik website. As the server address, you can enter the address of the website where you have MobiDziennik. "</string>
<string name="permissions_generate_timetable">In order to be able to save the generated timetable, you must grant access rights to the device\'s memory.\n\nClick OK to grant permissions.</string>
<string name="login_summary_account_child">(Child)</string>
<string name="login_summary_account_parent">(Parent)</string>
<string name="menu_teachers">Teachers</string>

View File

@ -916,7 +916,7 @@
<string name="settings_about_licenses_text">Licencje open-source</string>
<string name="settings_about_privacy_policy_text">Polityka prywatności</string>
<string name="settings_card_register_title">E-dziennik</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński, wrzesień 2018 - 2022</string>
<string name="settings_about_title_subtext">© Kuba Szczodrzyński, wrzesień 2018 - 2023</string>
<string name="settings_about_update_subtext">Kliknij, aby sprawdzić aktualizacje</string>
<string name="settings_about_update_text">Aktualizacja</string>
<string name="settings_about_version_text">Wersja</string>
@ -1416,7 +1416,6 @@
<string name="build_dialog_open_repo">Sprawdź kod</string>
<string name="error_no_api_access">Brak dostępu do API</string>
<string name="build_date">Data kompilacji</string>
<string name="permissions_generate_timetable">Aby móc zapisać wygenerowany plan lekcji musisz przyznać uprawnienia dostępu do pamięci urządzenia.\n\nKliknij OK, aby przyznać uprawnienia.</string>
<string name="privacy_policy_dialog_html"><![CDATA[Korzystając z aplikacji potwierdzasz <a href="https://szkolny.eu/privacy-policy">przeczytanie Polityki prywatności</a> i akceptujesz jej postanowienia.<br /><br />Autorzy aplikacji nie biorą odpowiedzialności za korzystanie z aplikacji Szkolny.eu.]]></string>
<string name="login_chooser_version_format">Szkolny.eu v%s\n%s</string>
<string name="menu_agenda_config">Ustawienia terminarza</string>

View File

@ -5,8 +5,8 @@ buildscript {
kotlin_version = '1.6.10'
release = [
versionName: "4.13.3",
versionCode: 4130399
versionName: "4.13.5",
versionCode: 4130599
]
setup = [