1
0

Compare commits

..

20 Commits
1.1.0 ... 1.1.1

Author SHA1 Message Date
60a9bcae46 Merge branch 'release/1.1.1' 2021-03-16 00:43:33 +01:00
eeb1341c1f Version 1.1.1 2021-03-16 00:43:28 +01:00
c77b50d51b New Crowdin updates (#1204) 2021-03-16 00:30:28 +01:00
8644ce32d5 Fix semester switch when student have only one semester (#1215) 2021-03-15 23:58:50 +01:00
94506aca52 Add github actions config to deploy apk to App Center (#1220) 2021-03-15 18:18:08 +01:00
eee4e1f4b5 Fix empty view in attendance (#1217) 2021-03-15 00:33:40 +01:00
c1942d012f Maybe fix fragment commits after activity state is saved (#1216) 2021-03-13 20:15:12 +01:00
fe846b463a Update material chips input (#1214) 2021-03-13 20:14:36 +01:00
0ea2e68249 Fix and clean proguard/r8 file (#1213) 2021-03-13 20:14:10 +01:00
be0445b227 Change the absence request confirmation message string (#1212) 2021-03-13 20:13:57 +01:00
48249f3093 Update kotlin coroutines (#1211) 2021-03-13 20:13:48 +01:00
8d7110735d Ignore no current student during avatar loading (#1210) 2021-03-13 20:13:37 +01:00
94957850c3 Bump fragment-ktx from 1.3.0 to 1.3.1 (#1205) 2021-03-12 21:50:59 +00:00
fa2cfc8427 Bump room from 2.3.0-beta02 to 2.3.0-beta03 (#1207) 2021-03-12 21:33:30 +00:00
3d467c43ba Use new kotlin compiler backend (#1202) 2021-03-12 22:07:27 +01:00
b76032044d Bump activity-ktx from 1.2.0 to 1.2.1 (#1206) 2021-03-12 21:06:33 +00:00
495b84204c Bump work_hilt from 1.0.0-alpha03 to 1.0.0-beta01 (#1208) 2021-03-12 21:06:27 +00:00
ea4b299de6 Bump firebase-bom from 26.6.0 to 26.7.0 (#1209) 2021-03-12 12:39:01 +00:00
acb5e2afd4 Replace dash mark with no data string in SchoolFragment (#1203) 2021-03-09 17:49:24 +01:00
50863d6ac2 Merge branch 'release/1.1.0' into develop 2021-03-07 21:58:30 +01:00
22 changed files with 230 additions and 90 deletions

View File

@ -68,3 +68,135 @@ jobs:
PLAY_SERVICE_ACCOUNT_EMAIL: ${{ secrets.PLAY_SERVICE_ACCOUNT_EMAIL }}
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
run: ./gradlew publishPlayRelease -PenableFirebase --stacktrace;
deploy-appcenter:
name: Deploy to App Center
runs-on: ubuntu-latest
timeout-minutes: 10
environment: app-center
if: github.ref != 'refs/heads/develop'
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 11
- uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
- name: Set run number with offset
env:
BUILD_NUMBER_OFFSET: ${{ secrets.BUILD_NUMBER_OFFSET }}
run: echo "RUN_NUMBER=$((GITHUB_RUN_NUMBER+BUILD_NUMBER_OFFSET))" >> $GITHUB_ENV
- name: Prepare build configuration
run: |
sed -i -e "s#applicationIdSuffix \".dev\"#applicationIdSuffix \".${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/build.gradle
sed -i -e "s#.dev\"#.${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/src/debug/google-services.json
sed -i -e "s#.dev\"#.${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/src/debug/agconnect-services.json
sed -i -e '/versionNameSuffix/d' app/build.gradle
- name: Add signing config
run: |
cat >> app/build.gradle <<EOF
android.signingConfigs.debug {
storeFile file("bitrise.jks")
storePassword System.getenv("BITRISE_KEYSTORE_PASSWORD")
keyAlias System.getenv("BITRISE_KEY_ALIAS")
keyPassword System.getenv("BITRISE_KEY_PASSWORD")
}
EOF
- name: Decrypt keys
env:
BITRISE_ENCRYPT_KEY: ${{ secrets.BITRISE_ENCRYPT_KEY }}
run: |
gpg --yes --batch --passphrase=$BITRISE_ENCRYPT_KEY ./app/bitrise.jks.gpg
- name: Bump version
uses: chkfung/android-version-actions@v1.1
with:
gradlePath: app/build.gradle
versionCode: ${{ env.RUN_NUMBER }}
versionName: ${{ env.RUN_NUMBER }}-${{ github.head_ref }}
- name: Build apk
env:
BITRISE_KEYSTORE_PASSWORD: ${{ secrets.BITRISE_KEYSTORE_PASSWORD }}
BITRISE_KEY_ALIAS: ${{ secrets.BITRISE_KEY_ALIAS }}
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
run: ./gradlew assembleFdroidDebug --stacktrace
- name: Upload apk to github artifacts
uses: actions/upload-artifact@v2
with:
name: wulkanowyDEV-${{ env.RUN_NUMBER }}.apk
path: app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk
- name: Deploy to app center
uses: wzieba/AppCenter-Github-Action@v1
with:
appName: wulkanowy/wulkanowy
token: ${{ secrets.APP_CENTER_TOKEN }}
group: Testers
file: app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk
notifyTesters: true
debug: true
deploy-app-distribution:
name: Deploy to AppDistribution
runs-on: ubuntu-latest
timeout-minutes: 10
environment: app-distribution
if: github.ref == 'refs/heads/develop'
steps:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 11
- uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
- name: Set run number with offset
env:
BUILD_NUMBER_OFFSET: ${{ secrets.BUILD_NUMBER_OFFSET }}
run: echo "RUN_NUMBER=$((GITHUB_RUN_NUMBER+BUILD_NUMBER_OFFSET))" >> $GITHUB_ENV
- name: Add signing config
run: |
cat >> app/build.gradle <<EOF
android.signingConfigs.debug {
storeFile file("bitrise.jks")
storePassword System.getenv("BITRISE_KEYSTORE_PASSWORD")
keyAlias System.getenv("BITRISE_KEY_ALIAS")
keyPassword System.getenv("BITRISE_KEY_PASSWORD")
}
EOF
- name: Decrypt keys
env:
BITRISE_ENCRYPT_KEY: ${{ secrets.BITRISE_ENCRYPT_KEY }}
BITRISE_SERVICES_ENCRYPT_KEY: ${{ secrets.BITRISE_SERVICES_ENCRYPT_KEY }}
run: |
gpg --yes --batch --passphrase=$BITRISE_SERVICES_ENCRYPT_KEY ./app/src/debug/google-services.json.gpg
gpg --yes --batch --passphrase=$BITRISE_ENCRYPT_KEY ./app/bitrise.jks.gpg
- name: Bump version
uses: chkfung/android-version-actions@v1.1
with:
gradlePath: app/build.gradle
versionCode: ${{ env.RUN_NUMBER }}
versionName: ${{ env.RUN_NUMBER }}
- name: Build apk
env:
BITRISE_KEYSTORE_PASSWORD: ${{ secrets.BITRISE_KEYSTORE_PASSWORD }}
BITRISE_KEY_ALIAS: ${{ secrets.BITRISE_KEY_ALIAS }}
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
run: ./gradlew assemblePlayDebug -PenableFirebase --stacktrace
- name: Upload apk to github artifacts
uses: actions/upload-artifact@v2
with:
name: wulkanowyDEV-${{ env.RUN_NUMBER }}-dev.apk
path: app/build/outputs/apk/play/debug/app-play-debug.apk
- name: Deploy to app distribution
uses: wzieba/Firebase-Distribution-Github-Action@v1
with:
appId: ${{ secrets.FIREBASE_APP_ID }}
token: ${{ secrets.FIREBASE_TOKEN }}
groups: discord
file: app/build/outputs/apk/play/debug/app-play-debug.apk

BIN
app/bitrise.jks Normal file

Binary file not shown.

BIN
app/bitrise.jks.gpg Normal file

Binary file not shown.

View File

@ -5,6 +5,8 @@ apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.github.triplet.play'
apply plugin: 'com.mikepenz.aboutlibraries.plugin'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.huawei.agconnect'
apply from: 'jacoco.gradle'
apply from: 'sonarqube.gradle'
apply from: 'hooks.gradle'
@ -18,8 +20,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 17
targetSdkVersion 30
versionCode 86
versionName "1.1.0"
versionCode 87
versionName "1.1.1"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@ -58,7 +60,7 @@ android {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
@ -115,6 +117,7 @@ android {
}
kotlinOptions {
useIR = true
jvmTarget = "1.8"
freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"]
}
@ -134,31 +137,31 @@ play {
serviceAccountCredentials = file('key.p12')
defaultToAppBundles = false
track = 'production'
updatePriority = 3
updatePriority = 5
}
ext {
work_manager = "2.5.0"
work_hilt = "1.0.0-alpha03"
room = "2.3.0-beta02"
work_hilt = "1.0.0-beta01"
room = "2.3.0-beta03"
chucker = "3.4.0"
mockk = "1.10.6"
moshi = "1.11.0"
}
dependencies {
implementation "io.github.wulkanowy:sdk:1.1.0"
implementation "io.github.wulkanowy:sdk:1.1.1"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3'
implementation "androidx.core:core-ktx:1.3.2"
implementation "androidx.activity:activity-ktx:1.2.0"
implementation "androidx.activity:activity-ktx:1.2.1"
implementation "androidx.appcompat:appcompat:1.2.0"
implementation "androidx.appcompat:appcompat-resources:1.2.0"
implementation "androidx.fragment:fragment-ktx:1.3.0"
implementation "androidx.fragment:fragment-ktx:1.3.1"
implementation "androidx.annotation:annotation:1.1.0"
implementation "androidx.multidex:multidex:2.0.1"
@ -169,7 +172,7 @@ dependencies {
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
implementation "com.google.android.material:material:1.3.0"
implementation "com.github.wulkanowy:material-chips-input:2.1.1"
implementation "com.github.wulkanowy:material-chips-input:2.2.0"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation 'com.mikhaellopez:circularimageview:4.2.0'
@ -203,7 +206,7 @@ dependencies {
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
playImplementation platform('com.google.firebase:firebase-bom:26.6.0')
playImplementation platform('com.google.firebase:firebase-bom:26.7.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx'
playImplementation "com.google.firebase:firebase-inappmessaging-ktx"
@ -239,6 +242,3 @@ dependencies {
androidTestImplementation "io.mockk:mockk-android:$mockk"
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
}
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.huawei.agconnect'

View File

@ -1,33 +1,21 @@
# Optimizations
-optimizationpasses 5
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
# General
-dontobfuscate
-allowaccessmodification
-repackageclasses ''
-verbose
#Keep all wulkanowy files
#Config for wulkanowy
-keep class io.github.wulkanowy.** {*;}
#Config for anallitycs
-keepattributes *Annotation*
#Config for firebase crashlitycs
-keepattributes SourceFile,LineNumberTable
-keep class com.crashlytics.** {*;}
-keep public class * extends java.lang.Exception
-dontwarn com.crashlytics.**
#Config for OkHttp
#Config for Okio and OkHttp
-dontwarn javax.annotation.**
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
-dontwarn org.codehaus.mojo.animal_sniffer.*
-dontwarn okhttp3.internal.platform.ConscryptPlatform
-dontwarn javax.annotation.**
#Config for MPAndroidChart
@ -35,11 +23,4 @@
#Config for Material Components
-keep class com.google.android.material.tabs.** { *; }
#Config for About Libraries
-keep class .R
-keep class **.R$* {
<fields>;
}
-keep class com.google.android.material.tabs.** { *; }

View File

@ -192,7 +192,7 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
}
override fun showContent(show: Boolean) {
binding. attendanceRecycler.visibility = if (show) VISIBLE else GONE
binding.attendanceRecycler.visibility = if (show) VISIBLE else GONE
}
override fun showRefresh(show: Boolean) {

View File

@ -190,35 +190,48 @@ class AttendancePresenter @Inject constructor(
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
attendanceRepository.getAttendance(student, semester, currentDate, currentDate, forceRefresh)
attendanceRepository.getAttendance(
student,
semester,
currentDate,
currentDate,
forceRefresh
)
}.onEach {
when (it.status) {
Status.LOADING -> {
view?.showExcuseButton(false)
if (!it.data.isNullOrEmpty()) {
val filteredAttendance = if (prefRepository.isShowPresent) {
it.data
} else {
it.data.filter { item -> !item.presence }
}
view?.run {
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
updateData(it.data.let { items ->
if (prefRepository.isShowPresent) items
else items.filter { item -> !item.presence }
}.sortedBy { item -> item.number })
showEmpty(filteredAttendance.isEmpty())
showContent(filteredAttendance.isNotEmpty())
updateData(filteredAttendance.sortedBy { item -> item.number })
}
}
}
Status.SUCCESS -> {
Timber.i("Loading attendance result: Success")
val filteredAttendance = if (prefRepository.isShowPresent) {
it.data.orEmpty()
} else {
it.data?.filter { item -> !item.presence }.orEmpty()
}
view?.apply {
updateData(it.data!!.let { items ->
if (prefRepository.isShowPresent) items
else items.filter { item -> !item.presence }
}.sortedBy { item -> item.number })
showEmpty(it.data.isEmpty())
updateData(filteredAttendance.sortedBy { item -> item.number })
showEmpty(filteredAttendance.isEmpty())
showErrorView(false)
showContent(it.data.isNotEmpty())
showExcuseButton(it.data.any { item -> item.excusable })
showContent(filteredAttendance.isNotEmpty())
showExcuseButton(filteredAttendance.any { item -> item.excusable })
}
analytics.logEvent(
"load_data",

View File

@ -10,6 +10,7 @@ import android.view.View.VISIBLE
import androidx.appcompat.app.AlertDialog
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.databinding.FragmentGradeBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
@ -121,11 +122,9 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
semesterSwitchMenu?.isVisible = show
}
override fun showSemesterDialog(selectedIndex: Int) {
val choices = arrayOf(
getString(R.string.grade_semester, 1),
getString(R.string.grade_semester, 2)
)
override fun showSemesterDialog(selectedIndex: Int, semesters: List<Semester>) {
val choices = semesters.map { getString(R.string.grade_semester, it.semesterName) }
.toTypedArray()
AlertDialog.Builder(requireContext())
.setSingleChoiceItems(choices, selectedIndex) { dialog, which ->

View File

@ -49,7 +49,9 @@ class GradePresenter @Inject constructor(
}
fun onSemesterSwitch(): Boolean {
if (semesters.isNotEmpty()) view?.showSemesterDialog(selectedIndex - 1)
if (semesters.isNotEmpty()) {
view?.showSemesterDialog(selectedIndex - 1, semesters.slice(0..1))
}
return true
}
@ -137,11 +139,17 @@ class GradePresenter @Inject constructor(
private fun loadChild(index: Int, forceRefresh: Boolean = false) {
Timber.d("Load grade tab child. Selected semester: $selectedIndex, semesters: ${semesters.joinToString { it.semesterName.toString() }}")
semesters.first { it.semesterName == selectedIndex }.semesterId.also {
if (forceRefresh || loadedSemesterId[index] != it) {
Timber.i("Load grade child view index: $index")
view?.notifyChildLoadData(index, it, forceRefresh)
}
val newSelectedSemesterId = try {
semesters.first { it.semesterName == selectedIndex }.semesterId
} catch (e: NoSuchElementException) {
Timber.e(e, "Selected semester no exists")
return
}
if (forceRefresh || loadedSemesterId[index] != newSelectedSemesterId) {
Timber.i("Load grade child view index: $index")
view?.notifyChildLoadData(index, newSelectedSemesterId, forceRefresh)
}
}

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.ui.base.BaseView
interface GradeView : BaseView {
@ -18,7 +19,7 @@ interface GradeView : BaseView {
fun showSemesterSwitch(show: Boolean)
fun showSemesterDialog(selectedIndex: Int)
fun showSemesterDialog(selectedIndex: Int, semesters: List<Semester>)
fun setCurrentSemesterName(semester: Int, schoolYear: Int)

View File

@ -291,7 +291,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
): Boolean {
val fragment =
supportFragmentManager.fragmentFactory.instantiate(classLoader, pref.fragment)
navController.pushFragment(fragment)
pushView(fragment)
return true
}
@ -305,6 +305,8 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
}
override fun switchMenuView(position: Int) {
if (supportFragmentManager.isStateSaved) return
analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
navController.switchTab(position)
}
@ -322,6 +324,8 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
}
override fun showAccountPicker(studentWithSemesters: List<StudentWithSemesters>) {
if (supportFragmentManager.isStateSaved) return
navController.showDialogFragment(AccountQuickDialog.newInstance(studentWithSemesters))
}
@ -339,15 +343,21 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
}
fun showDialogFragment(dialog: DialogFragment) {
if (supportFragmentManager.isStateSaved) return
navController.showDialogFragment(dialog)
}
fun pushView(fragment: Fragment) {
if (supportFragmentManager.isStateSaved) return
analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
navController.pushFragment(fragment)
}
override fun popView(depth: Int) {
if (supportFragmentManager.isStateSaved) return
analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
navController.safelyPopFragments(depth)
}

View File

@ -113,7 +113,7 @@ class MainPresenter @Inject constructor(
private fun showCurrentStudentAvatar() {
val currentStudent =
studentsWitSemesters!!.single { it.student.isCurrent }.student
studentsWitSemesters?.singleOrNull { it.student.isCurrent }?.student ?: return
view?.showStudentAvatar(currentStudent)
}

View File

@ -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 }
}
}

View File

@ -1,11 +1,6 @@
Wersja 1.1.0
Wersja 1.1.1
- dodaliśmy wyświetlanie inicjałów imienia ucznia jako awatar widoczny w aplikacji
- dodaliśmy historię szczęśliwego numerka
- dodaliśmy język słowacki
- zmieniliśmy kolor górnego i dolnego paska systemowego lepiej dostosowując je do aplikacji
- zmieniliśmy wygląd ustawień dzieląc je na sekcje
- naprawiliśmy problem dublujących się czasem ocen
- naprawiliśmy kilka innych błędów i poprawiliśmy stabilność aplikacji
- naprawiliśmy wyświetlanie planu lekcji
- naprawiliśmy kilka rzadkich problemów ze stabilnością
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases

View File

@ -192,7 +192,7 @@
</plurals>
<string name="attendance_excuse_dialog_reason">Důvod nepřítomnosti (volitelný)</string>
<string name="attendance_excuse_dialog_submit">Poslat</string>
<string name="attendance_excuse_success">Nepřítomnost úspěšně omluvena!</string>
<string name="attendance_excuse_success">Žádost o omluvu nepřítomnosti byla úspěšně odeslána!</string>
<string name="attendance_excuse_no_selection">Musíte vybrat alespoň jednu nepřítomnost!</string>
<string name="attendance_excuse_title">Ospravedlnit</string>
<!--Attendance summary-->

View File

@ -176,7 +176,7 @@
</plurals>
<string name="attendance_excuse_dialog_reason">Abwesenheitsgrund (optional)</string>
<string name="attendance_excuse_dialog_submit">Senden</string>
<string name="attendance_excuse_success">Abwesenheit erfolgreich entschuldigt!</string>
<string name="attendance_excuse_success">Absence excuse request sent successfully!</string>
<string name="attendance_excuse_no_selection">Sie müssen mindestens eine Abwesenheit auswählen!</string>
<string name="attendance_excuse_title">Verzeihung</string>
<!--Attendance summary-->

View File

@ -192,7 +192,7 @@
</plurals>
<string name="attendance_excuse_dialog_reason">Absence reason (optional)</string>
<string name="attendance_excuse_dialog_submit">Send</string>
<string name="attendance_excuse_success">Absence excused successfully!</string>
<string name="attendance_excuse_success">Absence excuse request sent successfully!</string>
<string name="attendance_excuse_no_selection">You must select at least one absence!</string>
<string name="attendance_excuse_title">Excuse</string>
<!--Attendance summary-->

View File

@ -192,7 +192,7 @@
</plurals>
<string name="attendance_excuse_dialog_reason">Powód nieobecności (opcjonalny)</string>
<string name="attendance_excuse_dialog_submit">Wyślij</string>
<string name="attendance_excuse_success">Usprawiedliwiono pomyślnie!</string>
<string name="attendance_excuse_success">Prośba o usprawiedliwienie została pomyślnie wysłana!</string>
<string name="attendance_excuse_no_selection">Musisz wybrać co najmniej jedną nieobecność!</string>
<string name="attendance_excuse_title">Usprawiedliw</string>
<!--Attendance summary-->
@ -317,7 +317,7 @@
<string name="lucky_number_history_button">Pokaż historię</string>
<!--Lucky number history-->
<string name="lucky_number_history_title">Historia numerków</string>
<string name="lucky_number_history_empty">Brak informacji o szczęśliwych numerach</string>
<string name="lucky_number_history_empty">Brak informacji o szczęśliwych numerkach</string>
<!--Mobile devices-->
<string name="mobile_devices_title">Dostęp mobilny</string>
<string name="mobile_devices_no_items">Brak urządzeń</string>

View File

@ -192,7 +192,7 @@
</plurals>
<string name="attendance_excuse_dialog_reason">Причина отсутствия (необязательно)</string>
<string name="attendance_excuse_dialog_submit">Послать</string>
<string name="attendance_excuse_success">Статус отсутствия изменён</string>
<string name="attendance_excuse_success">Absence excuse request sent successfully!</string>
<string name="attendance_excuse_no_selection">Выберите хотя-бы одно отсутствие</string>
<string name="attendance_excuse_title">Изменить статус</string>
<!--Attendance summary-->

View File

@ -192,7 +192,7 @@
</plurals>
<string name="attendance_excuse_dialog_reason">Dôvod neprítomnosti (voliteľný)</string>
<string name="attendance_excuse_dialog_submit">Poslať</string>
<string name="attendance_excuse_success">Neprítomnosť úspešne ospravedlnená!</string>
<string name="attendance_excuse_success">Žiadosť o ospravedlnenie neprítomnosti bola úspešne odoslaná!</string>
<string name="attendance_excuse_no_selection">Musíte vybrať aspoň jednu neprítomnosť!</string>
<string name="attendance_excuse_title">Ospravedlniť</string>
<!--Attendance summary-->

View File

@ -192,7 +192,7 @@
</plurals>
<string name="attendance_excuse_dialog_reason">Причина відсутності (необов’язково)</string>
<string name="attendance_excuse_dialog_submit">Надіслати</string>
<string name="attendance_excuse_success">Змінено статус відсутності</string>
<string name="attendance_excuse_success">Absence excuse request sent successfully!</string>
<string name="attendance_excuse_no_selection">Оберіть хоча б одну відсутність</string>
<string name="attendance_excuse_title">Змінити статус</string>
<!--Attendance summary-->

View File

@ -194,7 +194,7 @@
</plurals>
<string name="attendance_excuse_dialog_reason">Absence reason (optional)</string>
<string name="attendance_excuse_dialog_submit">Send</string>
<string name="attendance_excuse_success">Absence excused successfully!</string>
<string name="attendance_excuse_success">Absence excuse request sent successfully!</string>
<string name="attendance_excuse_no_selection">You must select at least one absence!</string>
<string name="attendance_excuse_title">Excuse</string>