├── .idea ├── .name ├── sonarlint │ └── issuestore │ │ └── index.pb ├── .gitignore ├── codeStyles │ └── codeStyleConfig.xml ├── easycode.ignore ├── compiler.xml ├── kotlinc.xml ├── dictionaries │ └── Piotr.xml ├── vcs.xml ├── AndroidProjectSystem.xml ├── migrations.xml ├── misc.xml ├── deploymentTargetSelector.xml ├── gradle.xml └── jarRepositories.xml ├── androidApp ├── .gitignore ├── src │ ├── main │ │ ├── ic_launcher-web.png │ │ ├── res │ │ │ ├── drawable │ │ │ │ ├── square.webp │ │ │ │ ├── circle_fill.webp │ │ │ │ ├── circle_empty.webp │ │ │ │ ├── logo_transparent.png │ │ │ │ ├── bg_in_tile_exercise_disabled.xml │ │ │ │ ├── bg_in_transparent.xml │ │ │ │ ├── bg_in_button_help.xml │ │ │ │ ├── bg_in_tile_exercise.xml │ │ │ │ ├── ic_send_48dp.xml │ │ │ │ ├── ic_add_black_24dp.xml │ │ │ │ ├── ic_check.xml │ │ │ │ ├── ic_star.xml │ │ │ │ ├── ic_star_yellow_24dp.xml │ │ │ │ ├── bg_in_tile_menu.xml │ │ │ │ ├── bg_in_tile_mode_light.xml │ │ │ │ ├── bg_in_tile_mode_dark.xml │ │ │ │ ├── bg_gradient.xml │ │ │ │ ├── ic_backspace_48dp.xml │ │ │ │ ├── bg_in_button_yes.xml │ │ │ │ ├── ic_gamepad_solid.xml │ │ │ │ ├── bg_in_button_no.xml │ │ │ │ ├── ic_dumbbell_solid.xml │ │ │ │ ├── ic_trophy_solid.xml │ │ │ │ ├── ic_settings_24dp.xml │ │ │ │ ├── progress_drawable.xml │ │ │ │ ├── ic_units.xml │ │ │ │ ├── ic_help.xml │ │ │ │ └── logo.xml │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── values │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ ├── preloaded_fonts.xml │ │ │ │ ├── attrs.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── styles.xml │ │ │ │ ├── font_certs.xml │ │ │ │ └── strings.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ └── ic_launcher.xml │ │ │ ├── font │ │ │ │ ├── fira_sans_thin.xml │ │ │ │ └── fira_sans_medium.xml │ │ │ ├── layout │ │ │ │ ├── player_row.xml │ │ │ │ ├── main_activity.xml │ │ │ │ ├── score_row.xml │ │ │ │ ├── fragment_exercise_list.xml │ │ │ │ ├── fragment_battle_game.xml │ │ │ │ ├── units_help_dialog.xml │ │ │ │ ├── fragment_scoreboard.xml │ │ │ │ ├── normal_exercise_item.xml │ │ │ │ ├── divisibility_exercise_item.xml │ │ │ │ ├── units_exercise_item.xml │ │ │ │ ├── normal_rate_dialog.xml │ │ │ │ ├── fragment_table_game.xml │ │ │ │ ├── fragment_choose_player.xml │ │ │ │ └── fragment_units_game.xml │ │ │ ├── values-cs │ │ │ │ └── strings.xml │ │ │ ├── values-sk │ │ │ │ └── strings.xml │ │ │ ├── values-pl │ │ │ │ └── strings.xml │ │ │ ├── values-hu │ │ │ │ └── strings.xml │ │ │ ├── values-nl │ │ │ │ └── strings.xml │ │ │ ├── values-pt │ │ │ │ └── strings.xml │ │ │ ├── values-de │ │ │ │ └── strings.xml │ │ │ └── values-fr │ │ │ │ └── strings.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── octbit │ │ │ │ └── rutmath │ │ │ │ ├── util │ │ │ │ ├── ViewExtension.kt │ │ │ │ └── base │ │ │ │ │ ├── DisposableViewModel.kt │ │ │ │ │ ├── BaseFragment.kt │ │ │ │ │ └── BaseApplication.kt │ │ │ │ ├── data │ │ │ │ ├── model │ │ │ │ │ ├── PlayerWithExercises.kt │ │ │ │ │ ├── Player.kt │ │ │ │ │ ├── Score.kt │ │ │ │ │ ├── Equation.kt │ │ │ │ │ ├── Settings.kt │ │ │ │ │ ├── EquationUnits.kt │ │ │ │ │ ├── Operation.kt │ │ │ │ │ └── ExerciseType.kt │ │ │ │ ├── dao │ │ │ │ │ ├── SettingsDao.kt │ │ │ │ │ ├── ScoreDao.kt │ │ │ │ │ ├── UserDao.kt │ │ │ │ │ └── ExerciseTypeDao.kt │ │ │ │ └── AppDatabase.kt │ │ │ │ ├── ui │ │ │ │ ├── fragment │ │ │ │ │ ├── scoreboard │ │ │ │ │ │ ├── ScoreboardViewHolder.kt │ │ │ │ │ │ ├── ScoreboardAdapter.kt │ │ │ │ │ │ └── ScoreboardFragment.kt │ │ │ │ │ ├── choosePlayer │ │ │ │ │ │ ├── PlayerViewHolder.kt │ │ │ │ │ │ ├── ChoosePlayerAdapter.kt │ │ │ │ │ │ └── ChoosePlayerViewModel.kt │ │ │ │ │ ├── addSubList │ │ │ │ │ │ ├── AddSubListAdapter.kt │ │ │ │ │ │ └── AddSubViewHolder.kt │ │ │ │ │ ├── mulDivList │ │ │ │ │ │ ├── MulDivListAdapter.kt │ │ │ │ │ │ └── MulDivViewHolder.kt │ │ │ │ │ ├── unitsList │ │ │ │ │ │ ├── UnitsListAdapter.kt │ │ │ │ │ │ └── UnitsViewHolder.kt │ │ │ │ │ ├── divisibilityList │ │ │ │ │ │ ├── DivisibilityListAdapter.kt │ │ │ │ │ │ └── DivisibilityViewHolder.kt │ │ │ │ │ ├── MenuFragment.kt │ │ │ │ │ └── chooseMode │ │ │ │ │ │ └── ChooseModeFragment.kt │ │ │ │ ├── view │ │ │ │ │ ├── UnitsHelpDialog.kt │ │ │ │ │ ├── NormalRateDialog.kt │ │ │ │ │ ├── TableRateDialog.kt │ │ │ │ │ └── GridSpacingItemDecoration.kt │ │ │ │ └── activity │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── di │ │ │ │ └── AppModule.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── octbit │ │ │ └── rutmath │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── octbit │ │ └── rutmath │ │ └── ExampleInstrumentedTest.kt └── build.gradle ├── fastlane └── metadata │ └── android │ ├── en-US │ ├── title.txt │ ├── short_description.txt │ ├── images │ │ ├── icon.png │ │ └── phoneScreenshots │ │ │ ├── 1.jpg │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ └── 4.png │ └── full_description.txt │ ├── pl-PL │ ├── title.txt │ ├── short_description.txt │ └── full_description.txt │ └── pt-BR │ ├── title.txt │ ├── short_description.txt │ └── full_description.txt ├── .gitattributes ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── iosApp ├── iosApp │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── iOSApp.swift │ ├── Info.plist │ └── ContentView.swift ├── Configuration │ └── Config.xcconfig └── iosApp.xcodeproj │ └── project.xcworkspace │ └── contents.xcworkspacedata ├── shared ├── src │ ├── commonMain │ │ └── kotlin │ │ │ └── com │ │ │ └── octbit │ │ │ └── rutmath │ │ │ └── shared │ │ │ ├── model │ │ │ ├── Player.kt │ │ │ ├── Score.kt │ │ │ ├── Equation.kt │ │ │ ├── Operation.kt │ │ │ ├── Settings.kt │ │ │ ├── EquationUnits.kt │ │ │ └── ExerciseType.kt │ │ │ ├── di │ │ │ └── SharedModule.kt │ │ │ ├── localization │ │ │ └── StringProvider.kt │ │ │ ├── data │ │ │ └── DatabaseRepository.kt │ │ │ └── game │ │ │ └── EquationGenerator.kt │ ├── iosMain │ │ └── kotlin │ │ │ └── com │ │ │ └── octbit │ │ │ └── rutmath │ │ │ └── shared │ │ │ ├── di │ │ │ └── IosSharedModule.kt │ │ │ └── localization │ │ │ └── IosStringProvider.kt │ └── androidMain │ │ └── kotlin │ │ └── com │ │ └── octbit │ │ └── rutmath │ │ └── shared │ │ └── di │ │ └── AndroidSharedModule.kt └── build.gradle ├── .gitignore ├── metadata └── com.octbit.rutmath.yml ├── gradle.properties ├── .github └── workflows │ ├── test.yml │ ├── build.yml │ └── release.yml ├── README.md └── gradlew.bat /.idea/.name: -------------------------------------------------------------------------------- 1 | RutMath -------------------------------------------------------------------------------- /androidApp/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /.idea/sonarlint/issuestore/index.pb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | RUTMath -------------------------------------------------------------------------------- /fastlane/metadata/android/pl-PL/title.txt: -------------------------------------------------------------------------------- 1 | RUTMath -------------------------------------------------------------------------------- /fastlane/metadata/android/pt-BR/title.txt: -------------------------------------------------------------------------------- 1 | RUTMath 2 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | A math learning app for children. -------------------------------------------------------------------------------- /fastlane/metadata/android/pl-PL/short_description.txt: -------------------------------------------------------------------------------- 1 | Aplikacja do nauki matematyki dla dzieci. -------------------------------------------------------------------------------- /fastlane/metadata/android/pt-BR/short_description.txt: -------------------------------------------------------------------------------- 1 | Um aplicativo matemático para crianças. 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':androidApp' 2 | include ':shared' 3 | rootProject.name='RutMath' 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /androidApp/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /iosApp/iosApp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/square.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/drawable/square.webp -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/circle_fill.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/drawable/circle_fill.webp -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/circle_empty.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/drawable/circle_empty.webp -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/logo_transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/drawable/logo_transparent.png -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/przemarbor/RUTMath/HEAD/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /androidApp/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFF2FF 4 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/easycode.ignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | node_modules/ 4 | dist/ 5 | vendor/ 6 | cache/ 7 | .*/ 8 | *.min.* 9 | *.test.* 10 | *.spec.* 11 | *.bundle.* 12 | *.bundle-min.* 13 | *.log 14 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /iosApp/iosApp/iOSApp.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @main 4 | struct iOSApp: App { 5 | var body: some Scene { 6 | WindowGroup { 7 | ContentView() 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /iosApp/Configuration/Config.xcconfig: -------------------------------------------------------------------------------- 1 | TEAM_ID= 2 | 3 | PRODUCT_NAME=KotlinProject 4 | PRODUCT_BUNDLE_IDENTIFIER=org.example.project.KotlinProject$(TEAM_ID) 5 | 6 | CURRENT_PROJECT_VERSION=1 7 | MARKETING_VERSION=1.0 -------------------------------------------------------------------------------- /iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/dictionaries/Piotr.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | additionandsubtraction 5 | octbit 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/AndroidProjectSystem.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/bg_in_tile_exercise_disabled.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /androidApp/src/main/res/values/preloaded_fonts.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @font/fira_sans_medium 5 | @font/fira_sans_thin 6 | 7 | 8 | -------------------------------------------------------------------------------- /iosApp/iosApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CADisableMinimumFrameDurationOnPhone 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/bg_in_transparent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/util/ViewExtension.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.util 2 | 3 | import android.view.View 4 | 5 | fun View.gone() { 6 | visibility = View.GONE 7 | } 8 | 9 | fun View.visible() { 10 | visibility = View.VISIBLE 11 | } 12 | 13 | fun View.invisible() { 14 | visibility = View.INVISIBLE 15 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/bg_in_button_help.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/bg_in_tile_exercise.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/ic_send_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/data/model/PlayerWithExercises.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.data.model 2 | 3 | import androidx.room.Embedded 4 | import androidx.room.Relation 5 | 6 | data class PlayerWithExercises( 7 | @Embedded val player: Player, 8 | @Relation( 9 | parentColumn = "nick", 10 | entityColumn = "userNick" 11 | ) 12 | val scores: List 13 | ) -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/ic_add_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/ic_check.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/data/model/Player.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.data.model 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import java.io.Serializable 7 | 8 | @Entity(tableName = "Player") 9 | data class Player( 10 | @PrimaryKey(autoGenerate = false) 11 | @ColumnInfo(name = "nick") 12 | val nick: String 13 | ) : Serializable -------------------------------------------------------------------------------- /shared/src/commonMain/kotlin/com/octbit/rutmath/shared/model/Player.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * Player model representing a game player. 7 | * 8 | * @param nickname player's nickname 9 | * @param id unique identifier for the player 10 | */ 11 | @Serializable 12 | data class Player( 13 | val nickname: String, 14 | val id: Int = 0 15 | ) -------------------------------------------------------------------------------- /androidApp/src/main/res/font/fira_sans_thin.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /androidApp/src/main/res/font/fira_sans_medium.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/ic_star.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/ic_star_yellow_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /androidApp/src/test/java/com/octbit/rutmath/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/bg_in_tile_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/bg_in_tile_mode_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/data/model/Score.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.data.model 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import java.io.Serializable 7 | 8 | @Entity(tableName = "Score") 9 | data class Score( 10 | @ColumnInfo(name = "nick") 11 | val nick: String, 12 | @ColumnInfo(name = "score") 13 | val score: Int, 14 | @PrimaryKey(autoGenerate = true) 15 | var id: Int = 0 16 | ) : Serializable -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/bg_in_tile_mode_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/bg_gradient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /shared/src/commonMain/kotlin/com/octbit/rutmath/shared/model/Score.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * Score model representing a player's score in a game. 7 | * 8 | * @param playerName name of the player who achieved this score 9 | * @param score the score value achieved 10 | * @param id unique identifier for the score record 11 | */ 12 | @Serializable 13 | data class Score( 14 | val playerName: String, 15 | val score: Int, 16 | val id: Int = 0 17 | ) -------------------------------------------------------------------------------- /androidApp/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/ic_backspace_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/data/model/Equation.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.data.model 2 | 3 | /** 4 | * Equation model. 5 | * 6 | * @param componentA value A 7 | * @param componentB value B 8 | * @param operation mathematical representation of operation. 9 | * @param correctAnswer answer of whole equation. 10 | * 11 | * For example: (5+2) <=> (componentA = 5, componentB = 2, operation = PLUS, correctAnswer = 7) 12 | * 13 | */ 14 | 15 | data class Equation( 16 | val componentA: Int, 17 | val componentB: Int, 18 | val operation: Operation, 19 | val correctAnswer: Int 20 | ) 21 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/data/model/Settings.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.data.model 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity(tableName = "Settings") 8 | data class Settings( 9 | @PrimaryKey(autoGenerate = true) 10 | var id: Int = 0, 11 | @ColumnInfo(name = "maxNumberInBattleMode") 12 | var maxNumberInBattleMode: Int, 13 | @ColumnInfo(name = "language") 14 | var language: String, 15 | @ColumnInfo(name = "lastNickname1") 16 | var lastNickname1: String, 17 | @ColumnInfo(name = "lastNickname2") 18 | var lastNickname2: String 19 | ) -------------------------------------------------------------------------------- /shared/src/commonMain/kotlin/com/octbit/rutmath/shared/model/Equation.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * Equation model. 7 | * 8 | * @param componentA value A 9 | * @param componentB value B 10 | * @param operation mathematical representation of operation. 11 | * @param correctAnswer answer of whole equation. 12 | * 13 | * For example: (5+2) <=> (componentA = 5, componentB = 2, operation = PLUS, correctAnswer = 7) 14 | */ 15 | @Serializable 16 | data class Equation( 17 | val componentA: Int, 18 | val componentB: Int, 19 | val operation: Operation, 20 | val correctAnswer: Int 21 | ) -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/scoreboard/ScoreboardViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.scoreboard 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import com.octbit.rutmath.R 5 | import com.octbit.rutmath.data.model.Score 6 | import com.octbit.rutmath.databinding.ScoreRowBinding 7 | 8 | class ScoreboardViewHolder( 9 | private val binding: ScoreRowBinding 10 | ) : RecyclerView.ViewHolder(binding.root) { 11 | 12 | fun bind(position: Int, score: Score) = with(binding) { 13 | nick.text = root.context.getString(R.string.scoreboard_format, position, score.nick) 14 | binding.score.text = score.score.toString() 15 | } 16 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/bg_in_button_yes.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/ic_gamepad_solid.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/bg_in_button_no.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /shared/src/commonMain/kotlin/com/octbit/rutmath/shared/model/Operation.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * Types of operations that can exists in exercises. 7 | */ 8 | @Serializable 9 | enum class Operation { 10 | PLUS, 11 | MINUS, 12 | PLUS_MINUS, 13 | MULTIPLY, 14 | DIVIDE, 15 | MULTIPLY_DIVIDE, 16 | DIVISIBILITY, 17 | UNITS_TIME, 18 | UNITS_LENGTH, 19 | UNITS_WEIGHT, 20 | UNITS_SURFACE, 21 | UNITS_ALL, 22 | // Adding negative plus and negative minus 23 | NEGATIVE_PLUS, 24 | NEGATIVE_MINUS, 25 | NEGATIVE_PLUS_MINUS, 26 | NEGATIVE_MUL, 27 | NEGATIVE_DIV, 28 | NEGATIVE_MUL_DIV 29 | } -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/data/dao/SettingsDao.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.data.dao 2 | 3 | import androidx.room.* 4 | import com.octbit.rutmath.data.model.Settings 5 | import io.reactivex.Completable 6 | import io.reactivex.Single 7 | 8 | @Dao 9 | interface SettingsDao { 10 | @Query("SELECT * FROM Settings LIMIT 1") 11 | fun getAll(): Single> 12 | 13 | @Query("SELECT * FROM Settings WHERE id LIKE :id") 14 | fun findById(id: String): Single 15 | 16 | @Insert 17 | fun insertAll(exerciseTypes: List): Completable 18 | 19 | @Delete 20 | fun delete(exerciseType: Settings): Completable 21 | 22 | @Update 23 | fun update(exerciseType: Settings): Completable 24 | } -------------------------------------------------------------------------------- /shared/src/commonMain/kotlin/com/octbit/rutmath/shared/model/Settings.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * Settings model for application configuration. 7 | * 8 | * @param maxNumberInBattleMode maximum number that can appear in battle mode equations 9 | * @param lastNickname1 last used nickname for player 1 10 | * @param lastNickname2 last used nickname for player 2 11 | * @param language application language code (e.g., "en", "pl", "fr", "pt") 12 | */ 13 | @Serializable 14 | data class Settings( 15 | val maxNumberInBattleMode: Int = 100, 16 | val lastNickname1: String = "Player 1", 17 | val lastNickname2: String = "Player 2", 18 | val language: String = "en" 19 | ) -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/data/dao/ScoreDao.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.data.dao 2 | 3 | import androidx.room.* 4 | import com.octbit.rutmath.data.model.Score 5 | import io.reactivex.Completable 6 | import io.reactivex.Single 7 | 8 | @Dao 9 | interface ScoreDao { 10 | @Query("SELECT * FROM Score") 11 | fun getAll(): Single> 12 | 13 | @Query("SELECT * FROM Score WHERE id LIKE :id") 14 | fun findById(id: String): Single 15 | 16 | @Insert 17 | fun insertAll(scores: List): Completable 18 | 19 | @Insert 20 | fun insert(score: Score): Completable 21 | 22 | @Delete 23 | fun delete(score: Score): Completable 24 | 25 | @Update 26 | fun update(score: Score): Completable 27 | } -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | RUTMath is an interactive educational app that helps school-aged children learn math. The application offers many different levels of difficulty to adjust to the user's skill level, and provides exercises and games that help with learning basic math operations such as addition, subtraction, multiplication, and division. Additionally, the application allows to learn how to convert units, such as converting meters to centimeters, kilograms to grams, etc. There is also the possibility of learning multiplication tables and divisibility of numbers, which are very important for further mathematical education. Thus, the application is an excellent tool for teachers and parents who want to help children learn math in a fun and effective way. -------------------------------------------------------------------------------- /fastlane/metadata/android/pl-PL/full_description.txt: -------------------------------------------------------------------------------- 1 | RUTMath to interaktywna aplikacja edukacyjna, która pomaga dzieciom w wieku szkolnym w nauce matematyki. Oferuje ona wiele poziomów trudności, aby dostosować się do poziomu umiejętności użytkownika, a także zapewnia ćwiczenia i gry, które pomagają w nauce podstawowych działań matematycznych, takich jak dodawanie, odejmowanie, mnożenie i dzielenie. Ponadto, w aplikacji można nauczyć się zamieniać jednostki, czyli przeliczać metry na centymetry, kilogramy na gramy itp. Istnieje również możliwość nauki tabliczki mnożenia oraz podzielności liczb, które są bardzo ważne dla dalszej edukacji matematycznej. Dzięki temu aplikacja jest doskonałym narzędziem dla nauczycieli i rodziców, którzy chcą pomóc dzieciom w nauce matematyki w sposób przyjemny i efektywny. -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/choosePlayer/PlayerViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.choosePlayer 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import com.octbit.rutmath.R 5 | import com.octbit.rutmath.data.model.Player 6 | import com.octbit.rutmath.databinding.PlayerRowBinding 7 | 8 | class PlayerViewHolder( 9 | private val binding: PlayerRowBinding, 10 | private val onItemClickedListener: (Player) -> Unit 11 | ) : RecyclerView.ViewHolder(binding.root) { 12 | 13 | fun bind(position: Int, player: Player) = with(binding){ 14 | root.setOnClickListener { 15 | onItemClickedListener.invoke(player) 16 | } 17 | nick.text = root.context.getString(R.string.scoreboard_format, position, player.nick) 18 | } 19 | } -------------------------------------------------------------------------------- /shared/src/iosMain/kotlin/com/octbit/rutmath/shared/di/IosSharedModule.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.di 2 | 3 | import com.octbit.rutmath.shared.data.DatabaseRepository 4 | import com.octbit.rutmath.shared.data.IosDatabaseRepository 5 | import com.octbit.rutmath.shared.localization.IosStringProvider 6 | import com.octbit.rutmath.shared.localization.StringProvider 7 | import org.koin.dsl.module 8 | 9 | /** 10 | * iOS-specific shared module containing iOS implementations. 11 | */ 12 | val iosSharedModule = module { 13 | 14 | // Database Repository - iOS implementation using UserDefaults 15 | single { IosDatabaseRepository() } 16 | 17 | // String Provider - iOS implementation using NSLocalizedString 18 | single { IosStringProvider() } 19 | 20 | } -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "idiom" : "universal", 16 | "platform" : "ios", 17 | "size" : "1024x1024" 18 | }, 19 | { 20 | "appearances" : [ 21 | { 22 | "appearance" : "luminosity", 23 | "value" : "tinted" 24 | } 25 | ], 26 | "idiom" : "universal", 27 | "platform" : "ios", 28 | "size" : "1024x1024" 29 | } 30 | ], 31 | "info" : { 32 | "author" : "xcode", 33 | "version" : 1 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /androidApp/src/androidTest/java/com/octbit/rutmath/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.octbit.rutmath", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /shared/src/commonMain/kotlin/com/octbit/rutmath/shared/di/SharedModule.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.di 2 | 3 | import com.octbit.rutmath.shared.game.EquationGenerator 4 | import com.octbit.rutmath.shared.game.MathEquationGenerator 5 | import com.octbit.rutmath.shared.usecase.GameUseCase 6 | import com.octbit.rutmath.shared.usecase.UnitsGameUseCase 7 | import com.octbit.rutmath.shared.usecase.DataUseCase 8 | import org.koin.dsl.module 9 | 10 | /** 11 | * Shared Koin module containing platform-independent dependencies. 12 | */ 13 | val sharedModule = module { 14 | 15 | // Equation Generation 16 | single { MathEquationGenerator() } 17 | 18 | // Use Cases 19 | single { GameUseCase(get()) } 20 | single { UnitsGameUseCase(get()) } 21 | single { DataUseCase(get()) } 22 | 23 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/player_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 21 | -------------------------------------------------------------------------------- /fastlane/metadata/android/pt-BR/full_description.txt: -------------------------------------------------------------------------------- 1 | RUTMath é um aplicativo educacional interativo que auxilia crianças em idade escolar a aprender matemática. O aplicativo oferece vários níveis diferentes de dificuldade para se adequar ao nível de habilidade do usuário e disponibiliza exercícios e jogos que ajudam no aprendizado de operações matemáticas básicas, como adição, subtração, multiplicação e divisão. Além disso, o aplicativo permite aprender como converter unidades, como transformar metros em centímetros, quilogramas em gramas, etc. Há também a possibilidade de aprender as tabuadas de multiplicação e a divisibilidade dos números, que são muito importantes para a educação matemática posterior. Assim, o aplicativo é uma excelente ferramenta para professores e pais que desejam ajudar as crianças a aprender matemática de forma divertida e eficaz. 2 | -------------------------------------------------------------------------------- /shared/src/androidMain/kotlin/com/octbit/rutmath/shared/di/AndroidSharedModule.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.di 2 | 3 | import com.octbit.rutmath.shared.data.AndroidDatabaseRepository 4 | import com.octbit.rutmath.shared.data.DatabaseRepository 5 | import com.octbit.rutmath.shared.localization.AndroidStringProvider 6 | import com.octbit.rutmath.shared.localization.StringProvider 7 | import org.koin.dsl.module 8 | 9 | /** 10 | * Android-specific shared module containing Android implementations. 11 | */ 12 | val androidSharedModule = module { 13 | 14 | // Database Repository - Android implementation using Room 15 | single { AndroidDatabaseRepository(get()) } 16 | 17 | // String Provider - Android implementation using resources 18 | single { AndroidStringProvider(get()) } 19 | 20 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #CBCBCB 4 | #4F585A 5 | 6 | #F8A95D 7 | #e79b54 8 | #d59559 9 | #cc8d52 10 | #F69A3D 11 | #FC943C 12 | #e4d2c1 13 | #98A8CEE8 14 | 15 | #66000000 16 | #ffffff 17 | #E83030 18 | #22A322 19 | #4D788183 20 | 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | .idea/deploymentTargetDropDown.xml 16 | .idea/misc.xml 17 | .idea/runConfigurations.xml 18 | 19 | # Kotlin Multiplatform 20 | **/build/ 21 | kotlin-js-store/ 22 | node_modules/ 23 | 24 | # Android 25 | *.apk 26 | *.ap_ 27 | *.dex 28 | *.class 29 | local.properties 30 | *.log 31 | release/ 32 | 33 | # iOS 34 | *.xcuserstate 35 | *.xcworkspace/xcuserdata/ 36 | *.xcodeproj/xcuserdata/ 37 | *.xcodeproj/project.xcworkspace/xcuserdata/ 38 | DerivedData/ 39 | build/ 40 | *.ipa 41 | 42 | # Fastlane 43 | fastlane/report.xml 44 | fastlane/Preview.html 45 | fastlane/screenshots 46 | fastlane/test_output 47 | 48 | # Emacs aux 49 | *~ 50 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/data/model/EquationUnits.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.data.model 2 | 3 | /** 4 | * EquationUnits model. 5 | * 6 | * @param componentA value to convert 7 | * @param componentAUnitId ID of the unit of the componentA value 8 | * @param operation mathematical representation of units exchange operation. 9 | * @param correctAnswer result of exchange. 10 | * @param answerUnitId ID of the unit of the correctAnswer value 11 | * 12 | * For example: 13 | * (5 meters = 50 decimeters) 14 | * <=> 15 | * (componentA = 5, 16 | * componentAUnitId = 1 (m), 17 | * correctAnswer = 50, 18 | * answerUnitId = 2 (dm), 19 | * operation = Operation.UnitsLength) 20 | */ 21 | 22 | data class EquationUnits( 23 | val componentA: Int, 24 | val componentAUnitId: Int, 25 | val operation: Operation, 26 | val correctAnswer: Int, 27 | val answerUnitId: Int 28 | ) -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/data/dao/UserDao.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.data.dao 2 | 3 | import androidx.room.* 4 | import com.octbit.rutmath.data.model.Player 5 | import com.octbit.rutmath.data.model.PlayerWithExercises 6 | import io.reactivex.Completable 7 | import io.reactivex.Single 8 | 9 | @Dao 10 | interface UserDao { 11 | @Query("SELECT * FROM Player") 12 | fun getAll(): Single> 13 | 14 | @Query("SELECT * FROM Player WHERE nick LIKE :nick") 15 | fun findByNick(nick: String): Single 16 | 17 | @Insert 18 | fun insert(player: Player): Completable 19 | 20 | @Transaction 21 | @Query("SELECT * FROM Player WHERE nick LIKE :nick") 22 | fun getPlayerWithScores(nick: String): Single> 23 | 24 | @Delete 25 | fun delete(player: Player): Completable 26 | 27 | @Update 28 | fun update(player: Player): Completable 29 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/ic_dumbbell_solid.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/main_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 20 | 21 | -------------------------------------------------------------------------------- /shared/src/commonMain/kotlin/com/octbit/rutmath/shared/model/EquationUnits.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * EquationUnits model. 7 | * 8 | * @param componentA value to convert 9 | * @param componentAUnitId ID of the unit of the componentA value 10 | * @param operation mathematical representation of units exchange operation. 11 | * @param correctAnswer result of exchange. 12 | * @param answerUnitId ID of the unit of the correctAnswer value 13 | * 14 | * For example: 15 | * (5 meters = 50 decimeters) 16 | * <=> 17 | * (componentA = 5, 18 | * componentAUnitId = 1 (m), 19 | * correctAnswer = 50, 20 | * answerUnitId = 2 (dm), 21 | * operation = Operation.UnitsLength) 22 | */ 23 | @Serializable 24 | data class EquationUnits( 25 | val componentA: Int, 26 | val componentAUnitId: Int, 27 | val operation: Operation, 28 | val correctAnswer: Int, 29 | val answerUnitId: Int 30 | ) -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/util/base/DisposableViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.util.base 2 | 3 | import androidx.lifecycle.ViewModel 4 | import io.reactivex.disposables.CompositeDisposable 5 | import io.reactivex.disposables.Disposable 6 | 7 | /** 8 | * ViewModel that have auto disposable mechanism. 9 | * Every disposable that should be disposed in end of ViewModel cycle of life should 10 | * be added in manageDisposable block of code. 11 | * 12 | * ViewModel with autoDisposable mechanism for all Disposable objects that will be 13 | * created in the code block using manageDisposable() 14 | */ 15 | open class DisposableViewModel : ViewModel() { 16 | 17 | private val compositeDisposable = CompositeDisposable() 18 | 19 | fun manageDisposable(factory: () -> Disposable) { 20 | compositeDisposable.add(factory.invoke()) 21 | } 22 | 23 | override fun onCleared() { 24 | compositeDisposable.dispose() 25 | super.onCleared() 26 | } 27 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/ic_trophy_solid.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/ic_settings_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/view/UnitsHelpDialog.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.view 2 | 3 | import android.app.Dialog 4 | import android.content.Context 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.Window 8 | import com.octbit.rutmath.databinding.UnitsHelpDialogBinding 9 | 10 | 11 | /** 12 | * Dialog after clicking help in UnitsGame 13 | * 14 | */ 15 | class UnitsHelpDialog(context: Context, private val text: String) : Dialog(context) { 16 | 17 | init { 18 | setCancelable(false) 19 | } 20 | 21 | override fun onCreate(savedInstanceState: Bundle?) { 22 | super.onCreate(savedInstanceState) 23 | val binding = UnitsHelpDialogBinding.inflate(LayoutInflater.from(context)) 24 | requestWindowFeature(Window.FEATURE_NO_TITLE) 25 | setContentView(binding.root) 26 | binding.helpText.text = text 27 | binding.okButton.setOnClickListener { 28 | dismiss() 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /metadata/com.octbit.rutmath.yml: -------------------------------------------------------------------------------- 1 | Categories: 2 | - Games 3 | - Science & Education 4 | License: GPL-3.0-or-later 5 | AuthorName: RUTTeam 6 | AuthorEmail: marbor@prz.edu.pl 7 | WebSite: https://github.com/przemarbor/RUTMath 8 | SourceCode: https://github.com/przemarbor/RUTMath 9 | IssueTracker: https://github.com/przemarbor/RUTMath/issues 10 | Changelog: https://github.com/przemarbor/RUTMath/releases 11 | 12 | AutoName: RUTMath 13 | Name: RUTMath 14 | 15 | RepoType: git 16 | Repo: https://github.com/przemarbor/RUTMath.git 17 | 18 | Builds: 19 | - versionName: 0.2.5 20 | versionCode: 10 21 | commit: v0.2.5 22 | subdir: androidApp 23 | gradle: 24 | - release 25 | prebuild: echo 'org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8' >> ../gradle.properties 26 | 27 | MaintainerNotes: | 28 | This is a Kotlin Multiplatform Mobile project. 29 | The Android app is built from the androidApp subdirectory. 30 | 31 | AutoUpdateMode: Version 32 | UpdateCheckMode: Tags 33 | CurrentVersion: 0.2.5 34 | CurrentVersionCode: 10 35 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/progress_drawable.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /iosApp/iosApp/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import Shared 3 | 4 | struct ContentView: View { 5 | @State private var showContent = false 6 | var body: some View { 7 | VStack { 8 | Button("Click me!") { 9 | withAnimation { 10 | showContent = !showContent 11 | } 12 | } 13 | 14 | if showContent { 15 | VStack(spacing: 16) { 16 | Image(systemName: "swift") 17 | .font(.system(size: 200)) 18 | .foregroundColor(.accentColor) 19 | Text("SwiftUI: \(Greeting().greet())") 20 | } 21 | .transition(.move(edge: .top).combined(with: .opacity)) 22 | } 23 | } 24 | .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) 25 | .padding() 26 | } 27 | } 28 | 29 | struct ContentView_Previews: PreviewProvider { 30 | static var previews: some View { 31 | ContentView() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/scoreboard/ScoreboardAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.scoreboard 2 | 3 | import android.annotation.SuppressLint 4 | import android.view.LayoutInflater 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.octbit.rutmath.data.model.Score 8 | import com.octbit.rutmath.databinding.ScoreRowBinding 9 | 10 | class ScoreboardAdapter(private val scoresList: List) : 11 | RecyclerView.Adapter() { 12 | 13 | @SuppressLint("InflateParams") 14 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ScoreboardViewHolder { 15 | val binding = ScoreRowBinding.inflate(LayoutInflater.from(parent.context), parent, false) 16 | return ScoreboardViewHolder(binding) 17 | } 18 | 19 | override fun getItemCount(): Int = scoresList.size 20 | 21 | override fun onBindViewHolder(holder: ScoreboardViewHolder, position: Int) { 22 | holder.bind(position + 1, scoresList[position]) 23 | } 24 | } -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/addSubList/AddSubListAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.addSubList 2 | 3 | import android.annotation.SuppressLint 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.RecyclerView 6 | import android.view.LayoutInflater 7 | import com.octbit.rutmath.data.model.ExerciseType 8 | import com.octbit.rutmath.databinding.NormalExerciseItemBinding 9 | 10 | /** 11 | * Adapter that contains exercise list. 12 | * 13 | */ 14 | class AddSubListAdapter( 15 | private val exercises: ArrayList, 16 | private val clickCallback: (exerciseType: ExerciseType) -> Unit, 17 | ) : RecyclerView.Adapter() { 18 | 19 | @SuppressLint("InflateParams") 20 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddSubViewHolder { 21 | val binding = NormalExerciseItemBinding.inflate(LayoutInflater.from(parent.context)) 22 | return AddSubViewHolder(binding, clickCallback) 23 | } 24 | override fun getItemCount(): Int = exercises.size 25 | 26 | override fun onBindViewHolder(holder: AddSubViewHolder, position: Int) { 27 | holder.bind(exercises[position]) 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/mulDivList/MulDivListAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.mulDivList 2 | 3 | import android.annotation.SuppressLint 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.RecyclerView 6 | import android.view.LayoutInflater 7 | import com.octbit.rutmath.data.model.ExerciseType 8 | import com.octbit.rutmath.databinding.NormalExerciseItemBinding 9 | 10 | /** 11 | * Adapter that contains exercise list. 12 | * 13 | */ 14 | class MulDivListAdapter( 15 | private val exercises: ArrayList, 16 | private val clickCallback: (exerciseType: ExerciseType) -> Unit 17 | ) : RecyclerView.Adapter() { 18 | 19 | @SuppressLint("InflateParams") 20 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MulDivViewHolder { 21 | val binding = NormalExerciseItemBinding.inflate(LayoutInflater.from(parent.context)) 22 | return MulDivViewHolder(binding, clickCallback) 23 | } 24 | 25 | override fun getItemCount(): Int = exercises.size 26 | 27 | override fun onBindViewHolder(holder: MulDivViewHolder, position: Int) { 28 | holder.bind(exercises[position]) 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/unitsList/UnitsListAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.unitsList 2 | 3 | import android.annotation.SuppressLint 4 | import android.view.LayoutInflater 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.octbit.rutmath.data.model.ExerciseType 8 | import com.octbit.rutmath.databinding.UnitsExerciseItemBinding 9 | 10 | /** 11 | * Adapter that contains units exercise list. 12 | * 13 | */ 14 | class UnitsListAdapter( 15 | private val exercises: ArrayList, 16 | private val clickCallback: (exerciseType: ExerciseType) -> Unit 17 | ) : RecyclerView.Adapter() { 18 | 19 | @SuppressLint("InflateParams") 20 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UnitsViewHolder { 21 | val binding = UnitsExerciseItemBinding.inflate(LayoutInflater.from(parent.context)) 22 | return UnitsViewHolder(binding, clickCallback) 23 | } 24 | 25 | override fun getItemCount(): Int = exercises.size 26 | 27 | override fun onBindViewHolder(holder: UnitsViewHolder, position: Int) { 28 | holder.bind(exercises[position]) 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/view/NormalRateDialog.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.view 2 | 3 | import android.app.Dialog 4 | import android.content.Context 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.Window 8 | import com.octbit.rutmath.databinding.NormalRateDialogBinding 9 | import com.octbit.rutmath.util.visible 10 | 11 | /** 12 | * Dialog with stars after finishing NormalGame 13 | * 14 | */ 15 | class NormalRateDialog(context: Context, private val rate: Int) : Dialog(context) { 16 | 17 | init { 18 | setCancelable(false) 19 | } 20 | 21 | override fun onCreate(savedInstanceState: Bundle?) { 22 | super.onCreate(savedInstanceState) 23 | val binding = NormalRateDialogBinding.inflate(LayoutInflater.from(context)) 24 | requestWindowFeature(Window.FEATURE_NO_TITLE) 25 | setContentView(binding.root) 26 | 27 | val stars = arrayOf(binding.star1, binding.star2, binding.star3, binding.star4, binding.star5) 28 | for (index in 0 until rate) { 29 | stars[index].visible() 30 | } 31 | binding.okButton.setOnClickListener { 32 | dismiss() 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/score_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 21 | 31 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/divisibilityList/DivisibilityListAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.divisibilityList 2 | 3 | import android.annotation.SuppressLint 4 | import android.view.LayoutInflater 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.octbit.rutmath.data.model.ExerciseType 8 | import com.octbit.rutmath.databinding.DivisibilityExerciseItemBinding 9 | 10 | /** 11 | * Adapter that contains divisibility exercise list. 12 | * 13 | */ 14 | class DivisibilityListAdapter( 15 | private val exercises: ArrayList, 16 | private val clickCallback: (exerciseType: ExerciseType) -> Unit 17 | ) : RecyclerView.Adapter() { 18 | 19 | @SuppressLint("InflateParams") 20 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DivisibilityViewHolder { 21 | val binding = DivisibilityExerciseItemBinding.inflate(LayoutInflater.from(parent.context)) 22 | return DivisibilityViewHolder(binding, clickCallback) 23 | } 24 | 25 | override fun getItemCount(): Int = exercises.size 26 | 27 | override fun onBindViewHolder(holder: DivisibilityViewHolder, position: Int) { 28 | holder.bind(exercises[position]) 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/fragment_exercise_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 26 | 27 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/view/TableRateDialog.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.view 2 | 3 | import android.app.Dialog 4 | import android.content.Context 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.Window 8 | import com.octbit.rutmath.databinding.TableRateDialogBinding 9 | import com.octbit.rutmath.util.visible 10 | 11 | /** 12 | * Dialog with stars after finishing TableGame 13 | * 14 | */ 15 | class TableRateDialog(context: Context, private val resultPercentage: Int) : Dialog(context) { 16 | 17 | init { 18 | setCancelable(false) 19 | } 20 | 21 | override fun onCreate(savedInstanceState: Bundle?) { 22 | super.onCreate(savedInstanceState) 23 | val binding = TableRateDialogBinding.inflate(LayoutInflater.from(context)) 24 | 25 | requestWindowFeature(Window.FEATURE_NO_TITLE) 26 | setContentView(binding.root) 27 | 28 | val stars = arrayOf(binding.star1, binding.star2, binding.star3, binding.star4, binding.star5) 29 | for (index in 0 until resultPercentage/20) { 30 | stars[index].visible() 31 | } 32 | binding.scoreText.text = " $resultPercentage%" 33 | binding.okButton.setOnClickListener { 34 | dismiss() 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/data/dao/ExerciseTypeDao.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.data.dao 2 | 3 | import androidx.room.* 4 | import com.octbit.rutmath.data.model.ExerciseType 5 | import com.octbit.rutmath.data.model.Operation 6 | import io.reactivex.Completable 7 | import io.reactivex.Single 8 | 9 | @Dao 10 | interface ExerciseTypeDao { 11 | @Query("SELECT * FROM ExerciseType WHERE userNick LIKE :nick") 12 | fun getAll(nick: String): Single> 13 | 14 | @Query("SELECT * FROM ExerciseType WHERE userNick LIKE :nick AND operation IN (:operations)") 15 | fun getAll(nick: String, operations: List): Single> 16 | 17 | @Query("SELECT * FROM ExerciseType WHERE id LIKE :id") 18 | fun findById(id: String): Single 19 | 20 | @Query("SELECT * FROM ExerciseType WHERE userNick LIKE :nick AND operation LIKE :operation AND difficulty > :prevDifficulty ORDER BY difficulty LIMIT 1") 21 | fun findExerciseType(nick: String, operation:Operation, prevDifficulty: Int): Single 22 | 23 | @Insert 24 | fun insertAll(exerciseTypes: List): Completable 25 | 26 | @Delete 27 | fun delete(exerciseType: ExerciseType): Completable 28 | 29 | @Update 30 | fun update(exerciseType: ExerciseType): Completable 31 | } -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /shared/src/commonMain/kotlin/com/octbit/rutmath/shared/model/ExerciseType.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.model 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | /** 6 | * ExerciseType is a data model representing a type of mathematical exercise. 7 | * 8 | * @param operation type of mathematical operation 9 | * @param difficulty modifies difficulty of the Exercise. For example: when difficulty equals to 10 10 | * and operation equals to Operation.PLUS then user will see only exercises with answer 11 | * that is equal or less than 10. 12 | * Every operation has specific difficulty range. 13 | * For example: 14 | * Operation.PLUS can use difficulty range: (5-200). 15 | * Operation.DIVISIBILITY can use difficulty range: (1-10). 16 | * Check specific GameViewModel for specific difficulty range. 17 | * @param rate number of stars reached in this type of exercises. 18 | * @param userNick nickname of the user who played this exercise type 19 | * @param isUnlocked whether exercise is unlocked and can be accessed by the user. 20 | * @param id unique identification number. 21 | */ 22 | @Serializable 23 | data class ExerciseType( 24 | val operation: Operation, 25 | val difficulty: Int, 26 | val rate: Int = 0, 27 | val userNick: String, 28 | val isUnlocked: Boolean, 29 | val id: Int = 0 30 | ) -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/choosePlayer/ChoosePlayerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.choosePlayer 2 | 3 | import android.annotation.SuppressLint 4 | import android.view.LayoutInflater 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.octbit.rutmath.data.model.Player 8 | import com.octbit.rutmath.databinding.PlayerRowBinding 9 | 10 | class ChoosePlayerAdapter( 11 | private val onItemClickedListener: (Player) -> Unit 12 | ) : RecyclerView.Adapter() { 13 | 14 | private val playersList = arrayListOf() 15 | 16 | @SuppressLint("InflateParams") 17 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlayerViewHolder { 18 | val binding = PlayerRowBinding.inflate(LayoutInflater.from(parent.context), parent, false) 19 | return PlayerViewHolder( 20 | binding, 21 | onItemClickedListener 22 | ) 23 | } 24 | 25 | override fun getItemCount(): Int = playersList.size 26 | 27 | override fun onBindViewHolder(holder: PlayerViewHolder, position: Int) { 28 | holder.bind(position + 1, playersList[position]) 29 | } 30 | 31 | fun refreshAdapter(list: List) { 32 | playersList.apply { 33 | clear() 34 | addAll(list) 35 | } 36 | notifyDataSetChanged() 37 | } 38 | } -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/data/model/Operation.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.data.model 2 | 3 | import androidx.room.TypeConverter 4 | 5 | /** 6 | * Types of operations that can exists in exercises. 7 | */ 8 | 9 | enum class Operation { 10 | PLUS, 11 | MINUS, 12 | PLUS_MINUS, 13 | MULTIPLY, 14 | DIVIDE, 15 | MULTIPLY_DIVIDE, 16 | DIVISIBILITY, 17 | UNITS_TIME, 18 | UNITS_LENGTH, 19 | UNITS_WEIGHT, 20 | UNITS_SURFACE, 21 | UNITS_ALL, 22 | //adding negative plus and negative minus 23 | NEGATIVE_PLUS, 24 | NEGATIVE_MINUS, 25 | NEGATIVE_PLUS_MINUS, 26 | //new code added here 27 | // NEGATIVE_PLUS_MUL, 28 | // NEGATIVE_MINUS_MUL, 29 | // NEGATIVE_PLUS_DIV, 30 | // NEGATIVE_MINUS_DIV, 31 | // NEGATIVE_PLUS_MINUS_MUL, 32 | // NEGATIVE_PLUS_MINUS_DIV 33 | NEGATIVE_MUL, 34 | NEGATIVE_DIV, 35 | NEGATIVE_MUL_DIV 36 | } 37 | 38 | /** 39 | * Converter for database. It is not primitive type then it will be stored in database as String. 40 | * When database will retrieve it will be converted from String to Operation with stringToOperation method. 41 | */ 42 | class OperationConverter { 43 | @TypeConverter 44 | fun stringToOperation(value: String): Operation { 45 | return Operation.valueOf(value) 46 | } 47 | 48 | @TypeConverter 49 | fun operationToString(value: Operation): String { 50 | return value.name 51 | } 52 | } -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/util/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.util.base 2 | 3 | import android.app.AlertDialog 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import androidx.annotation.LayoutRes 9 | import androidx.fragment.app.Fragment 10 | import com.octbit.rutmath.R 11 | 12 | /** 13 | * BaseFragment for all fragments in app. It contains logic for easier inflating view from file. 14 | * 15 | * Base class for fragments with convenience for inflating view from file. 16 | */ 17 | abstract class BaseFragment : Fragment() { 18 | 19 | @get:LayoutRes 20 | abstract val layout: Int 21 | 22 | override fun onCreateView( 23 | inflater: LayoutInflater, container: ViewGroup?, 24 | savedInstanceState: Bundle? 25 | ): View? { 26 | return inflater.inflate(layout, container, false) 27 | } 28 | 29 | protected fun showSimpleDialog(title: String? = null, message: String? = null) { 30 | val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext()) 31 | title?.let { 32 | builder.setTitle(it) 33 | } 34 | message?.let { 35 | builder.setMessage(it) 36 | } 37 | builder.setPositiveButton( 38 | getString(R.string.ok) 39 | ) { dialog, _ -> 40 | dialog.dismiss() 41 | } 42 | builder.show() 43 | } 44 | } -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/data/model/ExerciseType.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.data.model 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | import java.io.Serializable 7 | 8 | /** 9 | * ExerciseType is a database model. 10 | * @param difficulty modifies difficulty of the Exercise. For example: when difficulty equals to 10 11 | * and operation equals to Operation.PLUS then user will see only exercises with answer 12 | * that is equal or less than 10. 13 | * Every operation has specific difficulty range. 14 | * For example: 15 | * Operation.PLUS can use difficulty range: (5-200). 16 | * Operation.DIVISIBILITY can use difficulty range: (1-10). 17 | * Check specific GameViewModel for specific difficulty range. 18 | * @param rate number of stars reached in this type of exercises. 19 | * @param isUnlocked whether exercise is unlocked and can be accessed by the user. 20 | * @param id unique identification number in database. 21 | */ 22 | 23 | @Entity(tableName = "ExerciseType") 24 | data class ExerciseType( 25 | @ColumnInfo(name = "operation") 26 | var operation: Operation, 27 | @ColumnInfo(name = "difficulty") 28 | var difficulty: Int, 29 | @ColumnInfo(name = "rate") 30 | var rate: Int = 0, 31 | val userNick: String, 32 | var isUnlocked: Boolean, 33 | @PrimaryKey(autoGenerate = true) 34 | var id: Int = 0 35 | ) : Serializable 36 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | org.gradle.parallel=true 14 | org.gradle.caching=true 15 | org.gradle.configureondemand=true 16 | # AndroidX package structure to make it clearer which packages are bundled with the 17 | # Android operating system, and which are packaged with your app's APK 18 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 19 | android.useAndroidX=true 20 | # Automatically convert third-party libraries to use AndroidX 21 | android.enableJetifier=true 22 | # Kotlin code style for this project: "official" or "obsolete": 23 | kotlin.code.style=official 24 | 25 | # Kotlin Multiplatform settings 26 | kotlin.mpp.androidGradlePluginCompatibility.nowarn=true 27 | kotlin.mpp.androidSourceSetLayoutV2AndroidStyleDirs.nowarn=true 28 | kotlin.native.ignoreDisabledTargets=true -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: "Check Android signing secrets" 2 | 3 | on: 4 | workflow_dispatch: # Run manually from GitHub UI 5 | 6 | jobs: 7 | check-secrets: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: "Check if secrets exist" 11 | run: | 12 | if [ -z "${{ secrets.KEYSTORE_FILE }}" ]; then 13 | echo "❌ Secret KEYSTORE_FILE is missing" 14 | exit 1 15 | fi 16 | if [ -z "${{ secrets.KEYSTORE_PASSWORD }}" ]; then 17 | echo "❌ Secret KEYSTORE_PASSWORD is missing" 18 | exit 1 19 | fi 20 | if [ -z "${{ secrets.KEY_ALIAS }}" ]; then 21 | echo "❌ Secret KEY_ALIAS is missing" 22 | exit 1 23 | fi 24 | if [ -z "${{ secrets.KEY_PASSWORD }}" ]; then 25 | echo "❌ Secret KEY_PASSWORD is missing" 26 | exit 1 27 | fi 28 | echo "✅ All secrets exist" 29 | 30 | - name: "Decode keystore" 31 | run: echo "${{ secrets.KEYSTORE_FILE }}" | base64 --decode > release.keystore 32 | 33 | - name: "Verify keystore" 34 | run: | 35 | set +e 36 | keytool -list -v \ 37 | -keystore release.keystore \ 38 | -storepass "${{ secrets.KEYSTORE_PASSWORD }}" \ 39 | -keypass "${{ secrets.KEY_PASSWORD }}" \ 40 | -alias "${{ secrets.KEY_ALIAS }}" 41 | if [ $? -ne 0 ]; then 42 | echo "❌ Failed to open keystore with provided secrets" 43 | exit 1 44 | fi 45 | echo "✅ Keystore and credentials are valid" 46 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/data/AppDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.data 2 | 3 | import android.content.Context 4 | import androidx.room.Database 5 | import androidx.room.Room 6 | import androidx.room.RoomDatabase 7 | import androidx.room.TypeConverters 8 | import com.octbit.rutmath.data.dao.ExerciseTypeDao 9 | import com.octbit.rutmath.data.dao.ScoreDao 10 | import com.octbit.rutmath.data.dao.SettingsDao 11 | import com.octbit.rutmath.data.dao.UserDao 12 | import com.octbit.rutmath.data.model.* 13 | 14 | /** 15 | * Main abstraction for database. It contains whole Room database implementation. 16 | */ 17 | 18 | @Database(entities = [ExerciseType::class, Settings::class, Score::class, Player::class], version = 4, exportSchema = false) 19 | @TypeConverters(OperationConverter::class) 20 | abstract class AppDatabase : RoomDatabase() { 21 | 22 | companion object { 23 | const val DATABASE_FILE = "rutmath.db" 24 | 25 | @Volatile 26 | private var instance: AppDatabase? = null 27 | private val lock = Any() 28 | 29 | operator fun invoke(context: Context) = instance ?: synchronized(lock) { 30 | instance ?: buildDatabase(context).also { instance = it } 31 | } 32 | 33 | private fun buildDatabase(context: Context) = Room.databaseBuilder( 34 | context, 35 | AppDatabase::class.java, 36 | DATABASE_FILE 37 | ).build() 38 | } 39 | 40 | abstract fun exerciseTypeDao(): ExerciseTypeDao 41 | 42 | abstract fun settingsDao(): SettingsDao 43 | 44 | abstract fun scoreDao(): ScoreDao 45 | 46 | abstract fun userDao(): UserDao 47 | } -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/activity/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.activity 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import com.octbit.rutmath.R 6 | import com.octbit.rutmath.data.AppDatabase 7 | import io.reactivex.android.schedulers.AndroidSchedulers 8 | import io.reactivex.schedulers.Schedulers 9 | import org.koin.android.ext.android.inject 10 | import io.reactivex.disposables.CompositeDisposable 11 | import java.util.* 12 | 13 | var isRecreated = false 14 | class MainActivity : AppCompatActivity() { 15 | private val database: AppDatabase by inject() 16 | private val compositeDisposable: CompositeDisposable = CompositeDisposable() 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | if (!isRecreated){ 21 | database.settingsDao().getAll() 22 | .observeOn(AndroidSchedulers.mainThread()) 23 | .subscribeOn(Schedulers.io()) 24 | .map { databaseSettings -> 25 | if (databaseSettings.isNotEmpty()) { 26 | setLocale(databaseSettings[0].language) 27 | isRecreated = true 28 | recreate() 29 | } 30 | }.subscribe() 31 | } 32 | setContentView(R.layout.main_activity) 33 | } 34 | 35 | /** 36 | * Sets the application language 37 | */ 38 | private fun setLocale(language:String){ 39 | val res = resources 40 | val dm = res.displayMetrics 41 | val conf = res.configuration 42 | conf.setLocale(Locale(language)) 43 | resources.updateConfiguration(conf,dm) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /androidApp/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 10 | 13 | 16 | 17 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/fragment_battle_game.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 26 | 27 | 38 | 39 | -------------------------------------------------------------------------------- /shared/src/commonMain/kotlin/com/octbit/rutmath/shared/localization/StringProvider.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.localization 2 | 3 | /** 4 | * Cross-platform string provider interface for localization. 5 | * Platform-specific implementations will provide localized strings. 6 | */ 7 | interface StringProvider { 8 | 9 | // Basic strings 10 | fun ok(): String 11 | fun cancel(): String 12 | fun yes(): String 13 | fun no(): String 14 | fun error(): String 15 | fun save(): String 16 | 17 | // Game strings 18 | fun score(): String 19 | fun player1(): String 20 | fun player2(): String 21 | fun scoreboard(): String 22 | 23 | // Game modes 24 | fun additionSubtraction(): String 25 | fun multiplicationDivision(): String 26 | fun divisibility(): String 27 | fun unitConversion(): String 28 | fun multiplicationTable(): String 29 | 30 | // Difficulties 31 | fun difficultyEasy(): String 32 | fun difficultyMedium(): String 33 | fun difficultyHard(): String 34 | fun difficultyVeryHard(): String 35 | 36 | // Units 37 | fun unitsTime(): String 38 | fun unitsLength(): String 39 | fun unitsWeight(): String 40 | fun unitsSurface(): String 41 | fun unitsAll(): String 42 | 43 | // Divisibility 44 | fun divisibilityQuestionPart1(): String // "Is" 45 | fun divisibilityQuestionPart2(): String // "divisible by" 46 | 47 | // Game feedback 48 | fun gameEnded(): String 49 | fun nicknameEmpty(): String 50 | fun nicknameExists(): String 51 | 52 | // Formatted strings 53 | fun duelScore(player1Name: String, player1Score: Int, player2Name: String, player2Score: Int): String 54 | fun scoreboardFormat(position: Int, playerName: String): String 55 | fun battleBonus(bonus: Int): String 56 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/units_help_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 32 | 33 | 43 | 44 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/view/GridSpacingItemDecoration.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.view 2 | 3 | import android.graphics.Rect 4 | import android.view.View 5 | import androidx.recyclerview.widget.RecyclerView 6 | 7 | /** 8 | * Add spacing for GridLayoutManager of RecyclerView 9 | * 10 | * Decorator for RecyclerView allowing to add spacing between views with grid layout. 11 | * 12 | * @param spanCount - columns count 13 | * @param spacing - space between views in DPI 14 | * @param includeEdge - should add space between views and edge of recyclerView 15 | */ 16 | class GridSpacingItemDecoration( 17 | private val spanCount: Int, 18 | private val spacing: Int, 19 | private val includeEdge: Boolean 20 | ) : RecyclerView.ItemDecoration() { 21 | 22 | override fun getItemOffsets( 23 | outRect: Rect, 24 | view: View, 25 | parent: RecyclerView, 26 | state: RecyclerView.State 27 | ) { 28 | val position = parent.getChildAdapterPosition(view) // item position 29 | val column = position % spanCount // item column 30 | 31 | if (includeEdge) { 32 | outRect.left = 33 | spacing - column * spacing / spanCount // spacing - column * ((1f / spanCount) * spacing) 34 | outRect.right = 35 | (column + 1) * spacing / spanCount // (column + 1) * ((1f / spanCount) * spacing) 36 | 37 | if (position < spanCount) { // top edge 38 | outRect.top = spacing 39 | } 40 | outRect.bottom = spacing // item bottom 41 | } else { 42 | outRect.left = column * spacing / spanCount // column * ((1f / spanCount) * spacing) 43 | outRect.right = 44 | spacing - (column + 1) * spacing / spanCount // spacing - (column + 1) * ((1f / spanCount) * spacing) 45 | if (position >= spanCount) { 46 | outRect.top = spacing // item top 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 18 | 19 | 22 | 23 | 31 | 32 | 39 | 40 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/ic_units.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 9 | 14 | 17 | 18 | 23 | 28 | 29 | 34 | 39 | 40 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: "Build App" 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - "androidApp/src/**" 7 | - "shared/src/**" 8 | - "*.gradle*" 9 | - "gradle/**" 10 | types: 11 | - "opened" 12 | - "reopened" 13 | - "synchronize" 14 | push: 15 | branches: 16 | - "main" 17 | - "master" 18 | workflow_dispatch: # Allow manual trigger from GitHub UI 19 | 20 | jobs: 21 | build: 22 | name: "Build" 23 | runs-on: ubuntu-latest 24 | permissions: 25 | contents: write 26 | pull-requests: write 27 | 28 | steps: 29 | - name: "Checkout code" 30 | uses: actions/checkout@v4 31 | 32 | - name: "Setup Java" 33 | uses: actions/setup-java@v4 34 | with: 35 | distribution: temurin 36 | java-version: 17 37 | cache: gradle 38 | 39 | - name: "Grant execute permission for gradlew" 40 | run: chmod +x gradlew 41 | 42 | - name: "Decode Keystore" 43 | run: echo "${{ secrets.SIGNING_KEYSTORE }}" | base64 --decode > androidApp/release.keystore 44 | 45 | - name: "Set up signing env" 46 | run: | 47 | echo "SIGNING_KEY_ALIAS=${{ secrets.SIGNING_KEY_ALIAS }}" >> $GITHUB_ENV 48 | echo "SIGNING_KEYSTORE_PASSWORD=${{ secrets.SIGNING_KEYSTORE_PASSWORD }}" >> $GITHUB_ENV 49 | echo "SIGNING_KEY_PASSWORD=${{ secrets.SIGNING_KEY_PASSWORD }}" >> $GITHUB_ENV 50 | 51 | - name: "Build APK" 52 | run: ./gradlew androidApp:assembleRelease --no-daemon 53 | 54 | - name: "Upload APK artifacts" 55 | uses: actions/upload-artifact@v4 56 | with: 57 | name: "app-release" 58 | path: androidApp/build/outputs/apk/release/androidApp-release.apk 59 | 60 | - name: "Build AAB" 61 | run: ./gradlew androidApp:bundleRelease --no-daemon 62 | 63 | - name: "Upload AAB artifacts" 64 | uses: actions/upload-artifact@v4 65 | with: 66 | name: "app-release-bundle" 67 | path: androidApp/build/outputs/bundle/release/androidApp-release.aab 68 | -------------------------------------------------------------------------------- /shared/src/commonMain/kotlin/com/octbit/rutmath/shared/data/DatabaseRepository.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.data 2 | 3 | import com.octbit.rutmath.shared.model.ExerciseType 4 | import com.octbit.rutmath.shared.model.Player 5 | import com.octbit.rutmath.shared.model.Score 6 | import com.octbit.rutmath.shared.model.Settings 7 | 8 | /** 9 | * Repository interface for database operations. 10 | * This abstraction allows different platforms to implement their own database solutions. 11 | */ 12 | interface DatabaseRepository { 13 | 14 | /** 15 | * Settings related operations 16 | */ 17 | suspend fun getSettings(): Settings? 18 | suspend fun updateSettings(settings: Settings) 19 | suspend fun insertDefaultSettings(): Settings 20 | 21 | /** 22 | * Exercise type related operations 23 | */ 24 | suspend fun getExerciseTypes(): List 25 | suspend fun getExerciseType(id: Int): ExerciseType? 26 | suspend fun updateExerciseType(exerciseType: ExerciseType) 27 | suspend fun insertExerciseType(exerciseType: ExerciseType): Int 28 | suspend fun deleteExerciseType(id: Int) 29 | 30 | /** 31 | * Score related operations 32 | */ 33 | suspend fun saveScore(score: Score): Int 34 | suspend fun saveScores(scores: List) 35 | suspend fun getTopScores(limit: Int = 10): List 36 | suspend fun getAllScores(): List 37 | suspend fun deleteScore(id: Int) 38 | 39 | /** 40 | * Player related operations 41 | */ 42 | suspend fun getPlayers(): List 43 | suspend fun getPlayer(id: Int): Player? 44 | suspend fun insertPlayer(player: Player): Int 45 | suspend fun updatePlayer(player: Player) 46 | suspend fun deletePlayer(id: Int) 47 | } 48 | 49 | /** 50 | * Local data source interface for caching and offline storage. 51 | */ 52 | interface LocalDataSource { 53 | suspend fun saveSettings(settings: Settings) 54 | suspend fun loadSettings(): Settings? 55 | suspend fun saveExerciseTypes(types: List) 56 | suspend fun loadExerciseTypes(): List 57 | suspend fun saveScores(scores: List) 58 | suspend fun loadScores(): List 59 | suspend fun clearCache() 60 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/fragment_scoreboard.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 32 | 33 | 38 | 39 | 51 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/divisibilityList/DivisibilityViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.divisibilityList 2 | 3 | import androidx.core.content.ContextCompat 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.octbit.rutmath.R 6 | import com.octbit.rutmath.data.model.ExerciseType 7 | import com.octbit.rutmath.databinding.DivisibilityExerciseItemBinding 8 | 9 | 10 | class DivisibilityViewHolder( 11 | private val binding: DivisibilityExerciseItemBinding, 12 | private val clickCallback: (exerciseType: ExerciseType) -> Unit) : 13 | RecyclerView.ViewHolder(binding.root) { 14 | 15 | private val difficultyList = listOf( 16 | binding.root.resources.getString(R.string.divisibility_difficulty_1), 17 | binding.root.resources.getString(R.string.divisibility_difficulty_2), 18 | binding.root.resources.getString(R.string.divisibility_difficulty_3), 19 | binding.root.resources.getString(R.string.divisibility_difficulty_4) 20 | ) 21 | 22 | fun bind(exerciseType: ExerciseType) = with(binding){ 23 | title.text = when (exerciseType.difficulty) { 24 | 1,2,3 -> difficultyList[0] 25 | 4,5,6 -> difficultyList[1] 26 | 9,8,7 -> difficultyList[2] 27 | 10 -> difficultyList[3] 28 | else -> null 29 | }.plus(" ").plus(((exerciseType.difficulty-1) % 3)+1) 30 | if (exerciseType.difficulty == 10) 31 | title.text = title.text.dropLast(2) 32 | 33 | /** 34 | * Set a listener on unlocked exercise tile and change its color 35 | */ 36 | if (exerciseType.isUnlocked) { 37 | root.setOnClickListener { 38 | clickCallback.invoke(exerciseType) 39 | } 40 | root.background = ContextCompat.getDrawable(root.context, R.drawable.bg_in_tile_exercise) 41 | } 42 | 43 | val stars = arrayOf(star1, star2, star3, star4, star5) 44 | 45 | // Reset all stars to the default white icon 46 | stars.forEach { star -> 47 | star.setImageResource(R.drawable.ic_star) 48 | } 49 | 50 | for (i in 1..exerciseType.rate) { 51 | stars[i - 1].setImageResource(R.drawable.ic_star_yellow_24dp) 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: "Release" 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" # np. v0.2.3 7 | workflow_dispatch: 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | jobs: 21 | release: 22 | name: "Release" 23 | runs-on: ubuntu-latest 24 | permissions: 25 | contents: write 26 | 27 | 28 | steps: 29 | - name: "Checkout source code" 30 | uses: actions/checkout@v4 31 | 32 | - name: "Setup Java" 33 | uses: actions/setup-java@v4 34 | with: 35 | distribution: temurin 36 | java-version: 17 37 | cache: gradle 38 | 39 | - name: "Grant execute permission for gradlew" 40 | run: chmod +x gradlew 41 | 42 | - name: "Decode keystore" 43 | run: echo "${{ secrets.SIGNING_KEYSTORE }}" | base64 --decode > androidApp/release.keystore 44 | 45 | - name: "Set up signing env" 46 | run: | 47 | echo "SIGNING_KEY_ALIAS=${{ secrets.SIGNING_KEY_ALIAS }}" >> $GITHUB_ENV 48 | echo "SIGNING_KEYSTORE_PASSWORD=${{ secrets.SIGNING_KEYSTORE_PASSWORD }}" >> $GITHUB_ENV 49 | echo "SIGNING_KEY_PASSWORD=${{ secrets.SIGNING_KEY_PASSWORD }}" >> $GITHUB_ENV 50 | 51 | - name: "Build Release APK" 52 | run: ./gradlew androidApp:assembleRelease --no-daemon 53 | 54 | - name: "Upload APK artifact" 55 | uses: actions/upload-artifact@v4 56 | with: 57 | name: app-release 58 | path: androidApp/build/outputs/apk/release/androidApp-release.apk 59 | 60 | - name: "Build Release AAB" 61 | run: ./gradlew androidApp:bundleRelease --no-daemon 62 | 63 | - name: "Upload AAB artifact" 64 | uses: actions/upload-artifact@v4 65 | with: 66 | name: app-release-bundle 67 | path: androidApp/build/outputs/bundle/release/androidApp-release.aab 68 | 69 | - name: "Create GitHub Release" 70 | uses: softprops/action-gh-release@v1 71 | with: 72 | files: | 73 | androidApp/build/outputs/apk/release/androidApp-release.apk 74 | androidApp/build/outputs/bundle/release/androidApp-release.aab 75 | draft: false 76 | prerelease: false 77 | generate_release_notes: true 78 | env: 79 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 80 | -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/normal_exercise_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 27 | 28 | 33 | 34 | 39 | 40 | 45 | 46 | 51 | 52 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /shared/src/commonMain/kotlin/com/octbit/rutmath/shared/game/EquationGenerator.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.game 2 | 3 | import com.octbit.rutmath.shared.model.Equation 4 | import com.octbit.rutmath.shared.model.EquationUnits 5 | import com.octbit.rutmath.shared.model.ExerciseType 6 | import com.octbit.rutmath.shared.model.Operation 7 | 8 | /** 9 | * Interface for generating mathematical equations based on operation type and difficulty. 10 | */ 11 | interface EquationGenerator { 12 | /** 13 | * Generates a single equation based on operation and difficulty. 14 | * 15 | * @param operation The type of mathematical operation 16 | * @param difficulty The difficulty level (affects number ranges) 17 | * @return Generated equation 18 | */ 19 | fun generateEquation(operation: Operation, difficulty: Int): Equation 20 | 21 | /** 22 | * Generates a units equation for unit conversion exercises. 23 | * 24 | * @param operation The type of units operation 25 | * @param difficulty The difficulty level 26 | * @return Generated units equation 27 | */ 28 | fun generateUnitsEquation(operation: Operation, difficulty: Int): EquationUnits 29 | 30 | /** 31 | * Generates multiple answer choices including the correct answer. 32 | * 33 | * @param correctAnswer The correct answer to the equation 34 | * @param count Number of answer choices to generate (default 4) 35 | * @return List of answer choices (including correct answer) 36 | */ 37 | fun generateAnswers(correctAnswer: Int, count: Int = 4): List 38 | 39 | /** 40 | * Generates a list of equations for a complete exercise. 41 | * 42 | * @param exerciseType The exercise type configuration 43 | * @param exerciseCount Number of equations to generate 44 | * @return List of generated equations 45 | */ 46 | fun generateEquations(exerciseType: ExerciseType, exerciseCount: Int): List 47 | 48 | /** 49 | * Generates a list of units equations for a complete exercise. 50 | * 51 | * @param exerciseType The exercise type configuration 52 | * @param exerciseCount Number of equations to generate 53 | * @return List of generated units equations 54 | */ 55 | fun generateUnitsEquations(exerciseType: ExerciseType, exerciseCount: Int): List 56 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/ic_help.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 17 | 18 | 23 | 28 | 29 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/unitsList/UnitsViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.unitsList 2 | 3 | import androidx.core.content.ContextCompat 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.octbit.rutmath.R 6 | import com.octbit.rutmath.data.model.ExerciseType 7 | import com.octbit.rutmath.data.model.Operation 8 | import com.octbit.rutmath.databinding.UnitsExerciseItemBinding 9 | 10 | class UnitsViewHolder( 11 | private val binding: UnitsExerciseItemBinding, 12 | private val clickCallback: (exerciseType: ExerciseType) -> Unit 13 | ) : 14 | RecyclerView.ViewHolder(binding.root) { 15 | 16 | private val TIME = binding.root.resources.getString(R.string.units_time) 17 | private val LENGTH = binding.root.resources.getString(R.string.units_length) 18 | private val WEIGHT = binding.root.resources.getString(R.string.units_weight) 19 | private val SURFACE = binding.root.resources.getString(R.string.units_surface) 20 | private val ALL = binding.root.resources.getString(R.string.units_all) 21 | 22 | fun bind(exerciseType: ExerciseType) = with(binding){ 23 | title.text = when (exerciseType.operation) { 24 | Operation.UNITS_TIME -> TIME 25 | Operation.UNITS_LENGTH -> LENGTH 26 | Operation.UNITS_WEIGHT -> WEIGHT 27 | Operation.UNITS_SURFACE -> SURFACE 28 | Operation.UNITS_ALL -> ALL 29 | else -> null 30 | }.plus(" ").plus(((exerciseType.difficulty-1) % 3)+1) 31 | if (exerciseType.difficulty == 10) 32 | title.text = title.text.dropLast(2) 33 | 34 | /** 35 | * Set a listener on unlocked exercise tile and change its color 36 | */ 37 | if (exerciseType.isUnlocked) { 38 | root.setOnClickListener { 39 | clickCallback.invoke(exerciseType) 40 | } 41 | root.background = ContextCompat.getDrawable(root.context, R.drawable.bg_in_tile_exercise) 42 | } 43 | 44 | val stars = arrayOf(star1, star2, star3, star4, star5) 45 | 46 | // Reset all stars to the default white icon 47 | stars.forEach { star -> 48 | star.setImageResource(R.drawable.ic_star) 49 | } 50 | 51 | for (i in 1..exerciseType.rate) { 52 | stars[i - 1].setImageResource(R.drawable.ic_star_yellow_24dp) 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/divisibility_exercise_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 28 | 29 | 34 | 35 | 40 | 41 | 46 | 47 | 52 | 53 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/scoreboard/ScoreboardFragment.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.scoreboard 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.recyclerview.widget.LinearLayoutManager 8 | import com.octbit.rutmath.R 9 | import com.octbit.rutmath.data.AppDatabase 10 | import com.octbit.rutmath.databinding.FragmentScoreboardBinding 11 | import com.octbit.rutmath.util.base.BaseFragment 12 | import com.octbit.rutmath.util.gone 13 | import com.octbit.rutmath.util.visible 14 | import io.reactivex.android.schedulers.AndroidSchedulers 15 | import io.reactivex.disposables.CompositeDisposable 16 | import io.reactivex.schedulers.Schedulers 17 | import org.koin.android.ext.android.inject 18 | 19 | class ScoreboardFragment : BaseFragment() { 20 | 21 | override val layout = R.layout.fragment_scoreboard 22 | private var _binding: FragmentScoreboardBinding? = null 23 | private val binding get() = _binding!! 24 | private val database: AppDatabase by inject() 25 | 26 | private val disposables = CompositeDisposable() 27 | override fun onCreateView( 28 | inflater: LayoutInflater, 29 | container: ViewGroup?, 30 | savedInstanceState: Bundle? 31 | ): View? { 32 | _binding = FragmentScoreboardBinding.inflate(inflater, container, false) 33 | return binding.root 34 | } 35 | override fun onViewCreated(view: View, savedInstanceState: Bundle?){ 36 | super.onViewCreated(view, savedInstanceState) 37 | disposables.add(database.scoreDao().getAll() 38 | .observeOn(AndroidSchedulers.mainThread()) 39 | .subscribeOn(Schedulers.io()) 40 | .subscribe { scoreList -> 41 | val sortedList = scoreList.sortedBy { it.score }.reversed() 42 | binding.recyclerView.adapter = ScoreboardAdapter(sortedList) 43 | if (sortedList.isNotEmpty()) { 44 | binding.emptyListInstruction.gone() 45 | binding.scoreBoardInfo.visible() 46 | } 47 | }) 48 | binding.recyclerView.apply { 49 | layoutManager = LinearLayoutManager(context) 50 | } 51 | } 52 | 53 | override fun onDestroy() { 54 | disposables.clear() 55 | super.onDestroy() 56 | _binding = null 57 | } 58 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/units_exercise_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 20 | 21 | 29 | 30 | 35 | 36 | 41 | 42 | 47 | 48 | 53 | 54 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.di 2 | 3 | import androidx.room.Room 4 | import com.octbit.rutmath.data.AppDatabase 5 | import com.octbit.rutmath.ui.fragment.addSubList.AddSubListViewModel 6 | import com.octbit.rutmath.ui.fragment.game.battle.BattleFragmentViewModel 7 | import com.octbit.rutmath.ui.fragment.choosePlayer.ChoosePlayerViewModel 8 | import com.octbit.rutmath.ui.fragment.divisibilityList.DivisibilityListViewModel 9 | import com.octbit.rutmath.ui.fragment.game.divisibility.DivisibilityGameViewModel 10 | import com.octbit.rutmath.ui.fragment.game.normal.NormalGameViewModel 11 | import com.octbit.rutmath.ui.fragment.game.table.TableGameViewModel 12 | import com.octbit.rutmath.ui.fragment.game.units.UnitsGameViewModel 13 | import com.octbit.rutmath.ui.fragment.mulDivList.MulDivListViewModel 14 | import com.octbit.rutmath.ui.fragment.unitsList.UnitsListViewModel 15 | import com.octbit.rutmath.ui.game.SharedNormalGameViewModel 16 | import com.octbit.rutmath.ui.game.SharedBattleViewModel 17 | import com.octbit.rutmath.ui.game.SharedUnitsGameViewModel 18 | import com.octbit.rutmath.ui.game.SharedTableGameViewModel 19 | import com.octbit.rutmath.ui.game.SharedDivisibilityGameViewModel 20 | import org.koin.android.ext.koin.androidContext 21 | import org.koin.androidx.viewmodel.dsl.viewModel 22 | import org.koin.dsl.module 23 | 24 | val appModule = module { 25 | 26 | single { 27 | Room.databaseBuilder( 28 | androidContext(), 29 | AppDatabase::class.java, 30 | AppDatabase.DATABASE_FILE 31 | ).build() 32 | } 33 | 34 | viewModel { AddSubListViewModel(get()) } 35 | viewModel { MulDivListViewModel(get()) } 36 | viewModel { DivisibilityListViewModel(get()) } 37 | viewModel { UnitsListViewModel(get()) } 38 | viewModel { NormalGameViewModel() } 39 | viewModel { DivisibilityGameViewModel() } 40 | viewModel { TableGameViewModel() } 41 | viewModel { UnitsGameViewModel() } 42 | viewModel { BattleFragmentViewModel(get()) } 43 | viewModel { 44 | ChoosePlayerViewModel( 45 | get() 46 | ) 47 | } 48 | 49 | viewModel { SharedNormalGameViewModel(get(), get()) } 50 | viewModel { SharedBattleViewModel(get(), get()) } 51 | viewModel { SharedUnitsGameViewModel(get(), get()) } 52 | viewModel { SharedTableGameViewModel(get(), get()) } 53 | viewModel { SharedDivisibilityGameViewModel(get(), get()) } 54 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RUTMath 2 | Rzeszów University of Technology Math is an open source (GPL) application created in order to help preschool or early school children to learn mathematics. 3 | 4 | [Get it on F-Droid](https://f-droid.org/packages/com.octbit.rutmath/) 7 | 8 | Or get the latest APK from the [Releases Section](https://github.com/przemarbor/RUTMath/releases/latest). 9 | 10 | # Requirements 11 | To use this app you need mobile device using Android Version >= 5.1. 12 | 13 | # Language 14 | This app is available in the following languages: 15 | - English 16 | - Polish 17 | - French 18 | - Portuguese 19 | - Spanish 20 | - Greek 21 | - Dutch 22 | - Slovak 23 | - Italian 24 | - German 25 | - Hungarian 26 | - Czech 27 | 28 | # License 29 | This software is licensed under the terms of the GNU General Public License version 3 (GPLv3).
30 | Full text of the license is available online https://opensource.org/licenses/gpl-3.0.html 31 | 32 | # Acknowledgements 33 | Thank you for your contribution! 34 | 35 | Code development: 36 | - [@krolik7](https://www.github.com/krolik7) (original developer) 37 | - [Arkadiusz Połeć](https://github.com/Nydeyas) (developer) 38 | - [@WegWojciech](https://github.com/WegWojciech) (developer) 39 | - [@RadimDev](https://github.com/RadimDev) (current maintainer) 40 | - [@przemarbor](https://github.com/przemarbor) (good spirit/éminence grise) 41 | 42 | 43 | Translations: 44 | - [Benoît Harrault](https://github.com/benoitharrault) (French) 45 | - [Ramon Rodrigues](https://github.com/ramonrwx) (Portuguese) 46 | 47 | # Screenshots 48 | 49 | 50 | 53 | 56 | 59 | 62 | 63 |
51 | 52 | 54 | 55 | 57 | 58 | 60 | 61 |
64 | 65 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/addSubList/AddSubViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.addSubList 2 | 3 | import androidx.core.content.ContextCompat 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.octbit.rutmath.R 6 | import com.octbit.rutmath.data.model.ExerciseType 7 | import com.octbit.rutmath.data.model.Operation 8 | import com.octbit.rutmath.databinding.NormalExerciseItemBinding 9 | 10 | class AddSubViewHolder(private val binding: NormalExerciseItemBinding, private val clickCallback: (exerciseType: ExerciseType) -> Unit) 11 | : RecyclerView.ViewHolder(binding.root) { 12 | 13 | companion object { 14 | const val PLUS_VALUE = "+" 15 | const val MINUS_VALUE = "-" 16 | const val PLUS_MINUS_VALUE = "±" 17 | // new code here for negative number 18 | const val NEGATIVE_PLUS_VALUE="+(-)" 19 | const val NEGATIVE_MINUS_VALUE="-(-)" 20 | const val NEGATIVE_PLUS_MINUS_VALUE="±(-)" 21 | } 22 | 23 | fun bind(exerciseType: ExerciseType) = with(binding) { 24 | title.text = when (exerciseType.operation) { 25 | Operation.PLUS -> PLUS_VALUE 26 | Operation.MINUS -> MINUS_VALUE 27 | Operation.PLUS_MINUS -> PLUS_MINUS_VALUE 28 | Operation.NEGATIVE_PLUS -> NEGATIVE_PLUS_VALUE 29 | Operation.NEGATIVE_MINUS -> NEGATIVE_MINUS_VALUE 30 | Operation.NEGATIVE_PLUS_MINUS -> NEGATIVE_PLUS_MINUS_VALUE 31 | else -> null 32 | }.plus(" ").plus(exerciseType.difficulty) 33 | 34 | // Set a listener on unlocked exercise tile and change its color 35 | if (exerciseType.isUnlocked) { 36 | root.setOnClickListener { 37 | clickCallback.invoke(exerciseType) 38 | } 39 | root.background = ContextCompat.getDrawable(root.context, R.drawable.bg_in_tile_exercise) 40 | } else { 41 | root.setOnClickListener(null) 42 | root.background = ContextCompat.getDrawable(root.context, R.drawable.bg_in_tile_exercise_disabled) 43 | } 44 | 45 | val stars = arrayOf(binding.star1, binding.star2, binding.star3, binding.star4, binding.star5) 46 | // Reset all stars to the default white icon 47 | stars.forEach { star -> 48 | star.setImageResource(R.drawable.ic_star) // Use your default star icon 49 | } 50 | for (i in 1..exerciseType.rate.coerceIn(0, 5)) { 51 | stars[i - 1].setImageResource(R.drawable.ic_star_yellow_24dp) 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/MenuFragment.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment 2 | 3 | import android.content.Intent 4 | import android.net.Uri 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.navigation.fragment.findNavController 10 | import com.octbit.rutmath.R 11 | import com.octbit.rutmath.databinding.FragmentMenuBinding 12 | import com.octbit.rutmath.util.base.BaseFragment 13 | 14 | class MenuFragment : BaseFragment() { 15 | override val layout: Int = R.layout.fragment_menu 16 | private var _binding: FragmentMenuBinding? = null 17 | private val binding get() = _binding!! 18 | 19 | override fun onCreateView( 20 | inflater: LayoutInflater, 21 | container: ViewGroup?, 22 | savedInstanceState: Bundle? 23 | ): View { 24 | _binding = FragmentMenuBinding.inflate(inflater, container, false) 25 | return binding.root 26 | } 27 | 28 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 29 | super.onViewCreated(view, savedInstanceState) 30 | initButtons() 31 | } 32 | 33 | private fun initButtons() = with(binding) { 34 | // University Logo in top left corner - redirects to PRz Website based on language selected 35 | 36 | przLogo.setOnClickListener { 37 | startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.prz_url)))) 38 | } 39 | 40 | // Faculty Logo in top center - redirects to WEiI Website based on language selected 41 | weiiLogo.setOnClickListener { 42 | startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.weii_url)))) 43 | } 44 | 45 | modesButton.setOnClickListener { 46 | findNavController().navigate( 47 | MenuFragmentDirections.actionMenuFragmentToChoosePlayerFragment() 48 | ) 49 | } 50 | pvpButton.setOnClickListener { 51 | findNavController().navigate( 52 | MenuFragmentDirections.actionMenuFragmentToPlayersNamesFragment() 53 | ) 54 | } 55 | settingsButton.setOnClickListener { 56 | findNavController().navigate( 57 | MenuFragmentDirections.actionMenuFragmentToSettingsFragment() 58 | ) 59 | } 60 | leaderboardButton.setOnClickListener { 61 | findNavController().navigate(MenuFragmentDirections.actionMenuFragmentToScoreboardFragment()) 62 | } 63 | } 64 | 65 | override fun onDestroy() { 66 | super.onDestroy() 67 | _binding = null 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /shared/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id('com.android.library') 3 | id('org.jetbrains.kotlin.multiplatform') 4 | id('org.jetbrains.kotlin.plugin.serialization') 5 | id('kotlin-parcelize') 6 | } 7 | 8 | kotlin { 9 | androidTarget { 10 | compilations.all { 11 | kotlinOptions { 12 | jvmTarget = "11" 13 | } 14 | } 15 | } 16 | 17 | // iOS targets with framework configuration 18 | iosX64() 19 | iosArm64() 20 | iosSimulatorArm64() 21 | 22 | // Configure iOS framework 23 | targets.withType(org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget) { 24 | binaries.withType(org.jetbrains.kotlin.gradle.plugin.mpp.Framework) { 25 | baseName = "RutMathShared" 26 | export("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") 27 | export("org.jetbrains.kotlinx:kotlinx-datetime:0.4.1") 28 | export("io.insert-koin:koin-core:3.5.0") 29 | } 30 | } 31 | 32 | sourceSets { 33 | commonMain { 34 | dependencies { 35 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3' 36 | implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0' 37 | implementation 'org.jetbrains.kotlinx:kotlinx-datetime:0.4.1' 38 | implementation 'io.insert-koin:koin-core:3.5.0' 39 | } 40 | } 41 | commonTest { 42 | dependencies { 43 | implementation 'org.jetbrains.kotlin:kotlin-test:1.9.20' 44 | } 45 | } 46 | androidMain { 47 | dependencies { 48 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3' 49 | implementation 'io.insert-koin:koin-android:3.5.0' 50 | implementation 'androidx.room:room-runtime:2.6.1' 51 | implementation 'androidx.room:room-ktx:2.6.1' 52 | } 53 | } 54 | androidUnitTest { 55 | dependencies { 56 | implementation 'androidx.test.ext:junit:1.1.5' 57 | implementation 'androidx.test.espresso:espresso-core:3.5.1' 58 | } 59 | } 60 | iosMain { 61 | dependencies { 62 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3' 63 | } 64 | } 65 | iosTest { 66 | dependencies { 67 | implementation 'org.jetbrains.kotlin:kotlin-test:1.9.20' 68 | } 69 | } 70 | } 71 | } 72 | 73 | android { 74 | namespace 'com.octbit.rutmath.shared' 75 | compileSdk 34 76 | 77 | defaultConfig { 78 | minSdk 22 79 | targetSdk 34 80 | } 81 | 82 | compileOptions { 83 | sourceCompatibility JavaVersion.VERSION_11 84 | targetCompatibility JavaVersion.VERSION_11 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/normal_rate_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 20 | 21 | 28 | 29 | 36 | 37 | 44 | 45 | 52 | 53 | 60 | 61 | 62 | 71 | 72 | -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/fragment_table_game.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 27 | 28 | 40 | 41 | 47 | 48 | 56 | 57 | 58 | 59 | 69 | 70 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/choosePlayer/ChoosePlayerViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.choosePlayer 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import com.octbit.rutmath.data.AppDatabase 6 | import com.octbit.rutmath.data.model.Player 7 | import com.octbit.rutmath.util.base.DisposableViewModel 8 | import io.reactivex.Single 9 | import io.reactivex.android.schedulers.AndroidSchedulers 10 | import io.reactivex.schedulers.Schedulers 11 | 12 | class ChoosePlayerViewModel( 13 | private val database: AppDatabase 14 | ) : DisposableViewModel() { 15 | 16 | enum class PlayerCreationEvent { 17 | SUCCESS, 18 | NICKNAME_EXISTS, 19 | NICKNAME_EMPTY 20 | } 21 | 22 | private val playersList = arrayListOf() 23 | 24 | private val refreshPlayersListEvent = MutableLiveData() 25 | 26 | private val playerCreationEvent = MutableLiveData() 27 | 28 | fun getRefreshPlayersListEvent(): LiveData = refreshPlayersListEvent 29 | 30 | fun getPlayersList(): List = playersList 31 | 32 | fun playerCreationEvent(): LiveData = playerCreationEvent 33 | 34 | fun loadPlayersList() { 35 | manageDisposable { 36 | database 37 | .userDao() 38 | .getAll() 39 | .observeOn(AndroidSchedulers.mainThread()) 40 | .subscribeOn(Schedulers.io()) 41 | .subscribe { list -> 42 | playersList.apply { 43 | clear() 44 | addAll(list) 45 | } 46 | refreshPlayersListEvent.postValue(Unit) 47 | } 48 | } 49 | } 50 | 51 | fun createPlayer(nick: String) { 52 | manageDisposable { 53 | database.userDao() 54 | .getAll() 55 | .observeOn(AndroidSchedulers.mainThread()) 56 | .subscribeOn(Schedulers.io()) 57 | .map { 58 | it.firstOrNull { player -> player.nick == nick } != null 59 | }.flatMap { nickExists -> 60 | if (nick.trim().isEmpty()) { 61 | return@flatMap Single.just(PlayerCreationEvent.NICKNAME_EMPTY) 62 | } 63 | if (nickExists) { 64 | Single.just(PlayerCreationEvent.NICKNAME_EXISTS) 65 | } else { 66 | database 67 | .userDao() 68 | .insert(Player(nick)) 69 | .observeOn(AndroidSchedulers.mainThread()) 70 | .subscribeOn(Schedulers.io()) 71 | .andThen(Single.just(PlayerCreationEvent.SUCCESS) 72 | ) 73 | } 74 | }.subscribe { result -> 75 | playerCreationEvent.postValue(result) 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/fragment_choose_player.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 30 | 31 | 40 | 41 | 55 | 56 | 66 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /shared/src/iosMain/kotlin/com/octbit/rutmath/shared/localization/IosStringProvider.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.shared.localization 2 | 3 | import platform.Foundation.NSBundle 4 | import platform.Foundation.NSString 5 | import platform.Foundation.NSLocalizedString 6 | 7 | /** 8 | * iOS implementation of StringProvider using NSLocalizedString. 9 | * For now uses hardcoded English strings, but can be extended to use iOS localization. 10 | */ 11 | class IosStringProvider : StringProvider { 12 | 13 | override fun ok(): String = NSLocalizedString("OK", "") 14 | override fun cancel(): String = NSLocalizedString("Cancel", "") 15 | override fun yes(): String = NSLocalizedString("Yes", "") 16 | override fun no(): String = NSLocalizedString("No", "") 17 | override fun error(): String = NSLocalizedString("Error", "") 18 | override fun save(): String = NSLocalizedString("Save", "") 19 | 20 | override fun score(): String = NSLocalizedString("Score:", "") 21 | override fun player1(): String = NSLocalizedString("Player1", "") 22 | override fun player2(): String = NSLocalizedString("Player2", "") 23 | override fun scoreboard(): String = NSLocalizedString("Scoreboard:", "") 24 | 25 | override fun additionSubtraction(): String = NSLocalizedString("Addition and Subtraction", "") 26 | override fun multiplicationDivision(): String = NSLocalizedString("Multiplication and Division", "") 27 | override fun divisibility(): String = NSLocalizedString("Divisibility", "") 28 | override fun unitConversion(): String = NSLocalizedString("Unit conversion", "") 29 | override fun multiplicationTable(): String = NSLocalizedString("Multiplication table", "") 30 | 31 | override fun difficultyEasy(): String = NSLocalizedString("Easy", "") 32 | override fun difficultyMedium(): String = NSLocalizedString("Medium", "") 33 | override fun difficultyHard(): String = NSLocalizedString("Hard", "") 34 | override fun difficultyVeryHard(): String = NSLocalizedString("Very Hard", "") 35 | 36 | override fun unitsTime(): String = NSLocalizedString("Time", "") 37 | override fun unitsLength(): String = NSLocalizedString("Length", "") 38 | override fun unitsWeight(): String = NSLocalizedString("Weight", "") 39 | override fun unitsSurface(): String = NSLocalizedString("Surface", "") 40 | override fun unitsAll(): String = NSLocalizedString("All", "") 41 | 42 | override fun divisibilityQuestionPart1(): String = NSLocalizedString("Is", "") 43 | override fun divisibilityQuestionPart2(): String = NSLocalizedString("divisible by", "") 44 | 45 | override fun gameEnded(): String = NSLocalizedString("Game ended", "") 46 | override fun nicknameEmpty(): String = NSLocalizedString("Nickname can not be empty!", "") 47 | override fun nicknameExists(): String = NSLocalizedString("Nickname exists!", "") 48 | 49 | override fun duelScore(player1Name: String, player1Score: Int, player2Name: String, player2Score: Int): String { 50 | return "$player1Name score: $player1Score\n$player2Name score: $player2Score" 51 | } 52 | 53 | override fun scoreboardFormat(position: Int, playerName: String): String { 54 | return "$position. $playerName" 55 | } 56 | 57 | override fun battleBonus(bonus: Int): String { 58 | return "Sequential bonus +$bonus" 59 | } 60 | } -------------------------------------------------------------------------------- /androidApp/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: "androidx.navigation.safeargs.kotlin" 4 | apply plugin: "kotlin-kapt" 5 | 6 | android { 7 | namespace "com.octbit.rutmath" 8 | 9 | signingConfigs { 10 | release { 11 | storeFile file("release.keystore") // Keystore dekodowany w workflow 12 | storePassword System.getenv("SIGNING_KEYSTORE_PASSWORD") 13 | keyAlias System.getenv("SIGNING_KEY_ALIAS") 14 | keyPassword System.getenv("SIGNING_KEY_PASSWORD") 15 | } 16 | } 17 | 18 | defaultConfig { 19 | compileSdk 34 20 | applicationId "com.octbit.rutmath" 21 | minSdkVersion 22 22 | targetSdkVersion 34 23 | versionCode 10 24 | versionName "0.2.5" 25 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 26 | } 27 | 28 | buildTypes { 29 | release { 30 | signingConfig signingConfigs.release 31 | minifyEnabled false 32 | shrinkResources false 33 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 34 | } 35 | debug { 36 | debuggable true 37 | signingConfig signingConfigs.debug 38 | } 39 | } 40 | 41 | compileOptions { 42 | sourceCompatibility JavaVersion.VERSION_11 43 | targetCompatibility JavaVersion.VERSION_11 44 | } 45 | 46 | kotlinOptions { 47 | jvmTarget = "11" 48 | } 49 | 50 | buildFeatures { 51 | viewBinding = true 52 | } 53 | 54 | dependenciesInfo { 55 | includeInApk = false 56 | includeInBundle = false 57 | } 58 | } 59 | 60 | dependencies { 61 | def nav_version = "2.5.3" 62 | 63 | implementation project(':shared') 64 | 65 | implementation fileTree(dir: 'libs', include: ['*.jar']) 66 | implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.20" 67 | implementation 'androidx.appcompat:appcompat:1.1.0' 68 | implementation 'androidx.core:core-ktx:1.3.0' 69 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 70 | testImplementation 'junit:junit:4.12' 71 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 72 | 73 | implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" 74 | implementation "androidx.navigation:navigation-ui-ktx:$nav_version" 75 | implementation "androidx.constraintlayout:constraintlayout:2.0.0-beta3" 76 | implementation 'com.google.android.material:material:1.0.0' 77 | 78 | implementation "androidx.room:room-runtime:2.6.1" 79 | implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' 80 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0' 81 | implementation 'androidx.room:room-rxjava2:2.6.1' 82 | kapt "androidx.room:room-compiler:2.6.1" 83 | 84 | implementation "io.insert-koin:koin-android:3.5.0" 85 | implementation "io.insert-koin:koin-androidx-navigation:3.5.0" 86 | implementation "io.insert-koin:koin-androidx-workmanager:3.5.0" 87 | 88 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3' 89 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' 90 | implementation 'io.reactivex.rxjava2:rxjava:2.2.11' 91 | implementation 'com.google.android.material:material:1.0.0' 92 | } 93 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/mulDivList/MulDivViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.mulDivList 2 | 3 | import androidx.core.content.ContextCompat 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.octbit.rutmath.R 6 | import com.octbit.rutmath.data.model.ExerciseType 7 | import com.octbit.rutmath.data.model.Operation 8 | import com.octbit.rutmath.databinding.NormalExerciseItemBinding 9 | 10 | class MulDivViewHolder( 11 | private val binding: NormalExerciseItemBinding, 12 | private val clickCallback: (exerciseType: ExerciseType) -> Unit) : 13 | RecyclerView.ViewHolder(binding.root) { 14 | 15 | companion object { 16 | const val MULTIPLY_VALUE = "×" 17 | const val DIVIDE_VALUE = "÷" 18 | const val MULTIPLY_DIVIDE_VALUE = "×/÷" 19 | 20 | // const val NEGATIVE_PLUS_MUL_VALUE="×+(-)" 21 | // const val NEGATIVE_MINUS_MUL_VALUE="×-(-)" 22 | // const val NEGATIVE_PLUS_DIV_VALUE="÷+(-)" 23 | // const val NEGATIVE_MINUS_DIV_VALUE="÷-(-)" 24 | // 25 | // const val NEGATIVE_PLUS_MINUS_MULTIPLY_VALUE = "×±(-)" 26 | // const val NEGATIVE_PLUS_MINUS_DIVIDE_VALUE = "÷±(-)" 27 | const val NEGATIVE_MUL = "× -" 28 | const val NEGATIVE_DIV = "÷ -" 29 | const val NEGATIVE_MUL_DIV = "×/÷ -" 30 | 31 | 32 | } 33 | 34 | fun bind(exerciseType: ExerciseType) = with(binding){ 35 | title.text = when (exerciseType.operation) { 36 | Operation.MULTIPLY-> MULTIPLY_VALUE 37 | Operation.DIVIDE-> DIVIDE_VALUE 38 | Operation.MULTIPLY_DIVIDE -> MULTIPLY_DIVIDE_VALUE 39 | 40 | Operation.NEGATIVE_MUL -> NEGATIVE_MUL 41 | Operation.NEGATIVE_DIV -> NEGATIVE_DIV 42 | Operation.NEGATIVE_MUL_DIV -> NEGATIVE_MUL_DIV 43 | //new code here 44 | // Operation.NEGATIVE_PLUS_MUL->NEGATIVE_PLUS_MUL_VALUE 45 | // Operation.NEGATIVE_MINUS_MUL-> NEGATIVE_MINUS_MUL_VALUE 46 | // Operation.NEGATIVE_PLUS_DIV->NEGATIVE_PLUS_DIV_VALUE 47 | // Operation.NEGATIVE_MINUS_DIV-> NEGATIVE_MINUS_DIV_VALUE 48 | // Operation.NEGATIVE_PLUS_MINUS_MUL-> NEGATIVE_PLUS_MINUS_MULTIPLY_VALUE 49 | // Operation.NEGATIVE_PLUS_MINUS_DIV-> NEGATIVE_PLUS_MINUS_DIVIDE_VALUE 50 | else -> null 51 | }.plus(" ").plus(exerciseType.difficulty) 52 | 53 | /** 54 | * Set a listener on unlocked exercise tile and change its color 55 | */ 56 | 57 | if (exerciseType.isUnlocked) { 58 | root.setOnClickListener { 59 | clickCallback.invoke(exerciseType) 60 | } 61 | root.background = ContextCompat.getDrawable(root.context,R.drawable.bg_in_tile_exercise) 62 | } 63 | else 64 | { 65 | root.setOnClickListener(null) 66 | root.background = ContextCompat.getDrawable(root.context,R.drawable.bg_in_tile_exercise_disabled) 67 | } 68 | 69 | 70 | val stars = arrayOf(star1, star2, star3, star4, star5) 71 | 72 | // Reset all stars to the default white icon 73 | stars.forEach { star -> 74 | star.setImageResource(R.drawable.ic_star) 75 | } 76 | 77 | for (i in 1..exerciseType.rate) { 78 | stars[i - 1].setImageResource(R.drawable.ic_star_yellow_24dp) 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/ui/fragment/chooseMode/ChooseModeFragment.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.ui.fragment.chooseMode 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.navigation.fragment.findNavController 8 | import androidx.navigation.fragment.navArgs 9 | import com.octbit.rutmath.util.base.BaseFragment 10 | import com.octbit.rutmath.R 11 | import com.octbit.rutmath.databinding.FragmentChooseModeBinding 12 | import com.octbit.rutmath.ui.view.TableRateDialog 13 | 14 | 15 | class ChooseModeFragment : BaseFragment() { 16 | override val layout: Int = R.layout.fragment_choose_mode 17 | private var _binding: FragmentChooseModeBinding? = null 18 | private val binding get() = _binding!! 19 | private val args: ChooseModeFragmentArgs by navArgs() 20 | 21 | override fun onCreateView( 22 | inflater: LayoutInflater, 23 | container: ViewGroup?, 24 | savedInstanceState: Bundle? 25 | ): View? { 26 | _binding = FragmentChooseModeBinding.inflate(inflater, container, false) 27 | return binding.root 28 | } 29 | 30 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 31 | super.onViewCreated(view, savedInstanceState) 32 | args.res.let { 33 | if (args.res > 0){ 34 | TableRateDialog(requireContext(), args.res).show() 35 | }} 36 | initButtons() 37 | } 38 | 39 | private fun initButtons() = with(binding){ 40 | addSubLayout.setOnClickListener { 41 | findNavController().navigate( 42 | ChooseModeFragmentDirections.actionChooseModeFragmentToAddSubListFragment( 43 | rate = -1, 44 | exerciseType = null, 45 | player = args.player 46 | ) 47 | ) 48 | } 49 | mulDivLayout.setOnClickListener { 50 | findNavController().navigate( 51 | ChooseModeFragmentDirections.actionChooseModeFragmentToMulDivListFragment( 52 | rate = -1, 53 | exerciseType = null, 54 | player = args.player 55 | ) 56 | ) 57 | } 58 | divisibilityLayout.setOnClickListener { 59 | findNavController().navigate( 60 | ChooseModeFragmentDirections.actionChooseModeFragmentToDivisibilityListFragment( 61 | rate = -1, 62 | exerciseType = null, 63 | player = args.player 64 | ) 65 | ) 66 | } 67 | unitsLayout.setOnClickListener { 68 | findNavController().navigate( 69 | ChooseModeFragmentDirections.actionChooseModeFragmentToUnitsListFragment( 70 | rate = -1, 71 | exerciseType = null, 72 | player = args.player 73 | ) 74 | ) 75 | } 76 | tableLayout.setOnClickListener { 77 | findNavController().navigate( 78 | ChooseModeFragmentDirections.actionChooseModeFragmentToTableGameFragment( 79 | args.player 80 | ) 81 | ) 82 | } 83 | } 84 | 85 | override fun onDestroy() { 86 | super.onDestroy() 87 | _binding = null 88 | } 89 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/values/font_certs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @array/com_google_android_gms_fonts_certs_dev 5 | @array/com_google_android_gms_fonts_certs_prod 6 | 7 | 8 | 9 | MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs= 10 | 11 | 12 | 13 | 14 | MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /androidApp/src/main/java/com/octbit/rutmath/util/base/BaseApplication.kt: -------------------------------------------------------------------------------- 1 | package com.octbit.rutmath.util.base 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.Application 5 | import com.octbit.rutmath.R 6 | import com.octbit.rutmath.data.AppDatabase 7 | import com.octbit.rutmath.data.model.Settings 8 | import com.octbit.rutmath.di.appModule 9 | import com.octbit.rutmath.shared.di.androidSharedModule 10 | import com.octbit.rutmath.shared.di.sharedModule 11 | import com.octbit.rutmath.shared.usecase.DataUseCase 12 | import io.reactivex.Completable 13 | import io.reactivex.android.schedulers.AndroidSchedulers 14 | import io.reactivex.schedulers.Schedulers 15 | import kotlinx.coroutines.CoroutineScope 16 | import kotlinx.coroutines.Dispatchers 17 | import kotlinx.coroutines.SupervisorJob 18 | import kotlinx.coroutines.launch 19 | import org.koin.android.ext.android.inject 20 | //import org.koin.android.ext.android.startKoin 21 | import org.koin.android.ext.koin.androidContext 22 | import org.koin.core.context.startKoin 23 | 24 | /** 25 | * Main application object. 26 | */ 27 | class BaseApplication : Application() { 28 | 29 | private val database: AppDatabase by inject() 30 | private val dataUseCase: DataUseCase by inject() 31 | 32 | // Application scope for coroutines 33 | private val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) 34 | 35 | override fun onCreate() { 36 | super.onCreate() 37 | startKoin { 38 | androidContext(this@BaseApplication) 39 | modules( 40 | appModule, // Existing Android module 41 | sharedModule, // Shared KMP module 42 | androidSharedModule // Android-specific shared module 43 | ) 44 | } 45 | initDatabaseIfNeeded() 46 | initSharedData() 47 | } 48 | 49 | @SuppressLint("CheckResult") 50 | private fun initDatabaseIfNeeded() { 51 | database.settingsDao().getAll() 52 | .observeOn(AndroidSchedulers.mainThread()) 53 | .subscribeOn(Schedulers.io()) 54 | .flatMapCompletable { databaseSettings -> 55 | if (databaseSettings.isEmpty()) { 56 | database.settingsDao() 57 | .insertAll( 58 | arrayListOf( 59 | Settings( 60 | maxNumberInBattleMode = 100, 61 | lastNickname1 = getString(R.string.player1), 62 | lastNickname2 = getString(R.string.player2), 63 | language = "en" 64 | ) 65 | ) 66 | ) 67 | .observeOn(AndroidSchedulers.mainThread()) 68 | .subscribeOn(Schedulers.io()) 69 | } else { 70 | Completable.complete() 71 | } 72 | } 73 | .subscribe() 74 | } 75 | 76 | /** 77 | * Initialize shared data using the new KMP architecture. 78 | */ 79 | private fun initSharedData() { 80 | applicationScope.launch { 81 | try { 82 | // Initialize default exercise types if needed 83 | dataUseCase.initializeDefaultExercisesIfNeeded() 84 | 85 | // Ensure settings exist 86 | dataUseCase.getSettings() 87 | } catch (e: Exception) { 88 | // Log error in production 89 | e.printStackTrace() 90 | } 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/logo.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 16 | 21 | 26 | 31 | 36 | 41 | 46 | 51 | 56 | 57 | -------------------------------------------------------------------------------- /androidApp/src/main/res/values-cs/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OK 4 | Zrušit 5 | Ano 6 | Ne 7 | Chyba 8 | Uložit 9 | Prázdné 10 | Tabulka výsledků: 11 | Hráč1 12 | Hráč2 13 | Skóre: 14 | %s skóre: %d\n%s skóre: %d 15 | %d. %s 16 | Přezdívka nemůže být prázdná! 17 | Vybrat jazyk 18 | Max. číslo pro souboj 19 | Technická univerzita Rzeszów 20 | Postupný bonus +%d 21 | Vyberte cvičení ze seznamu: 22 | Sčítání a odčítání 23 | Násobení a dělení 24 | Dělitelnost 25 | Převod jednotek 26 | Násobilka 27 | Vyberte hráče ze seznamu\nnebo vytvořte nového tlačítkem plus 28 | Zadejte jména hráčů 29 | Přezdívka Hráče1 (nahoře): 30 | Přezdívka Hráče2 (dole): 31 | Start 32 | Seznam výsledků je prázdný.\nHrajte v bojovém režimu, abyste viděli nejlepší výsledky hráčů! 33 | Nápověda 34 | Hodnota nemůže být větší než: %1$d 35 | Hodnota nemůže být menší než: %1$d 36 | Hra skončena 37 | Zadejte přezdívku nového hráče: 38 | Přezdívka již existuje! 39 | Lehké 40 | Střední 41 | Těžké 42 | Velmi těžké 43 | Je 44 | dělitelné 45 | Čas 46 | Délka 47 | Hmotnost 48 | Plocha 49 | Všechny 50 | d,h,min,sek 51 | \? 52 | https://w.prz.edu.pl/en/ 53 | https://weii.prz.edu.pl/en/ 54 | Cvičení 55 | Nastavení 56 | Souboj 57 | Žebříček 58 | -------------------------------------------------------------------------------- /androidApp/src/main/res/values-sk/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OK 4 | Zrušiť 5 | Áno 6 | Nie 7 | Chyba 8 | Uložiť 9 | Prázdne 10 | Tabuľka výsledkov: 11 | Hráč1 12 | Hráč2 13 | Skóre: 14 | %s skóre: %d\n%s skóre: %d 15 | %d. %s 16 | Prezývka nemôže byť prázdna! 17 | Vybrať jazyk 18 | Max. číslo pre súboj 19 | Technická univerzita Rzeszów 20 | Postupný bonus +%d 21 | Vyberte cvičenie zo zoznamu: 22 | Sčítanie a odčítanie 23 | Násobenie a delenie 24 | Deliteľnosť 25 | Konverzia jednotiek 26 | Násobiaca tabuľka 27 | Vyberte hráča zo zoznamu\nalebo vytvorte nového tlačidlom plus 28 | Zadajte mená hráčov 29 | Prezývka Hráča1 (hore): 30 | Prezývka Hráča2 (dole): 31 | Štart 32 | Zoznam výsledkov je prázdny.\nHrajte v súbojovom režime, aby ste videli najlepšie výsledky hráčov! 33 | Nápoveda 34 | Hodnota nemôže byť väčšia ako: %1$d 35 | Hodnota nemôže byť menšia ako: %1$d 36 | Hra skončená 37 | Zadajte prezývku nového hráča: 38 | Prezývka už existuje! 39 | Ľahké 40 | Stredné 41 | Ťažké 42 | Veľmi ťažké 43 | Je 44 | deliteľné 45 | Čas 46 | Dĺžka 47 | Hmotnosť 48 | Plocha 49 | Všetky 50 | d,h,min,sek 51 | \? 52 | https://w.prz.edu.pl/en/ 53 | https://weii.prz.edu.pl/en/ 54 | Cvičenia 55 | Nastavenia 56 | Súboj 57 | Rebríček 58 | -------------------------------------------------------------------------------- /androidApp/src/main/res/values-pl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OK 4 | Anuluj 5 | Tak 6 | Nie 7 | Błąd 8 | Zapisz 9 | Pusto 10 | Tablica wyników: 11 | Gracz1 12 | Gracz2 13 | Wynik: 14 | %s wynik: %d\n%s wynik: %d 15 | %d. %s 16 | Nazwa nie może być pusta! 17 | Wybierz język 18 | Maks. liczba dla pojedynku 19 | Politechnika Rzeszowska 20 | Bonus za sekwencję +%d 21 | Wybierz ćwiczenie z listy: 22 | Dodawanie i odejmowanie 23 | Mnożenie i dzielenie 24 | Podzielność 25 | Zamiana jednostek 26 | Tabliczka mnożenia 27 | Wybierz gracza z listy\nlub stwórz nowego za pomocą plusa 28 | Podaj nazwy graczy 29 | Nazwa Gracza1 (góra): 30 | Nazwa Gracza2 (dół): 31 | Rozpocznij 32 | Tablica wyników jest pusta.\nGraj w trybie bitwy by zobaczyć najlepsze wyniki! 33 | Podpowiedź 34 | Wartość nie może być większa niż: %1$d 35 | Wartość nie może być mniejsza niż: %1$d 36 | Gra zakończona 37 | Podaj nazwę nowego gracza: 38 | Nazwa już istnieje! 39 | Łatwy 40 | Średni 41 | Trudny 42 | Bardzo Trudny 43 | Czy 44 | jest podzielne przez 45 | Czas 46 | Długość 47 | Waga 48 | Powierzchnia 49 | Wszystkie 50 | d,godz,min,sek 51 | \? 52 | https://w.prz.edu.pl 53 | https://weii.prz.edu.pl 54 | Ćwiczenia 55 | Ustawienia 56 | Pojedynek 57 | Wyniki 58 | 59 | -------------------------------------------------------------------------------- /androidApp/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RUTMath 3 | OK 4 | Cancel 5 | Yes 6 | No 7 | Error 8 | Save 9 | Empty 10 | Scoreboard: 11 | Player1 12 | Player2 13 | Score: 14 | %s score: %d\n%s score: %d 15 | %d. %s 16 | Nickname can not be empty! 17 | Select language 18 | Max. number for duel 19 | Rzeszów University of Technology 20 | Sequential bonus +%d 21 | Select an exercise from the list: 22 | Addition and Subtraction 23 | Multiplication and Division 24 | Divisibility 25 | Unit conversion 26 | Multiplication table 27 | Choose a player from the list\nor create new one with plus button 28 | Enter players nicknames 29 | Player1 (top) nickname: 30 | Player2 (bottom) nickname: 31 | Start 32 | Scores list is empty.\nPlay in battle mode to see best players results! 33 | Hint 34 | Value can not be bigger than: %1$d 35 | Value can not be smaller than: %1$d 36 | Game ended 37 | Enter nickname of new player: 38 | Nickname exists! 39 | Easy 40 | Medium 41 | Hard 42 | Very Hard 43 | Is 44 | divisible by 45 | Time 46 | Length 47 | Weight 48 | Surface 49 | All 50 | d,h,min,sec 51 | ? 52 | https://w.prz.edu.pl/en/ 53 | https://weii.prz.edu.pl/en/ 54 | Exercises 55 | Settings 56 | Battle 57 | Leaderboard 58 | 59 | 60 | -------------------------------------------------------------------------------- /androidApp/src/main/res/layout/fragment_units_game.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 27 | 28 | 38 | 39 | 49 | 50 | 61 | 62 | 68 | 69 | 77 | 78 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /androidApp/src/main/res/values-hu/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OK 4 | Mégse 5 | Igen 6 | Nem 7 | Hiba 8 | Mentés 9 | Üres 10 | Eredménytábla: 11 | Játékos1 12 | Játékos2 13 | Pontszám: 14 | %s pontszám: %d\n%s pontszám: %d 15 | %d. %s 16 | A becenév nem lehet üres! 17 | Nyelv kiválasztása 18 | Max. szám párbajhoz 19 | Rzeszówi Műszaki Egyetem 20 | Egymást követő bónusz +%d 21 | Válasszon gyakorlatot a listából: 22 | Összeadás és kivonás 23 | Szorzás és osztás 24 | Oszthatóság 25 | Mértékegység átváltás 26 | Szorzótábla 27 | Válasszon játékost a listából\nvagy hozzon létre újat a plusz gombbal 28 | Adja meg a játékosok neveit 29 | Játékos1 (felső) becenév: 30 | Játékos2 (alsó) becenév: 31 | Indítás 32 | Az eredménylista üres.\nJátsszon csata módban a legjobb játékos eredmények megtekintéséhez! 33 | Tipp 34 | Az érték nem lehet nagyobb mint: %1$d 35 | Az érték nem lehet kisebb mint: %1$d 36 | Játék befejezve 37 | Adja meg az új játékos becenevét: 38 | A becenév már létezik! 39 | Könnyű 40 | Közepes 41 | Nehéz 42 | Nagyon nehéz 43 | Osztható-e 44 | ezzel 45 | Idő 46 | Hosszúság 47 | Súly 48 | Felület 49 | Összes 50 | n,ó,perc,mp 51 | \? 52 | https://w.prz.edu.pl/en/ 53 | https://weii.prz.edu.pl/en/ 54 | Gyakorlatok 55 | Beállítások 56 | Csata 57 | Ranglista 58 | -------------------------------------------------------------------------------- /androidApp/src/main/res/values-nl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OK 4 | Annuleren 5 | Ja 6 | Nee 7 | Fout 8 | Opslaan 9 | Leeg 10 | Scorebord: 11 | Speler1 12 | Speler2 13 | Score: 14 | %s score: %d\n%s score: %d 15 | %d. %s 16 | Bijnaam mag niet leeg zijn! 17 | Selecteer taal 18 | Max. getal voor duel 19 | Universiteit van Technologie Rzeszów 20 | Opeenvolgende bonus +%d 21 | Selecteer een oefening uit de lijst: 22 | Optellen en aftrekken 23 | Vermenigvuldigen en delen 24 | Deelbaarheid 25 | Eenheidsconversie 26 | Vermenigvuldigingstafel 27 | Kies een speler uit de lijst\nof maak een nieuwe aan met de plus knop 28 | Voer spelersnamen in 29 | Speler1 (boven) bijnaam: 30 | Speler2 (onder) bijnaam: 31 | Start 32 | Scorelijst is leeg.\nSpeel in gevechtsmode om de beste spelerresultaten te zien! 33 | Hint 34 | Waarde mag niet groter zijn dan: %1$d 35 | Waarde mag niet kleiner zijn dan: %1$d 36 | Spel beëindigd 37 | Voer bijnaam van nieuwe speler in: 38 | Bijnaam bestaat al! 39 | Makkelijk 40 | Gemiddeld 41 | Moeilijk 42 | Zeer moeilijk 43 | Is 44 | deelbaar door 45 | Tijd 46 | Lengte 47 | Gewicht 48 | Oppervlakte 49 | Alle 50 | d,u,min,sec 51 | \? 52 | https://w.prz.edu.pl/en/ 53 | https://weii.prz.edu.pl/en/ 54 | Oefeningen 55 | Instellingen 56 | Gevecht 57 | Klassement 58 | -------------------------------------------------------------------------------- /androidApp/src/main/res/values-pt/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | OK 3 | Cancelar 4 | Sim 5 | Não 6 | Error 7 | Salvar 8 | Vazio 9 | Placar: 10 | Jogador1 11 | Jogador2 12 | Pontuação: 13 | %s pontos: %d\n%s pontos: %d 14 | %d. %s 15 | Apelido não pode ser vazio! 16 | Selecionar linguagem 17 | Número máximo para duelos 18 | Universidade de Tecnologia de Rzeszów 19 | Bônus de sequência +%d 20 | Selecione um exercício da lista: 21 | Adição e Subtração 22 | Multiplicação e Divisão 23 | Divisibilidade 24 | Conversão de unidades 25 | Tabela de multiplicação 26 | Escolha um jogador da lista\nou crie um novo com o botão mais 27 | Entre com os apelidos dos jogadores 28 | Apelido Jogador1 (cima): 29 | Apelido Jogador2 (baixo): 30 | Iniciar 31 | A lista de pontuações está vazia.\nJogue no modo de batalha para ver os melhores resultados dos jogadores! 32 | Dica 33 | O valor não pode ser maior do que: %1$d 34 | O valor não pode ser menor do que: %1$d 35 | Jogo encerrado 36 | Digite o apelido do novo jogador: 37 | O apelido já existe! 38 | Fácil 39 | Médio 40 | Difícil 41 | Muito Difícil 42 | É 43 | Divisível por 44 | Tempo 45 | Comprimento 46 | Peso 47 | Área 48 | Todos 49 | d,h,min,seg 50 | \? 51 | https://w.prz.edu.pl/en/ 52 | https://weii.prz.edu.pl/en/ 53 | Exercícios 54 | Configurações 55 | Batalha 56 | Lista de Pontuações 57 | 58 | 59 | -------------------------------------------------------------------------------- /androidApp/src/main/res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OK 4 | Abbrechen 5 | Ja 6 | Nein 7 | Fehler 8 | Speichern 9 | Leer 10 | Punktetafel: 11 | Spieler1 12 | Spieler2 13 | Punkte: 14 | %s Punkte: %d\n%s Punkte: %d 15 | %d. %s 16 | Spitzname darf nicht leer sein! 17 | Sprache auswählen 18 | Max. Zahl für Duell 19 | Technische Universität Rzeszów 20 | Aufeinanderfolgender Bonus +%d 21 | Wählen Sie eine Übung aus der Liste: 22 | Addition und Subtraktion 23 | Multiplikation und Division 24 | Teilbarkeit 25 | Einheitenumrechnung 26 | Multiplikationstabelle 27 | Wählen Sie einen Spieler aus der Liste\noder erstellen Sie einen neuen mit dem Plus-Button 28 | Spielernamen eingeben 29 | Spieler1 (oben) Spitzname: 30 | Spieler2 (unten) Spitzname: 31 | Start 32 | Die Punkteliste ist leer.\nSpielen Sie im Kampfmodus, um die besten Spielerergebnisse zu sehen! 33 | Hinweis 34 | Wert darf nicht größer sein als: %1$d 35 | Wert darf nicht kleiner sein als: %1$d 36 | Spiel beendet 37 | Spitzname des neuen Spielers eingeben: 38 | Spitzname existiert bereits! 39 | Einfach 40 | Mittel 41 | Schwer 42 | Sehr schwer 43 | Ist 44 | teilbar durch 45 | Zeit 46 | Länge 47 | Gewicht 48 | Fläche 49 | Alle 50 | T,Std,Min,Sek 51 | \? 52 | https://w.prz.edu.pl/en/ 53 | https://weii.prz.edu.pl/en/ 54 | Übungen 55 | Einstellungen 56 | Kampf 57 | Bestenliste 58 | -------------------------------------------------------------------------------- /androidApp/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | OK 3 | Annuler 4 | Oui 5 | Non 6 | Erreur 7 | Enregistrer 8 | Vide 9 | Tableau des scores : 10 | Joueur1 11 | Joueur2 12 | Score : 13 | %s score : %d\n%s score : %d 14 | %d. %s 15 | Le nom ne peut pas être vide ! 16 | Sélectionnez la langue 17 | Nombre maximal pour les duels 18 | Université de Technologie de Rzeszów 19 | Bonus de séquence : +%d 20 | Choisissez un exercice dans la liste : 21 | Additions et Soustractions 22 | Multiplications et Divisions 23 | Divisibilité 24 | Conversion d\'unités 25 | Tables de multiplications 26 | Choisissez une joueur dans la liste\nou ajoutez un joueur avec le bouton + 27 | Entrez le nom des joueurs 28 | Joueur1 (en haut) : 29 | Joueur2 (en bas) : 30 | Démarrer 31 | La liste des scores est vide. Jouez en mode duel pour savoir qui est le meilleur joueur ! 32 | Conseil 33 | La valeur ne peut pas être plus grande que : %1$d 34 | La valeur ne peut pas être plus petite que : %1$d 35 | Partie terminée 36 | Entrez le nom du nouveau joueur : 37 | Ce nom existe déjà ! 38 | Facile 39 | Intermédiaire 40 | Difficile 41 | Très difficile 42 | Est-ce que 43 | est divisible par 44 | Durée 45 | Poids 46 | Surface 47 | Tout 48 | j,h,min,sec 49 | Longueur 50 | \? 51 | https://w.prz.edu.pl/en/ 52 | https://weii.prz.edu.pl/en/ 53 | Exercices 54 | Paramètres 55 | Duel 56 | Liste des Scores 57 | 58 | 59 | --------------------------------------------------------------------------------