diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..d43da689 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +# icons are binary +*.png binary + +# code files and `gradlew` should always have LF line endings +*.kt text +*.xml text +gradlew text + +# other files are treated as text by default +* text=auto diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 07234ec8..01e7e702 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -9,6 +9,7 @@ on: - master - dev pull_request: + workflow_dispatch: jobs: build-and-test: # instrumental tests run on MacOS to enable hardware acceleration @@ -27,7 +28,7 @@ jobs: run: ./gradlew test - name: Run instrumental tests # warning: it depends on "grant permission to gradlew" step - uses: reactivecircus/android-emulator-runner@v1 + uses: reactivecircus/android-emulator-runner@v2 with: api-level: 29 script: ./gradlew connectedCheck diff --git a/.gitignore b/.gitignore index 9544a0dc..b9f22659 100644 --- a/.gitignore +++ b/.gitignore @@ -1,227 +1,227 @@ -# auto-generated scripts -detekt -android-wait-for-emulator - -# Created by https://www.toptal.com/developers/gitignore/api/androidstudio,android,gradle -# Edit at https://www.toptal.com/developers/gitignore?templates=androidstudio,android,gradle - -### Android ### -# Built application files -*.apk -*.aar -*.ap_ -*.aab - -# Files for the ART/Dalvik VM -*.dex - -# Java class files -*.class - -# Generated files -bin/ -gen/ -out/ -# Uncomment the following line in case you need and you don't have the release build type files in your app -# release/ - -# Gradle files -.gradle/ -build/ - -# Local configuration file (sdk path, etc) -local.properties - -# Proguard folder generated by Eclipse -proguard/ - -# Log Files -*.log - -# Android Studio Navigation editor temp files -.navigation/ - -# Android Studio captures folder -captures/ - -# IntelliJ -*.iml -.idea/workspace.xml -.idea/tasks.xml -.idea/gradle.xml -.idea/assetWizardSettings.xml -.idea/dictionaries -.idea/libraries -# Android Studio 3 in .gitignore file. -.idea/caches -.idea/modules.xml -# Comment next line if keeping position of elements in Navigation Editor is relevant for you -.idea/navEditor.xml - -# Keystore files -# Uncomment the following lines if you do not want to check your keystore files in. -#*.jks -#*.keystore - -# External native build folder generated in Android Studio 2.2 and later -.externalNativeBuild -.cxx/ - -# Google Services (e.g. APIs or Firebase) -# google-services.json - -# Freeline -freeline.py -freeline/ -freeline_project_description.json - -# fastlane -fastlane/report.xml -fastlane/Preview.html -fastlane/screenshots -fastlane/test_output -fastlane/readme.md - -# Version control -vcs.xml - -# lint -lint/intermediates/ -lint/generated/ -lint/outputs/ -lint/tmp/ -# lint/reports/ - -### Android Patch ### -gen-external-apklibs -output.json - -# Replacement of .externalNativeBuild directories introduced -# with Android Studio 3.5. - -### Gradle ### -.gradle - -# Ignore Gradle GUI config -gradle-app.setting - -# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) -!gradle-wrapper.jar - -# Cache of project -.gradletasknamecache - -# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 -# gradle/wrapper/gradle-wrapper.properties - -### Gradle Patch ### -**/build/ - -### AndroidStudio ### -# Covers files to be ignored for android development using Android Studio. - -# Built application files - -# Files for the ART/Dalvik VM - -# Java class files - -# Generated files - -# Gradle files - -# Signing files -.signing/ - -# Local configuration file (sdk path, etc) - -# Proguard folder generated by Eclipse - -# Log Files - -# Android Studio -/*/build/ -/*/local.properties -/*/out -/*/*/build -/*/*/production -*.ipr -*~ -*.swp - -# Android Patch - -# External native build folder generated in Android Studio 2.2 and later - -# NDK -obj/ - -# IntelliJ IDEA -*.iws -/out/ - -# User-specific configurations -.idea/caches/ -.idea/libraries/ -.idea/shelf/ -.idea/.name -.idea/compiler.xml -.idea/copyright/profiles_settings.xml -.idea/encodings.xml -.idea/misc.xml -.idea/scopes/scope_settings.xml -.idea/vcs.xml -.idea/jsLibraryMappings.xml -.idea/datasources.xml -.idea/dataSources.ids -.idea/sqlDataSources.xml -.idea/dynamic.xml -.idea/uiDesigner.xml - -# OS-specific files -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db - -# Legacy Eclipse project files -.classpath -.project -.cproject -.settings/ - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.war -*.ear - -# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) -hs_err_pid* - -## Plugin-specific files: - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Mongo Explorer plugin -.idea/mongoSettings.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -### AndroidStudio Patch ### - -!/gradle/wrapper/gradle-wrapper.jar - -# End of https://www.toptal.com/developers/gitignore/api/androidstudio,android,gradle +# auto-generated scripts +detekt +android-wait-for-emulator + +# Created by https://www.toptal.com/developers/gitignore/api/androidstudio,android,gradle +# Edit at https://www.toptal.com/developers/gitignore?templates=androidstudio,android,gradle + +### Android ### +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +### Android Patch ### +gen-external-apklibs +output.json + +# Replacement of .externalNativeBuild directories introduced +# with Android Studio 3.5. + +### Gradle ### +.gradle + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties + +### Gradle Patch ### +**/build/ + +### AndroidStudio ### +# Covers files to be ignored for android development using Android Studio. + +# Built application files + +# Files for the ART/Dalvik VM + +# Java class files + +# Generated files + +# Gradle files + +# Signing files +.signing/ + +# Local configuration file (sdk path, etc) + +# Proguard folder generated by Eclipse + +# Log Files + +# Android Studio +/*/build/ +/*/local.properties +/*/out +/*/*/build +/*/*/production +*.ipr +*~ +*.swp + +# Android Patch + +# External native build folder generated in Android Studio 2.2 and later + +# NDK +obj/ + +# IntelliJ IDEA +*.iws +/out/ + +# User-specific configurations +.idea/caches/ +.idea/libraries/ +.idea/shelf/ +.idea/.name +.idea/compiler.xml +.idea/copyright/profiles_settings.xml +.idea/encodings.xml +.idea/misc.xml +.idea/scopes/scope_settings.xml +.idea/vcs.xml +.idea/jsLibraryMappings.xml +.idea/datasources.xml +.idea/dataSources.ids +.idea/sqlDataSources.xml +.idea/dynamic.xml +.idea/uiDesigner.xml + +# OS-specific files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Legacy Eclipse project files +.classpath +.project +.cproject +.settings/ + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.war +*.ear + +# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) +hs_err_pid* + +## Plugin-specific files: + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Mongo Explorer plugin +.idea/mongoSettings.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### AndroidStudio Patch ### + +!/gradle/wrapper/gradle-wrapper.jar + +# End of https://www.toptal.com/developers/gitignore/api/androidstudio,android,gradle diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8c8710a..8ea931cd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,57 +1,57 @@ -# Contributing to Learn Braille - -## Build - -To work with the project you need `android-studio >= 3.3`, you can get it [here](https:://developer.android.com/studio). - -For building and running the app, your either need to download a virtual Android device image (this can be done from within the Android Studio) or connect a physical device (with developer options enabled and USB debugging turned on) via USB cable. - -## Git workflow - -- Write short and informative commit messages. Mention the issue you're working on (begins with `#`), e. g. `implement something(#0)`. [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/). -- Follow [a successful Git branching model](https://nvie.com/posts/a-successful-git-branching-model/). - -## Coding style - -- Kotlin [coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html) + Java [google style guides](https://google.github.io/styleguide/javaguide.html) and [oracle](https://www.oracle.com/technetwork/java/codeconvtoc-136057.html). -- Also look around each time you do something new to see, how such a thing was formatted and implemented before. -- Set up Android Studio proper Kotlin code style `editor -> code style -> kotlin -> set from -> predefined -> Kotlin style guide`. -- Apply autoformatting to edited files each time before commit. - -## Adding content - -There is a handy DSL that allows writing content in a typesafe way. - -- All app content should be placed into `com.github.braillesystems.learnbraille.res` package. -- Use `DslTest.kt` file as DSL tutorial. - -Information correctness should be checked in compile-time or during app initialization runtime as much as possible. If some additional info is needed, do not hardcode it. Just request the new DSL feature via GitHub Issues. - -Adding rules, prevent lambda of capturing context that will be invalid next time the fragment entered, so use `Fragment.getString` outside of lambdas. - -#### Adding course - -1. Create lessons by `lessons` delegate. -2. Create a course in `CourseBuilder` and add lessons to it. - -Always use `com.github.braillesystems.learnbraille.res.content` value to get materials, they are indexed here in a proper way. - -#### Adding deck - -1. Add a new deck tag to `DeckTags`. -2. Map tag to deck's predicate in `DecksBuilder`. -3. Map deck's tag to user-visible string in `deckTagToName`. - -#### Adding materials - -1. Create materials by one of delegates: `markers` or `symbols`. -2. Add created materials to the `contens` (`materials` delegate). -3. Add to `inputSymbolPrintRules` and `showSymbolPrintRules`, or to `inputMarkerPrintRules` and `showMarkerPrintRules`. - -Symbols that are not from a particular alphabet and do not exist on the classical American keyboard should be treated as special and be added via `enum class`. - -New materials can be marked as known by default in `knownMaterials` (`known` delegate). - -## Database - -Database scheme is described [here](https://github.com/braille-systems/learn-braille/blob/master/database.md). +# Contributing to Learn Braille + +## Build + +To work with the project you need `android-studio >= 3.3`, you can get it [here](https:://developer.android.com/studio). + +For building and running the app, your either need to download a virtual Android device image (this can be done from within the Android Studio) or connect a physical device (with developer options enabled and USB debugging turned on) via USB cable. + +## Git workflow + +- Write short and informative commit messages. Mention the issue you're working on (begins with `#`), e. g. `implement something(#0)`. [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/). +- Follow [a successful Git branching model](https://nvie.com/posts/a-successful-git-branching-model/). + +## Coding style + +- Kotlin [coding conventions](https://kotlinlang.org/docs/reference/coding-conventions.html) + Java [google style guides](https://google.github.io/styleguide/javaguide.html) and [oracle](https://www.oracle.com/technetwork/java/codeconvtoc-136057.html). +- Also look around each time you do something new to see, how such a thing was formatted and implemented before. +- Set up Android Studio proper Kotlin code style `editor -> code style -> kotlin -> set from -> predefined -> Kotlin style guide`. +- Apply autoformatting to edited files each time before commit. + +## Adding content + +There is a handy DSL that allows writing content in a typesafe way. + +- All app content should be placed into `com.github.braillesystems.learnbraille.res` package. +- Use `DslTest.kt` file as DSL tutorial. + +Information correctness should be checked in compile-time or during app initialization runtime as much as possible. If some additional info is needed, do not hardcode it. Just request the new DSL feature via GitHub Issues. + +Adding rules, prevent lambda of capturing context that will be invalid next time the fragment entered, so use `Fragment.getString` outside of lambdas. + +#### Adding course + +1. Create lessons by `lessons` delegate. +2. Create a course in `CourseBuilder` and add lessons to it. + +Always use `com.github.braillesystems.learnbraille.res.content` value to get materials, they are indexed here in a proper way. + +#### Adding deck + +1. Add a new deck tag to `DeckTags`. +2. Map tag to deck's predicate in `DecksBuilder`. +3. Map deck's tag to user-visible string in `deckTagToName`. + +#### Adding materials + +1. Create materials by one of delegates: `markers` or `symbols`. +2. Add created materials to the `contens` (`materials` delegate). +3. Add to `inputSymbolPrintRules` and `showSymbolPrintRules`, or to `inputMarkerPrintRules` and `showMarkerPrintRules`. + +Symbols that are not from a particular alphabet and do not exist on the classical American keyboard should be treated as special and be added via `enum class`. + +New materials can be marked as known by default in `knownMaterials` (`known` delegate). + +## Database + +Database scheme is described [here](https://github.com/braille-systems/learn-braille/blob/master/database.md). diff --git a/README.md b/README.md index 4ad82ddf..8e89fe57 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,33 @@ -# Learn Braille - -[![Actions Status](https://github.com/braille-systems/learn-braille/workflows/Android%20CI/badge.svg)](https://github.com/braille-systems/learn-braille/actions) -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) - -Learn Braille is an Android application for teaching the tactile writing system created -by Louis Braille. - -It is one of the few apps primarily designed for the Russian Braille system, -but also other systems could be easily added. - -The app can be used with -[Braille Trainer](https://github.com/braille-systems/braille-trainer) -and [tiles](https://github.com/braille-systems/braille-tiles) -or without them. - -Feel free to [contribute](https://github.com/braille-systems/learn-braille/blob/master/CONTRIBUTING.md)!
-Take a look at [wiki pages (Russian)](https://github.com/braille-systems/learn-braille/wiki)
- - -## User's Guidelines - -The app is [available](https://play.google.com/store/apps/details?id=com.github.braillesystems.learnbraille&hl=ru) in the Google Play. - -### System Requirements and limitations - -To successfully run this application, your smartphone or tablet PC must satisfy the following conditions: -- Android 4.4 KitKat or higher (`5.1` is required for `Google TalkBack` optimizations). -- Screen of size not less than 4 inches. - -For accessibility, you will require TalkBack service. -It is pre-installed by default on a majority of devices.
-For others, it is available in the Google Play. +# Learn Braille + +[![Actions Status](https://github.com/braille-systems/learn-braille/workflows/Android%20CI/badge.svg)](https://github.com/braille-systems/learn-braille/actions) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) + +Learn Braille is an Android application for teaching the tactile writing system created +by Louis Braille. + +It is one of the few apps primarily designed for the Russian Braille system, +but also other systems could be easily added. + +The app can be used with +[Braille Trainer](https://github.com/braille-systems/braille-trainer) +and [tiles](https://github.com/braille-systems/braille-tiles) +or without them. + +Feel free to [contribute](https://github.com/braille-systems/learn-braille/blob/master/CONTRIBUTING.md)!
+Take a look at [wiki pages (Russian)](https://github.com/braille-systems/learn-braille/wiki)
+ + +## User's Guidelines + +The app is [available](https://play.google.com/store/apps/details?id=com.github.braillesystems.learnbraille&hl=ru) in the Google Play. + +### System Requirements and limitations + +To successfully run this application, your smartphone or tablet PC must satisfy the following conditions: +- Android 4.4 KitKat or higher (`5.1` is required for `Google TalkBack` optimizations). +- Screen of size not less than 4 inches. + +For accessibility, you will require TalkBack service. +It is pre-installed by default on a majority of devices.
+For others, it is available in the Google Play. diff --git a/app/.gitignore b/app/.gitignore index 3543521e..796b96d1 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1 +1 @@ -/build +/build diff --git a/app/app.iml b/app/app.iml index 2cc3fdd3..7329b281 100644 --- a/app/app.iml +++ b/app/app.iml @@ -1,258 +1,258 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index dd487745..ae6e74d9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,122 +1,125 @@ -apply plugin: 'com.android.application' - -apply plugin: 'kotlin-android' - -apply plugin: 'kotlin-android-extensions' - -apply plugin: 'kotlin-kapt' - -apply plugin: 'androidx.navigation.safeargs' - -apply plugin: 'kotlinx-serialization' - -apply plugin: 'org.jetbrains.dokka' - -android { - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - compileSdkVersion 29 - buildToolsVersion "29.0.3" - dataBinding { - enabled = true - } - defaultConfig { - applicationId "com.github.braillesystems.learnbraille" - minSdkVersion 19 - targetSdkVersion 29 - versionCode 15 - versionName "1.2.1" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - vectorDrawables.useSupportLibrary = true - multiDexEnabled = true - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - productFlavors { - } - dokka { - outputDirectory = "$buildDir/dokka" - packageOptions { - prefix = "android" - suppress = true - } - packageOptions{ - prefix = "androidx" - suppress = true - } - } - lintOptions { - abortOnError false - } -} - -dependencies { - def lifecycle_version = "2.2.0" - def koin_version = '2.1.5' - - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.0.2' - implementation 'androidx.core:core-ktx:1.0.2' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - testImplementation 'junit:junit:4.12' - - // Usb serial - implementation 'com.github.felHR85:UsbSerial:6.1.0' - - // Room - implementation "androidx.room:room-runtime:$version_room" - kapt "androidx.room:room-compiler:$version_room" - implementation "androidx.room:room-ktx:$version_room" - testImplementation "androidx.room:room-testing:$version_room" - implementation "androidx.lifecycle:lifecycle-extensions:2.0.0" - - // Testing - androidTestImplementation 'androidx.test.ext:junit:1.1.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' - - // Navigation - implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1' - implementation 'androidx.navigation:navigation-ui-ktx:2.2.1' - - // Timber logging - implementation 'com.jakewharton.timber:timber:4.7.1' - - // Lifecycle - implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" - - //Design - implementation 'com.google.android.material:material:1.1.0' - implementation 'androidx.appcompat:appcompat:1.1.0' - - // Serialization - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" // or "kotlin-stdlib-jdk8" - // Downgraded to '0.14.0' because of https://github.com/Kotlin/kotlinx.serialization/issues/576 - implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0" // JVM dependency - - // Koin - implementation "org.koin:koin-core:$koin_version" - implementation "org.koin:koin-core-ext:$koin_version" - implementation "org.koin:koin-android:$koin_version" - implementation "org.koin:koin-android-viewmodel:$koin_version" - implementation "org.koin:koin-android-ext:$koin_version" - - // Many methods - implementation 'com.android.support:multidex:1.0.3' - - //Settings - implementation 'androidx.preference:preference:1.1.1' -} - -android { - lintOptions { - disable 'MissingTranslation' - } -} +apply plugin: 'com.android.application' + +apply plugin: 'kotlin-android' + +apply plugin: 'kotlin-android-extensions' + +apply plugin: 'kotlin-kapt' + +apply plugin: 'androidx.navigation.safeargs' + +apply plugin: 'kotlinx-serialization' + +apply plugin: 'org.jetbrains.dokka' + +android { + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + compileSdkVersion 29 + buildToolsVersion "29.0.3" + dataBinding { + enabled = true + } + defaultConfig { + applicationId "com.github.braillesystems.learnbraille" + minSdkVersion 19 + targetSdkVersion 29 + versionCode 17 + versionName "1.3.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables.useSupportLibrary = true + multiDexEnabled = true + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + productFlavors { + } + dokka { + outputDirectory = "$buildDir/dokka" + packageOptions { + prefix = "android" + suppress = true + } + packageOptions{ + prefix = "androidx" + suppress = true + } + } + lintOptions { + abortOnError false + } +} + +dependencies { + def lifecycle_version = "2.2.0" + def koin_version = '2.1.5' + + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.core:core-ktx:1.0.2' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + testImplementation 'junit:junit:4.12' + + // Usb serial + implementation 'com.github.felHR85:UsbSerial:6.1.0' + + // Room + implementation "androidx.room:room-runtime:$version_room" + kapt "androidx.room:room-compiler:$version_room" + implementation "androidx.room:room-ktx:$version_room" + testImplementation "androidx.room:room-testing:$version_room" + implementation "androidx.lifecycle:lifecycle-extensions:2.0.0" + + // Testing + androidTestImplementation 'androidx.test.ext:junit:1.1.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + + // Navigation + implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1' + implementation 'androidx.navigation:navigation-ui-ktx:2.2.1' + + // Timber logging + implementation 'com.jakewharton.timber:timber:4.7.1' + + // Lifecycle + implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" + + //Design + implementation 'com.google.android.material:material:1.1.0' + implementation 'androidx.appcompat:appcompat:1.1.0' + + // Serialization + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" // or "kotlin-stdlib-jdk8" + // Downgraded to '0.14.0' because of https://github.com/Kotlin/kotlinx.serialization/issues/576 + implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0" // JVM dependency + + // Koin + implementation "org.koin:koin-core:$koin_version" + implementation "org.koin:koin-core-ext:$koin_version" + implementation "org.koin:koin-android:$koin_version" + implementation "org.koin:koin-android-viewmodel:$koin_version" + implementation "org.koin:koin-android-ext:$koin_version" + + // Many methods + implementation 'com.android.support:multidex:1.0.3' + + //Settings + implementation 'androidx.preference:preference:1.1.1' + + // Playing musical notes + implementation 'com.github.braille-systems:perfectTune:bc09ec2890' +} + +android { + lintOptions { + disable 'MissingTranslation' + } +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 6e7ffa99..f1b42451 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,21 +1,21 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/Utils.kt b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/Utils.kt new file mode 100644 index 00000000..24cdf5a8 --- /dev/null +++ b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/Utils.kt @@ -0,0 +1,37 @@ +package com.github.braillesystems.learnbraille.data + +import com.github.braillesystems.learnbraille.data.entities.DBid +import com.github.braillesystems.learnbraille.data.entities.User +import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository +import com.github.braillesystems.learnbraille.utils.unreachable + +open class UnreachablePreferencesRepository : PreferenceRepository { + override val buzzEnabled: Boolean + get() = unreachable + override val toastsEnabled: Boolean + get() = unreachable + override val golubinaBookStepsEnabled: Boolean + get() = unreachable + override val slateStylusStepsEnabled: Boolean + get() = unreachable + override val traverseDotsInEnumerationOrder: Boolean + get() = unreachable + override val inputOnFlyCheck: Boolean + get() = unreachable + override val additionalAnnouncementsEnabled: Boolean + get() = unreachable + override val practiceUseOnlyKnownMaterials: Boolean + get() = unreachable + override val extendedAccessibilityEnabled: Boolean + get() = unreachable + override val additionalQrCodeButtonEnabled: Boolean + get() = unreachable + override val isWriteModeFirst: Boolean + get() = unreachable + override val teacherModeEnabled: Boolean + get() = unreachable + override val currentUserId: DBid + get() = unreachable + + override suspend fun getCurrentUser(): User = unreachable +} diff --git a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/db/LearnBrailleDatabaseTest.kt b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/db/LearnBrailleDatabaseTest.kt index f67a9ab5..b97aea4a 100644 --- a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/db/LearnBrailleDatabaseTest.kt +++ b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/db/LearnBrailleDatabaseTest.kt @@ -1,183 +1,183 @@ -package com.github.braillesystems.learnbraille.data.db - -import androidx.room.Room -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import com.github.braillesystems.learnbraille.data.entities.* -import com.github.braillesystems.learnbraille.data.entities.BrailleDot.E -import com.github.braillesystems.learnbraille.data.entities.BrailleDot.F -import com.github.braillesystems.learnbraille.res.SymbolType -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import java.io.IOException - -@RunWith(AndroidJUnit4::class) -class LearnBrailleDatabaseTest { - - private lateinit var db: LearnBrailleDatabase - - private val users = listOf( - User( - login = "default", - name = "John Smith" - ) - ) - private val materials = listOf( - Material( - 1, Symbol( - char = 'А', - brailleDots = BrailleDots(F, E, E, E, E, E), - type = SymbolType.ru - ) - ) - ) - private val decks = listOf( - Deck( - id = 1, - tag = "Ru letters" - ) - ) - private val cards = listOf( - Card( - deckId = 1, - materialId = 1 - ) - ) - private val courses = listOf( - Course( - id = 1, - name = "Super course", - description = "Oh, it's so good" - ) - ) - private val lessons = listOf( - Lesson( - id = 1, - name = "First", - description = "First First First", - courseId = 1 - ), - Lesson( - id = 2, - name = "Last", - description = "Last Last Last", - courseId = 1 - ) - ) - private val steps = listOf( - Step( - id = 1, - data = FirstInfo("FirstInfo"), - lessonId = 1, courseId = 1 - ), - Step( - id = 2, - data = Info("Open your book"), - lessonId = 1, courseId = 1 - ), - Step( - id = 3, - data = ShowDots( - text = "Перед Вами полное шеститочие", - brailleDots = BrailleDots(F, F, F, F, F, F) - ), - lessonId = 1, courseId = 1 - ), - Step( - id = 4, - data = InputDots( - text = "Введите все шесть точек", - brailleDots = BrailleDots(F, F, F, F, F, F) - ), - lessonId = 2, courseId = 1 - ), - Step( - id = 5, - data = Show(materials.first()), - lessonId = 2, courseId = 1 - ), - Step( - id = 6, - data = Input(materials.first()), - lessonId = 2, courseId = 1 - ), - Step( - id = 7, - data = LastInfo("LastInfo"), - lessonId = 2, courseId = 1 - ) - ) - private val annotations = listOf( - StepAnnotation(id = 1, name = "a1"), - StepAnnotation(id = 2, name = "a2") - ) - private val stepAnnotations = listOf( - StepHasAnnotation( - courseId = 1, - lessonId = 3, - stepId = 2, - annotationId = 1 - ) - ) - - @Before - fun createDB() { - val context = InstrumentationRegistry.getInstrumentation().targetContext - db = Room - .inMemoryDatabaseBuilder(context, LearnBrailleDatabase::class.java) - .allowMainThreadQueries() - .build().apply { - runBlocking { - userDao.insert(users) - materialDao.insert(materials) - deckDao.insert(decks) - cardDao.insert(cards) - courseDao.insert(courses) - lessonDao.insert(lessons) - stepDao.insert(steps) - stepAnnotationDao.insert(annotations) - stepHasAnnotationDao.insert(stepAnnotations) - } - } - } - - @After - @Throws(IOException::class) - fun closeDB() { - db.close() - } - - @Test - fun testUsers() = runBlocking { - assertEquals("default", db.userDao.user(1)!!.login) - } - - @Test - fun testMaterials() = runBlocking { - val data = db.materialDao.material(1)!!.data - require(data is Symbol) - assertEquals(BrailleDots(F, E, E, E, E, E), data.brailleDots) - } - - @Test - fun testDecks() = runBlocking { - assertEquals("Ru letters", db.deckDao.deck(1)!!.tag) - } - - @Test - fun testCourses() = runBlocking { - assertEquals("Super course", db.courseDao.course(1)!!.name) - } - - @Test - fun testSteps() = runBlocking { - for ((i, step) in steps.withIndex()) { - val fromDb = db.stepDao.step(i + 1L)!! - assertEquals(step, fromDb) - } - } -} +package com.github.braillesystems.learnbraille.data.db + +import androidx.room.Room +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.github.braillesystems.learnbraille.data.entities.* +import com.github.braillesystems.learnbraille.data.entities.BrailleDot.E +import com.github.braillesystems.learnbraille.data.entities.BrailleDot.F +import com.github.braillesystems.learnbraille.res.SymbolType +import kotlinx.coroutines.runBlocking +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.io.IOException + +@RunWith(AndroidJUnit4::class) +class LearnBrailleDatabaseTest { + + private lateinit var db: LearnBrailleDatabase + + private val users = listOf( + User( + login = "default", + name = "John Smith" + ) + ) + private val materials = listOf( + Material( + 1, Symbol( + char = 'А', + brailleDots = BrailleDots(F, E, E, E, E, E), + type = SymbolType.ru + ) + ) + ) + private val decks = listOf( + Deck( + id = 1, + tag = "Ru letters" + ) + ) + private val cards = listOf( + Card( + deckId = 1, + materialId = 1 + ) + ) + private val courses = listOf( + Course( + id = 1, + name = "Super course", + description = "Oh, it's so good" + ) + ) + private val lessons = listOf( + Lesson( + id = 1, + name = "First", + description = "First First First", + courseId = 1 + ), + Lesson( + id = 2, + name = "Last", + description = "Last Last Last", + courseId = 1 + ) + ) + private val steps = listOf( + Step( + id = 1, + data = FirstInfo("FirstInfo"), + lessonId = 1, courseId = 1 + ), + Step( + id = 2, + data = Info("Open your book"), + lessonId = 1, courseId = 1 + ), + Step( + id = 3, + data = ShowDots( + text = "Перед Вами полное шеститочие", + brailleDots = BrailleDots(F, F, F, F, F, F) + ), + lessonId = 1, courseId = 1 + ), + Step( + id = 4, + data = InputDots( + text = "Введите все шесть точек", + brailleDots = BrailleDots(F, F, F, F, F, F) + ), + lessonId = 2, courseId = 1 + ), + Step( + id = 5, + data = Show(materials.first()), + lessonId = 2, courseId = 1 + ), + Step( + id = 6, + data = Input(materials.first()), + lessonId = 2, courseId = 1 + ), + Step( + id = 7, + data = LastInfo("LastInfo"), + lessonId = 2, courseId = 1 + ) + ) + private val annotations = listOf( + StepAnnotation(id = 1, name = "a1"), + StepAnnotation(id = 2, name = "a2") + ) + private val stepAnnotations = listOf( + StepHasAnnotation( + courseId = 1, + lessonId = 3, + stepId = 2, + annotationId = 1 + ) + ) + + @Before + fun createDB() { + val context = InstrumentationRegistry.getInstrumentation().targetContext + db = Room + .inMemoryDatabaseBuilder(context, LearnBrailleDatabase::class.java) + .allowMainThreadQueries() + .build().apply { + runBlocking { + userDao.insert(users) + materialDao.insert(materials) + deckDao.insert(decks) + cardDao.insert(cards) + courseDao.insert(courses) + lessonDao.insert(lessons) + stepDao.insert(steps) + stepAnnotationDao.insert(annotations) + stepHasAnnotationDao.insert(stepAnnotations) + } + } + } + + @After + @Throws(IOException::class) + fun closeDB() { + db.close() + } + + @Test + fun testUsers() = runBlocking { + assertEquals("default", db.userDao.user(1)!!.login) + } + + @Test + fun testMaterials() = runBlocking { + val data = db.materialDao.material(1)!!.data + require(data is Symbol) + assertEquals(BrailleDots(F, E, E, E, E, E), data.brailleDots) + } + + @Test + fun testDecks() = runBlocking { + assertEquals("Ru letters", db.deckDao.deck(1)!!.tag) + } + + @Test + fun testCourses() = runBlocking { + assertEquals("Super course", db.courseDao.course(1)!!.name) + } + + @Test + fun testSteps() = runBlocking { + for ((i, step) in steps.withIndex()) { + val fromDb = db.stepDao.step(i + 1L)!! + assertEquals(step, fromDb) + } + } +} diff --git a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepositoryTest.kt b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepositoryTest.kt index 4109b78b..9bb302f4 100644 --- a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepositoryTest.kt +++ b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepositoryTest.kt @@ -3,12 +3,12 @@ package com.github.braillesystems.learnbraille.data.repository import androidx.room.Room import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry +import com.github.braillesystems.learnbraille.data.UnreachablePreferencesRepository import com.github.braillesystems.learnbraille.data.db.LearnBrailleDatabase -import com.github.braillesystems.learnbraille.data.entities.Action -import com.github.braillesystems.learnbraille.data.entities.PracticeHintAction -import com.github.braillesystems.learnbraille.data.entities.TheoryPassStep +import com.github.braillesystems.learnbraille.data.entities.* import com.github.braillesystems.learnbraille.utils.Days import com.github.braillesystems.learnbraille.utils.minus +import com.github.braillesystems.learnbraille.utils.unreachable import kotlinx.coroutines.runBlocking import org.junit.After import org.junit.Assert.assertEquals @@ -42,6 +42,7 @@ class ActionsRepositoryTest { } repo = ActionsRepositoryImpl( db.actionDao, + UnreachablePreferencesRepository(), getCurrDate = { currDate }, keepActionsTime = Days(100) ) diff --git a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepositoryTest.kt b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepositoryTest.kt index 22dac0f2..c9061447 100644 --- a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepositoryTest.kt +++ b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepositoryTest.kt @@ -3,13 +3,13 @@ package com.github.braillesystems.learnbraille.data.repository import androidx.room.Room import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry +import com.github.braillesystems.learnbraille.data.UnreachablePreferencesRepository import com.github.braillesystems.learnbraille.data.db.LearnBrailleDatabase import com.github.braillesystems.learnbraille.data.entities.* -import com.github.braillesystems.learnbraille.data.entities.BrailleDot.E -import com.github.braillesystems.learnbraille.data.entities.BrailleDot.F -import com.github.braillesystems.learnbraille.res.MarkerType +import com.github.braillesystems.learnbraille.data.entities.StepAnnotation +import com.github.braillesystems.learnbraille.data.entities.BrailleDot.* +import com.github.braillesystems.learnbraille.res.* import com.github.braillesystems.learnbraille.res.SymbolType -import com.github.braillesystems.learnbraille.utils.unreachable import kotlinx.coroutines.runBlocking import org.junit.After import org.junit.Assert.assertEquals @@ -157,29 +157,9 @@ class MaterialsRepositoryTest { repo = MaterialsRepositoryImpl( db.deckDao, db.cardDao, - object : PreferenceRepository { - override val buzzEnabled: Boolean - get() = unreachable - override val toastsEnabled: Boolean - get() = unreachable - override val golubinaBookStepsEnabled: Boolean - get() = unreachable - override val slateStylusStepsEnabled: Boolean - get() = unreachable - override val traverseDotsInEnumerationOrder: Boolean - get() = unreachable - override val inputOnFlyCheck: Boolean - get() = unreachable - override val additionalAnnouncementsEnabled: Boolean - get() = unreachable + object : UnreachablePreferencesRepository() { override val practiceUseOnlyKnownMaterials: Boolean get() = true - override val extendedAccessibilityEnabled: Boolean - get() = unreachable - override val additionalQrCodeButtonEnabled: Boolean - get() = unreachable - override val isWriteModeFirst: Boolean - get() = unreachable override val currentUserId: DBid get() = 1 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5a937f5c..30863e5c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,27 +1,29 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt b/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt index 51dc64a5..41d6256b 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt @@ -1,122 +1,122 @@ -package com.github.braillesystems.learnbraille - -import android.app.Application -import com.github.braillesystems.learnbraille.data.db.LearnBrailleDatabase -import com.github.braillesystems.learnbraille.data.dsl.UsersCourse -import com.github.braillesystems.learnbraille.data.entities.BrailleDots -import com.github.braillesystems.learnbraille.data.repository.* -import com.github.braillesystems.learnbraille.ui.screens.practice.CardViewModelFactory -import com.github.braillesystems.learnbraille.utils.devnull -import org.koin.android.ext.android.get -import org.koin.android.ext.koin.androidContext -import org.koin.core.Koin -import org.koin.core.context.startKoin -import org.koin.dsl.module -import timber.log.Timber - -class LearnBrailleApplication : Application() { - - override fun onCreate() { - super.onCreate() - Timber.plant(Timber.DebugTree()) - Timber.i("onCreate") - - val koinModule = module { - - single { LearnBrailleDatabase.buildDatabase(this@LearnBrailleApplication) } - - factory { - ActionsRepositoryImpl(get().actionDao) - } - factory { - ActionsRepositoryImpl(get().actionDao) - } - - factory { - PreferenceRepositoryImpl( - this@LearnBrailleApplication, - get().userDao - ) - } - factory { - PreferenceRepositoryImpl( - this@LearnBrailleApplication, - get().userDao - ) - } - - factory { - val db = get() - MaterialsRepositoryImpl(db.deckDao, db.cardDao, get()) - } - - factory { - val db = get() - PracticeRepositoryImpl( - this@LearnBrailleApplication, - db.deckDao, get(), get() - ) - } - factory { - val db = get() - PracticeRepositoryImpl( - this@LearnBrailleApplication, - db.deckDao, get(), get() - ) - } - - factory { - BrowserRepositoryImpl( - this@LearnBrailleApplication, - get(), get() - ) - } - factory { - BrowserRepositoryImpl( - this@LearnBrailleApplication, - get(), get() - ) - } - - factory { - get().run { - TheoryRepositoryImpl( - lessonDao, stepDao, - currentStepDao, lastCourseStepDao, lastLessonStepDao, knownMaterialDao, - get(), get() - ) - } - } - factory { - get().run { - TheoryRepositoryImpl( - lessonDao, stepDao, - currentStepDao, lastCourseStepDao, lastLessonStepDao, knownMaterialDao, - get(), get() - ) - } - } - - factory { (getEnteredDots: () -> BrailleDots) -> - CardViewModelFactory( - get(), get(), get(), - this@LearnBrailleApplication, - getEnteredDots - ) - } - } - - koin = startKoin { - androidContext(this@LearnBrailleApplication) - modules(koinModule) - }.koin - - // Touch database to force it's preparation - get().devnull - } -} - -lateinit var koin: Koin - private set - -val COURSE = UsersCourse +package com.github.braillesystems.learnbraille + +import android.app.Application +import com.github.braillesystems.learnbraille.data.db.LearnBrailleDatabase +import com.github.braillesystems.learnbraille.data.dsl.UsersCourse +import com.github.braillesystems.learnbraille.data.entities.BrailleDots +import com.github.braillesystems.learnbraille.data.repository.* +import com.github.braillesystems.learnbraille.ui.screens.practice.CardViewModelFactory +import com.github.braillesystems.learnbraille.utils.devnull +import org.koin.android.ext.android.get +import org.koin.android.ext.koin.androidContext +import org.koin.core.Koin +import org.koin.core.context.startKoin +import org.koin.dsl.module +import timber.log.Timber + +class LearnBrailleApplication : Application() { + + override fun onCreate() { + super.onCreate() + Timber.plant(Timber.DebugTree()) + Timber.i("onCreate") + + val koinModule = module { + + single { LearnBrailleDatabase.buildDatabase(this@LearnBrailleApplication) } + + factory { + PreferenceRepositoryImpl( + this@LearnBrailleApplication, + get().userDao + ) + } + factory { + PreferenceRepositoryImpl( + this@LearnBrailleApplication, + get().userDao + ) + } + + factory { + ActionsRepositoryImpl(get().actionDao, get()) + } + factory { + ActionsRepositoryImpl(get().actionDao, get()) + } + + factory { + val db = get() + MaterialsRepositoryImpl(db.deckDao, db.cardDao, get()) + } + + factory { + val db = get() + PracticeRepositoryImpl( + this@LearnBrailleApplication, + db.deckDao, get(), get() + ) + } + factory { + val db = get() + PracticeRepositoryImpl( + this@LearnBrailleApplication, + db.deckDao, get(), get() + ) + } + + factory { + BrowserRepositoryImpl( + this@LearnBrailleApplication, + get(), get() + ) + } + factory { + BrowserRepositoryImpl( + this@LearnBrailleApplication, + get(), get() + ) + } + + factory { + get().run { + TheoryRepositoryImpl( + lessonDao, stepDao, + currentStepDao, lastCourseStepDao, lastLessonStepDao, knownMaterialDao, + get(), get() + ) + } + } + factory { + get().run { + TheoryRepositoryImpl( + lessonDao, stepDao, + currentStepDao, lastCourseStepDao, lastLessonStepDao, knownMaterialDao, + get(), get() + ) + } + } + + factory { (getEnteredDots: () -> BrailleDots) -> + CardViewModelFactory( + get(), get(), get(), + this@LearnBrailleApplication, + getEnteredDots + ) + } + } + + koin = startKoin { + androidContext(this@LearnBrailleApplication) + modules(koinModule) + }.koin + + // Touch database to force it's preparation + get().devnull + } +} + +lateinit var koin: Koin + private set + +val COURSE = UsersCourse diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/db/LearnBrailleDatabase.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/db/LearnBrailleDatabase.kt index 3f52dad7..90896701 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/db/LearnBrailleDatabase.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/db/LearnBrailleDatabase.kt @@ -1,327 +1,341 @@ -package com.github.braillesystems.learnbraille.data.db - -import android.annotation.SuppressLint -import android.content.ContentValues -import android.content.Context -import android.database.sqlite.SQLiteDatabase -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import androidx.room.TypeConverters -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase -import com.github.braillesystems.learnbraille.data.entities.* -import com.github.braillesystems.learnbraille.res.prepopulationData -import com.github.braillesystems.learnbraille.utils.DateConverters -import com.github.braillesystems.learnbraille.utils.devnull -import com.github.braillesystems.learnbraille.utils.logged -import com.github.braillesystems.learnbraille.utils.scope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import org.koin.core.KoinComponent -import org.koin.core.get -import timber.log.Timber - -@Database( - entities = - [ - User::class, Material::class, KnownMaterial::class, - Deck::class, Card::class, - Course::class, Lesson::class, Step::class, StepAnnotation::class, StepHasAnnotation::class, - CurrentStep::class, LastCourseStep::class, LastLessonStep::class, - Action::class - ], - version = 19, - exportSchema = true -) -@TypeConverters( - BrailleDotsConverters::class, - MaterialDataTypeConverters::class, - StepDataConverters::class, - ActionTypeConverters::class, - DateConverters::class -) -abstract class LearnBrailleDatabase : RoomDatabase(), KoinComponent { - - abstract val userDao: UserDao - abstract val materialDao: MaterialDao - abstract val knownMaterialDao: KnownMaterialDao - - abstract val deckDao: DeckDao - abstract val cardDao: CardDao - - abstract val courseDao: CourseDao - abstract val lessonDao: LessonDao - abstract val stepDao: StepDao - abstract val stepAnnotationDao: StepAnnotationDao - abstract val stepHasAnnotationDao: StepHasAnnotationDao - - abstract val currentStepDao: CurrentStepDao - abstract val lastCourseStepDao: LastCourseStepDao - abstract val lastLessonStepDao: LastLessonStepDao - - abstract val actionDao: ActionDao - - @Volatile - private lateinit var prepareDbJob: Job - - /** - * Android Room prepopulation and migrations are lazy, - * they will start with the first request, blocking it. - */ - private fun init(): LearnBrailleDatabase = this.also { - prepareDbJob = scope().launch { - Timber.i("Requesting value from database to force database callbacks and migrations") - Timber.i("Start database preparation") - userDao.user(1).devnull - Timber.i("Finnish database preparation") - } - } - - val isInitialized: Boolean by logged { - prepareDbJob.isCompleted - } - - companion object { - - const val name = "learn_braille_database" - - /** - * Try to run `buildDatabase` before first user's request (mb in Application's `onCreate`) - * to make DB likely prepared until it is really needed. - */ - fun buildDatabase(context: Context) = Room - .databaseBuilder( - context.applicationContext, - LearnBrailleDatabase::class.java, - name - ) - .addCallback(object : Callback(), KoinComponent { - - @SuppressLint("SyntheticAccessor") - override fun onCreate(db: SupportSQLiteDatabase) { - super.onCreate(db) - Timber.d("onCreate") - prepopulate() - } - - override fun onDestructiveMigration(db: SupportSQLiteDatabase) { - super.onDestructiveMigration(db) - Timber.i("onDestructiveMigration") - prepopulate() - } - - private fun prepopulate() { - Timber.i("Prepopulate DB") - get().apply { - scope(prepareDbJob).launch { - prepopulationData.run { - users?.let { userDao.insert(it) } - materials?.let { materialDao.insert(it) } - decks?.let { deckDao.insert(it) } - cards?.let { cardDao.insert(it) } - courses?.let { courseDao.insert(it) } - lessons?.let { lessonDao.insert(it) } - steps?.let { stepDao.insert(it) } - stepAnnotations?.let { stepAnnotationDao.insert(it) } - stepsHasAnnotations?.let { stepHasAnnotationDao.insert(it) } - knownMaterials?.let { knownMaterialDao.insert(it) } - } - } - } - } - }) - .addMigrations( - MIGRATION_16_17, - MIGRATION_17_18, - MIGRATION_18_19 - ) - .build() - .init() - } -} - -private val MIGRATION_16_17 = object : Migration(16, 17), KoinComponent { - override fun migrate(database: SupportSQLiteDatabase) { - Timber.i("Start 16-17 migration") - - database.execSQL("delete from materials") - database.execSQL("delete from steps") - database.execSQL("delete from step_has_annotations") - - Timber.i("Removed old content") - - prepopulationData.run { - materials?.forEach { - database.insert( - "materials", - SQLiteDatabase.CONFLICT_IGNORE, - it.run { - ContentValues().apply { - put("id", id) - put("data", MaterialDataTypeConverters().to(data)) - } - } - ) - } - Timber.i("Materials loaded") - - steps?.forEach { - database.insert( - "steps", - SQLiteDatabase.CONFLICT_IGNORE, - it.run { - ContentValues().apply { - put("id", id) - put("course_id", courseId) - put("lesson_id", lessonId) - put("data", StepDataConverters().to(data)) - } - } - ) - } - Timber.i("Steps loaded") - - stepsHasAnnotations?.forEach { - database.insert( - "step_has_annotations", - SQLiteDatabase.CONFLICT_IGNORE, - it.run { - ContentValues().apply { - put("course_id", courseId) - put("lesson_id", lessonId) - put("step_id", stepId) - put("annotation_id", annotationId) - } - } - ) - } - Timber.i("Steps-annotations mapping loaded") - } - } -} - -private val MIGRATION_17_18 = object : Migration(17, 18) { - override fun migrate(database: SupportSQLiteDatabase) { - Timber.i("Start 17-18 migration") - database.execSQL(Action.creationQuery) - Timber.i("Actions table created") - } -} - -private val MIGRATION_18_19 = object : Migration(18, 19) { - override fun migrate(database: SupportSQLiteDatabase) { - Timber.i("Start 18-19 migration") - - database.execSQL("delete from lessons") - database.execSQL("delete from steps") - database.execSQL("delete from decks") - database.execSQL("delete from cards") - database.execSQL("delete from materials") - database.execSQL("delete from step_has_annotations") - database.execSQL("delete from step_annotations") - - Timber.i("Old data removed") - - prepopulationData.run { - lessons?.forEach { - database.insert( - "lessons", - SQLiteDatabase.CONFLICT_ABORT, - it.run { - ContentValues().apply { - put("id", id) - put("course_id", courseId) - put("name", name) - put("description", description) - } - } - ) - } - - steps?.forEach { - database.insert( - "steps", - SQLiteDatabase.CONFLICT_ABORT, - it.run { - ContentValues().apply { - put("id", id) - put("course_id", courseId) - put("lesson_id", lessonId) - put("data", StepDataConverters().to(data)) - } - } - ) - } - - decks?.forEach { - database.insert( - "decks", - SQLiteDatabase.CONFLICT_ABORT, - it.run { - ContentValues().apply { - put("id", id) - put("tag", tag) - } - } - ) - } - - cards?.forEach { - database.insert( - "cards", - SQLiteDatabase.CONFLICT_ABORT, - it.run { - ContentValues().apply { - put("deck_id", deckId) - put("material_id", materialId) - } - } - ) - } - - materials?.forEach { - database.insert( - "materials", - SQLiteDatabase.CONFLICT_ABORT, - it.run { - ContentValues().apply { - put("id", id) - put("data", MaterialDataTypeConverters().to(data)) - } - } - ) - } - - stepAnnotations?.forEach { - database.insert( - "step_annotations", - SQLiteDatabase.CONFLICT_ABORT, - it.run { - ContentValues().apply { - put("id", id) - put("name", name) - } - } - ) - } - - stepsHasAnnotations?.forEach { - database.insert( - "step_has_annotations", - SQLiteDatabase.CONFLICT_ABORT, - it.run { - ContentValues().apply { - put("course_id", courseId) - put("lesson_id", lessonId) - put("step_id", stepId) - put("annotation_id", annotationId) - } - } - ) - } - } - - Timber.i("Finish 18-19 migration") - } -} +package com.github.braillesystems.learnbraille.data.db + +import android.annotation.SuppressLint +import android.content.ContentValues +import android.content.Context +import android.database.sqlite.SQLiteDatabase +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import com.github.braillesystems.learnbraille.data.entities.* +import com.github.braillesystems.learnbraille.res.prepopulationData +import com.github.braillesystems.learnbraille.utils.DateConverters +import com.github.braillesystems.learnbraille.utils.devnull +import com.github.braillesystems.learnbraille.utils.logged +import com.github.braillesystems.learnbraille.utils.scope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import org.koin.core.KoinComponent +import org.koin.core.get +import timber.log.Timber + +@Database( + entities = + [ + User::class, Material::class, KnownMaterial::class, + Deck::class, Card::class, + Course::class, Lesson::class, Step::class, StepAnnotation::class, StepHasAnnotation::class, + CurrentStep::class, LastCourseStep::class, LastLessonStep::class, + Action::class + ], + version = 20, + exportSchema = true +) +@TypeConverters( + BrailleDotsConverters::class, + MaterialDataTypeConverters::class, + StepDataConverters::class, + ActionTypeConverters::class, + DateConverters::class +) +abstract class LearnBrailleDatabase : RoomDatabase(), KoinComponent { + + abstract val userDao: UserDao + abstract val materialDao: MaterialDao + abstract val knownMaterialDao: KnownMaterialDao + + abstract val deckDao: DeckDao + abstract val cardDao: CardDao + + abstract val courseDao: CourseDao + abstract val lessonDao: LessonDao + abstract val stepDao: StepDao + abstract val stepAnnotationDao: StepAnnotationDao + abstract val stepHasAnnotationDao: StepHasAnnotationDao + + abstract val currentStepDao: CurrentStepDao + abstract val lastCourseStepDao: LastCourseStepDao + abstract val lastLessonStepDao: LastLessonStepDao + + abstract val actionDao: ActionDao + + @Volatile + private lateinit var prepareDbJob: Job + + /** + * Android Room prepopulation and migrations are lazy, + * they will start with the first request, blocking it. + */ + private fun init(): LearnBrailleDatabase = this.also { + prepareDbJob = scope().launch { + Timber.i("Requesting value from database to force database callbacks and migrations") + Timber.i("Start database preparation") + userDao.user(1).devnull + Timber.i("Finnish database preparation") + } + } + + val isInitialized: Boolean by logged { + prepareDbJob.isCompleted + } + + companion object { + + const val name = "learn_braille_database" + + /** + * Try to run `buildDatabase` before first user's request (mb in Application's `onCreate`) + * to make DB likely prepared until it is really needed. + */ + fun buildDatabase(context: Context) = Room + .databaseBuilder( + context.applicationContext, + LearnBrailleDatabase::class.java, + name + ) + .addCallback(object : Callback(), KoinComponent { + + @SuppressLint("SyntheticAccessor") + override fun onCreate(db: SupportSQLiteDatabase) { + super.onCreate(db) + Timber.d("onCreate") + prepopulate() + } + + override fun onDestructiveMigration(db: SupportSQLiteDatabase) { + super.onDestructiveMigration(db) + Timber.i("onDestructiveMigration") + prepopulate() + } + + private fun prepopulate() { + Timber.i("Prepopulate DB") + get().apply { + scope(prepareDbJob).launch { + prepopulationData.run { + users?.let { userDao.insert(it) } + materials?.let { materialDao.insert(it) } + decks?.let { deckDao.insert(it) } + cards?.let { cardDao.insert(it) } + courses?.let { courseDao.insert(it) } + lessons?.let { lessonDao.insert(it) } + steps?.let { stepDao.insert(it) } + stepAnnotations?.let { stepAnnotationDao.insert(it) } + stepsHasAnnotations?.let { stepHasAnnotationDao.insert(it) } + knownMaterials?.let { knownMaterialDao.insert(it) } + } + } + } + } + }) + .addMigrations( + MIGRATION_16_17, + MIGRATION_17_18, + MIGRATION_18_19, + MIGRATION_19_20 + ) + .build() + .init() + } +} + +private val MIGRATION_16_17 = object : Migration(16, 17), KoinComponent { + override fun migrate(database: SupportSQLiteDatabase) { + Timber.i("Start 16-17 migration") + + database.execSQL("delete from materials") + database.execSQL("delete from steps") + database.execSQL("delete from step_has_annotations") + + Timber.i("Removed old content") + + prepopulationData.run { + materials?.forEach { + database.insert( + "materials", + SQLiteDatabase.CONFLICT_IGNORE, + it.run { + ContentValues().apply { + put("id", id) + put("data", MaterialDataTypeConverters().to(data)) + } + } + ) + } + Timber.i("Materials loaded") + + steps?.forEach { + database.insert( + "steps", + SQLiteDatabase.CONFLICT_IGNORE, + it.run { + ContentValues().apply { + put("id", id) + put("course_id", courseId) + put("lesson_id", lessonId) + put("data", StepDataConverters().to(data)) + } + } + ) + } + Timber.i("Steps loaded") + + stepsHasAnnotations?.forEach { + database.insert( + "step_has_annotations", + SQLiteDatabase.CONFLICT_IGNORE, + it.run { + ContentValues().apply { + put("course_id", courseId) + put("lesson_id", lessonId) + put("step_id", stepId) + put("annotation_id", annotationId) + } + } + ) + } + Timber.i("Steps-annotations mapping loaded") + } + } +} + +private val MIGRATION_17_18 = object : Migration(17, 18) { + override fun migrate(database: SupportSQLiteDatabase) { + Timber.i("Start 17-18 migration") + database.execSQL(Action.creationQuery) + Timber.i("Actions table created") + } +} + +fun updateTheoryAndMaterials(database: SupportSQLiteDatabase) { + database.execSQL("delete from lessons") + database.execSQL("delete from steps") + database.execSQL("delete from decks") + database.execSQL("delete from cards") + database.execSQL("delete from materials") + database.execSQL("delete from step_has_annotations") + database.execSQL("delete from step_annotations") + + Timber.i("Old data removed") + + prepopulationData.run { + lessons?.forEach { + database.insert( + "lessons", + SQLiteDatabase.CONFLICT_ABORT, + it.run { + ContentValues().apply { + put("id", id) + put("course_id", courseId) + put("name", name) + put("description", description) + } + } + ) + } + + steps?.forEach { + database.insert( + "steps", + SQLiteDatabase.CONFLICT_ABORT, + it.run { + ContentValues().apply { + put("id", id) + put("course_id", courseId) + put("lesson_id", lessonId) + put("data", StepDataConverters().to(data)) + } + } + ) + } + + decks?.forEach { + database.insert( + "decks", + SQLiteDatabase.CONFLICT_ABORT, + it.run { + ContentValues().apply { + put("id", id) + put("tag", tag) + } + } + ) + } + + cards?.forEach { + database.insert( + "cards", + SQLiteDatabase.CONFLICT_ABORT, + it.run { + ContentValues().apply { + put("deck_id", deckId) + put("material_id", materialId) + } + } + ) + } + + materials?.forEach { + database.insert( + "materials", + SQLiteDatabase.CONFLICT_ABORT, + it.run { + ContentValues().apply { + put("id", id) + put("data", MaterialDataTypeConverters().to(data)) + } + } + ) + } + + stepAnnotations?.forEach { + database.insert( + "step_annotations", + SQLiteDatabase.CONFLICT_ABORT, + it.run { + ContentValues().apply { + put("id", id) + put("name", name) + } + } + ) + } + + stepsHasAnnotations?.forEach { + database.insert( + "step_has_annotations", + SQLiteDatabase.CONFLICT_ABORT, + it.run { + ContentValues().apply { + put("course_id", courseId) + put("lesson_id", lessonId) + put("step_id", stepId) + put("annotation_id", annotationId) + } + } + ) + } + } + + Timber.i("New data inserted") +} + +private val MIGRATION_18_19 = object : Migration(18, 19) { + override fun migrate(database: SupportSQLiteDatabase) { + Timber.i("Start 18-19 migration") + updateTheoryAndMaterials(database) + Timber.i("Finish 18-19 migration") + } +} + +private val MIGRATION_19_20 = object : Migration(19, 20) { + override fun migrate(database: SupportSQLiteDatabase) { + Timber.i("Start 19-20 migration") + updateTheoryAndMaterials(database) + Timber.i("Finish 19-20 migration") + } +} + diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/DataStorage.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/DataStorage.kt index 23f71f28..31655a87 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/DataStorage.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/DataStorage.kt @@ -141,7 +141,7 @@ class DataBuilder( DecksBuilder(block).side { it.deckToPredicate.forEach { (deck, p) -> val deckId = - if (deck.tag == DeckTags.all) ALL_CARDS_DECK_ID + if (deck.tag == DeckTags.Grouping.All.tag) ALL_CARDS_DECK_ID else decks.size + 2L val deckMaterials = materials.filter { material -> p(material.data) } if (deckMaterials.isNotEmpty()) { diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/BrailleDots.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/BrailleDots.kt index e62f0bd0..8aa8e47b 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/BrailleDots.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/BrailleDots.kt @@ -1,78 +1,78 @@ -package com.github.braillesystems.learnbraille.data.entities - -import androidx.room.TypeConverter -import com.github.braillesystems.learnbraille.data.entities.BrailleDot.E -import com.github.braillesystems.learnbraille.data.entities.BrailleDot.F -import kotlinx.serialization.Serializable - -/** - * State of one Braille dot. - */ -enum class BrailleDot { - E, // Empty - F; // Filled - - companion object Factories { - fun valueOf(b: Boolean) = if (b) F else E - fun valueOf(c: Char) = valueOf(c.toString()) - } -} - -/** - * Combination on Braille dots for one symbol in 6-dots notation. - */ -@Serializable -data class BrailleDots( - val b1: BrailleDot = E, val b2: BrailleDot = E, val b3: BrailleDot = E, - val b4: BrailleDot = E, val b5: BrailleDot = E, val b6: BrailleDot = E -) { - - constructor(dots: BooleanArray) : this( - dots.map(BrailleDot.Factories::valueOf) - ) - - constructor(dots: List) : this( - b1 = dots[0], - b2 = dots[1], - b3 = dots[2], - b4 = dots[3], - b5 = dots[4], - b6 = dots[5] - ) { - require(dots.size == 6) { - "Only 6 dots braille notation supported" - } - } - - constructor(string: String) : this( - string - .toCharArray() - .map(BrailleDot.Factories::valueOf) - ) - - override fun toString() = "$b1$b2$b3$b4$b5$b6" -} - -val BrailleDots.list: List - get() = listOf(b1, b2, b3, b4, b5, b6) - -val BrailleDots.spelling: String - get() = filled.joinToString(separator = ", ", transform = Int::toString) - -val BrailleDots.filled: List - get() = list - .mapIndexedNotNull { index, brailleDot -> - if (brailleDot == F) index + 1 - else null - } - -operator fun BrailleDots.contains(i: Int) = i in filled - -class BrailleDotsConverters { - - @TypeConverter - fun to(brailleDots: BrailleDots) = brailleDots.toString() - - @TypeConverter - fun from(data: String) = BrailleDots(data) -} +package com.github.braillesystems.learnbraille.data.entities + +import androidx.room.TypeConverter +import com.github.braillesystems.learnbraille.data.entities.BrailleDot.E +import com.github.braillesystems.learnbraille.data.entities.BrailleDot.F +import kotlinx.serialization.Serializable + +/** + * State of one Braille dot. + */ +enum class BrailleDot { + E, // Empty + F; // Filled + + companion object Factories { + fun valueOf(b: Boolean) = if (b) F else E + fun valueOf(c: Char) = valueOf(c.toString()) + } +} + +/** + * Combination on Braille dots for one symbol in 6-dots notation. + */ +@Serializable +data class BrailleDots( + val b1: BrailleDot = E, val b2: BrailleDot = E, val b3: BrailleDot = E, + val b4: BrailleDot = E, val b5: BrailleDot = E, val b6: BrailleDot = E +) { + + constructor(dots: BooleanArray) : this( + dots.map(BrailleDot.Factories::valueOf) + ) + + constructor(dots: List) : this( + b1 = dots[0], + b2 = dots[1], + b3 = dots[2], + b4 = dots[3], + b5 = dots[4], + b6 = dots[5] + ) { + require(dots.size == 6) { + "Only 6 dots braille notation supported" + } + } + + constructor(string: String) : this( + string + .toCharArray() + .map(BrailleDot.Factories::valueOf) + ) + + override fun toString() = "$b1$b2$b3$b4$b5$b6" +} + +val BrailleDots.list: List + get() = listOf(b1, b2, b3, b4, b5, b6) + +val BrailleDots.spelling: String + get() = filled.joinToString(separator = ", ", transform = Int::toString) + +val BrailleDots.filled: List + get() = list + .mapIndexedNotNull { index, brailleDot -> + if (brailleDot == F) index + 1 + else null + } + +operator fun BrailleDots.contains(i: Int) = i in filled + +class BrailleDotsConverters { + + @TypeConverter + fun to(brailleDots: BrailleDots) = brailleDots.toString() + + @TypeConverter + fun from(data: String) = BrailleDots(data) +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Lessons.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Lessons.kt index 15fa45c2..05a34c6a 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Lessons.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Lessons.kt @@ -1,34 +1,34 @@ -package com.github.braillesystems.learnbraille.data.entities - -import androidx.room.* - -typealias LessonName = String -typealias LessonDesc = String - -@Entity(tableName = "lessons", primaryKeys = ["id", "course_id"]) -data class Lesson( - val id: DBid, - @ColumnInfo(name = "course_id") - val courseId: DBid, - val name: LessonName, - val description: LessonDesc -) - -@Dao -interface LessonDao { - - @Insert - suspend fun insert(lessons: List) - - @Query( - """ - select * from lessons - where course_id = :courseId - order by id - """ - ) - suspend fun allCourseLessons(courseId: DBid): List - - @Query("delete from lessons") - suspend fun clear() -} +package com.github.braillesystems.learnbraille.data.entities + +import androidx.room.* + +typealias LessonName = String +typealias LessonDesc = String + +@Entity(tableName = "lessons", primaryKeys = ["id", "course_id"]) +data class Lesson( + val id: DBid, + @ColumnInfo(name = "course_id") + val courseId: DBid, + val name: LessonName, + val description: LessonDesc +) + +@Dao +interface LessonDao { + + @Insert + suspend fun insert(lessons: List) + + @Query( + """ + select * from lessons + where course_id = :courseId + order by id + """ + ) + suspend fun allCourseLessons(courseId: DBid): List + + @Query("delete from lessons") + suspend fun clear() +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepData.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepData.kt index ede280b8..25ab9b00 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepData.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepData.kt @@ -83,6 +83,20 @@ data class Input( } } +typealias Phrase = List + +@Serializable +data class InputPhraseLetter( + val phrase: Phrase, + val pos: Int +) : BaseInput() { + + override val brailleDots: BrailleDots + get() = when (val data = phrase[pos].data) { + is OneBrailleSymbol -> data.brailleDots + } +} + /** * Step prompts the user to enter dots with specific numbers. * diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Steps.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Steps.kt index fe68006c..58cfc949 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Steps.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Steps.kt @@ -1,6 +1,7 @@ package com.github.braillesystems.learnbraille.data.entities import androidx.room.* +import com.github.braillesystems.learnbraille.data.dsl.CourseID import com.github.braillesystems.learnbraille.utils.compareTo import kotlinx.serialization.Serializable @@ -37,6 +38,14 @@ interface StepDao { @Query("select * from steps where id = :id") suspend fun step(id: DBid): Step? + @Query( + """ + select * from steps + where course_id = :courseId and lesson_id = :lessonId + """ + ) + suspend fun step(courseId: DBid, lessonId: DBid): Step? + @Query( """ select steps.* from current_step as cs diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Users.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Users.kt index 995c0ea4..73cbfcb0 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Users.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Users.kt @@ -1,33 +1,33 @@ -package com.github.braillesystems.learnbraille.data.entities - -import androidx.room.* - -typealias UserLogin = String -typealias UserName = String - -@Entity(tableName = "users", indices = [Index(value = ["login"], unique = true)]) -data class User( - @PrimaryKey(autoGenerate = true) - var id: DBid = 0, - val login: UserLogin, - val name: UserName -) - -@Dao -interface UserDao { - - @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insert(user: User) - - @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insert(users: List) - - @Query("select * from users where :login = login limit 1") - suspend fun user(login: UserLogin): User? - - @Query("select * from users where :id = id limit 1") - suspend fun user(id: DBid): User? - - @Query("delete from users") - suspend fun clear() -} +package com.github.braillesystems.learnbraille.data.entities + +import androidx.room.* + +typealias UserLogin = String +typealias UserName = String + +@Entity(tableName = "users", indices = [Index(value = ["login"], unique = true)]) +data class User( + @PrimaryKey(autoGenerate = true) + var id: DBid = 0, + val login: UserLogin, + val name: UserName +) + +@Dao +interface UserDao { + + @Insert(onConflict = OnConflictStrategy.IGNORE) + suspend fun insert(user: User) + + @Insert(onConflict = OnConflictStrategy.IGNORE) + suspend fun insert(users: List) + + @Query("select * from users where :login = login limit 1") + suspend fun user(login: UserLogin): User? + + @Query("select * from users where :id = id limit 1") + suspend fun user(id: DBid): User? + + @Query("delete from users") + suspend fun clear() +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepository.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepository.kt index 83dd9011..c77f007c 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepository.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepository.kt @@ -22,14 +22,17 @@ interface MutableActionsRepository : ActionsRepository { class ActionsRepositoryImpl( private val actionsDao: ActionDao, + private val preferenceRepository: PreferenceRepository, private val getCurrDate: () -> Date = { Date() }, private val keepActionsTime: Days = Days(30) ) : MutableActionsRepository { - override suspend fun addAction(type: ActionType) = - actionsDao.insert( - Action(type = type, date = getCurrDate()) - ) + override suspend fun addAction(type: ActionType) { + if (!preferenceRepository.teacherModeEnabled) { + val action = Action(type = type, date = getCurrDate()) + actionsDao.insert(action) + } + } override suspend fun clearAllStats() = actionsDao.clear() diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepository.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepository.kt index 79654363..cfc88872 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepository.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepository.kt @@ -1,6 +1,7 @@ package com.github.braillesystems.learnbraille.data.repository import com.github.braillesystems.learnbraille.data.entities.* +import com.github.braillesystems.learnbraille.res.DeckTags data class DeckWithAvailability( val deck: Deck, @@ -8,12 +9,14 @@ data class DeckWithAvailability( ) interface MaterialsRepository { + suspend fun deck(id: DBid): Deck? suspend fun randomMaterialFromDeck(id: DBid): Material? suspend fun randomKnownMaterialFromDeck(id: DBid): Material? suspend fun allMaterialsFromDeck(id: DBid): List suspend fun allDecks(): List suspend fun availableDecks(): List suspend fun allDecksWithAvailability(): List + suspend fun allUniqueDecks(): List } open class MaterialsRepositoryImpl( @@ -22,6 +25,8 @@ open class MaterialsRepositoryImpl( private val preferenceRepository: PreferenceRepository ) : MaterialsRepository { + override suspend fun deck(id: DBid): Deck? = deckDao.deck(id) + override suspend fun randomMaterialFromDeck(id: DBid): Material? = cardDao.randomMaterialFromDeck(id) @@ -44,4 +49,9 @@ open class MaterialsRepositoryImpl( } else { deckDao.allDecks().map { DeckWithAvailability(it, true) } } + + override suspend fun allUniqueDecks(): List { + val uniqueDecksTags = DeckTags.Unique.values().map { it.tag } + return deckDao.allDecks().filter { it.tag in uniqueDecksTags } + } } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt index 3ce51ad4..8e15e839 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt @@ -25,6 +25,7 @@ interface PreferenceRepository { val extendedAccessibilityEnabled: Boolean val additionalQrCodeButtonEnabled: Boolean val isWriteModeFirst: Boolean + val teacherModeEnabled: Boolean val currentUserId: DBid suspend fun getCurrentUser(): User @@ -122,6 +123,13 @@ class PreferenceRepositoryImpl( ) } + override val teacherModeEnabled: Boolean by logged { + context.preferences.getBoolean( + context.getString(R.string.preference_teacher_mode_enabled), + false + ) + } + override val currentUserId: DBid by logged { context.preferences.getLong( context.getString(R.string.preference_current_user), 1 diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepository.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepository.kt index c3582605..fc3e1ab9 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepository.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepository.kt @@ -13,6 +13,7 @@ interface TheoryRepository { } interface MutableTheoryRepository : TheoryRepository { + suspend fun setCurrentStep(curr: CurrentStep) suspend fun nextStepAndMove(thisStep: Step, markThisAsPassed: Boolean = false): Step? suspend fun prevStepAndMove(thisStep: Step): Step? suspend fun currentStepAndMove(courseId: DBid): Step @@ -38,6 +39,9 @@ class TheoryRepositoryImpl( else StepAnnotation.slateStylusRequired ) + override suspend fun setCurrentStep(curr: CurrentStep) = + currentStepDao.update(curr) + @Suppress("ReturnCount") override suspend fun nextStepAndMove(thisStep: Step, markThisAsPassed: Boolean): Step? { val next = stepDao @@ -87,14 +91,23 @@ class TheoryRepositoryImpl( /** * LessonId is supposed to exist for this courseId. */ - override suspend fun lastLessonOrCurrentStepAndMove(courseId: DBid, lessonId: DBid): Step = - stepDao - .lastStep(preferenceRepository.currentUserId, courseId, lessonId) - ?.also { updateLast(it) } - ?: error( - "No such lessonId ($lessonId) exists for course ($courseId) " + - "or current step is behind lesson with such lessonId" - ) + override suspend fun lastLessonOrCurrentStepAndMove(courseId: DBid, lessonId: DBid): Step { + val lastStep = stepDao.lastStep(preferenceRepository.currentUserId, courseId, lessonId) + if (lastStep != null) { + updateLast(lastStep) + return lastStep + } + if (preferenceRepository.teacherModeEnabled) { + val step = stepDao.step(courseId, lessonId) + ?: error("No such lessonId ($lessonId) exists for course ($courseId)") + updateLast(step) + return step + } + error( + "No such lessonId ($lessonId) exists for course ($courseId) " + + "or current step is behind lesson with such lessonId" + ) + } override suspend fun currentStep(courseId: DBid): Step = stepDao diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt b/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt index 578dcf10..2a53851d 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt @@ -9,6 +9,34 @@ import com.github.braillesystems.learnbraille.data.entities.Symbol import com.github.braillesystems.learnbraille.utils.contextNotNull import com.github.braillesystems.learnbraille.utils.lazyWithContext +val musicalNotesTypes = listOf( + MarkerType.NoteC, + MarkerType.NoteD, + MarkerType.NoteE, + MarkerType.NoteF, + MarkerType.NoteG, + MarkerType.NoteA, + MarkerType.NoteB +) + +val otherMusicalTypes = listOf( + MarkerType.MusicRest8th, + MarkerType.MusicRest4th, + MarkerType.MusicRestHalf, + MarkerType.MusicRestFull, + MarkerType.OctaveMark1, + MarkerType.OctaveMark2, + MarkerType.OctaveMark3, + MarkerType.OctaveMark4, + MarkerType.OctaveMark5, + MarkerType.OctaveMark6, + MarkerType.OctaveMark7, + MarkerType.MusicSharp, + MarkerType.MusicFlat, + MarkerType.MusicNatural, + MarkerType.MusicHyphen +) + val prepopulationData by data( materials = content, stepAnnotationNames = listOf( @@ -46,35 +74,42 @@ val prepopulationData by data( decks { // All cards deck should always exist and be first in the list - deck(DeckTags.all) { true } + deck(DeckTags.Grouping.All.tag) { true } - deck(DeckTags.allWithRus) { data -> + deck(DeckTags.Grouping.AllWithRus.tag) { data -> val isNative = data is Symbol && data.type != SymbolType.greek && data.type != SymbolType.latin isNative || data !is Symbol } - deck(DeckTags.ruLetters) { data -> + deck(DeckTags.Unique.RuLetters.tag) { data -> data is Symbol && data.type == SymbolType.ru } - deck(DeckTags.latinLetters) { data -> + deck(DeckTags.Unique.LatinLetters.tag) { data -> data is Symbol && data.type == SymbolType.latin } - deck(DeckTags.greekLetters) { data -> + deck(DeckTags.Unique.GreekLetters.tag) { data -> data is Symbol && data.type == SymbolType.greek } - deck(DeckTags.special) { data -> + deck(DeckTags.Unique.Special.tag) { data -> data is Symbol && data.type == SymbolType.special } - deck(DeckTags.markers) { data -> - data is MarkerSymbol + deck(DeckTags.Unique.Markers.tag) { data -> + data is MarkerSymbol && data.type !in (musicalNotesTypes + otherMusicalTypes) + } + deck(DeckTags.Unique.MusicalNotes.tag) { data -> + data is MarkerSymbol && data.type in musicalNotesTypes } - deck(DeckTags.digits) { data -> + deck(DeckTags.Unique.Digits.tag) { data -> data is Symbol && data.type == SymbolType.digit } - deck(DeckTags.math) { data -> + deck(DeckTags.Unique.Math.tag) { data -> data is Symbol && data.type == SymbolType.math } + deck(DeckTags.Unique.OtherMusic.tag) { data -> + data is MarkerSymbol && data.type in otherMusicalTypes + } + } } @@ -84,31 +119,38 @@ object StepAnnotation { } object DeckTags { - const val all = "all" - const val allWithRus = "all_with_rus" - const val ruLetters = "ru_letters" - const val latinLetters = "latin_letters" - const val greekLetters = "greek_letters" - const val digits = "digits" - const val markers = "markers" - const val special = "special" - const val math = "math" + enum class Grouping(val tag: String) { + All("all"), + AllWithRus("all_with_rus") + } + + enum class Unique(val tag: String) { + RuLetters("ru_letters"), + LatinLetters("latin_letters"), + GreekLetters("greek_letters"), + Digits("digits"), + Markers("markers"), + Special("special"), + Math("math"), + MusicalNotes("notes"), + OtherMusic("other_music") + } } val Context.deckTagToName: Map by lazyWithContext { - DeckTags.run { - mapOf( - all to getString(R.string.deck_name_all), - allWithRus to getString(R.string.deck_name_all_but_foreign), - ruLetters to getString(R.string.deck_name_ru_letters), - latinLetters to getString(R.string.deck_name_latin_letters), - greekLetters to getString(R.string.deck_name_greek_letters), - digits to getString(R.string.deck_name_digits), - markers to getString(R.string.deck_name_markers), - special to getString(R.string.deck_name_punctuation), - math to getString(R.string.deck_name_math) - ) - } + mapOf( + DeckTags.Grouping.All.tag to getString(R.string.deck_name_all), + DeckTags.Grouping.AllWithRus.tag to getString(R.string.deck_name_all_but_foreign), + DeckTags.Unique.RuLetters.tag to getString(R.string.deck_name_ru_letters), + DeckTags.Unique.LatinLetters.tag to getString(R.string.deck_name_latin_letters), + DeckTags.Unique.GreekLetters.tag to getString(R.string.deck_name_greek_letters), + DeckTags.Unique.Digits.tag to getString(R.string.deck_name_digits), + DeckTags.Unique.Markers.tag to getString(R.string.deck_name_markers), + DeckTags.Unique.Special.tag to getString(R.string.deck_name_punctuation), + DeckTags.Unique.Math.tag to getString(R.string.deck_name_math), + DeckTags.Unique.MusicalNotes.tag to getString(R.string.deck_name_musical_notes), + DeckTags.Unique.OtherMusic.tag to getString(R.string.deck_other_musical_symbols) + ) } val Fragment.deckTagToName diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt b/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt index d0c51b4e..31b53479 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt @@ -35,7 +35,7 @@ internal val golubinaIntroLessons by lessons { """ Урок $iLesson. Тренировка чтения и ввода отдельных комбинаций.

- В азбуке Брайля основой любого символа является шеститочие, в котором точки + В системе Брайля основой любого символа является шеститочие, в котором точки расположены в два столбца по три точки. Каждая точка имеет определённый порядковый номер.
@@ -176,17 +176,26 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Е')) +Input(content.symbols.getValue('Е')) +Info("""В следующих трёх шагах нужно ввести буквы Б, А, Ц (вместе это слово БАЦ).""") - inputChars("БАЦ") + inputPhraseByLetters("БАЦ") +Info("""Теперь наберите в следующих трёх шагах, вводя букву за буквой, слово ДЕД.""") - inputChars("ДЕД") + inputPhraseByLetters("ДЕД") +Info("""Далее в следующих четырёх шагах введите слово БАБА.""") - inputChars("БАБА") + inputPhraseByLetters("БАБА") +Info( """Запишите на брайлевском приборе три строки: из букв Ц, Д и Е.
После этого запишите через пробел слова: БАЦ, ДЕД, БАБА. """ ).annotate(StepAnnotation.slateStylusRequired) + +Info("""Отметим, что правила написания букв и других символов соответствуют + стандартам общеупотребительного Брайля, принятым в России, которые + утверждены Комиссией Центрального + правления Всероссийского общества слепых в 2013 году. + Подробнее об этом можно прочесть на сайте комиссии: +
+ www.chtenie.spb.ru/komissia.htm + """ + ) +Info(InfoInterpolation.run { """Урок $iLesson закончен. В следующем занятии займёмся повторением букв А, Б, Ц, Д, Е и потренируемся писать цифры от 1 до 5, образуемые с помощью этих букв. Рекомендуем @@ -230,9 +239,14 @@ internal val golubinaIntroLessons by lessons { Они получаются из букв А, Б, Ц, Д, Е добавлением цифрового знака. Например, цифра 3, как и число 3 - это цифровой знак + Ц. Число двадцать четыре - это цифровой знак, затем буквы Б и Д. - В уроках мы для краткости не будем всякий раз ставить цифровой знак.""" + В следующих шагах просмотрите, а затем введите число «двенадцать тысяч + триста сорок пять».""" ) - showAndInputChars("12345") + +Show(content.markers.getValue(MarkerType.NumberSign)) + for (char in "12345") { + +Show(content.symbols.getValue(char)) + } + inputNumber(12345) +Info( """ На пятой сверху строчке на странице 14 пособия, под строкой с цифровым знаком, @@ -251,7 +265,9 @@ internal val golubinaIntroLessons by lessons { Урок $iLesson пройден! Рекомендуем самостоятельно изучить цифры и числа на странице 15 в пособии (внизу страницы).
- Следующий урок будет посвящён буквам Ф, Г и цифрам 6, 7.""" + Также цифры можно повторять в разделе приложения "Практика". + В разделе "Практика" не нужно вводить перед цифрами цифровой знак, + но при письме обязательно ставить его перед каждым числом.""" }) } @@ -265,7 +281,7 @@ internal val golubinaIntroLessons by lessons { Перед прохождением нового материала повторим пройденное. В следующих пяти шагах нужно ввести буквы Б, E, Д, А (вместе это слово БЕДА).""" }) - inputChars("БЕДА") + inputPhraseByLetters("БЕДА") +Info( """ Теперь познакомимся с буквами Ф и Г. @@ -298,7 +314,7 @@ internal val golubinaIntroLessons by lessons { """ В следующих трёх шагах введите по буквам слово БЕГ.""" ) - inputChars("БЕГ") + inputPhraseByLetters("БЕГ") +Info( """ Цифровой знак и буква Ф за ним обозначают арабскую цифру 6 или число 6. @@ -307,8 +323,7 @@ internal val golubinaIntroLessons by lessons {
В следующих трёх шагах введите число ШЕСТЬДЕСЯТ СЕМЬ.""" ) - +Input(content.markers.getValue(MarkerType.NumberSign)) - inputChars("67") + inputNumber(67) +Info( """Напишите слово БЕГ и число "ШЕСТДЕСЯТ СЕМЬ" при помощи брайлевского прибора.""" ).annotate(StepAnnotation.slateStylusRequired) @@ -327,16 +342,15 @@ internal val golubinaIntroLessons by lessons { По окончании этого урока Вы узнаете букву Х и цифру 8. Но перед изучением нового повторим пройденное. Введите по буквам слово ФЕБ.""" }) - inputChars("ФЕБ") + inputPhraseByLetters("ФЕБ") +Info("""В следующих шести шагах введите по буквам слово БАГДАД.""") - inputChars("БАГДАД") + inputPhraseByLetters("БАГДАД") +Info( """Наберите в следующих четырёх шагах число СТО ДВАДЦАТЬ ТРИ, поставив перед ним цифровой знак. """ ) - +Input(content.markers.getValue(MarkerType.NumberSign)) - inputChars("123") + inputNumber(123) +Info( """ Буква Х обозначается точками 1, 2 и 5. @@ -356,12 +370,11 @@ internal val golubinaIntroLessons by lessons { +Input(content.symbols.getValue('8')) slateStylusLine('Х') +Info("""В следующих шагах введите по буквам слово ЦЕХ""") - inputChars("ЦЕХ") + inputPhraseByLetters("ЦЕХ") +Info("""Наберите восклицание АХ""") - inputChars("АХ") + inputPhraseByLetters("АХ") +Info("""Далее введите, поставив цифровой знак, число "ВОСЕМЬСОТ СЕМЬДЕСЯТ ШЕСТЬ" """) - +Input(content.markers.getValue(MarkerType.NumberSign)) - inputChars("876") + inputNumber(876) +Info( """Введите на брайлевском приборе слова ЦЕХ, АХ и число ВОСЕМЬСОТ СЕМЬДЕСЯТ ШЕСТЬ.""" ).annotate(StepAnnotation.slateStylusRequired) @@ -398,25 +411,24 @@ internal val golubinaIntroLessons by lessons { +Info( """Введите буквы, составляющие слово БАГАЖ""" ) - inputChars("БАГАЖ") + inputPhraseByLetters("БАГАЖ") +Info( """Введите буквы, составляющие слово ЖАЖДА""" ) - inputChars("ЖАЖДА") + inputPhraseByLetters("ЖАЖДА") +Info( """Напомним, буква И кодируется двумя точками с номерами 2 и 4. Введите буквы, образующие слово ГИД""" ) - inputChars("ГИД") + inputPhraseByLetters("ГИД") +Info( """Введите буквы, которые образуют слово ИЖИЦА""" ) - inputChars("ИЖИЦА") + inputPhraseByLetters("ИЖИЦА") +Info( """Введите по символам число 850""" ) - +Input(content.markers.getValue(MarkerType.NumberSign)) - inputChars("850") + inputNumber(850) +Info( """ Напишите с помощью брайлевского прибора слова, набранные до этого на экране: @@ -447,7 +459,7 @@ internal val golubinaIntroLessons by lessons { Перед занятием повторим пройденное. Введите по буквам слово ЕЖИХА. """ }) - inputChars("ЕЖИХА") + inputPhraseByLetters("ЕЖИХА") +Info( """ Теперь перейдём к изучению материала. @@ -472,11 +484,11 @@ internal val golubinaIntroLessons by lessons { +Info( """Введите по буквам слово КЛАД""" ) - inputChars("КЛАД") + inputPhraseByLetters("КЛАД") +Info( """Введите по буквам слово БЕЛКА""" ) - inputChars("БЕЛКА") + inputPhraseByLetters("БЕЛКА") +Info( """ Сегодня осталось изучить букву М. @@ -498,15 +510,15 @@ internal val golubinaIntroLessons by lessons { +Info( """В следующих трёх шагах ведите по буквам слово МЕЛ""" ) - inputChars("МЕЛ") + inputPhraseByLetters("МЕЛ") +Info( """Введите по буквам слово МАК""" ) - inputChars("МАК") + inputPhraseByLetters("МАК") +Info( """Напоследок введите по буквам слово ФИАЛКА""" ) - inputChars("ФИАЛКА") + inputPhraseByLetters("ФИАЛКА") +Info( """ Напишите на брайлевском приборе пройденные в уроке слова: @@ -535,7 +547,7 @@ internal val golubinaIntroLessons by lessons { Начнём занятие с повторения. Введите по буквам слово КАМБАЛА. """ }) - inputChars("КАМБАЛА") + inputPhraseByLetters("КАМБАЛА") +Info( """ Отлично, теперь познакомимся с буквой Н. @@ -557,15 +569,15 @@ internal val golubinaIntroLessons by lessons { +Info( """Введите по буквам слово БАНАН""" ) - inputChars("БАНАН") + inputPhraseByLetters("БАНАН") +Info( """Введите по буквам слово ЦЕНА""" ) - inputChars("ЦЕНА") + inputPhraseByLetters("ЦЕНА") +Info( """Теперь введите по буквам слово БЛАНК""" ) - inputChars("БЛАНК") + inputPhraseByLetters("БЛАНК") +Info( """ С помощью брайлевского прибора запишите введённые ранее слова: @@ -593,7 +605,7 @@ internal val golubinaIntroLessons by lessons { Введите по буквам слово НАДЕЖДА. """ }) - inputChars("НАДЕЖДА") + inputPhraseByLetters("НАДЕЖДА") +Info( """ Идём дальше: теперь изучим букву 'О'. @@ -613,7 +625,7 @@ internal val golubinaIntroLessons by lessons { +Info( """Наберите по буквам слово ОБЛАКО""" ) - inputChars("ОБЛАКО") + inputPhraseByLetters("ОБЛАКО") +Info( """ Кратко ознакомимся со знаком 'Запятая'. @@ -626,15 +638,15 @@ internal val golubinaIntroLessons by lessons { +Info( """Наберите по символам слово ЛОДКА и поставьте в конце запятую""" ) - inputChars("ЛОДКА,") + inputPhraseByLetters("ЛОДКА,") +Info( """Аналогично предыдущему, введите слово КОФЕ и запятую""" ) - inputChars("КОФЕ,") + inputPhraseByLetters("КОФЕ,") +Info( """Напоследок введите слово ГЕОЛОГ и тоже поставьте запятую в конце""" ) - inputChars("ГЕОЛОГ,") + inputPhraseByLetters("ГЕОЛОГ,") +Info( """ Используя брайлевский прибор, запишите фразу: @@ -662,7 +674,7 @@ internal val golubinaIntroLessons by lessons { Введите по буквам слово МОЛОКО и поставьте после него запятую. """ }) - inputChars("МОЛОКО,") + inputPhraseByLetters("МОЛОКО,") +Info( """ Давайте познакомимся с буквой 'П'. @@ -681,19 +693,19 @@ internal val golubinaIntroLessons by lessons { +Info( """Введите отдельными символами слово ПЕНА""" ) - inputChars("ПЕНА") + inputPhraseByLetters("ПЕНА") +Info( """Теперь нужно набрать по буквам слово ЛАМПА""" ) - inputChars("ЛАМПА") + inputPhraseByLetters("ЛАМПА") +Info( """Наберите по буквам слово ПЛАН""" ) - inputChars("ПЛАН") + inputPhraseByLetters("ПЛАН") +Info( """И последнее в этом уроке: введите по буквам слово КАПКАН""" ) - inputChars("КАПКАН") + inputPhraseByLetters("КАПКАН") +Info(InfoInterpolation.run { """ Вот и пройден урок $iLesson. Следующее занятие отведено для изучения буквы Ч. @@ -713,18 +725,17 @@ internal val golubinaIntroLessons by lessons { Введите по буквам слово ХЛОПОК. """ }) - inputChars("ХЛОПОК") + inputPhraseByLetters("ХЛОПОК") +Info( """Введите отдельными символами число 215, поставив перед ним цифровой знак""" ) - +Input(content.markers.getValue(MarkerType.NumberSign)) - inputChars("215") + inputNumber(215) +Info( """ Переходим к изучению новой буквы: 'Ч'.
- Русская буква 'Ч' в азбуке Брайля составлена из точек 1, 2, 3, 4 и 5. + Русская буква 'Ч' в системе Брайля составлена из точек 1, 2, 3, 4 и 5. Её можно получить из буквы Г, если дополнить точкой 3. """ ) @@ -737,19 +748,19 @@ internal val golubinaIntroLessons by lessons { +Info( """Введите по буквам слово ЧЕК""" ) - inputChars("ЧЕК") + inputPhraseByLetters("ЧЕК") +Info( """Наберите отдельными буквами слово ОЧКИ""" ) - inputChars("ОЧКИ") + inputPhraseByLetters("ОЧКИ") +Info( """Далее введите по буквам слово КОЧАН""" ) - inputChars("КОЧАН") + inputPhraseByLetters("КОЧАН") +Info( """Введите по буквам ещё одно, последнее слово: БОЧОНОК""" ) - inputChars("БОЧОНОК") + inputPhraseByLetters("БОЧОНОК") +Info( """ Запишите на брайлевском приборе изученные слова, разделяя их запятыми: @@ -777,7 +788,7 @@ internal val golubinaIntroLessons by lessons { Введите по символам слово ПЧЕЛА. """ }) - inputChars("ПЧЕЛА") + inputPhraseByLetters("ПЧЕЛА") +Info( """ Сегодня мы изучим букву 'Р'. @@ -796,23 +807,23 @@ internal val golubinaIntroLessons by lessons { +Info( """Наберите последовательно, по буквам слово РЕКА""" ) - inputChars("РЕКА") + inputPhraseByLetters("РЕКА") +Info( """Наберите отдельными символами слово КРАН""" ) - inputChars("КРАН") + inputPhraseByLetters("КРАН") +Info( """Далее в четырёх шагах введите по буквам слово КРЕМ""" ) - inputChars("КРЕМ") + inputPhraseByLetters("КРЕМ") +Info( """Теперь введите по символам слово ФАРА""" ) - inputChars("ФАРА") + inputPhraseByLetters("ФАРА") +Info( """Наберите аналогичным образом последнее слово в сегодняшнем уроке: ГАРАЖ""" ) - inputChars("ГАРАЖ") + inputPhraseByLetters("ГАРАЖ") +Info( """ Возьмите брайлевский прибор и запишите через запятую пройденные слова: @@ -838,12 +849,12 @@ internal val golubinaIntroLessons by lessons { Введите по буквам слово МАРАФОН """ }) - inputChars("МАРАФОН") + inputPhraseByLetters("МАРАФОН") +Info( """ Предмет этого урока - буква 'С'.
- Буква 'С' в азбуке Брайля представлена точками 2, 3 и 4. + Буква 'С' в системе Брайля представлена точками 2, 3 и 4. Точками 2 и 4, как вы помните, обозначается буква И. Буква С - это буква И, дополненная точкой номер 3. """ @@ -858,23 +869,23 @@ internal val golubinaIntroLessons by lessons { +Info( """В следующих шагах наберите слово САЖА""" ) - inputChars("САЖА") + inputPhraseByLetters("САЖА") +Info( """Наберите по буквам слово СЛЕД""" ) - inputChars("СЛЕД") + inputPhraseByLetters("СЛЕД") +Info( """Введите буквы, образующие слово ФАСАД""" ) - inputChars("ФАСАД") + inputPhraseByLetters("ФАСАД") +Info( """Введите буквы, которые составляют слово ДОСКА""" ) - inputChars("ДОСКА") + inputPhraseByLetters("ДОСКА") +Info( """В завершение урока введите по буквам слово ЧЕСНОК""" ) - inputChars("ЧЕСНОК") + inputPhraseByLetters("ЧЕСНОК") +Info( """ Осталось немного поработать с брайлевским прибором. Запишите на нём слова: @@ -900,12 +911,11 @@ internal val golubinaIntroLessons by lessons { Введите символы, составляющие слово СОЛНЦЕ """ }) - inputChars("СОЛНЦЕ") + inputPhraseByLetters("СОЛНЦЕ") +Info( """Теперь наберите число 870, сначала поставив цифровой знак""" ) - +Input(content.markers.getValue(MarkerType.NumberSign)) - inputChars("870") + inputNumber(870) +Info( """ Теперь рассмотрим букву 'Т'. @@ -924,11 +934,11 @@ internal val golubinaIntroLessons by lessons { +Info( """Далее, пожалуйста, введите слово ТЕКСТ""" ) - inputChars("ТЕКСТ") + inputPhraseByLetters("ТЕКСТ") +Info( """Введите по буквам слово ТОРТ""" ) - inputChars("ТОРТ") + inputPhraseByLetters("ТОРТ") +Info( """ С этого момента мы начинаем осваивать символы, содержащие точку 6. @@ -948,11 +958,11 @@ internal val golubinaIntroLessons by lessons { +Info( """Введите по символам слово ПОЛ-ЛИМОНА""" ) - inputChars("ПОЛ${Hyphen.c}ЛИМОНА") + inputPhraseByLetters("ПОЛ${Hyphen.c}ЛИМОНА") +Info( """И последнее слово, которое нужно ввести в этом уроке — местоимение ГДЕ-ЛИБО""" ) - inputChars("ГДЕ${Hyphen.c}ЛИБО") + inputPhraseByLetters("ГДЕ${Hyphen.c}ЛИБО") +Info( """ Теперь рекомендуем Вам записать на брайлевском приборе изученные слова: @@ -979,7 +989,7 @@ internal val golubinaIntroLessons by lessons { На следующих этапах введите по буквам слово ТЕЛЕФОН """ }) - inputChars("ТЕЛЕФОН") + inputPhraseByLetters("ТЕЛЕФОН") +Info( """ Начинаем знакомство с буквой У. Эта буква первая в нашем курсе содержит точку 6. @@ -999,23 +1009,23 @@ internal val golubinaIntroLessons by lessons { +Info( """В следующих шагах введите, пожалуйста, слово УГОЛ""" ) - inputChars("УГОЛ") + inputPhraseByLetters("УГОЛ") +Info( """Наберите, последовательно вводя буквы, слово ЛУНА""" ) - inputChars("ЛУНА") + inputPhraseByLetters("ЛУНА") +Info( """Введите буквы, формирующие слово КРУГ""" ) - inputChars("КРУГ") + inputPhraseByLetters("КРУГ") +Info( """Введите буквы, которые бы составили слово ТРУД""" ) - inputChars("ТРУД") + inputPhraseByLetters("ТРУД") +Info( """Последнее, что нужно набрать в этом уроке — слово ЧУДЕСА""" ) - inputChars("ЧУДЕСА") + inputPhraseByLetters("ЧУДЕСА") +Info( """ Набранные в этом уроке слова запишем и на брайлевском приборе через запятую: @@ -1042,7 +1052,7 @@ internal val golubinaIntroLessons by lessons { Введите буквы, которые сложатся в слово ХУДОЖНИК """ }) - inputChars("ХУДОЖНИК") + inputPhraseByLetters("ХУДОЖНИК") +Info( """ Ознакомимся с буквой Щ. @@ -1063,23 +1073,23 @@ internal val golubinaIntroLessons by lessons { +Info( """Вводя букву за буквой, составьте слово ЩЕГОЛ""" ) - inputChars("ЩЕГОЛ") + inputPhraseByLetters("ЩЕГОЛ") +Info( """Теперь наберите, пожалуйста, слово ПЛАЩ""" ) - inputChars("ПЛАЩ") + inputPhraseByLetters("ПЛАЩ") +Info( """Наберите отдельными буквами слово КЛЕЩ""" ) - inputChars("КЛЕЩ") + inputPhraseByLetters("КЛЕЩ") +Info( """Наберите буквы, которые бы составили слово ЩЕПКА""" ) - inputChars("ЩЕПКА") + inputPhraseByLetters("ЩЕПКА") +Info( """И последнее: введите слово ОЩУЩЕНИЕ""" ) - inputChars("ОЩУЩЕНИЕ") + inputPhraseByLetters("ОЩУЩЕНИЕ") +Info( """ Перед тем, как мы закончим занятие, запишите на брайлевском приборе все пройденные слова: @@ -1105,7 +1115,7 @@ internal val golubinaIntroLessons by lessons { Далее введите буквы слова ЩЕПОТКА """ }) - inputChars("ЩЕПОТКА") + inputPhraseByLetters("ЩЕПОТКА") +Info( """ Новая буква, которую нам надо изучить - З. @@ -1125,23 +1135,23 @@ internal val golubinaIntroLessons by lessons { +Info( """Последовательно, по буквам введите слово ЗАКАЗ""" ) - inputChars("ЗАКАЗ") + inputPhraseByLetters("ЗАКАЗ") +Info( """Теперь из отдельных букв наберите слово ЗЕРНО""" ) - inputChars("ЗЕРНО") + inputPhraseByLetters("ЗЕРНО") +Info( """Наберите по буквам очень важное для нас слово АЗБУКА""" ) - inputChars("АЗБУКА") + inputPhraseByLetters("АЗБУКА") +Info( """В следующих пяти шагах введите слово АРБУЗ""" ) - inputChars("АРБУЗ") + inputPhraseByLetters("АРБУЗ") +Info( """И последнее, что сегодня осталось ввести: слово ЗАДАЧА""" ) - inputChars("ЗАДАЧА") + inputPhraseByLetters("ЗАДАЧА") +Info( """ Для закрепления материала напишите слова на брайлевском приборе: @@ -1167,12 +1177,11 @@ internal val golubinaIntroLessons by lessons { Пожалуйста, введите буквы, образующие слово МИМОЗА """ }) - inputChars("МИМОЗА") + inputPhraseByLetters("МИМОЗА") +Info( """Также в качестве повторения введите цифровой знак, а за ним число 964""" ) - +Input(content.markers.getValue(MarkerType.NumberSign)) - inputChars("964") + inputNumber(964) +Info( """ Переходим к главному в сегодняшнем уроке: ознакомимся с буквой 'И краткое'. @@ -1194,23 +1203,23 @@ internal val golubinaIntroLessons by lessons { +Info( """Буква за буквой, введите точечным шрифтом слово КЛЕЙ""" ) - inputChars("КЛЕЙ") + inputPhraseByLetters("КЛЕЙ") +Info( """Введите отдельными символами второе слово с буквой Й: УЛЕЙ""" ) - inputChars("УЛЕЙ") + inputPhraseByLetters("УЛЕЙ") +Info( """Наберите, пожалуйста, по буквам слово РЕЙС""" ) - inputChars("РЕЙС") + inputPhraseByLetters("РЕЙС") +Info( """Осталось ещё два задания. В следующих четырёх шагах введите слово ЗНОЙ""" ) - inputChars("ЗНОЙ") + inputPhraseByLetters("ЗНОЙ") +Info( """Наконец, введите последнее слово в этом уроке: РАЙОН""" ) - inputChars("РАЙОН") + inputPhraseByLetters("РАЙОН") +Info( """ Все пройденные слова следует записать на брайлевском приборе. Напомним, это слова @@ -1239,7 +1248,7 @@ internal val golubinaIntroLessons by lessons { Введите по буквам прилагательное ОСЕННИЙ """ }) - inputChars("ОСЕННИЙ") + inputPhraseByLetters("ОСЕННИЙ") +Info( """ Мы готовы к изучению твёрдого знака. @@ -1260,16 +1269,16 @@ internal val golubinaIntroLessons by lessons { +Info( """Введите по буквам слово СЪЕЗД""" ) - inputChars("СЪЕЗД") + inputPhraseByLetters("СЪЕЗД") +Info( """Символ за символом, наберите слово СУБЪЕКТ""" ) - inputChars("СУБЪЕКТ") + inputPhraseByLetters("СУБЪЕКТ") +Info( """ Теперь постараемся запомнить обозначение литературной точки.
- Знак препинания 'Литературная точка' образован в азбуке Брайля точками 2, 5 и 6. + Знак препинания 'Литературная точка' образован в системе Брайля точками 2, 5 и 6. Не следует путать его с буквой Д. """ ) @@ -1291,7 +1300,7 @@ internal val golubinaIntroLessons by lessons { В конце поставьте литературную точку. """ ) - inputChars("ЛЕНИНГРАД${Hyphen.c}ГОРОД${Hyphen.c}ГЕРОЙ.") + inputPhraseByLetters("ЛЕНИНГРАД${Hyphen.c}ГОРОД${Hyphen.c}ГЕРОЙ.") +Info( """ Теперь запишите на брайлевском приборе то же самое предложение: @@ -1317,7 +1326,7 @@ internal val golubinaIntroLessons by lessons { Ради закрепления пройденного введите по символам слово ОБЪЕДИНЕНИЕ """ }) - inputChars("ОБЪЕДИНЕНИЕ") + inputPhraseByLetters("ОБЪЕДИНЕНИЕ") +Info( """Также повторим специальные символы. Введите цифровой знак, запятую, дефис и точку""" @@ -1343,23 +1352,23 @@ internal val golubinaIntroLessons by lessons { +Info( """Набирая буквы одна за одной, введите слово МЫЛО""" ) - inputChars("МЫЛО") + inputPhraseByLetters("МЫЛО") +Info( """Далее введите ещё одно слово по буквам: ЛЫЖИ""" ) - inputChars("ЛЫЖИ") + inputPhraseByLetters("ЛЫЖИ") +Info( """Теперь введите буквы, из которых складывается слово КЛЫК""" ) - inputChars("КЛЫК") + inputPhraseByLetters("КЛЫК") +Info( """Наберите предпоследнее слово в этом уроке - слово МУЗЫКА""" ) - inputChars("МУЗЫКА") + inputPhraseByLetters("МУЗЫКА") +Info( """Введите последнее слово - существительное во множественном числе: КОЗЫ""" ) - inputChars("КОЗЫ") + inputPhraseByLetters("КОЗЫ") +Info( """ Все изученные слова, пожалуйста, запишите и на брайлевском приборе: @@ -1386,7 +1395,7 @@ internal val golubinaIntroLessons by lessons { Введите, набирая отдельные буквы, прилагательное РЫЖИЙ """ }) - inputChars("РЫЖИЙ") + inputPhraseByLetters("РЫЖИЙ") +Info( """ Два урока назад мы изучали твёрдый знак, а теперь освоим и мягкий знак тоже. @@ -1408,24 +1417,24 @@ internal val golubinaIntroLessons by lessons { +Info( """Введите по буквам существительное СОЛЬ""" ) - inputChars("СОЛЬ") + inputPhraseByLetters("СОЛЬ") +Info( """Теперь нужно ввести слово МЕЛЬ""" ) - inputChars("МЕЛЬ") + inputPhraseByLetters("МЕЛЬ") +Info( """Далее, пожалуйста, составьте из букв слово КОНЬ""" ) - inputChars("КОНЬ") + inputPhraseByLetters("КОНЬ") +Info( """Осталось ввести два слова. Первое - существительное во множественном числе: РУЧЬИ""" ) - inputChars("РУЧЬИ") + inputPhraseByLetters("РУЧЬИ") +Info( """Введите последнее в этом занятии слово: СТАЛЬ""" ) - inputChars("СТАЛЬ") + inputPhraseByLetters("СТАЛЬ") +Info( """ Как обычно, напишите на брайлевском приборе изученные слова: @@ -1451,7 +1460,7 @@ internal val golubinaIntroLessons by lessons { Введите шаг за шагом слово АПЕЛЬСИН """ }) - inputChars("АПЕЛЬСИН") + inputPhraseByLetters("АПЕЛЬСИН") +Info( """ Теперь приступим к изучению буквы, которая сравнительно недавно появилась @@ -1473,11 +1482,11 @@ internal val golubinaIntroLessons by lessons { +Info( """Шаг за шагом введите слово ЁЛКА""" ) - inputChars("ЁЛКА") + inputPhraseByLetters("ЁЛКА") +Info( """Введите далее название имя ещё одного дерева - КЛЁН""" ) - inputChars("КЛЁН") + inputPhraseByLetters("КЛЁН") +Info( """ Теперь давайте посмотрим на букву Ш. @@ -1496,19 +1505,19 @@ internal val golubinaIntroLessons by lessons { +Info( """Наберите отдельными символами слово ШЁЛК""" ) - inputChars("ШЁЛК") + inputPhraseByLetters("ШЁЛК") +Info( """Теперь введите буквы, которые вместе складываются в слово ШЕСТ""" ) - inputChars("ШЕСТ") + inputPhraseByLetters("ШЕСТ") +Info( """Введите предпоследнее слово в нашем уроке, слово ШАРФ""" ) - inputChars("ШАРФ") + inputPhraseByLetters("ШАРФ") +Info( """Наконец, перед тем, как мы закончим урок, введите слово КОШКА""" ) - inputChars("КОШКА") + inputPhraseByLetters("КОШКА") +Info( """ Используя брайлевский прибор, запишите изученные в этом уроке существительные: @@ -1536,7 +1545,7 @@ internal val golubinaIntroLessons by lessons { Введите по буквам слово МИШЕНЬ """ }) - inputChars("МИШЕНЬ") + inputPhraseByLetters("МИШЕНЬ") +Info( """ Познакомимся с точечным составом буквы, последней в русском алфавите, @@ -1558,25 +1567,25 @@ internal val golubinaIntroLessons by lessons { +Info( """Введите брайлевские буквы, которые составляют слово ЯБЛОКО""" ) - inputChars("ЯБЛОКО") + inputPhraseByLetters("ЯБЛОКО") +Info( """В следующих пяти шагах нужно ввести слово ЯГОДА""" ) - inputChars("ЯГОДА") + inputPhraseByLetters("ЯГОДА") +Info( """Далее, пожалуйста, наберите отдельными буквами слово ПЯТНО""" ) - inputChars("ПЯТНО") + inputPhraseByLetters("ПЯТНО") +Info( """Ещё два слова нужно ввести в этом уроке.
Первое - название птицы: ЦАПЛЯ""" ) - inputChars("ЦАПЛЯ") + inputPhraseByLetters("ЦАПЛЯ") +Info( """Последнее слово в сегодняшнем уроке - ЗЕМЛЯ""" ) - inputChars("ЗЕМЛЯ") + inputPhraseByLetters("ЗЕМЛЯ") +Info( """ Запишите на брайлевском приборе изученные слова с буквой Я: @@ -1602,7 +1611,7 @@ internal val golubinaIntroLessons by lessons { Введите, набирая отдельные буквы, слово ПОЛЯНА и поставьте после него запятую """ }) - inputChars("ПОЛЯНА,") + inputPhraseByLetters("ПОЛЯНА,") +Info( """ В этом уроке нам надо познакомиться с буквой Ю. @@ -1623,28 +1632,28 @@ internal val golubinaIntroLessons by lessons { +Info( """Наберите по символам слово с новой буквой: ЮБКА""" ) - inputChars("ЮБКА") + inputPhraseByLetters("ЮБКА") +Info( """В следующих четырёх шагах наберите слово УТЮГ""" ) - inputChars("УТЮГ") + inputPhraseByLetters("УТЮГ") +Info( """Заметьте, что буква Т и буква Ю получаются друг из друга отражением по вертикали.
После этого шага, пожалуйста, введите по буквам слово ТЮБИК""" ) - inputChars("ТЮБИК") + inputPhraseByLetters("ТЮБИК") +Info( """Нам осталось выполнить два задания, после чего урок заканчивается.
Первое: введите слово ТРЮК""" ) - inputChars("ТРЮК") + inputPhraseByLetters("ТРЮК") +Info( """Завершающее задание в этом уроке - введите буквы слова СЮЖЕТ""" ) - inputChars("СЮЖЕТ") + inputPhraseByLetters("СЮЖЕТ") +Info( """ Снова возьмите брайлевский прибор и напишите пройденные слова с буквами Ю, Т: @@ -1672,14 +1681,13 @@ internal val golubinaIntroLessons by lessons { Введите по буквам слово ИНТЕРЕСНЫЙ """ }) - inputChars("ИНТЕРЕСНЫЙ") + inputPhraseByLetters("ИНТЕРЕСНЫЙ") +Info( """Ещё перед началом урока мы чуть-чуть повторим числа. Введите цифровой знак и за ним число 1984 """ ) - +Input(content.markers.getValue(MarkerType.NumberSign)) - inputChars("1984") + inputNumber(1984) +Info( """ Давайте изучим букву 'Э'. @@ -1701,29 +1709,29 @@ internal val golubinaIntroLessons by lessons { +Info( """Вводя отдельные буквы, составьте слово ЭХО""" ) - inputChars("ЭХО") + inputPhraseByLetters("ЭХО") +Info( """Обратите внимание, буква 'Э' образуется зеркальным отражением буквы 'О' вдоль вертикальной оси.
В следующих пяти шагах наберите по буквам слово ПОЭМА""" ) - inputChars("ПОЭМА") + inputPhraseByLetters("ПОЭМА") +Info( """Теперь мы попросим Вас ввести слово ЭКРАН""" ) - inputChars("ЭКРАН") + inputPhraseByLetters("ЭКРАН") +Info( """ В этом уроке ещё два задания на ввод слов.
Первое из них: составьте слово ЭПИЗОД""" ) - inputChars("ЭПИЗОД") + inputPhraseByLetters("ЭПИЗОД") +Info( """В последнем задании нужно ввести слово ЭСТРАДА""" ) - inputChars("ЭСТРАДА") + inputPhraseByLetters("ЭСТРАДА") +Info( """ Теперь запишите введённые ранее слова на брайлевском приборе: @@ -1750,12 +1758,12 @@ internal val golubinaIntroLessons by lessons { Пожалуйста, наберите буквы, составляющие слово ЭНЦИКЛОПЕДИЯ """ }) - inputChars("ЭНЦИКЛОПЕДИЯ") + inputPhraseByLetters("ЭНЦИКЛОПЕДИЯ") +Info( """ Изучим последнюю букву, которую мы пока не рассматривали - букву 'В'.
- Буква 'В' в азбуке Брайля - это комбинация четырёх точек: 2, 4, 5 и 6. Её можно + Буква 'В' в системе Брайля - это комбинация четырёх точек: 2, 4, 5 и 6. Её можно составить, написав букву 'Ж' и добавив точку номер 6 в той же клетке. """ ) @@ -1773,29 +1781,29 @@ internal val golubinaIntroLessons by lessons { +Info( """Наберите первое слово с буквой 'В' - слово ВОР""" ) - inputChars("ВОР") + inputPhraseByLetters("ВОР") +Info( """Мы не случайно привели слово, содержащее буквы 'В' и 'Р'. Обратите внимание, что каждая из них получается зеркальным отражением другой по вертикали.
Далее введите по символам слово ВРАЧ""" ) - inputChars("ВРАЧ") + inputPhraseByLetters("ВРАЧ") +Info( """В следующих шести шагах введите, пожалуйста, буквы слова ВОРОНА""" ) - inputChars("ВОРОНА") + inputPhraseByLetters("ВОРОНА") +Info( """ Нужно составить ещё два слова.
Введите по буквам слово СЕВЕР""" ) - inputChars("СЕВЕР") + inputPhraseByLetters("СЕВЕР") +Info( """Заключительное упражнение: наберите слово КОРОВА""" ) - inputChars("КОРОВА") + inputPhraseByLetters("КОРОВА") +Info( """ Не забудьте для тренировки написать на брайлевском приборе набранные слова: @@ -1823,7 +1831,7 @@ internal val golubinaIntroLessons by lessons { В виде разминки перед уроком введите по буквам слово ДВОЕТОЧИЕ """ }) - inputChars("ДВОЕТОЧИЕ") + inputPhraseByLetters("ДВОЕТОЧИЕ") +Info( """ В рельефно-точечном алфавите точка с запятой обозначается точками с номерами 2 @@ -1848,10 +1856,10 @@ internal val golubinaIntroLessons by lessons { В следующих шагах введите слово РАЗДЕЛЕНИЕ; и поставьте в конце точку с запятой. """ ) - inputChars("РАЗДЕЛЕНИЕ;") + inputPhraseByLetters("РАЗДЕЛЕНИЕ;") +Info( """ - Двоеточие записывается в азбуке Брайля с помощью точек 2 и 5. Похоже на дефис, + Двоеточие записывается в системе Брайля с помощью точек 2 и 5. Похоже на дефис, но точки расположены на ряд выше.
Далее прочтите, а затем введите этот знак. @@ -1871,7 +1879,7 @@ internal val golubinaIntroLessons by lessons { +Info( """Введите по буквам слово ВЫВОД: и поставьте в конце двоеточие""" ) - inputChars("ВЫВОД:") + inputPhraseByLetters("ВЫВОД:") +Info( """ Перепишите, используя брайлевский прибор, предложение со страницы 54 пособия: @@ -1898,7 +1906,7 @@ internal val golubinaIntroLessons by lessons { Перед стартом немного повторения. Введите слово "ВОСКЛИЦАНИЕ" """ }) - inputChars("ВОСКЛИЦАНИЕ") + inputPhraseByLetters("ВОСКЛИЦАНИЕ") +Info( """ Вопросительный знак - точки 2 и 6. Как буква Е, но точки сдвинуты на один ряд вниз. @@ -1920,13 +1928,13 @@ internal val golubinaIntroLessons by lessons { +Info( """Введите вопрос: ГДЕ?""" ) - inputChars("ГДЕ?") + inputPhraseByLetters("ГДЕ?") +Info("""Наберите по буквам вопрос: КОГДА?""") - inputChars("КОГДА?") + inputPhraseByLetters("КОГДА?") +Info( """ - Восклицательный знак в азбуке Брайля - точки 2, 3 и 5. Не перепутайте с буквой Ф: + Восклицательный знак в системе Брайля - точки 2, 3 и 5. Не перепутайте с буквой Ф: точки расположены похожим образом, но каждая сдвинута на ряд ниже. """ ) @@ -1942,7 +1950,7 @@ internal val golubinaIntroLessons by lessons { """ ).annotate(StepAnnotation.golubinaBookRequired) +Info("""В завершение урока введите слово с восклицательным знаком на конце: ЧУДЕСНО!""") - inputChars("ЧУДЕСНО!") + inputPhraseByLetters("ЧУДЕСНО!") +Info(InfoInterpolation.run { """ Поздравляем. Урок $iLesson - восклицательный и вопросительный знак - завершён. @@ -1963,7 +1971,7 @@ internal val golubinaIntroLessons by lessons { восклицательный знак. """ }) - inputChars("ВНИМАНИЕ!") + inputPhraseByLetters("ВНИМАНИЕ!") +Info( """ Левая открывающая скобка обозначается точками с номерами 1, 2 и 6, правая - 3, @@ -1983,7 +1991,7 @@ internal val golubinaIntroLessons by lessons { """ ).annotate(StepAnnotation.golubinaBookRequired) +Info("""Введите слово (ВЕРОЯТНО), заключив его в скобки""") - inputChars("(ВЕРОЯТНО)") + inputPhraseByLetters("(ВЕРОЯТНО)") +Info( """ Теперь изучим кавычки. Левая (открывающая) кавычка - это точки 2, 3 и 6, правая @@ -1995,7 +2003,7 @@ internal val golubinaIntroLessons by lessons { ) showAndInputChars("«»") +Info("""Введите название повести Тургенева - «НАКАНУНЕ», заключив его в кавычки""") - inputChars("«НАКАНУНЕ»") + inputPhraseByLetters("«НАКАНУНЕ»") +Info( """ Нам осталось изучить ещё один символ в этом уроке - знак "Звёздочка". @@ -2151,10 +2159,11 @@ internal val golubinaIntroLessons by lessons { ).annotate(StepAnnotation.golubinaBookRequired) +Info("""В следующих шагах запишите: ДВА ПЛЮС ДВА РАВНО ЧЕТЫРЕ""") - +Input(content.markers.getValue(MarkerType.NumberSign)) - inputChars("2+") - +Input(content.markers.getValue(MarkerType.NumberSign)) - inputChars("2=4") + inputNumber(2) + +Input(content.symbols.getValue('+')) + inputNumber(2) + +Input(content.symbols.getValue('=')) + inputNumber(4) +Info( """ Запишите на брайлевском приборе примеры: @@ -2212,7 +2221,7 @@ internal val golubinaIntroLessons by lessons { """ ) +Input(content.markers.getValue(MarkerType.LatinCapital)) - inputChars("BRAILLE") + inputPhraseByLetters("BRAILLE") +Info(InfoInterpolation.run { """ На этом урок $iLesson завершается. В следующем занятии аналогичным образом @@ -2220,6 +2229,80 @@ internal val golubinaIntroLessons by lessons { """ }) } + lesson("Ноты") { + +Info(InfoInterpolation.run { + """ + Урок $iLesson: Основы нотной грамоты. +

+ В этом кратком уроке мы узнаем, как в брайлевской нотации записываются семь нот, + начиная с ДО и заканчивая СИ. +
+ В зависимости от длительности ноты обозначаются разными шеститочечными символами. + Здесь приведены те, что соответствуют продолжительности одна восьмая. + """ + }) + showAndInputMarkers(musicalNotesTypes) + +Info( + """ + Ноты длительности четвертная образуются путём добавления точки 6. +
+ Длительности половинная -- путём добавления точки 3. +
+ Целая нота -- путём добавления точек 3 и 6 вместе. + """ + ) + +Input(additionalContent.markers.getValue(MarkerType.NoteCQuarter)) + +Input(additionalContent.markers.getValue(MarkerType.NoteDHalf)) + +Input(additionalContent.markers.getValue(MarkerType.NoteEFull)) + +Info( + """ + Есть обозначения для пауз разной длительности (восьмая, четвертная, половинная + и целая), но они образованы не путём изменений в точках 5 и 6, а немного иначе. + В следующих шагах ознакомимся с этими символами. + """ + ) + showAndInputMarkers( + listOf( + MarkerType.MusicRest8th, + MarkerType.MusicRest4th, + MarkerType.MusicRestHalf, + MarkerType.MusicRestFull + ) + ) + +Info(""" + Каждая строка в нотах должна начинаться с обозначения октавы. В следующих шагах + посмотрим на их обозначения. + """) + +Show(content.markers.getValue(MarkerType.OctaveMark1)) + +Show(content.markers.getValue(MarkerType.OctaveMark2)) + +Show(content.markers.getValue(MarkerType.OctaveMark3)) + +Show(content.markers.getValue(MarkerType.OctaveMark4)) + +Show(content.markers.getValue(MarkerType.OctaveMark5)) + +Show(content.markers.getValue(MarkerType.OctaveMark6)) + +Show(content.markers.getValue(MarkerType.OctaveMark7)) + +Info(""" + Последний набор символов, с которыми мы сегодня ознакомимся: +
+ Знаки альтерации - диез, бемоль и бекар; + Музыкальный перенос - аналог обычного текстового переноса в нотах. + Он обозначается точкой 4, как и обозначение шестой октавы, но, поскольку ставится всегда + в конце строки, а не в начале, то неоднозначности не возникает. + """) + +Show(content.markers.getValue(MarkerType.MusicSharp)) + +Show(content.markers.getValue(MarkerType.MusicFlat)) + +Show(content.markers.getValue(MarkerType.MusicNatural)) + +Info(InfoInterpolation.run { + """ + Урок $iLesson на этом завершается. +
+ В заключение стоит отметить: + Луи Брайль был музыкантом и изначально позаботился о том, чтобы его система могла + представить ноты любой сложности. В рельефно-точечной нотной грамоте более 60 + различных символов; некоторые из них обозначаются несколькими шеститочиями. + Конечно же, мы не имеем возможности изучить их все. + """ + }) + } lesson("Греческий алфавит") { +Info(InfoInterpolation.run { """ @@ -2236,11 +2319,15 @@ internal val golubinaIntroLessons by lessons { """ Пособие под редакцией В. Голубиной включает и греческий алфавит. Его можно найти на страницах 93-99. Для каждой буквы приведено её название (на русском языке), - обозначение в азбуке Брайля (вместе с признаком заглавной буквы), а также + обозначение в системе Брайля (вместе с признаком заглавной буквы), а также рельефно-графическое изображение. """ ).annotate(StepAnnotation.golubinaBookRequired) showAndInputChars("ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ") + +Info(""" + Мы изучили греческие символы так, как они пишутся в советских и российских математических учебниках. + Есть и другие варианты записи греческих брайлевских букв, но в нашем кратком курсе они не рассматриваются. + """) +LastInfo(InfoInterpolation.run { """ Урок $iLesson закончен. diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt b/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt index a6a2c6f8..5b6f694b 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt @@ -34,7 +34,32 @@ enum class MarkerType { LatinSmall, BoldFont, ItalicFont, - NumberSign + NumberSign, + NoteC, + NoteD, + NoteE, + NoteF, + NoteG, + NoteA, + NoteB, + NoteCQuarter, + NoteDHalf, + NoteEFull, + MusicRest8th, + MusicRest4th, + MusicRestHalf, + MusicRestFull, + OctaveMark1, + OctaveMark2, + OctaveMark3, + OctaveMark4, + OctaveMark5, + OctaveMark6, + OctaveMark7, + MusicHyphen, + MusicSharp, + MusicFlat, + MusicNatural } /** @@ -50,6 +75,12 @@ val content by materials { +greekLetters +mathSigns +ms + +musicalNotes + +otherMusicalSymbols +} + +val additionalContent by materials { // for the course, but not for the browser / practice + +notesWithDuration } val knownMaterials by known( @@ -216,6 +247,41 @@ private val ms by markers { marker(MarkerType.NumberSign, BrailleDots(E, E, F, F, F, F)) } +private val musicalNotes by markers { + marker(MarkerType.NoteC, BrailleDots(F, E, E, F, F, E)) + marker(MarkerType.NoteD, BrailleDots(F, E, E, E, F, E)) + marker(MarkerType.NoteE, BrailleDots(F, F, E, F, E, E)) + marker(MarkerType.NoteF, BrailleDots(F, F, E, F, F, E)) + marker(MarkerType.NoteG, BrailleDots(F, F, E, E, F, E)) + marker(MarkerType.NoteA, BrailleDots(E, F, E, F, E, E)) + marker(MarkerType.NoteB, BrailleDots(E, F, E, F, F, E)) +} + +private val otherMusicalSymbols by markers { + marker(MarkerType.MusicRest8th, BrailleDots(F, E, F, F, E, F)) + marker(MarkerType.MusicRest4th, BrailleDots(F, F, F, E, E, F)) + marker(MarkerType.MusicRestHalf, BrailleDots(F, E, F, E, E, F)) + marker(MarkerType.MusicRestFull, BrailleDots(F, E, F, F, E, E)) + marker(MarkerType.OctaveMark1, BrailleDots(E, E, E, F, E, E)) + marker(MarkerType.OctaveMark2, BrailleDots(E, E, E, F, F, E)) + marker(MarkerType.OctaveMark3, BrailleDots(E, E, E, F, F, F)) + marker(MarkerType.OctaveMark4, BrailleDots(E, E, E, E, F, E)) + marker(MarkerType.OctaveMark5, BrailleDots(E, E, E, F, E, F)) + marker(MarkerType.OctaveMark6, BrailleDots(E, E, E, E, F, F)) + marker(MarkerType.OctaveMark7, BrailleDots(E, E, E, E, E, F)) + marker(MarkerType.MusicHyphen, BrailleDots(E, E, E, E, F, E)) + marker(MarkerType.MusicSharp, brailleDots = BrailleDots(F, E, E, F, E, F)) + marker(MarkerType.MusicFlat, brailleDots = BrailleDots(F, F, E, E, E, F)) + marker(MarkerType.MusicNatural, brailleDots = BrailleDots(F, E, E, E, E, F)) +} + + +private val notesWithDuration by markers { + marker(MarkerType.NoteCQuarter, BrailleDots(F, E, E, F, F, F)) + marker(MarkerType.NoteDHalf, BrailleDots(F, E, F, E, F, E)) + marker(MarkerType.NoteEFull, BrailleDots(F, F, F, F, E, F)) +} + /* * Add here rules, how to display hints for symbols. @@ -475,6 +541,139 @@ val Context.inputMarkerPrintRules by rules( { val s = getString(R.string.input_mod_num_sign) MarkerType.NumberSign::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_note_c) + MarkerType.NoteC::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_note_d) + MarkerType.NoteD::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_note_e) + MarkerType.NoteE::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_note_e) + MarkerType.NoteE::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_note_f) + MarkerType.NoteF::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_note_g) + MarkerType.NoteG::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_note_a) + MarkerType.NoteA::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_note_b) + MarkerType.NoteB::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.note_title_duration_template) + .format(getString(R.string.input_music_note_c), getString(R.string.note_duration_4th)) + MarkerType.NoteCQuarter::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.note_title_duration_template) + .format(getString(R.string.input_music_note_d), getString(R.string.note_duration_half)) + MarkerType.NoteDHalf::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.note_title_duration_template) + .format(getString(R.string.input_music_note_e), getString(R.string.note_duration_full)) + MarkerType.NoteEFull::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_rest_8th) + MarkerType.MusicRest8th::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_rest_4th) + MarkerType.MusicRest4th::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_rest_half) + MarkerType.MusicRestHalf::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_rest_full) + MarkerType.MusicRestFull::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_octave1) + MarkerType.OctaveMark1::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_octave2) + MarkerType.OctaveMark2::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_octave3) + MarkerType.OctaveMark3::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_octave4) + MarkerType.OctaveMark4::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_octave5) + MarkerType.OctaveMark5::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_octave6) + MarkerType.OctaveMark6::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_octave7) + MarkerType.OctaveMark7::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_hyphen) + MarkerType.MusicHyphen::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_sharp) + MarkerType.MusicSharp::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_flat) + MarkerType.MusicFlat::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_music_natural) + MarkerType.MusicNatural::equals to { _: MarkerType -> s } } ) @@ -517,5 +716,129 @@ val Context.showMarkerPrintRules by rules( { val s = getString(R.string.show_mod_num_sign) MarkerType.NumberSign::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_note_c) + MarkerType.NoteC::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_note_d) + MarkerType.NoteD::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_note_e) + MarkerType.NoteE::equals to { _: MarkerType -> s } + }, + { + val s = getString(R.string.show_music_note_f) + MarkerType.NoteF::equals to { _: MarkerType -> s } + }, + { + val s = getString(R.string.show_music_note_g) + MarkerType.NoteG::equals to { _: MarkerType -> s } + }, + { + val s = getString(R.string.show_music_note_a) + MarkerType.NoteA::equals to { _: MarkerType -> s } + }, + { + val s = getString(R.string.show_music_note_b) + MarkerType.NoteB::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.note_title_duration_template) + .format(getString(R.string.show_music_note_c), getString(R.string.note_duration_4th)) + MarkerType.NoteCQuarter::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.note_title_duration_template) + .format(getString(R.string.show_music_note_d), getString(R.string.note_duration_half)) + MarkerType.NoteDHalf::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.note_title_duration_template) + .format(getString(R.string.show_music_note_e), getString(R.string.note_duration_full)) + MarkerType.NoteEFull::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_rest_8th) + MarkerType.MusicRest8th::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_rest_4th) + MarkerType.MusicRest4th::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_rest_half) + MarkerType.MusicRestHalf::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_rest_full) + MarkerType.MusicRestFull::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_octave1) + MarkerType.OctaveMark1::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_octave2) + MarkerType.OctaveMark2::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_octave3) + MarkerType.OctaveMark3::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_octave4) + MarkerType.OctaveMark4::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_octave5) + MarkerType.OctaveMark5::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_octave6) + MarkerType.OctaveMark6::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_octave7) + MarkerType.OctaveMark7::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_hyphen) + MarkerType.MusicHyphen::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_sharp) + MarkerType.MusicSharp::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_flat) + MarkerType.MusicFlat::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_music_natural) + MarkerType.MusicNatural::equals to { _: MarkerType -> s } } ) diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/res/TestCourse.kt b/app/src/main/java/com/github/braillesystems/learnbraille/res/TestCourse.kt index 5d38c1a2..99a983b9 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/res/TestCourse.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/res/TestCourse.kt @@ -23,7 +23,7 @@ internal val testLessons by lessons { ) +Info(name1) +Info( - """В рельефной азбуке Брайля любой символ - это шеститочие. + """В рельефной системе Брайля любой символ - это шеститочие. Каждая точка из шести может быть выдавлена или пропущена. В следующем шаге все 6 точек выведены на экран.""" ) diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/res/_DSL.kt b/app/src/main/java/com/github/braillesystems/learnbraille/res/_DSL.kt index 6b1d111d..eb828e57 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/res/_DSL.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/res/_DSL.kt @@ -2,23 +2,42 @@ package com.github.braillesystems.learnbraille.res import com.github.braillesystems.learnbraille.data.dsl.StepsBuilder import com.github.braillesystems.learnbraille.data.dsl.annotate -import com.github.braillesystems.learnbraille.data.entities.Info -import com.github.braillesystems.learnbraille.data.entities.Input -import com.github.braillesystems.learnbraille.data.entities.Show +import com.github.braillesystems.learnbraille.data.entities.* fun StepsBuilder.inputChars(chars: String): Unit = chars .map(Char::toUpperCase) .forEach { c -> +Input(content.symbols.getValue(c)) } +fun StepsBuilder.inputPhraseByLetters(phrase: String) { + val materials = phrase + .map(Char::toUpperCase) + .map(content.symbols::getValue) + materials.forEachIndexed { i, _ -> + +InputPhraseLetter(materials, i) + } +} + fun StepsBuilder.showAndInputChars(chars: String): Unit = chars.map(Char::toUpperCase).forEach { +Show(content.symbols.getValue(it)) +Input(content.symbols.getValue(it)) } +fun StepsBuilder.showAndInputMarkers(markers: List) { + markers.forEach { + +Show(content.markers.getValue(it)) + +Input(content.markers.getValue(it)) + } +} + fun StepsBuilder.slateStylusLine(char: Char) { +Info( "Запишите на брайлевском приборе строку, состоящую из одного символа: $char." ).annotate(StepAnnotation.slateStylusRequired) } + +fun StepsBuilder.inputNumber(number: Int) { + +Input(content.markers.getValue(MarkerType.NumberSign)) + inputChars(number.toString()) +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/MainActivity.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/MainActivity.kt index 7ba77c61..69bfd0e8 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/MainActivity.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/MainActivity.kt @@ -1,37 +1,37 @@ -package com.github.braillesystems.learnbraille.ui.screens - -import android.content.pm.ActivityInfo -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import androidx.navigation.NavController -import androidx.navigation.findNavController -import androidx.navigation.ui.NavigationUI -import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.ui.brailletrainer.BrailleTrainer -import timber.log.Timber - -class MainActivity : AppCompatActivity() { - - private lateinit var navController: NavController - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - Timber.i("onCreate") - setContentView(R.layout.activity_main) - - navController = findNavController(R.id.navHostFragment) - NavigationUI.setupActionBarWithNavController(this, navController) - - BrailleTrainer.init(this) - - requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - } - - override fun onSupportNavigateUp(): Boolean = try { - navController.navigateUp() - } catch (e: IllegalArgumentException) { - Timber.e(e, "Multitouch navigation") - false - } -} +package com.github.braillesystems.learnbraille.ui.screens + +import android.content.pm.ActivityInfo +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.navigation.NavController +import androidx.navigation.findNavController +import androidx.navigation.ui.NavigationUI +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.ui.brailletrainer.BrailleTrainer +import timber.log.Timber + +class MainActivity : AppCompatActivity() { + + private lateinit var navController: NavController + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + Timber.i("onCreate") + setContentView(R.layout.activity_main) + + navController = findNavController(R.id.navHostFragment) + NavigationUI.setupActionBarWithNavController(this, navController) + + BrailleTrainer.init(this) + + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } + + override fun onSupportNavigateUp(): Boolean = try { + navController.navigateUp() + } catch (e: IllegalArgumentException) { + Timber.e(e, "Multitouch navigation") + false + } +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/BrowserDeckFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/BrowserDeckFragment.kt new file mode 100644 index 00000000..7e29e7bc --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/BrowserDeckFragment.kt @@ -0,0 +1,80 @@ +package com.github.braillesystems.learnbraille.ui.screens.browser + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.lifecycle.lifecycleScope +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.MarkerSymbol +import com.github.braillesystems.learnbraille.data.entities.Material +import com.github.braillesystems.learnbraille.data.entities.Symbol +import com.github.braillesystems.learnbraille.data.entities.spelling +import com.github.braillesystems.learnbraille.data.repository.BrowserRepository +import com.github.braillesystems.learnbraille.databinding.FragmentBrowserBinding +import com.github.braillesystems.learnbraille.res.deckTagToName +import com.github.braillesystems.learnbraille.res.showMarkerPrintRules +import com.github.braillesystems.learnbraille.res.showSymbolPrintRules +import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp +import com.github.braillesystems.learnbraille.utils.getValue +import com.github.braillesystems.learnbraille.utils.navigate +import com.github.braillesystems.learnbraille.utils.stringify +import com.github.braillesystems.learnbraille.utils.title +import kotlinx.coroutines.launch +import org.koin.android.ext.android.inject + +class BrowserDeckFragment : AbstractFragmentWithHelp(R.string.browser_deck_help) { + + private val browserRepository: BrowserRepository by inject() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_browser, + container, + false + ).ini().also { binding -> + lifecycleScope.launch { + val deckId = browserRepository.currentDeckId + browserRepository.deck(deckId)?.also { title = deckTagToName.getValue(it.tag) } + val materials = + browserRepository.allMaterialsFromDeck(deckId).map { DeckOrMaterial(it) } + binding.materialsList.adapter = BrowserListAdapter(materials) { item -> + this.item = item + item.material?.apply { + materialText.text = when (data) { + is Symbol -> getString(R.string.browser_represent_template).format( + showSymbolPrintRules.getValue(data.char), + data.brailleDots.spelling + ) + is MarkerSymbol -> getString(R.string.browser_represent_template).format( + showMarkerPrintRules.getValue(data.type), + data.brailleDots.spelling + ) + } + } + clickListener = object : BrowserItemListener { + override fun onClick(item: DeckOrMaterial) { + item.material?.apply { + val arg = stringify(Material.serializer(), this) + when (data) { + is Symbol -> navigate( + BrowserDeckFragmentDirections + .actionBrowserDeckFragmentToSymbolViewFragment(arg) + ) + is MarkerSymbol -> navigate( + BrowserDeckFragmentDirections + .actionBrowserDeckFragmentToMarkerViewFragment(arg) + ) + } + } + } + } + } + } + + }.root +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/BrowserFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/BrowserFragment.kt index eb8eaa15..369bc919 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/BrowserFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/BrowserFragment.kt @@ -7,25 +7,20 @@ import androidx.databinding.DataBindingUtil import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.data.entities.MarkerSymbol +import com.github.braillesystems.learnbraille.data.entities.Deck import com.github.braillesystems.learnbraille.data.entities.Material -import com.github.braillesystems.learnbraille.data.entities.Symbol -import com.github.braillesystems.learnbraille.data.entities.spelling -import com.github.braillesystems.learnbraille.data.repository.BrowserRepository +import com.github.braillesystems.learnbraille.data.repository.MutableBrowserRepository import com.github.braillesystems.learnbraille.databinding.BrowserListItemBinding import com.github.braillesystems.learnbraille.databinding.FragmentBrowserBinding -import com.github.braillesystems.learnbraille.res.showMarkerPrintRules -import com.github.braillesystems.learnbraille.res.showSymbolPrintRules +import com.github.braillesystems.learnbraille.res.deckTagToName import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp -import com.github.braillesystems.learnbraille.utils.getValue import com.github.braillesystems.learnbraille.utils.navigate -import com.github.braillesystems.learnbraille.utils.stringify import kotlinx.coroutines.launch import org.koin.android.ext.android.inject class BrowserFragment : AbstractFragmentWithHelp(R.string.browser_help) { - private val browserRepository: BrowserRepository by inject() + private val browserRepository: MutableBrowserRepository by inject() override fun onCreateView( inflater: LayoutInflater, @@ -39,45 +34,44 @@ class BrowserFragment : AbstractFragmentWithHelp(R.string.browser_help) { ).ini().also { binding -> lifecycleScope.launch { - val deckId = browserRepository.currentDeckId - val materials = browserRepository.allMaterialsFromDeck(deckId) - val listener = object : BrowserItemListener { - override fun onClick(item: Material) { - val arg = stringify(Material.serializer(), item) - when (item.data) { - is Symbol -> navigate( - BrowserFragmentDirections - .actionBrowserFragmentToSymbolViewFragment(arg) - ) - is MarkerSymbol -> navigate( - BrowserFragmentDirections - .actionBrowserFragmentToMarkerViewFragment(arg) - ) - } - } - } - binding.materialsList.adapter = BrowserListAdapter(materials) { item -> + val decks = browserRepository.allUniqueDecks().map { DeckOrMaterial(it) } + binding.materialsList.adapter = BrowserListAdapter(decks) { item -> this.item = item - materialText.text = when (item.data) { - is Symbol -> getString(R.string.browser_represent_template).format( - showSymbolPrintRules.getValue(item.data.char), - item.data.brailleDots.spelling - ) - is MarkerSymbol -> getString(R.string.browser_represent_template).format( - showMarkerPrintRules.getValue(item.data.type), - item.data.brailleDots.spelling - ) + item.deck?.apply { + materialText.text = deckTagToName.getValue(tag) + } + clickListener = object : BrowserItemListener { + override fun onClick(item: DeckOrMaterial) { + item.deck?.apply { + // TODO Pass deck id to the BrowserDeckFragment via the fragment arguments + // TODO instead of the global persistent state. + browserRepository.currentDeckId = id + navigate(BrowserFragmentDirections.actionBrowserFragmentToBrowserDeckFragment()) + } + } } - clickListener = listener } } }.root } -private class BrowserListAdapter( - private val materials: List, - private val bind: BrowserListItemBinding.(Material) -> Unit +class DeckOrMaterial { + var material: Material? = null + var deck: Deck? = null + + constructor(material: Material) { + this.material = material + } + + constructor(deck: Deck) { + this.deck = deck + } +} + +class BrowserListAdapter( + private val items: List, + private val bind: BrowserListItemBinding.(T) -> Unit ) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = @@ -89,18 +83,18 @@ private class BrowserListAdapter( ) ) - override fun getItemCount(): Int = materials.size + override fun getItemCount(): Int = items.size override fun onBindViewHolder(holder: BrowserItemViewHolder, position: Int) { - val item = materials[position] + val item = items[position] holder.binding.bind(item) } } -private class BrowserItemViewHolder( +class BrowserItemViewHolder( val binding: BrowserListItemBinding ) : RecyclerView.ViewHolder(binding.root) interface BrowserItemListener { - fun onClick(item: Material) + fun onClick(item: DeckOrMaterial) } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/MarkerViewFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/MarkerViewFragment.kt index 3e1a82a6..007cef08 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/MarkerViewFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/MarkerViewFragment.kt @@ -1,15 +1,21 @@ package com.github.braillesystems.learnbraille.ui.screens.browser +import android.app.AlertDialog import android.os.Bundle import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import android.widget.Button +import android.widget.RadioButton +import android.widget.RadioGroup import android.widget.TextView +import androidx.core.view.setPadding import androidx.databinding.DataBindingUtil import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.data.entities.MarkerSymbol -import com.github.braillesystems.learnbraille.data.entities.Material +import com.github.braillesystems.learnbraille.data.entities.* import com.github.braillesystems.learnbraille.databinding.FragmentMarkerViewBinding +import com.github.braillesystems.learnbraille.res.MarkerType +import com.github.braillesystems.learnbraille.res.musicalNotesTypes import com.github.braillesystems.learnbraille.res.showMarkerPrintRules import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp import com.github.braillesystems.learnbraille.ui.screens.BrailleDotsInfo @@ -18,9 +24,79 @@ import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode import com.github.braillesystems.learnbraille.ui.views.display import com.github.braillesystems.learnbraille.ui.views.dotsState import com.github.braillesystems.learnbraille.utils.* +import com.karlotoy.perfectune.instance.PerfectTune +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +enum class NoteDuration( + val titleStrId: Int, + val dot3: Boolean, + val dot6: Boolean, + val valueMillis: Long +) { + EIGHTH(titleStrId = R.string.note_duration_8th, dot3 = false, dot6 = false, valueMillis = 125), + QUARTER(titleStrId = R.string.note_duration_4th, dot3 = false, dot6 = true, valueMillis = 250), + HALF(titleStrId = R.string.note_duration_half, dot3 = true, dot6 = false, valueMillis = 500), + FULL(titleStrId = R.string.note_duration_full, dot3 = true, dot6 = true, valueMillis = 1000); + + /// Converts a note of any duration in Braille to the note of the current duration + fun modifiedNote(note: BrailleDots): BrailleDots = + note.copy(b3 = BrailleDot.valueOf(dot3), b6 = BrailleDot.valueOf(dot6)) +} + +private val noteToFreq = mapOf( + MarkerType.NoteC to 261.63, // C4 https://pages.mtu.edu/~suits/notefreqs.html + MarkerType.NoteD to 293.66, + MarkerType.NoteE to 329.63, + MarkerType.NoteF to 349.23, + MarkerType.NoteG to 392.00, + MarkerType.NoteA to 440.00, + MarkerType.NoteB to 493.88 +) class MarkerViewFragment : AbstractFragmentWithHelp(R.string.browser_marker_view_help) { + private fun chooseNoteDurationDialog( + brailleDots: BrailleDots, + previousDuration: NoteDuration, + block: (NoteDuration) -> Unit + ) { + val input = RadioGroup(context).apply { + setPadding(resources.getDimension(R.dimen.margin_text).toInt()) + } + var noteDuration = previousDuration + val durationToButtonsMap: MutableMap = mutableMapOf() + NoteDuration.values().forEach { duration -> + input.addView(RadioButton(context).apply { + text = getString(R.string.browser_represent_template).format( + getString(duration.titleStrId), + duration.modifiedNote(brailleDots).spelling + ) + textSize = resources.getDimension(R.dimen.dialog_items_text_size) + durationToButtonsMap[duration] = this + }) + } + + val dialog = AlertDialog.Builder(context) + .setTitle(getString(R.string.browser_note_duration_dialog_title)) + .setView(input) + .create() + + durationToButtonsMap.forEach { + val duration = it.key + val button = it.value + if (it.key == noteDuration) { + input.check(button.id) + } + button.setOnClickListener { + noteDuration = duration + block(noteDuration) + dialog.cancel() + } + } + dialog.show() + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -34,6 +110,8 @@ class MarkerViewFragment : AbstractFragmentWithHelp(R.string.browser_marker_view object : FragmentBinding { override val textView: TextView? = this@ini.infoTextView override val rightButton: Button? = this@ini.flipButton + override val leftButton: Button? = this@ini.durationButton + override val rightMiddleButton: Button? = this@ini.playButton override val brailleDotsInfo: BrailleDotsInfo? = this@ini.run { BrailleDotsInfo(brailleDots, BrailleDotsViewMode.Reading, infoTextView, flipButton) } @@ -43,13 +121,45 @@ class MarkerViewFragment : AbstractFragmentWithHelp(R.string.browser_marker_view val m: Material = parse(Material.serializer(), getFragmentStringArg("material")) require(m.data is MarkerSymbol) - val text = showMarkerPrintRules.getValue(m.data.type) + val baseText = showMarkerPrintRules.getValue(m.data.type) + var text = baseText + + var dots: BrailleDots = m.data.brailleDots + if (m.data.type in musicalNotesTypes) { + var noteDuration = NoteDuration.EIGHTH + val noteDurationTemplate = getString(R.string.note_title_duration_template) + text = noteDurationTemplate.format(text, getString(noteDuration.titleStrId)) + durationButton.visibility = View.VISIBLE + durationButton.setOnClickListener { + chooseNoteDurationDialog(brailleDots = dots, previousDuration = noteDuration) { + noteDuration = it + dots = it.modifiedNote(m.data.brailleDots) + brailleDots.dotsState.display(dots) + text = noteDurationTemplate.format(baseText, getString(it.titleStrId)) + infoTextView.text = text + checkedAnnounce(text) + } + } + + val defaultFrequency = 100.0 + val perfectTune = PerfectTune() + perfectTune.tuneFreq = noteToFreq[m.data.type] ?: defaultFrequency + perfectTune.tuneAmplitude = 60000 + playButton.setOnClickListener { + scope().launch { + perfectTune.playTune() + delay(noteDuration.valueMillis) + perfectTune.stopTune() + } + } + playButton.visibility = View.VISIBLE + } + infoTextView.text = text checkedAnnounce(text) - brailleDots.dotsState.display(m.data.brailleDots) - flipButton.setOnClickListener { - brailleDots.reflect().display(m.data.brailleDots) - } + + brailleDots.dotsState.display(dots) + flipButton.setOnClickListener { brailleDots.reflect().display(dots) } }.root } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt index 9506a7df..ef5864ef 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt @@ -1,41 +1,41 @@ -package com.github.braillesystems.learnbraille.ui.screens.exit - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.databinding.DataBindingUtil -import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.databinding.FragmentExitBinding -import com.github.braillesystems.learnbraille.ui.screens.AbstractFragment -import com.github.braillesystems.learnbraille.utils.checkedAnnounce -import com.github.braillesystems.learnbraille.utils.navigate -import com.github.braillesystems.learnbraille.utils.navigateToLauncher -import com.github.braillesystems.learnbraille.utils.title - -class ExitFragment : AbstractFragment() { - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ) = DataBindingUtil.inflate( - inflater, - R.layout.fragment_exit, - container, - false - ).ini().apply { - - title = getString(R.string.exit_question) - checkedAnnounce(title) - - exitButton.setOnClickListener { - navigate(R.id.action_global_menuFragment) - navigateToLauncher() - } - - continueButton.setOnClickListener { - navigate(R.id.action_global_menuFragment) - } - - }.root -} +package com.github.braillesystems.learnbraille.ui.screens.exit + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.databinding.FragmentExitBinding +import com.github.braillesystems.learnbraille.ui.screens.AbstractFragment +import com.github.braillesystems.learnbraille.utils.checkedAnnounce +import com.github.braillesystems.learnbraille.utils.navigate +import com.github.braillesystems.learnbraille.utils.navigateToLauncher +import com.github.braillesystems.learnbraille.utils.title + +class ExitFragment : AbstractFragment() { + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_exit, + container, + false + ).ini().apply { + + title = getString(R.string.exit_question) + checkedAnnounce(title) + + exitButton.setOnClickListener { + navigate(R.id.action_global_menuFragment) + navigateToLauncher() + } + + continueButton.setOnClickListener { + navigate(R.id.action_global_menuFragment) + } + + }.root +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/help/HelpFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/help/HelpFragment.kt index 23eb22a9..120c8b22 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/help/HelpFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/help/HelpFragment.kt @@ -1,33 +1,33 @@ -package com.github.braillesystems.learnbraille.ui.screens.help - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.databinding.DataBindingUtil -import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.databinding.FragmentHelpBinding -import com.github.braillesystems.learnbraille.ui.screens.AbstractFragment -import com.github.braillesystems.learnbraille.utils.checkedAnnounce -import com.github.braillesystems.learnbraille.utils.getFragmentStringArg -import com.github.braillesystems.learnbraille.utils.removeHtmlMarkup - -class HelpFragment : AbstractFragment() { - - private val helpMessageArgName = "help_message" - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ) = DataBindingUtil.inflate( - inflater, - R.layout.fragment_help, - container, - false - ).ini().apply { - - val content = getFragmentStringArg(helpMessageArgName) - helpView.setSeparatedText(content) - checkedAnnounce(content.removeHtmlMarkup()) - - }.root -} +package com.github.braillesystems.learnbraille.ui.screens.help + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.databinding.FragmentHelpBinding +import com.github.braillesystems.learnbraille.ui.screens.AbstractFragment +import com.github.braillesystems.learnbraille.utils.checkedAnnounce +import com.github.braillesystems.learnbraille.utils.getFragmentStringArg +import com.github.braillesystems.learnbraille.utils.removeHtmlMarkup + +class HelpFragment : AbstractFragment() { + + private val helpMessageArgName = "help_message" + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_help, + container, + false + ).ini().apply { + + val content = getFragmentStringArg(helpMessageArgName) + helpView.setSeparatedText(content) + checkedAnnounce(content.removeHtmlMarkup()) + + }.root +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt index 5a9e2ae9..0898f9ab 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt @@ -1,162 +1,166 @@ -package com.github.braillesystems.learnbraille.ui.screens.menu - -import android.app.Activity.RESULT_OK -import android.content.ActivityNotFoundException -import android.content.Intent -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.content.ContextCompat -import androidx.databinding.DataBindingUtil -import com.github.braillesystems.learnbraille.COURSE -import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.data.db.LearnBrailleDatabase -import com.github.braillesystems.learnbraille.databinding.FragmentMenuBinding -import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp -import com.github.braillesystems.learnbraille.ui.screens.theory.toLastCourseStep -import com.github.braillesystems.learnbraille.utils.* -import com.google.android.material.button.MaterialButton -import org.koin.android.ext.android.inject -import timber.log.Timber - -class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { - - private val db: LearnBrailleDatabase by inject() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ) = DataBindingUtil.inflate( - inflater, - R.layout.fragment_menu, - container, - false - ).ini().also { binding -> - - title = getString(R.string.menu_actionbar_text_template).format(appName) - - val buttons = mutableListOf() - - binding.lessonsButton.also { - buttons += it - }.setOnClickListener(interruptingOnClickListener { - toLastCourseStep(COURSE.id) - }) - - binding.practiceButton.also { - buttons += it - }.setOnClickListener(interruptingOnClickListener { - navigate(R.id.action_menuFragment_to_practiceFragment) - }) - - binding.browserButton.also { - buttons += it - }.setOnClickListener(interruptingOnClickListener { - navigate(R.id.action_menuFragment_to_browserFragment) - }) - - binding.statsButton.also { - buttons += it - }.setOnClickListener(interruptingOnClickListener { - navigate(R.id.action_menuFragment_to_statsFragment) - }) - - if (preferenceRepository.additionalQrCodeButtonEnabled) { - binding.qrPracticeButton.also { - buttons += it - }.setOnClickListener { - try { - val intent = Intent("com.google.zxing.client.android.SCAN") - intent.putExtra("SCAN_MODE", "QR_CODE_MODE") - startActivityForResult(intent, qrRequestCode) - } catch (e: ActivityNotFoundException) { - checkedToast(getString(R.string.qr_intent_cancelled)) - sendMarketIntent("com.google.zxing.client.android") - } - } - } else { - binding.qrPracticeButton.visibility = View.GONE - } - - binding.settingsButton.also { - buttons += it - }.setOnClickListener { - navigate(R.id.action_menuFragment_to_settingsFragment) - } - - if (preferenceRepository.extendedAccessibilityEnabled) { - binding.exitButton.also { - buttons += it - }.setOnClickListener { - navigate(R.id.action_menuFragment_to_exitFragment) - } - } else { - binding.exitButton.visibility = View.GONE - } - - colorButtons(buttons) - - }.root - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - when (requestCode) { - qrRequestCode -> processQrResult(resultCode, data) - } - } - - private fun processQrResult(resultCode: Int, data: Intent?) { - when (resultCode) { - RESULT_OK -> toast( - data?.getStringExtra("SCAN_RESULT") - ?: getString(R.string.menu_qr_empty_result).also { - Timber.e("QR: empty result with OK code") - } - ) - } - } - - private fun interruptingOnClickListener(block: (View) -> Unit) = - View.OnClickListener { - if (db.isInitialized) block(it) - else toast(getString(R.string.menu_db_not_initialized_warning)) - } - - private fun colorButtons(buttons: List) { - buttons - .filterIndexed { i, _ -> i % 2 == 0 } - .forEach { - it.setTextColor( - ContextCompat.getColor( - application, R.color.colorOnSecondary - ) - ) - it.setBackgroundColor( - ContextCompat.getColor( - application, R.color.colorSecondary - ) - ) - } - - buttons - .filterIndexed { i, _ -> i % 2 != 0 } - .forEach { - it.setTextColor( - ContextCompat.getColor( - application, R.color.colorOnPrimary - ) - ) - it.setBackgroundColor( - ContextCompat.getColor( - application, R.color.colorPrimary - ) - ) - } - } - - companion object { - private const val qrRequestCode = 0 - } -} +package com.github.braillesystems.learnbraille.ui.screens.menu + +import android.app.Activity.RESULT_OK +import android.content.ActivityNotFoundException +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.databinding.DataBindingUtil +import com.github.braillesystems.learnbraille.COURSE +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.db.LearnBrailleDatabase +import com.github.braillesystems.learnbraille.databinding.FragmentMenuBinding +import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp +import com.github.braillesystems.learnbraille.ui.screens.theory.toLastOrCurrCourseStep +import com.github.braillesystems.learnbraille.utils.* +import com.google.android.material.button.MaterialButton +import org.koin.android.ext.android.inject +import timber.log.Timber + +class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { + + private val db: LearnBrailleDatabase by inject() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_menu, + container, + false + ).ini().also { binding -> + + title = getString(R.string.menu_actionbar_text_template).format(appName) + + val buttons = mutableListOf() + + binding.lessonsButton.also { + buttons += it + }.setOnClickListener(interruptingOnClickListener { + toLastOrCurrCourseStep(COURSE.id) + }) + + binding.practiceButton.also { + buttons += it + }.setOnClickListener(interruptingOnClickListener { + navigate(R.id.action_menuFragment_to_practiceFragment) + }) + + binding.browserButton.also { + buttons += it + }.setOnClickListener(interruptingOnClickListener { + navigate(R.id.action_menuFragment_to_browserFragment) + }) + + if (!preferenceRepository.teacherModeEnabled) { + binding.statsButton.also { + buttons += it + }.setOnClickListener(interruptingOnClickListener { + navigate(R.id.action_menuFragment_to_statsFragment) + }) + } else { + binding.statsButton.visibility = View.GONE + } + + if (preferenceRepository.additionalQrCodeButtonEnabled) { + binding.qrPracticeButton.also { + buttons += it + }.setOnClickListener { + try { + val intent = Intent("com.google.zxing.client.android.SCAN") + intent.putExtra("SCAN_MODE", "QR_CODE_MODE") + startActivityForResult(intent, qrRequestCode) + } catch (e: ActivityNotFoundException) { + checkedToast(getString(R.string.qr_intent_cancelled)) + sendMarketIntent("com.google.zxing.client.android") + } + } + } else { + binding.qrPracticeButton.visibility = View.GONE + } + + binding.settingsButton.also { + buttons += it + }.setOnClickListener { + navigate(R.id.action_menuFragment_to_settingsFragment) + } + + if (preferenceRepository.extendedAccessibilityEnabled) { + binding.exitButton.also { + buttons += it + }.setOnClickListener { + navigate(R.id.action_menuFragment_to_exitFragment) + } + } else { + binding.exitButton.visibility = View.GONE + } + + colorButtons(buttons) + + }.root + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + qrRequestCode -> processQrResult(resultCode, data) + } + } + + private fun processQrResult(resultCode: Int, data: Intent?) { + when (resultCode) { + RESULT_OK -> toast( + data?.getStringExtra("SCAN_RESULT") + ?: getString(R.string.menu_qr_empty_result).also { + Timber.e("QR: empty result with OK code") + } + ) + } + } + + private fun interruptingOnClickListener(block: (View) -> Unit) = + View.OnClickListener { + if (db.isInitialized) block(it) + else toast(getString(R.string.menu_db_not_initialized_warning)) + } + + private fun colorButtons(buttons: List) { + buttons + .filterIndexed { i, _ -> i % 2 == 0 } + .forEach { + it.setTextColor( + ContextCompat.getColor( + application, R.color.colorOnSecondary + ) + ) + it.setBackgroundColor( + ContextCompat.getColor( + application, R.color.colorSecondary + ) + ) + } + + buttons + .filterIndexed { i, _ -> i % 2 != 0 } + .forEach { + it.setTextColor( + ContextCompat.getColor( + application, R.color.colorOnPrimary + ) + ) + it.setBackgroundColor( + ContextCompat.getColor( + application, R.color.colorPrimary + ) + ) + } + } + + companion object { + private const val qrRequestCode = 0 + } +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt index 2ea31ff6..57a4ad35 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt @@ -1,192 +1,198 @@ -package com.github.braillesystems.learnbraille.ui.screens.practice - -import android.os.Bundle -import android.os.Vibrator -import android.view.* -import android.widget.Button -import android.widget.TextView -import androidx.core.content.getSystemService -import androidx.databinding.DataBindingUtil -import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider -import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.data.entities.MarkerSymbol -import com.github.braillesystems.learnbraille.data.entities.Symbol -import com.github.braillesystems.learnbraille.databinding.FragmentCardBinding -import com.github.braillesystems.learnbraille.res.captionRules -import com.github.braillesystems.learnbraille.res.deckTagToName -import com.github.braillesystems.learnbraille.res.inputMarkerPrintRules -import com.github.braillesystems.learnbraille.ui.* -import com.github.braillesystems.learnbraille.ui.brailletrainer.BrailleTrainer -import com.github.braillesystems.learnbraille.ui.brailletrainer.BrailleTrainerSignalHandler -import com.github.braillesystems.learnbraille.ui.screens.* -import com.github.braillesystems.learnbraille.ui.views.* -import com.github.braillesystems.learnbraille.utils.* -import org.koin.android.ext.android.inject -import org.koin.core.parameter.parametersOf -import timber.log.Timber - -class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { - - // This value can change during ViewModel lifetime (ViewModelProvider does not call - // ViewModelFactory each time onCreateView runs). And once created ViewModel - // should be able to use up to date dotsState. - private lateinit var dotsState: BrailleDotsState - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ) = DataBindingUtil.inflate( - inflater, - R.layout.fragment_card, - container, - false - ).ini { - object : FragmentBinding { - override val leftButton: Button? = this@ini.hintButton - override val rightButton: Button? = this@ini.nextButton - override val rightMiddleButton: Button? = this@ini.flipButton - override val textView: TextView? = this@ini.markerDescription - override val brailleDotsInfo: BrailleDotsInfo? = this@ini.run { - BrailleDotsInfo( - brailleDots, - if (preferenceRepository.isWriteModeFirst) BrailleDotsViewMode.Writing - else BrailleDotsViewMode.Reading, - hintButton, flipButton - ) - } - } - }.also { binding -> - - Timber.i("onCreateView") - - title = title() - - dotsState = binding.brailleDots.dotsState - - val viewModelFactory: CardViewModelFactory by inject { - parametersOf({ dotsState.brailleDots }) - } - val viewModel = ViewModelProvider(this, viewModelFactory) - .get(CardViewModel::class.java) - - dotsState.subscribe(viewModel) - - val buzzer: Vibrator? = activity?.getSystemService() - - BrailleTrainer.setSignalHandler(object : BrailleTrainerSignalHandler { - override fun onJoystickRight() = viewModel.onCheck() - override fun onJoystickLeft() = viewModel.onHint() - }) - - binding.cardViewModel = viewModel - binding.lifecycleOwner = this@CardFragment - - binding.flipButton.setOnClickListener { - dotsState = binding.brailleDots.reflect().apply { - dotsState.subscribe(viewModel) - if (viewModel.state == DotsChecker.State.HINT) { - viewModel.expectedDots?.let { display(it) } - } - } - } - - viewModel.symbol.observe(viewLifecycleOwner, Observer { - if (it == null) return@Observer - when (it) { - is Symbol -> { - binding.letter.visibility = View.VISIBLE - binding.markerDescription.visibility = View.GONE - binding.letter.letter = it.char - binding.letterCaption.text = captionRules.getValue(it) - } - is MarkerSymbol -> { - binding.letter.visibility = View.GONE - binding.markerDescription.visibility = View.VISIBLE - binding.markerDescription.text = inputMarkerPrintRules[it.type] - binding.letterCaption.text = "" - } - } - }) - - viewModel.observeCheckedOnFly( - viewLifecycleOwner, { dotsState }, buzzer, - block = { title = title(viewModel) }, - softBlock = ::showCorrectToast - ) - - viewModel.observeEventIncorrect( - viewLifecycleOwner, { dotsState }, buzzer - ) { - viewModel.symbol.value - ?.let { showIncorrectToast(inputPrint(it)) } - ?: checkedToast(getString(R.string.input_loading)) - title = title(viewModel) - } - - viewModel.observeEventHint( - viewLifecycleOwner, { dotsState } - ) { expectedDots -> - showHintToast(expectedDots) - } - - viewModel.observeEventPassHint( - viewLifecycleOwner, { dotsState } - ) { - viewModel.symbol.value?.let { - checkedAnnounce(inputPrint(it)) - } - } - - viewModel.symbol.observe( - viewLifecycleOwner, - Observer { - if (it == null) return@Observer - checkedAnnounce(inputPrint(it)) - } - ) - - viewModel.deckTag.observe( - viewLifecycleOwner, - Observer { tag -> - if (tag == null) return@Observer - val template = if (preferenceRepository.practiceUseOnlyKnownMaterials) { - getString(R.string.practice_deck_name_enabled_template) - } else { - getString(R.string.practice_deck_name_disabled_template) - } - toast( - template.format(deckTagToName.getValue(tag)) - ) - } - ) - - if (viewModel.state == DotsChecker.State.HINT) { - viewModel.expectedDots?.let { dotsState.display(it) } - } - - }.root - - private fun title(viewModel: CardViewModel? = null): String = - getString(R.string.practice_actionbar_title_template).run { - if (viewModel == null) format(0, 0) - else format(viewModel.nCorrect, viewModel.nTries) - } - - private fun BrailleDotsState.subscribe(viewModel: CardViewModel) = - subscribe(View.OnClickListener { - viewModel.onSoftCheck() - }) - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.card_menu, menu) - } - - override fun onOptionsItemSelected(item: MenuItem) = false.also { - when (item.itemId) { - R.id.help -> navigateToHelp() - R.id.decks_list -> navigate(R.id.action_cardFragment_to_decksList) - } - } -} +package com.github.braillesystems.learnbraille.ui.screens.practice + +import android.os.Bundle +import android.os.Vibrator +import android.view.* +import android.widget.Button +import android.widget.TextView +import androidx.core.content.getSystemService +import androidx.databinding.DataBindingUtil +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.MarkerSymbol +import com.github.braillesystems.learnbraille.data.entities.Symbol +import com.github.braillesystems.learnbraille.databinding.FragmentCardBinding +import com.github.braillesystems.learnbraille.res.captionRules +import com.github.braillesystems.learnbraille.res.deckTagToName +import com.github.braillesystems.learnbraille.res.inputMarkerPrintRules +import com.github.braillesystems.learnbraille.res.musicalNotesTypes +import com.github.braillesystems.learnbraille.ui.* +import com.github.braillesystems.learnbraille.ui.brailletrainer.BrailleTrainer +import com.github.braillesystems.learnbraille.ui.brailletrainer.BrailleTrainerSignalHandler +import com.github.braillesystems.learnbraille.ui.screens.* +import com.github.braillesystems.learnbraille.ui.views.* +import com.github.braillesystems.learnbraille.utils.* +import org.koin.android.ext.android.inject +import org.koin.core.parameter.parametersOf +import timber.log.Timber + +class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { + + // This value can change during ViewModel lifetime (ViewModelProvider does not call + // ViewModelFactory each time onCreateView runs). And once created ViewModel + // should be able to use up to date dotsState. + private lateinit var dotsState: BrailleDotsState + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_card, + container, + false + ).ini { + object : FragmentBinding { + override val leftButton: Button? = this@ini.hintButton + override val rightButton: Button? = this@ini.nextButton + override val rightMiddleButton: Button? = this@ini.flipButton + override val textView: TextView? = this@ini.markerDescription + override val brailleDotsInfo: BrailleDotsInfo? = this@ini.run { + BrailleDotsInfo( + brailleDots, + if (preferenceRepository.isWriteModeFirst) BrailleDotsViewMode.Writing + else BrailleDotsViewMode.Reading, + hintButton, flipButton + ) + } + } + }.also { binding -> + + Timber.i("onCreateView") + + title = title() + + dotsState = binding.brailleDots.dotsState + + val viewModelFactory: CardViewModelFactory by inject { + parametersOf({ dotsState.brailleDots }) + } + val viewModel = ViewModelProvider(this, viewModelFactory) + .get(CardViewModel::class.java) + + dotsState.subscribe(viewModel) + + val buzzer: Vibrator? = activity?.getSystemService() + + BrailleTrainer.setSignalHandler(object : BrailleTrainerSignalHandler { + override fun onJoystickRight() = viewModel.onCheck() + override fun onJoystickLeft() = viewModel.onHint() + }) + + binding.cardViewModel = viewModel + binding.lifecycleOwner = this@CardFragment + + binding.flipButton.setOnClickListener { + dotsState = binding.brailleDots.reflect().apply { + dotsState.subscribe(viewModel) + if (viewModel.state == DotsChecker.State.HINT) { + viewModel.expectedDots?.let { display(it) } + } + } + } + + viewModel.symbol.observe(viewLifecycleOwner, Observer { + if (it == null) return@Observer + when (it) { + is Symbol -> { + binding.letter.visibility = View.VISIBLE + binding.markerDescription.visibility = View.GONE + binding.letter.letter = it.char + binding.letterCaption.text = captionRules.getValue(it) + } + is MarkerSymbol -> { + binding.letter.visibility = View.GONE + binding.markerDescription.visibility = View.VISIBLE + val baseDescription = inputMarkerPrintRules[it.type] + binding.markerDescription.text = if (it.type in musicalNotesTypes) { + getString(R.string.note_title_duration_template) + .format(baseDescription, getString(R.string.note_duration_8th)) + } else baseDescription + + binding.letterCaption.text = "" + } + } + }) + + viewModel.observeCheckedOnFly( + viewLifecycleOwner, { dotsState }, buzzer, + block = { title = title(viewModel) }, + softBlock = ::showCorrectToast + ) + + viewModel.observeEventIncorrect( + viewLifecycleOwner, { dotsState }, buzzer + ) { + viewModel.symbol.value + ?.let { showIncorrectToast(inputPrint(it)) } + ?: checkedToast(getString(R.string.input_loading)) + title = title(viewModel) + } + + viewModel.observeEventHint( + viewLifecycleOwner, { dotsState } + ) { expectedDots -> + showHintToast(expectedDots) + } + + viewModel.observeEventPassHint( + viewLifecycleOwner, { dotsState } + ) { + viewModel.symbol.value?.let { + checkedAnnounce(inputPrint(it)) + } + } + + viewModel.symbol.observe( + viewLifecycleOwner, + Observer { + if (it == null) return@Observer + checkedAnnounce(inputPrint(it)) + } + ) + + viewModel.deckTag.observe( + viewLifecycleOwner, + Observer { tag -> + if (tag == null) return@Observer + val template = if (preferenceRepository.practiceUseOnlyKnownMaterials) { + getString(R.string.practice_deck_name_enabled_template) + } else { + getString(R.string.practice_deck_name_disabled_template) + } + toast( + template.format(deckTagToName.getValue(tag)) + ) + } + ) + + if (viewModel.state == DotsChecker.State.HINT) { + viewModel.expectedDots?.let { dotsState.display(it) } + } + + }.root + + private fun title(viewModel: CardViewModel? = null): String = + getString(R.string.practice_actionbar_title_template).run { + if (viewModel == null) format(0, 0) + else format(viewModel.nCorrect, viewModel.nTries) + } + + private fun BrailleDotsState.subscribe(viewModel: CardViewModel) = + subscribe(View.OnClickListener { + viewModel.onSoftCheck() + }) + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.card_menu, menu) + } + + override fun onOptionsItemSelected(item: MenuItem) = false.also { + when (item.itemId) { + R.id.help -> navigateToHelp() + R.id.decks_list -> navigate(R.id.action_cardFragment_to_decksList) + } + } +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt index acb85db0..6fe66e3d 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt @@ -1,150 +1,150 @@ -package com.github.braillesystems.learnbraille.ui.screens.practice - -import android.app.Application -import androidx.lifecycle.* -import com.github.braillesystems.learnbraille.data.dsl.ALL_CARDS_DECK_ID -import com.github.braillesystems.learnbraille.data.entities.* -import com.github.braillesystems.learnbraille.data.repository.MutableActionsRepository -import com.github.braillesystems.learnbraille.data.repository.MutablePracticeRepository -import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository -import com.github.braillesystems.learnbraille.ui.screens.DotsChecker -import com.github.braillesystems.learnbraille.ui.screens.MutableDotsChecker -import com.github.braillesystems.learnbraille.utils.retryN -import com.github.braillesystems.learnbraille.utils.scope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import timber.log.Timber -import java.util.* -import java.util.concurrent.ArrayBlockingQueue - -class CardViewModelFactory( - private val practiceRepository: MutablePracticeRepository, - private val actionsRepository: MutableActionsRepository, - private val preferenceRepository: PreferenceRepository, - private val application: Application, - private val getEnteredDots: () -> BrailleDots -) : ViewModelProvider.Factory { - - override fun create(modelClass: Class): T = - if (modelClass.isAssignableFrom(CardViewModel::class.java)) { - @Suppress("UNCHECKED_CAST") - CardViewModel( - practiceRepository, - actionsRepository, - preferenceRepository, - application, - getEnteredDots - ) as T - } else { - throw IllegalArgumentException("Unknown ViewModel class") - } -} - -class CardViewModel( - private val practiceRepository: MutablePracticeRepository, - private val actionsRepository: MutableActionsRepository, - private val preferenceRepository: PreferenceRepository, - application: Application, - private val getEnteredDots: () -> BrailleDots, - private val dotsChecker: MutableDotsChecker = MutableDotsChecker.create() -) : AndroidViewModel(application), - DotsChecker by dotsChecker { - - private val _symbol = MutableLiveData() - val symbol: LiveData get() = _symbol - - private val _deckTag = MutableLiveData() - val deckTag: LiveData get() = _deckTag - - var nTries: Int = 0 - private set - - var nCorrect: Int = 0 - private set - - var expectedDots: BrailleDots? = null - private set - - private val job = Job() - private val uiScope = scope(job) - - private val nSkipMaterials = 2 - private val materialsQueue: Queue = ArrayBlockingQueue(nSkipMaterials) - - init { - Timber.i("Initialize practice view model") - initializeCard(firstTime = true) - - dotsChecker.apply { - getEnteredDots = this@CardViewModel.getEnteredDots - getExpectedDots = { expectedDots } - onCheckHandler = { - if (dotsChecker.state == DotsChecker.State.INPUT) { - nTries++ - } - } - onCorrectHandler = { - initializeCard() - nCorrect++ - uiScope.launch { - actionsRepository.addAction(PracticeSubmission(isCorrect = true)) - } - } - onHintHandler = { - uiScope.launch { - actionsRepository.addAction(PracticeHintAction) - } - } - onIncorrectHandler = { - uiScope.launch { - actionsRepository.addAction(PracticeSubmission(isCorrect = false)) - } - } - } - } - - override fun onCleared() { - super.onCleared() - job.cancel() - } - - private fun initializeCard(firstTime: Boolean = false) = uiScope.launch { - // If `use only known materials` turned on and current deck became unavailable - if (preferenceRepository.practiceUseOnlyKnownMaterials && - practiceRepository.randomKnownMaterial() == null - ) { - practiceRepository.currentDeckId = ALL_CARDS_DECK_ID - } - - if (firstTime) { - val deck = practiceRepository.currentDeck() - _deckTag.value = deck.tag - } - - val material = retryN(5) { - val m = nextMaterial() - if (m.data in materialsQueue) null - else m - } ?: nextMaterial() - - if (material.data !in materialsQueue) { - if (materialsQueue.size == nSkipMaterials) materialsQueue.poll() - materialsQueue.add(material.data) - } - - material.data.let { - _symbol.value = it - expectedDots = when (it) { - is OneBrailleSymbol -> it.brailleDots - } - } - } - - private suspend fun nextMaterial(): Material = - if (preferenceRepository.practiceUseOnlyKnownMaterials) { - practiceRepository.randomKnownMaterial() - } else { - practiceRepository.randomMaterial() - } - ?: error("Current deck should not be empty") -} +package com.github.braillesystems.learnbraille.ui.screens.practice + +import android.app.Application +import androidx.lifecycle.* +import com.github.braillesystems.learnbraille.data.dsl.ALL_CARDS_DECK_ID +import com.github.braillesystems.learnbraille.data.entities.* +import com.github.braillesystems.learnbraille.data.repository.MutableActionsRepository +import com.github.braillesystems.learnbraille.data.repository.MutablePracticeRepository +import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository +import com.github.braillesystems.learnbraille.ui.screens.DotsChecker +import com.github.braillesystems.learnbraille.ui.screens.MutableDotsChecker +import com.github.braillesystems.learnbraille.utils.retryN +import com.github.braillesystems.learnbraille.utils.scope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import timber.log.Timber +import java.util.* +import java.util.concurrent.ArrayBlockingQueue + +class CardViewModelFactory( + private val practiceRepository: MutablePracticeRepository, + private val actionsRepository: MutableActionsRepository, + private val preferenceRepository: PreferenceRepository, + private val application: Application, + private val getEnteredDots: () -> BrailleDots +) : ViewModelProvider.Factory { + + override fun create(modelClass: Class): T = + if (modelClass.isAssignableFrom(CardViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + CardViewModel( + practiceRepository, + actionsRepository, + preferenceRepository, + application, + getEnteredDots + ) as T + } else { + throw IllegalArgumentException("Unknown ViewModel class") + } +} + +class CardViewModel( + private val practiceRepository: MutablePracticeRepository, + private val actionsRepository: MutableActionsRepository, + private val preferenceRepository: PreferenceRepository, + application: Application, + private val getEnteredDots: () -> BrailleDots, + private val dotsChecker: MutableDotsChecker = MutableDotsChecker.create() +) : AndroidViewModel(application), + DotsChecker by dotsChecker { + + private val _symbol = MutableLiveData() + val symbol: LiveData get() = _symbol + + private val _deckTag = MutableLiveData() + val deckTag: LiveData get() = _deckTag + + var nTries: Int = 0 + private set + + var nCorrect: Int = 0 + private set + + var expectedDots: BrailleDots? = null + private set + + private val job = Job() + private val uiScope = scope(job) + + private val nSkipMaterials = 2 + private val materialsQueue: Queue = ArrayBlockingQueue(nSkipMaterials) + + init { + Timber.i("Initialize practice view model") + initializeCard(firstTime = true) + + dotsChecker.apply { + getEnteredDots = this@CardViewModel.getEnteredDots + getExpectedDots = { expectedDots } + onCheckHandler = { + if (dotsChecker.state == DotsChecker.State.INPUT) { + nTries++ + } + } + onCorrectHandler = { + initializeCard() + nCorrect++ + uiScope.launch { + actionsRepository.addAction(PracticeSubmission(isCorrect = true)) + } + } + onHintHandler = { + uiScope.launch { + actionsRepository.addAction(PracticeHintAction) + } + } + onIncorrectHandler = { + uiScope.launch { + actionsRepository.addAction(PracticeSubmission(isCorrect = false)) + } + } + } + } + + override fun onCleared() { + super.onCleared() + job.cancel() + } + + private fun initializeCard(firstTime: Boolean = false) = uiScope.launch { + // If `use only known materials` turned on and current deck became unavailable + if (preferenceRepository.practiceUseOnlyKnownMaterials && + practiceRepository.randomKnownMaterial() == null + ) { + practiceRepository.currentDeckId = ALL_CARDS_DECK_ID + } + + if (firstTime) { + val deck = practiceRepository.currentDeck() + _deckTag.value = deck.tag + } + + val material = retryN(5) { + val m = nextMaterial() + if (m.data in materialsQueue) null + else m + } ?: nextMaterial() + + if (material.data !in materialsQueue) { + if (materialsQueue.size == nSkipMaterials) materialsQueue.poll() + materialsQueue.add(material.data) + } + + material.data.let { + _symbol.value = it + expectedDots = when (it) { + is OneBrailleSymbol -> it.brailleDots + } + } + } + + private suspend fun nextMaterial(): Material = + if (preferenceRepository.practiceUseOnlyKnownMaterials) { + practiceRepository.randomKnownMaterial() + } else { + practiceRepository.randomMaterial() + } + ?: error("Current deck should not be empty") +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/settings/SettingsFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/settings/SettingsFragment.kt index c9a45232..45d2565d 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/settings/SettingsFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/settings/SettingsFragment.kt @@ -1,11 +1,102 @@ -package com.github.braillesystems.learnbraille.ui.screens.settings - -import android.os.Bundle -import com.github.braillesystems.learnbraille.R - -class SettingsFragment : androidx.preference.PreferenceFragmentCompat() { - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - setPreferencesFromResource(R.xml.settings_hierarchy, rootKey) - } -} +package com.github.braillesystems.learnbraille.ui.screens.settings + +import android.app.AlertDialog +import android.os.Bundle +import android.text.InputType +import android.widget.EditText +import androidx.lifecycle.lifecycleScope +import androidx.preference.Preference +import androidx.preference.SwitchPreferenceCompat +import com.github.braillesystems.learnbraille.COURSE +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.CurrentStep +import com.github.braillesystems.learnbraille.data.repository.MutableTheoryRepository +import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository +import com.github.braillesystems.learnbraille.utils.contextNotNull +import com.github.braillesystems.learnbraille.utils.preferences +import com.github.braillesystems.learnbraille.utils.toast +import kotlinx.coroutines.launch +import org.koin.android.ext.android.inject +import timber.log.Timber + +class SettingsFragment : androidx.preference.PreferenceFragmentCompat() { + + private val preferenceRepository: PreferenceRepository by inject() + private val theoryRepository: MutableTheoryRepository by inject() + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.settings_hierarchy, rootKey) + + val preference = findPreference( + getString(R.string.preference_teacher_mode_enabled) + ) ?: error("No teacher mode preference found") + + preference.onPreferenceChangeListener = + Preference.OnPreferenceChangeListener { _, newValue -> + require(preference is SwitchPreferenceCompat) + require(newValue is Boolean) + + val saveCurrStepPreference = + "save_curr_step_teacher_mode_${preferenceRepository.currentUserId}" + if (newValue) { + askCode { + val code = 8436 + if (it != code) { + Timber.i("Wrong code: $it, but expected $code") + toast(getString(R.string.preference_wrong_code_teacher_mode)) + } else { + Timber.i("Correct code: $code") + lifecycleScope.launch { + val curr = theoryRepository.currentStep(COURSE.id) + with(contextNotNull.preferences.edit()) { + putString( + saveCurrStepPreference, + listOf(curr.courseId, curr.lessonId, curr.id) + .joinToString(separator = ",") + ) + apply() + } + preference.isChecked = true + } + } + } + false + } else { + lifecycleScope.launch { + contextNotNull.preferences + .getString(saveCurrStepPreference, null) + ?.split(",") + ?.map { it.toLong() } + ?.let { (courseId, lessonId, stepId) -> + theoryRepository.setCurrentStep( + CurrentStep( + preferenceRepository.currentUserId, + courseId, + lessonId, + stepId + ) + ) + } + preference.isChecked = false + } + false + } + } + } + + private fun askCode(block: (Int?) -> Unit) { + val input = EditText(context).apply { + inputType = InputType.TYPE_CLASS_NUMBER + } + AlertDialog.Builder(context) + .setTitle(getString(R.string.preference_code_dialog_title)) + .setView(input) + .setPositiveButton(getString(R.string.preference_code_dialog_ok)) { _, _ -> + block(input.text.toString().toIntOrNull()) + } + .setNegativeButton(getString(R.string.preference_code_dialog_cancel)) { dialog, _ -> + dialog.cancel() + } + .show() + } +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/TheoryFreeNavigation.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/TheoryFreeNavigation.kt index cf26d453..dba5e42f 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/TheoryFreeNavigation.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/TheoryFreeNavigation.kt @@ -16,6 +16,7 @@ import com.github.braillesystems.learnbraille.utils.* import kotlinx.coroutines.launch import org.koin.android.ext.android.get import timber.log.Timber +import java.security.InvalidParameterException fun getAction(step: Step): NavDirections = stringify(Step.serializer(), step).let { arg -> @@ -33,9 +34,17 @@ fun getAction(step: Step): NavDirections = is Symbol -> MenuFragmentDirections.actionGlobalShowSymbolFragment(arg) is MarkerSymbol -> MenuFragmentDirections.actionGlobalShowMarkerFragment(arg) } + is InputPhraseLetter -> inputPhraseNavigation(step.data.phrase, step.data.pos, arg) } } +fun inputPhraseNavigation(phrase: Phrase, pos: Int, destination: String): NavDirections = + if (phrase[pos].data is Symbol) { + MenuFragmentDirections.actionGlobalInputPhraseSymbolFragment(destination) + } else { + throw InvalidParameterException("Only symbols are supported in InputPhraseLetter") + } + fun AbstractStepFragment.getStepArg(): Step = parse(Step.serializer(), getFragmentStringArg(stepArgName)) @@ -77,12 +86,17 @@ fun AbstractStepFragment.toCurrentStep( toStep(currStep) }.devnull -fun Fragment.toLastCourseStep( +fun Fragment.toLastOrCurrCourseStep( courseId: Long, theoryRepository: TheoryRepository = get() ): Unit = scope().launch { + val currStep = theoryRepository.currentStep(courseId) val lastStep = theoryRepository.lastCourseStep(courseId) - toStep(lastStep) + toStep( + // currStep < lastStep can occur after disabling teacher mode + if (currStep < lastStep) currStep + else lastStep + ) }.devnull fun Fragment.toLastLessonStep( diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt index 37addef9..2a7e2784 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt @@ -60,7 +60,7 @@ class LessonsListFragment : AbstractFragment() { if (item.id == last.lessonId) Typeface.BOLD else Typeface.NORMAL ) - if (item.id <= curr.lessonId) { + if (preferenceRepository.teacherModeEnabled || item.id <= curr.lessonId) { clickListener = activeListener lessonName.setTextColor( ContextCompat.getColor( diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt index 1d9bc363..58bf54e8 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt @@ -17,9 +17,9 @@ import com.github.braillesystems.learnbraille.ui.screens.theory.getStepArg import com.github.braillesystems.learnbraille.ui.screens.theory.toCurrentStep import com.github.braillesystems.learnbraille.ui.screens.theory.toNextStep import com.github.braillesystems.learnbraille.ui.screens.theory.toPrevStep -import com.github.braillesystems.learnbraille.utils.checkedAnnounce import com.github.braillesystems.learnbraille.utils.navigate import com.github.braillesystems.learnbraille.utils.title +import com.github.braillesystems.learnbraille.utils.unreachable interface StepBinding { val prevButton: Button? get() = null @@ -82,13 +82,17 @@ abstract class AbstractStepFragment(helpMsgId: HelpMsgId) : AbstractFragmentWith protected open fun iniStepHelper() = Unit - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) = inflater.inflate( - if (preferenceRepository.extendedAccessibilityEnabled) R.menu.steps_menu_hide - else R.menu.steps_menu, + when (preferenceRepository.extendedAccessibilityEnabled to preferenceRepository.teacherModeEnabled) { + true to true -> R.menu.steps_menu_hide_no_curr + true to false -> R.menu.steps_menu_hide + false to true -> R.menu.steps_menu_no_curr + false to false -> R.menu.steps_menu + else -> unreachable + }, menu ) - } override fun onOptionsItemSelected(item: MenuItem) = false.also { when (item.itemId) { diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/AbstractInfoStepFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/AbstractInfoStepFragment.kt index 60c05a0e..fc66e578 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/AbstractInfoStepFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/AbstractInfoStepFragment.kt @@ -5,6 +5,7 @@ import com.github.braillesystems.learnbraille.data.entities.BaseInfo import com.github.braillesystems.learnbraille.ui.screens.HelpMsgId import com.github.braillesystems.learnbraille.ui.screens.theory.steps.AbstractStepFragment import com.github.braillesystems.learnbraille.utils.checkedAnnounce +import com.github.braillesystems.learnbraille.utils.reduceWhitespaces abstract class AbstractInfoStepFragment(helpMsgId: HelpMsgId) : AbstractStepFragment(helpMsgId) { @@ -12,6 +13,6 @@ abstract class AbstractInfoStepFragment(helpMsgId: HelpMsgId) : AbstractStepFrag val data = step.data require(data is BaseInfo) stepBinding.textView?.text = data.text.parseAsHtml() - checkedAnnounce(data.text) + checkedAnnounce(data.text.reduceWhitespaces()) } } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/InfoFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/InfoFragment.kt index 711b7a0e..13ef0e2b 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/InfoFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/InfoFragment.kt @@ -1,6 +1,7 @@ package com.github.braillesystems.learnbraille.ui.screens.theory.steps.info import android.os.Bundle +import android.text.method.LinkMovementMethod import android.view.LayoutInflater import android.view.ViewGroup import android.widget.Button @@ -29,5 +30,7 @@ class InfoFragment : AbstractInfoStepFragment(R.string.lessons_help_info) { override val nextButton: Button? = this@iniStep.nextButton override val textView: TextView? = this@iniStep.infoTextView } + }.apply { + infoTextView.movementMethod = LinkMovementMethod.getInstance() }.root } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/AbstractInputStepFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/AbstractInputStepFragment.kt index ca7176e1..d5dd65c7 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/AbstractInputStepFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/AbstractInputStepFragment.kt @@ -88,7 +88,10 @@ abstract class AbstractInputStepFragment(helpMsgId: HelpMsgId) : AbstractStepFra ) } if (userTouchedDots) notify() - else toNextStep(step, markThisAsPassed = false) { notify() } + else toNextStep( + step, + markThisAsPassed = preferenceRepository.teacherModeEnabled + ) { notify() } } viewModel.observeEventPassHint( diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputPhraseSymbolFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputPhraseSymbolFragment.kt new file mode 100644 index 00000000..aefbd35c --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputPhraseSymbolFragment.kt @@ -0,0 +1,86 @@ +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.input + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.Button +import androidx.databinding.DataBindingUtil +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.InputPhraseLetter +import com.github.braillesystems.learnbraille.data.entities.Symbol +import com.github.braillesystems.learnbraille.databinding.FragmentLessonsInputPhraseSymbolBinding +import com.github.braillesystems.learnbraille.res.inputSymbolPrintRules +import com.github.braillesystems.learnbraille.ui.screens.BrailleDotsInfo +import com.github.braillesystems.learnbraille.ui.screens.theory.steps.StepBinding +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode +import com.github.braillesystems.learnbraille.utils.checkedAnnounce +import com.github.braillesystems.learnbraille.utils.getValue + +class InputPhraseSymbolFragment : AbstractInputStepFragment(R.string.lessons_help_input_symbol) { + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_lessons_input_phrase_symbol, + container, + false + ).iniStep( + titleId = R.string.lessons_title_input_symbol + ) { + object : StepBinding { + override val prevButton: Button? = this@iniStep.prevButton + override val nextButton: Button? = this@iniStep.nextButton + override val flipButton: Button? = this@iniStep.flipButton + override val hintButton: Button? = this@iniStep.hintButton + override val brailleDotsInfo: BrailleDotsInfo? = this@iniStep.run { + BrailleDotsInfo( + brailleDots, + if (preferenceRepository.isWriteModeFirst) BrailleDotsViewMode.Writing + else BrailleDotsViewMode.Reading, + prevButton, flipButton + ) + } + } + }.apply { + val stepData = step.data + require(stepData is InputPhraseLetter) + val symbols: List = stepData.phrase.mapNotNull { it.data as? Symbol } + val text: String = symbols.map { it.char }.joinToString(separator = "") + + val prevText = text.substring(0, stepData.pos) + val currentSymbol = symbols[stepData.pos] + val nextText = text.substring(stepData.pos + 1, symbols.size) + + prevLetters.text = prevText + currentLetter.text = currentSymbol.char.toString() + nextLetters.text = nextText + letter.letter = currentSymbol.char + + val description = taskDescription( + currentChar = currentSymbol.char, + prevText = prevText, + nextText = nextText + ) + + letter.contentDescription = description + checkedAnnounce(description) + + inputViewModel = viewModel + lifecycleOwner = this@InputPhraseSymbolFragment + + }.root + + private fun taskDescription(currentChar: Char, prevText: String, nextText: String): String { + val fullText = prevText + currentChar + nextText + var enterPrompt = getString( + if (fullText.contains(Regex("[ !?.,:;]"))) R.string.input_phrase_template + else R.string.input_word_template + ).format(fullText) + if (prevText.isNotEmpty()) enterPrompt += getString(R.string.input_phrase_complete_template) + .format(prevText) + return enterPrompt + inputSymbolPrintRules.getValue(currentChar) + } +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BrailleDotsView.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BrailleDotsView.kt index d4551ec8..7708f24e 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BrailleDotsView.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BrailleDotsView.kt @@ -1,232 +1,232 @@ -package com.github.braillesystems.learnbraille.ui.views - -import android.annotation.SuppressLint -import android.content.Context -import android.os.Build -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.View -import android.view.accessibility.AccessibilityNodeInfo -import android.widget.CheckBox -import androidx.annotation.RequiresApi -import androidx.constraintlayout.widget.ConstraintLayout -import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.data.entities.BrailleDot -import com.github.braillesystems.learnbraille.data.entities.BrailleDots -import com.github.braillesystems.learnbraille.data.entities.list -import com.github.braillesystems.learnbraille.data.entities.spelling -import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository -import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode.Reading -import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode.Writing -import com.github.braillesystems.learnbraille.utils.* -import kotlinx.android.synthetic.main.braille_dots_view.view.* -import org.koin.core.KoinComponent -import org.koin.core.inject -import timber.log.Timber - -@SuppressLint("AppCompatCustomView") // Causes BrailleDotView misplacement -class BrailleDotView : CheckBox { - - constructor(context: Context) : super(context) - - constructor(context: Context, attrSet: AttributeSet) : super(context, attrSet) - - constructor( - context: Context, attrSet: AttributeSet, defStyleAttr: Int - ) : super( - context, attrSet, defStyleAttr - ) - - override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) { - super.onInitializeAccessibilityNodeInfo(info) - info?.className = "" - } -} - -enum class BrailleDotsViewMode { - Reading, // 1, 2, 3 dots are on the LEFT - Writing // 1, 2, 3 dots are on the RIGHT -} - -val BrailleDotsViewMode.reflected: BrailleDotsViewMode - get() = when (this) { - Writing -> Reading - Reading -> Writing - } - -/** - * Represents six Braille dots view. - * - * Initialize by `setMode` before usage. - */ -class BrailleDotsView : ConstraintLayout, KoinComponent { - - private val preferenceRepository: PreferenceRepository by inject() - - constructor(context: Context) : super(context) - - constructor(context: Context, attrSet: AttributeSet) : super(context, attrSet) - - constructor( - context: Context, attrSet: AttributeSet, defStyleAttr: Int - ) : super( - context, attrSet, defStyleAttr - ) - - init { - LayoutInflater - .from(context) - .inflate(R.layout.braille_dots_view, this, true) - } - - // After changing traversal order neighbor views forget that braille dots are next - private lateinit var prevView: View - private lateinit var nextView: View - lateinit var mode: BrailleDotsViewMode - private set // It is not possible to use lateinit var with custom setter - - fun setMode(mode: BrailleDotsViewMode, prevView: View, nextView: View) { - this.prevView = prevView - this.nextView = nextView - - setDescriptionMode(mode) - if (this::mode.isInitialized && this.mode != mode) { - reflectChecks() - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { - setBackgroundMode(mode) - } else { - Timber.w("Unable to set braille dots background due to low API level") - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { - setTraversalMode(mode) - } else { - Timber.w("API level < 22, unable co control accessibility traversal order") - } - - context.announce( - when (mode) { - Writing -> context.getString(R.string.braille_dots_mode_writing) - Reading -> context.getString(R.string.braille_dots_mode_reading) - } - ) - - this.mode = mode - } - - fun reflect(): BrailleDotsState { - setMode(mode.reflected, prevView, nextView) - return dotsState - } - - private fun setDescriptionMode(mode: BrailleDotsViewMode) { - val dotsMapping = when (mode) { - Writing -> listOf( - Triple(dotButton4, R.string.braille_dot_1, R.string.braille_dot_1_text), - Triple(dotButton5, R.string.braille_dot_2, R.string.braille_dot_2_text), - Triple(dotButton6, R.string.braille_dot_3, R.string.braille_dot_3_text), - Triple(dotButton1, R.string.braille_dot_4, R.string.braille_dot_4_text), - Triple(dotButton2, R.string.braille_dot_5, R.string.braille_dot_5_text), - Triple(dotButton3, R.string.braille_dot_6, R.string.braille_dot_6_text) - ) - Reading -> listOf( - Triple(dotButton1, R.string.braille_dot_1, R.string.braille_dot_1_text), - Triple(dotButton2, R.string.braille_dot_2, R.string.braille_dot_2_text), - Triple(dotButton3, R.string.braille_dot_3, R.string.braille_dot_3_text), - Triple(dotButton4, R.string.braille_dot_4, R.string.braille_dot_4_text), - Triple(dotButton5, R.string.braille_dot_5, R.string.braille_dot_5_text), - Triple(dotButton6, R.string.braille_dot_6, R.string.braille_dot_6_text) - ) - } - dotsMapping.forEach { (dotButton, desc_id, caption_id) -> - dotButton.contentDescription = context.getString(desc_id) - dotButton.text = context.getString(caption_id) - } - } - - private fun reflectChecks() = forEach( - dotButton1 to dotButton4, - dotButton2 to dotButton5, - dotButton3 to dotButton6 - ) { (left, right) -> - left.isChecked = right.isChecked.also { - right.isChecked = left.isChecked - } - } - - @RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1) - private fun setTraversalMode(mode: BrailleDotsViewMode) { - val dotsOrder: Array = - when (mode to preferenceRepository.traverseDotsInEnumerationOrder) { - Writing to true -> arrayOf( - dotButton1, dotButton2, dotButton3, - dotButton4, dotButton5, dotButton6 - ) - Writing to false -> arrayOf( - dotButton1, dotButton4, dotButton2, - dotButton5, dotButton3, dotButton6 - ) - Reading to true -> arrayOf( - dotButton1, dotButton2, dotButton3, - dotButton4, dotButton5, dotButton6 - ) - Reading to false -> arrayOf( - dotButton1, dotButton4, dotButton2, - dotButton5, dotButton3, dotButton6 - ) - else -> unreachable - } - @Suppress("SpreadOperator") - chainify(prevView, *dotsOrder, nextView) { prev, next -> - prev.accessibilityTraversalBefore = next.id - next.accessibilityTraversalAfter = prev.id - } - } - - @RequiresApi(Build.VERSION_CODES.LOLLIPOP) - private fun setBackgroundMode(mode: BrailleDotsViewMode) { - background = when (mode) { - Writing -> context.getDrawable(R.drawable.right_border) - Reading -> context.getDrawable(R.drawable.left_border) - } - } -} - -val BrailleDotsView.dotsState: BrailleDotsState - get() = BrailleDotsState( - when (mode) { - Writing -> listOf( - dotButton4, dotButton5, dotButton6, - dotButton1, dotButton2, dotButton3 - ) - Reading -> listOf( - dotButton1, dotButton2, dotButton3, - dotButton4, dotButton5, dotButton6 - ) - } - ) - -class BrailleDotsState(val checkBoxes: List) - -val BrailleDotsState.spelling: String - get() = brailleDots.spelling - -val BrailleDotsState.brailleDots: BrailleDots - get() = BrailleDots( - checkBoxes.map(CheckBox::isChecked).toBooleanArray() - ) - -fun BrailleDotsState.uncheck() = checkBoxes.forEach { it.isChecked = false } - -fun BrailleDotsState.clickable(isClickable: Boolean) = - checkBoxes.forEach { it.isClickable = isClickable } - -fun BrailleDotsState.subscribe(listener: View.OnClickListener) = - checkBoxes.forEach { it.setOnClickListener(listener) } - -fun BrailleDotsState.display(brailleDots: BrailleDots): Unit = - (checkBoxes zip brailleDots.list) - .forEach { (checkBox, dot) -> checkBox.isChecked = dot == BrailleDot.F } - .also { clickable(false) } +package com.github.braillesystems.learnbraille.ui.views + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Build +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.accessibility.AccessibilityNodeInfo +import android.widget.CheckBox +import androidx.annotation.RequiresApi +import androidx.constraintlayout.widget.ConstraintLayout +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.BrailleDot +import com.github.braillesystems.learnbraille.data.entities.BrailleDots +import com.github.braillesystems.learnbraille.data.entities.list +import com.github.braillesystems.learnbraille.data.entities.spelling +import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode.Reading +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode.Writing +import com.github.braillesystems.learnbraille.utils.* +import kotlinx.android.synthetic.main.braille_dots_view.view.* +import org.koin.core.KoinComponent +import org.koin.core.inject +import timber.log.Timber + +@SuppressLint("AppCompatCustomView") // Causes BrailleDotView misplacement +class BrailleDotView : CheckBox { + + constructor(context: Context) : super(context) + + constructor(context: Context, attrSet: AttributeSet) : super(context, attrSet) + + constructor( + context: Context, attrSet: AttributeSet, defStyleAttr: Int + ) : super( + context, attrSet, defStyleAttr + ) + + override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) { + super.onInitializeAccessibilityNodeInfo(info) + info?.className = "" + } +} + +enum class BrailleDotsViewMode { + Reading, // 1, 2, 3 dots are on the LEFT + Writing // 1, 2, 3 dots are on the RIGHT +} + +val BrailleDotsViewMode.reflected: BrailleDotsViewMode + get() = when (this) { + Writing -> Reading + Reading -> Writing + } + +/** + * Represents six Braille dots view. + * + * Initialize by `setMode` before usage. + */ +class BrailleDotsView : ConstraintLayout, KoinComponent { + + private val preferenceRepository: PreferenceRepository by inject() + + constructor(context: Context) : super(context) + + constructor(context: Context, attrSet: AttributeSet) : super(context, attrSet) + + constructor( + context: Context, attrSet: AttributeSet, defStyleAttr: Int + ) : super( + context, attrSet, defStyleAttr + ) + + init { + LayoutInflater + .from(context) + .inflate(R.layout.braille_dots_view, this, true) + } + + // After changing traversal order neighbor views forget that braille dots are next + private lateinit var prevView: View + private lateinit var nextView: View + lateinit var mode: BrailleDotsViewMode + private set // It is not possible to use lateinit var with custom setter + + fun setMode(mode: BrailleDotsViewMode, prevView: View, nextView: View) { + this.prevView = prevView + this.nextView = nextView + + setDescriptionMode(mode) + if (this::mode.isInitialized && this.mode != mode) { + reflectChecks() + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { + setBackgroundMode(mode) + } else { + Timber.w("Unable to set braille dots background due to low API level") + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { + setTraversalMode(mode) + } else { + Timber.w("API level < 22, unable co control accessibility traversal order") + } + + context.announce( + when (mode) { + Writing -> context.getString(R.string.braille_dots_mode_writing) + Reading -> context.getString(R.string.braille_dots_mode_reading) + } + ) + + this.mode = mode + } + + fun reflect(): BrailleDotsState { + setMode(mode.reflected, prevView, nextView) + return dotsState + } + + private fun setDescriptionMode(mode: BrailleDotsViewMode) { + val dotsMapping = when (mode) { + Writing -> listOf( + Triple(dotButton4, R.string.braille_dot_1, R.string.braille_dot_1_text), + Triple(dotButton5, R.string.braille_dot_2, R.string.braille_dot_2_text), + Triple(dotButton6, R.string.braille_dot_3, R.string.braille_dot_3_text), + Triple(dotButton1, R.string.braille_dot_4, R.string.braille_dot_4_text), + Triple(dotButton2, R.string.braille_dot_5, R.string.braille_dot_5_text), + Triple(dotButton3, R.string.braille_dot_6, R.string.braille_dot_6_text) + ) + Reading -> listOf( + Triple(dotButton1, R.string.braille_dot_1, R.string.braille_dot_1_text), + Triple(dotButton2, R.string.braille_dot_2, R.string.braille_dot_2_text), + Triple(dotButton3, R.string.braille_dot_3, R.string.braille_dot_3_text), + Triple(dotButton4, R.string.braille_dot_4, R.string.braille_dot_4_text), + Triple(dotButton5, R.string.braille_dot_5, R.string.braille_dot_5_text), + Triple(dotButton6, R.string.braille_dot_6, R.string.braille_dot_6_text) + ) + } + dotsMapping.forEach { (dotButton, desc_id, caption_id) -> + dotButton.contentDescription = context.getString(desc_id) + dotButton.text = context.getString(caption_id) + } + } + + private fun reflectChecks() = forEach( + dotButton1 to dotButton4, + dotButton2 to dotButton5, + dotButton3 to dotButton6 + ) { (left, right) -> + left.isChecked = right.isChecked.also { + right.isChecked = left.isChecked + } + } + + @RequiresApi(Build.VERSION_CODES.LOLLIPOP_MR1) + private fun setTraversalMode(mode: BrailleDotsViewMode) { + val dotsOrder: Array = + when (mode to preferenceRepository.traverseDotsInEnumerationOrder) { + Writing to true -> arrayOf( + dotButton1, dotButton2, dotButton3, + dotButton4, dotButton5, dotButton6 + ) + Writing to false -> arrayOf( + dotButton1, dotButton4, dotButton2, + dotButton5, dotButton3, dotButton6 + ) + Reading to true -> arrayOf( + dotButton1, dotButton2, dotButton3, + dotButton4, dotButton5, dotButton6 + ) + Reading to false -> arrayOf( + dotButton1, dotButton4, dotButton2, + dotButton5, dotButton3, dotButton6 + ) + else -> unreachable + } + @Suppress("SpreadOperator") + chainify(prevView, *dotsOrder, nextView) { prev, next -> + prev.accessibilityTraversalBefore = next.id + next.accessibilityTraversalAfter = prev.id + } + } + + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + private fun setBackgroundMode(mode: BrailleDotsViewMode) { + background = when (mode) { + Writing -> context.getDrawable(R.drawable.right_border) + Reading -> context.getDrawable(R.drawable.left_border) + } + } +} + +val BrailleDotsView.dotsState: BrailleDotsState + get() = BrailleDotsState( + when (mode) { + Writing -> listOf( + dotButton4, dotButton5, dotButton6, + dotButton1, dotButton2, dotButton3 + ) + Reading -> listOf( + dotButton1, dotButton2, dotButton3, + dotButton4, dotButton5, dotButton6 + ) + } + ) + +class BrailleDotsState(val checkBoxes: List) + +val BrailleDotsState.spelling: String + get() = brailleDots.spelling + +val BrailleDotsState.brailleDots: BrailleDots + get() = BrailleDots( + checkBoxes.map(CheckBox::isChecked).toBooleanArray() + ) + +fun BrailleDotsState.uncheck() = checkBoxes.forEach { it.isChecked = false } + +fun BrailleDotsState.clickable(isClickable: Boolean) = + checkBoxes.forEach { it.isClickable = isClickable } + +fun BrailleDotsState.subscribe(listener: View.OnClickListener) = + checkBoxes.forEach { it.setOnClickListener(listener) } + +fun BrailleDotsState.display(brailleDots: BrailleDots): Unit = + (checkBoxes zip brailleDots.list) + .forEach { (checkBox, dot) -> checkBox.isChecked = dot == BrailleDot.F } + .also { clickable(false) } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Kotlin.kt b/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Kotlin.kt index 257cb37e..9774b77e 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Kotlin.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Kotlin.kt @@ -46,6 +46,8 @@ operator fun Pair.compareTo(other: Pair): Int fun String.removeHtmlMarkup() = Regex("""<[^>]*>|&""").replace(this, "") +fun String.reduceWhitespaces() = Regex(" +|\n").replace(this, " ") + inline fun runIf(cond: Boolean, block: () -> Unit) { if (cond) block() } diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml index 971add5e..1f6bb290 100644 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -1,34 +1,34 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/app/src/main/res/drawable/action_menu_help_button.xml b/app/src/main/res/drawable/action_menu_help_button.xml index 7ab092bb..4ba5e2f5 100644 --- a/app/src/main/res/drawable/action_menu_help_button.xml +++ b/app/src/main/res/drawable/action_menu_help_button.xml @@ -1,9 +1,9 @@ - - - + + + diff --git a/app/src/main/res/drawable/checked_round_checkbox.xml b/app/src/main/res/drawable/checked_round_checkbox.xml index 281e0730..be2c1947 100644 --- a/app/src/main/res/drawable/checked_round_checkbox.xml +++ b/app/src/main/res/drawable/checked_round_checkbox.xml @@ -1,8 +1,8 @@ - - - - + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_timer_white.xml b/app/src/main/res/drawable/ic_baseline_timer_white.xml new file mode 100644 index 00000000..28d60aa1 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_timer_white.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index eed7a425..0d025f9b 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,170 +1,170 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_play_circle_outline.xml b/app/src/main/res/drawable/ic_play_circle_outline.xml new file mode 100644 index 00000000..c4f8875e --- /dev/null +++ b/app/src/main/res/drawable/ic_play_circle_outline.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/round_checkbox.xml b/app/src/main/res/drawable/round_checkbox.xml index e67a3adc..416a34e6 100644 --- a/app/src/main/res/drawable/round_checkbox.xml +++ b/app/src/main/res/drawable/round_checkbox.xml @@ -1,6 +1,6 @@ - - - - - + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/top_bottom_borders.xml b/app/src/main/res/drawable/top_bottom_borders.xml new file mode 100644 index 00000000..2a3d63c7 --- /dev/null +++ b/app/src/main/res/drawable/top_bottom_borders.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/unchecked_round_checkbox.xml b/app/src/main/res/drawable/unchecked_round_checkbox.xml index 5909773d..5ffd335b 100644 --- a/app/src/main/res/drawable/unchecked_round_checkbox.xml +++ b/app/src/main/res/drawable/unchecked_round_checkbox.xml @@ -1,10 +1,10 @@ - - - - + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index c4ff5fa0..13c0c1a9 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,17 +1,17 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/braille_dots_view.xml b/app/src/main/res/layout/braille_dots_view.xml index b17e598b..16d0cef5 100644 --- a/app/src/main/res/layout/braille_dots_view.xml +++ b/app/src/main/res/layout/braille_dots_view.xml @@ -1,104 +1,104 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/browser_list_item.xml b/app/src/main/res/layout/browser_list_item.xml index 8b3b93db..4b84f47a 100644 --- a/app/src/main/res/layout/browser_list_item.xml +++ b/app/src/main/res/layout/browser_list_item.xml @@ -6,7 +6,7 @@ + type="com.github.braillesystems.learnbraille.ui.screens.browser.DeckOrMaterial" /> - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_exit.xml b/app/src/main/res/layout/fragment_exit.xml index 693849d3..12379a59 100644 --- a/app/src/main/res/layout/fragment_exit.xml +++ b/app/src/main/res/layout/fragment_exit.xml @@ -1,35 +1,35 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_help.xml b/app/src/main/res/layout/fragment_help.xml index 1f1369d7..e2710ff3 100644 --- a/app/src/main/res/layout/fragment_help.xml +++ b/app/src/main/res/layout/fragment_help.xml @@ -1,15 +1,15 @@ - - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_lessons_input_dots.xml b/app/src/main/res/layout/fragment_lessons_input_dots.xml index 464140f4..d9ce6739 100644 --- a/app/src/main/res/layout/fragment_lessons_input_dots.xml +++ b/app/src/main/res/layout/fragment_lessons_input_dots.xml @@ -16,11 +16,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_lessons_show_dots.xml b/app/src/main/res/layout/fragment_lessons_show_dots.xml index fe99f147..a6f6339f 100644 --- a/app/src/main/res/layout/fragment_lessons_show_dots.xml +++ b/app/src/main/res/layout/fragment_lessons_show_dots.xml @@ -9,11 +9,7 @@ + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/steps_menu_hide_no_curr.xml b/app/src/main/res/menu/steps_menu_hide_no_curr.xml new file mode 100644 index 00000000..c1d589ad --- /dev/null +++ b/app/src/main/res/menu/steps_menu_hide_no_curr.xml @@ -0,0 +1,26 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/steps_menu_no_curr.xml b/app/src/main/res/menu/steps_menu_no_curr.xml new file mode 100644 index 00000000..a485f443 --- /dev/null +++ b/app/src/main/res/menu/steps_menu_no_curr.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 56e36c84..be316184 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - - - - + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 56e36c84..be316184 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - - - - + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/navigation.xml b/app/src/main/res/navigation/navigation.xml index 901cfac5..8f68d8f7 100644 --- a/app/src/main/res/navigation/navigation.xml +++ b/app/src/main/res/navigation/navigation.xml @@ -1,248 +1,272 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-ab/strings.xml b/app/src/main/res/values-ab/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-ab/strings.xml +++ b/app/src/main/res/values-ab/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-ba/strings.xml b/app/src/main/res/values-ba/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-ba/strings.xml +++ b/app/src/main/res/values-ba/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-bua/strings.xml b/app/src/main/res/values-bua/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-bua/strings.xml +++ b/app/src/main/res/values-bua/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-cv/strings.xml b/app/src/main/res/values-cv/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-cv/strings.xml +++ b/app/src/main/res/values-cv/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-kv/strings.xml b/app/src/main/res/values-kv/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-kv/strings.xml +++ b/app/src/main/res/values-kv/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-ky/strings.xml +++ b/app/src/main/res/values-ky/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-mn/strings.xml b/app/src/main/res/values-mn/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-mn/strings.xml +++ b/app/src/main/res/values-mn/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-os/strings.xml b/app/src/main/res/values-os/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-os/strings.xml +++ b/app/src/main/res/values-os/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index becaea6d..2c115b37 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1,5 +1,5 @@ - Азбука Брайля + Учим Брайль diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml index 90791389..0929e3ee 100644 --- a/app/src/main/res/values-sw600dp/dimens.xml +++ b/app/src/main/res/values-sw600dp/dimens.xml @@ -1,37 +1,37 @@ - - - 25dp - 3dp - 30dp - 30dp - 20dp - 25dp - 2.6 - 1.8 - - 1dp - - 80dp - 65dp - 300dp - 150dp - 70dp - 60dp - 55dp - - 35sp - 0.05 - 450dp - 0dp - 30sp - 0.0 - 394dp - 175dp - - 285sp - - 30dp - - 45dp - + + + 25dp + 3dp + 30dp + 30dp + 20dp + 25dp + 2.6 + 1.8 + + 1dp + + 80dp + 65dp + 300dp + 150dp + 70dp + 60dp + 55dp + + 35sp + 0.05 + 450dp + 0dp + 30sp + 0.0 + 394dp + 175dp + + 285sp + + 30dp + + 45dp + \ No newline at end of file diff --git a/app/src/main/res/values-sw720dp/dimens.xml b/app/src/main/res/values-sw720dp/dimens.xml index 4722a3d3..2b037dd0 100644 --- a/app/src/main/res/values-sw720dp/dimens.xml +++ b/app/src/main/res/values-sw720dp/dimens.xml @@ -1,36 +1,36 @@ - - - 25dp - 3dp - 30dp - 30dp - 20dp - 25dp - 2.6 - 2.1 - - 1dp - - 90dp - 75dp - 320dp - 170dp - 90dp - 65dp - 60dp - - 40sp - 0.05 - 590dp - 0dp - 35sp - 0.0 - 394dp - 175dp - - 330sp - - 30dp - - 65dp + + + 25dp + 3dp + 30dp + 30dp + 20dp + 25dp + 2.6 + 2.1 + + 1dp + + 90dp + 75dp + 320dp + 170dp + 90dp + 65dp + 60dp + + 40sp + 0.05 + 590dp + 0dp + 35sp + 0.0 + 394dp + 175dp + + 330sp + + 30dp + + 65dp \ No newline at end of file diff --git a/app/src/main/res/values-tg/strings.xml b/app/src/main/res/values-tg/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-tg/strings.xml +++ b/app/src/main/res/values-tg/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-tk/strings.xml b/app/src/main/res/values-tk/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-tk/strings.xml +++ b/app/src/main/res/values-tk/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-tt/strings.xml b/app/src/main/res/values-tt/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-tt/strings.xml +++ b/app/src/main/res/values-tt/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values-uz/strings.xml b/app/src/main/res/values-uz/strings.xml index 2e320d3e..92ecba7e 100644 --- a/app/src/main/res/values-uz/strings.xml +++ b/app/src/main/res/values-uz/strings.xml @@ -1,4 +1,4 @@ - Азбука Брайля + Учим Брайль \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 74e896d7..c4b4abf6 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,10 +1,11 @@ - - - #263238 - #9e3d9a - #ffffff - #ffffff - #ffffff - #000000 - #504c51 - + + + #263238 + #9e3d9a + #ffffff + #ffffff + #ffffff + #000000 + #504c51 + #ffc4ee + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index bd2407b8..a3413578 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -29,12 +29,7 @@ 270dp 0dp 18sp - 0.0 - 394dp 175dp - 0.5 - 10dp - 0.2 170sp 5dp @@ -51,4 +46,6 @@ 8dp 20dp - \ No newline at end of file + 7.5sp + + diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml index 76c20fb9..957fecd0 100644 --- a/app/src/main/res/values/ic_launcher_background.xml +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -1,4 +1,4 @@ - - - #32D9DC + + + #32D9DC \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 75f8aa0c..b35fb5bd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,567 +1,671 @@ - - - - - - - Learn Braille - Hello blank fragment - No help message - - Вы не сможете использовать возможности голосового управления - - use_debug_lessons - enable_buzz - enable_toasts - current_user - speech_recognition_enabled - golubina_book_steps_enabled - slate_stylus_steps_enabled - traverse_dots_in_enumeration_order - enable_additional_announcements - practice_use_only_known_materials - extended_accessibility - additional_qrcode_button_enabled - is_write_mode_first - - Порядок точек: "письмо" - Порядок точек: "чтение" - - Правильно! - Неправильно! - Подождите, задание загружается - Ответ: точки %s - Ответ: %s - - точка 1 слева сверху - точка 2 слева посередине - точка 3 слева снизу - точка 4 справа сверху - точка 5 справа посередине - точка 6 справа снизу - - точка 4 слева сверху - точка 5 слева посередине - точка 6 слева снизу - точка 1 справа сверху - точка 2 справа посередине - точка 3 справа снизу - - Введите букву: %s - Введите латинскую букву: %s - Введите греческую букву: %s - Введите цифру: %s - Введите символ: %s - Введите математический символ: %s - Введите символ: Литературная точка - Введите символ: Дефис - Введите запятую - Введите восклицательный знак - Введите вопросительный знак - Введите открывающую кавычку - Введите закрывающую кавычку - Введите левую литературную скобку - Введите правую литературную скобку - Введите звёздочку - Введите двоеточие - Введите точку с запятой - Введите знак ударения - - Введите признак большой буквы греческого алфавита - Введите признак большой буквы латинского алфавита - Введите признак малой буквы латинского алфавита - Введите признак большой буквы русского алфавита - Введите цифровой знак - Введите признак жирного шрифта - Введите признак курсивного шрифта - - Введите знак Плюс - Введите знак Минус - Введите знак умножения точкой - Введите знак умножения крестом - Введите знак деления (углом) - Введите знак деления (двумя точками) - Введите знак равенства - - Буква %s - Латинская буква %s - Греческая буква %s - Цифра %s - Символ %s - Математический символ %s - Литературная точка - Дефис - Запятая - Восклицательный знак - Вопросительный знак - Открывающая кавычка - Закрывающая кавычка - Левая литературная скобка - Правая литературная скобка - Звёздочка - Двоеточие - Точка с запятой - Ударение - - Признак большой греческой буквы - Признак большой латинской буквы - Признак малой латинской буквы - Признак большой буквы русского алфавита - Цифровой знак - Признак жирного шрифта - Признак курсивного шрифта - - Знак Плюс - Знак Минус - Знак умножения точкой - Знак умножения крестом - Знак деления (углом) - Знак деления (двумя точками) - Знак равенства - - Русская буква - Греческая буква - Латинская буква - Цифра - Специальный символ - - - - Установите сканнер qr кодов и попробуйте снова - - - - \? - Подсказка - Далее - Практика: %d из %d - Практика - - Колода: \"%s\"\nПовторять только изученные: включено - - - Колода: \"%s\"\nПовторять только изученные: выключено - - Список колод - - Колода \"%s\" ещё недоступна, пройдите эти карточки в уроках или отключите - \"повторять только изученное\" в разделе \"настройки\". - - - Все символы - Все символы, кроме иностранных букв - Русские буквы - Латинские буквы - Греческие буквы - Цифры - Знаки препинания - Специальные символы - Математические символы - - - -
- В случайном порядке выдаются задания - \"карточки\" с символами.
- В верхней половине экрана выведен символ, - который нужно ввести в шеститочии в нижней половине экрана и нажать кнопку \“далее\” - справа. - &
- По умолчанию выдаются только карточки с символами, изученными в разделе \"Обучение\". Чтобы - повторять любые символы, измените это в настройках приложения. - &
- Шеститочие: письмо/чтение (розовая кнопка справа по центру) - изменяет порядок столбцов: точки - 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). - &
- Выход в меню приложения - кнопка вверху слева. - &
- Список колод - кнопка вверху справа. -
- Карточки объединены в колоды по типам символов, например, колода русских букв, колода цифр. - Эта кнопка ведёт в меню выбора колоды. - &
- Вызов справки - кнопка вверху справа, левее списка колод. - &
- Если Вы забыли символ, можно нажать кнопку \"подсказка\" слева внизу экрана. - По нажатию этой кнопки будет выведено сообщение с верными номерами точек, шеститочие будет - заполнено правильными точками и недоступно для переключения. После этого нужно нажать кнопку - \"далее\" и ввести тот же символ ещё раз. - ]]> -
- - - - Выйти - Вернуться в меню - Хотите ли Вы выйти? - - - - HeyHey! - Hello! - Выйти - Начать - - - - ОБУЧЕНИЕ - ПРАКТИКА - СИМВОЛЫ - СТАТИСТИКА - QR-КОД - СПРАВКА - НАСТРОЙКИ - ВЫХОД - %s. Меню - Меню - - Загружаем базу данных. Попробуйте ещё раз! - - Ошибка. Пустой результат сканирования - - - Обучение системе Луи Брайля: главное меню. - &

- Обучение: пошаговые уроки с демонстрацией плоскопечатных и - рельефно-точечных символов крупным шрифтом, вводом символов и поясняющими комментариями. - &
- Практика: Повторение символов. - &
- Символы: Список букв, специальных символов и цифр с указанием точечного состава. - &
- Статистика: Информация о Вашей активности в приложении. - &
- Настройки: Параметры приложения. - &
- В настройках можно включить ещё одну кнопку - \"QR-код\". - &
- QR-код: если у Вас есть набор карточек с текстом Брайля и - QR-кодами на обороте, по нажатию кнопки вы можете отсканировать код и проверить, - что написано на карточке. Для этого необходимо дополнительное приложение. - Если его ещё нет, по нажатию кнопки Вы будете перенаправлены на страницу для скачивания приложения. -
- &
- Находясь в любом разделе приложения, Вы всегда можете вызвать справку по данному разделу, - нажав экранную кнопку \"справка\" в правом верхнем углу экрана. - &
- Из всякого раздела, кроме главного меню приложения, можно выйти в предыдущий, нажав кнопку - \"Перейти вверх\" в левом верхнем углу экрана. - ]]> -
- - - - - Вперёд - Назад - Подсказка - К прогрессу - - Введите точки: %s - - - Выведены точки: %s - - - Урок %d ещё недоступен, пройдите сначала предыдущие уроки. \nВы остановились на уроке %d - - - Текст - Заключение - Введите точки - Введите точки - Изучение - Точки - Уроки - - - - %s - ]]> - - - - Переход к следующему шагу - кнопка \"вперёд\" справа по центру, - к предыдущему - кнопка "назад" слева по центру. - &
- Выход в меню приложения - кнопка \"перейти вверх\" в верхней панели слева. - &
- Вызов справки - кнопка в верхней панели справа. - &
- \"Другие функции\" - кнопка в верхней панели рядом с кнопкой \"справка\". - Нажатие этой кнопки вызывает выпадающее меню с функциями \"навигация по курсу\", то есть - переход к другому уроку, и \"к текущему шагу\", то есть переход к последнему - непройденному шагу. - &
- Чтобы урок стал доступен в меню \"навигация по курсу\", надо пройти все шаги в - предыдущих уроках. В уроке обычно от 20 до 30 шагов. Заголовок шага содержит номер урока, - затем номер шага после точки с запятой, затем тип шага: \"введите точки\", или \"точки\" - (то есть демонстрация символа), или \"текст\". - ]]> -
- - - шаг с вводом символа. - &

- В верхней половине экрана крупным шрифтом выведен символ, который нужно ввести в шеститочии - в нижней половине экрана. - &
- Над кнопкой \"назад\" находится кнопка подсказки, она работает так же, как и в практике. - По нажатию кнопки \"подсказка\" выводится сообщение с правильным ответом, и точки - переключаются в правильное положение. Чтобы пройти то же упражнение после подсказки, нужно - нажать кнопку \"вперёд\". - &
- Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки - 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). - ]]> -
- - - шаг с вводом точек. -

- Введите в шеститочии на экране точки с указанными номерами. - &
- Если нажать кнопку \"подсказка\", расположенную над кнопкой \"назад\", выведется сообщение - с правильным ответом и точки переключатся в правильное состояние. Чтобы пройти то же - упражнение после подсказки, нажмите кнопку \"вперёд\". - &
- Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки - 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). - ]]> -
- - - шаг с вводом специального символа. -

- В верхней половине экрана дано описание символа, который нужно ввести в шеститочии - в нижней половине экрана. - &
- Если нажать кнопку \"подсказка\", расположенную над кнопкой \"назад\", выведется сообщение - с правильным ответом и точки переключатся в правильное состояние. Чтобы пройти то же - упражнение после подсказки, нажмите кнопку \"вперёд\". - &
- Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки - 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). - ]]> -
- - - шаг с демонстрацией символа. -

- В верхней половине экрана крупным шрифтом выведен плоскопечатный символ, а в нижней части - - его представление точечным шрифтом. Нужно изучить точечный символ. - &
- Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки - 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). - ]]> -
- - - шаг с демонстрацией точек. -

- На экран выведен точечный символ. Внимательно изучите его. - &
- Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки - 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). - ]]> -
- - - шаг с демонстрацией специального символа. -

- В верхней половине экрана словами описан специальный символ, а в шеститочии в нижней части - - его точечный состав. Внимательно изучите этот символ. - &
- Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки - 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). - ]]> -
- - - шаг с информационным сообщением. -

- Ознакомьтесь с сообщением, выведенном в текстовом поле по центру экрана. - ]]> -
- - - Самый последний шаг курса! Поздравляем! -

- Чтобы выйти в главное меню, нажмите дважды кнопку слева сверху. - ]]> -
- - - - точка один - точка два - точка три - точка четыре - точка пять - точка шесть - - 1 - 2 - 3 - 4 - 5 - 6 - - - - Справка - К текущему шагу - Навигация по курсу - Скрыть это меню - Список колод - - - - Справка - - - - Установлено соединение с Тренажёром Брайля - Разрешение на подключение не выдано - Тренажёр Брайля отключён - Этот аппарат не поддерживается - - - - Список символов - %s: точки %s - Просмотр символа - - - - Помните, что при написании чисел нужно вначале ставить цифровой знак. При написании слов на - латинице или по-гречески внутри русского текста в начале слова надо ставить признак - латинской / греческой буквы. - &
- Нажатие на символ в списке (или двойное нажатие, если Вы используете программу TalkBack) - открывает окно, где этот символ крупным шрифтом выведен на экран. - &
- Из этого раздела можно выйти в главное меню, нажав кнопку \"Перейти вверх\" - в левом верхнем углу экрана. - ]]> -
- - - - Из этого раздела можно выйти обратно к списку символов, нажав кнопку \"Перейти вверх\" - в левом верхнем углу экрана. - ]]> - - - - - Из этого раздела можно выйти обратно к списку символов, нажав кнопку \"Перейти вверх\" - в левом верхнем углу экрана. - ]]> - - - - - Настройки - Всплывающие уведомления - Вибрация - Вибрация в ответ на результат выполнения задания. - Шаги с бумажным пособием - Шаги с брайлевским прибором - - Показывать шаги с обращением к бумажному пособию Голубиной в разделе "Обучение". - - - Показывать шаги для выполнения с брайлевским прибором в разделе "Обучение". - - Включить/отключить краткие всплывающие - подсказки (кроме самых важных). - Обход точек в порядке нумерации - Работает с Андроид 5.1. Если включено, - программа экранного доступа обходит точки шеститочия в порядке 1-2-3-4-5-6, - иначе 1-4-2-5-3-6 (при чтении). При письме, если включено - 4-5-6-1-2-3, иначе 4-1-5-2-6-3. - - Проверка ввода \"на лету\" - Выводить сообщение \"правильно\", - как только в шеститочии введена корректная комбинация, - не дожидаясь нажатия кнопки \"вперёд\". - - Повторять только изученное - - В разделе \"Практика\" повторять только символы, пройденные в разделе \"Обучение\". - - - Автоозвучка текстов - - - Автоматически озвучивать тексты (например, задания, справку) - средством экранного чтения, не дожидаясь фокусировки на текстовом поле. - - - Режим повышенной доступности - - - Крупный шрифт в разделе \"Обучение\". Дополнительные кнопки выхода (удобно при использовании программы - экранного доступа). Шире боковые кнопки в практике и обучении. - - - Возможность сканировать QR-код - - - Отображать в главном меню кнопку \"QR-код\" для игры с карточками Брайля. - - - При вводе столбец с точками 1, 2, 3 справа - - - При заходе в практику или шаг с вводом точек столбец с точками 1, 2, 3 находится справа, - как при письме на брайлевском приборе. - - - - - - - Из этого раздела можно выйти в главное меню, нажав кнопку \"Перейти вверх\" (стрелка влево) - в левом верхнем углу экрана. - ]]> - - - Статистика - Практика: - Пройдено карточек - Потребовалось подсказок - Потрачено попыток - Обучение: - Пройдено шагов - Пройдено шагов с вводом - За последние 7 дней - За последние 30 дней - Шеститочие: письмо/чтение -
+ + + + + + + Learn Braille + Hello blank fragment + No help message + + Вы не сможете использовать возможности голосового управления + + use_debug_lessons + enable_buzz + enable_toasts + current_user + speech_recognition_enabled + golubina_book_steps_enabled + slate_stylus_steps_enabled + traverse_dots_in_enumeration_order + enable_additional_announcements + practice_use_only_known_materials + extended_accessibility + additional_qrcode_button_enabled + is_write_mode_first + teacher_mode_enabled + + Порядок точек: "письмо" + Порядок точек: "чтение" + + Правильно! + Неправильно! + Подождите, задание загружается + Ответ: точки %s + Ответ: %s + + точка 1 слева сверху + точка 2 слева посередине + точка 3 слева снизу + точка 4 справа сверху + точка 5 справа посередине + точка 6 справа снизу + + точка 4 слева сверху + точка 5 слева посередине + точка 6 слева снизу + точка 1 справа сверху + точка 2 справа посередине + точка 3 справа снизу + + Введите букву: %s + Введите латинскую букву: %s + Введите греческую букву: %s + Введите цифру: %s + Введите символ: %s + Введите математический символ: %s + Введите символ: Литературная точка + Введите символ: Дефис + Введите запятую + Введите восклицательный знак + Введите вопросительный знак + Введите открывающую кавычку + Введите закрывающую кавычку + Введите левую литературную скобку + Введите правую литературную скобку + Введите звёздочку + Введите двоеточие + Введите точку с запятой + Введите знак ударения + + Введите признак большой буквы греческого алфавита + Введите признак большой буквы латинского алфавита + Введите признак малой буквы латинского алфавита + Введите признак большой буквы русского алфавита + Введите цифровой знак + Введите признак жирного шрифта + Введите признак курсивного шрифта + + Введите ноту ДО + Введите ноту РЕ + Введите ноту МИ + Введите ноту ФА + Введите ноту СОЛЬ + Введите ноту ЛЯ + Введите ноту СИ + + Введите паузу длительности одна восьмая + Введите паузу длительности одна четверть + Введите паузу половинной длительности + Введите целую паузу + Введите обозначение первой октавы + Введите обозначение второй октавы + Введите обозначение третьей октавы + Введите обозначение четвёртой октавы + Введите обозначение пятой октавы + Введите обозначение шестой октавы + Введите обозначение седьмой октавы + Введите музыкальный знак переноса + Введите диез + Введите бемоль + Введите бекар + + Введите знак Плюс + Введите знак Минус + Введите знак умножения точкой + Введите знак умножения крестом + Введите знак деления (углом) + Введите знак деления (двумя точками) + Введите знак равенства + + Ввод слова: %s. > + Ввод фразы: %s; + Введены символы: %s; + + Буква %s + Латинская буква %s + Греческая буква %s + Цифра %s + Символ %s + Математический символ %s + Литературная точка + Дефис + Запятая + Восклицательный знак + Вопросительный знак + Открывающая кавычка + Закрывающая кавычка + Левая литературная скобка + Правая литературная скобка + Звёздочка + Двоеточие + Точка с запятой + Ударение + + Признак большой греческой буквы + Признак большой латинской буквы + Признак малой латинской буквы + Признак большой буквы русского алфавита + Цифровой знак + Признак жирного шрифта + Признак курсивного шрифта + + Нота ДО + Нота РЕ + Нота МИ + Нота ФА + Нота СОЛЬ + Нота ЛЯ + Нота СИ + + Пауза длительности одна восьмая + Пауза длительности одна четверть + Пауза половинной длительности + Целая пауза + Обозначение первой октавы + Обозначение второй октавы + Обозначение третьей октавы + Обозначение четвёртой октавы + Обозначение пятой октавы + Обозначение шестой октавы + Обозначение седьмой октавы + Музыкальный знак переноса + Диез + Бемоль + Бекар + + Знак Плюс + Знак Минус + Знак умножения точкой + Знак умножения крестом + Знак деления (углом) + Знак деления (двумя точками) + Знак равенства + + Русская буква + Греческая буква + Латинская буква + Цифра + Специальный символ + + + + Установите сканнер qr кодов и попробуйте снова + + + + \? + Подсказка + Далее + Практика: %d из %d + Практика + + Колода: \"%s\"\nПовторять только изученные: включено + + + Колода: \"%s\"\nПовторять только изученные: выключено + + Список колод + + Колода \"%s\" ещё недоступна, пройдите эти карточки в уроках или отключите + \"повторять только изученное\" в разделе \"настройки\". + + + Все символы + Все символы, кроме иностранных букв + Русские буквы + Латинские буквы + Греческие буквы + Цифры + Знаки препинания + Специальные символы + Математические символы + Ноты + Прочие музыкальные знаки + + + +
+ В случайном порядке выдаются задания - \"карточки\" с символами.
+ В верхней половине экрана выведен символ, + который нужно ввести в шеститочии в нижней половине экрана и нажать кнопку \“далее\” + справа. + &
+ По умолчанию выдаются только карточки с символами, изученными в разделе \"Обучение\". Чтобы + повторять любые символы, измените это в настройках приложения. + &
+ Шеститочие: письмо/чтение (розовая кнопка справа по центру) - изменяет порядок столбцов: точки + 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). + &
+ Выход в меню приложения - кнопка вверху слева. + &
+ Список колод - кнопка вверху справа. +
+ Карточки объединены в колоды по типам символов, например, колода русских букв, колода цифр. + Эта кнопка ведёт в меню выбора колоды. + &
+ Вызов справки - кнопка вверху справа, левее списка колод. + &
+ Если Вы забыли символ, можно нажать кнопку \"подсказка\" слева внизу экрана. + По нажатию этой кнопки будет выведено сообщение с верными номерами точек, шеститочие будет + заполнено правильными точками и недоступно для переключения. После этого нужно нажать кнопку + \"далее\" и ввести тот же символ ещё раз. + &
+ При вводе цифр мы не требуем вводить цифровой знак, но помните, что его всегда нужно ставить + перед числами при письме. + ]]> +
+ + + + Выйти + Вернуться в меню + Хотите ли Вы выйти? + + + + HeyHey! + Hello! + Выйти + Начать + + + + ОБУЧЕНИЕ + ПРАКТИКА + СИМВОЛЫ + СТАТИСТИКА + QR-КОД + СПРАВКА + НАСТРОЙКИ + ВЫХОД + %s. Меню + Меню + + Загружаем базу данных. Попробуйте ещё раз! + + Ошибка. Пустой результат сканирования + + + Обучение системе Луи Брайля: главное меню. + &

+ Обучение: пошаговые уроки с демонстрацией плоскопечатных и + рельефно-точечных символов крупным шрифтом, вводом символов и поясняющими комментариями. + &
+ Практика: Повторение символов. + &
+ Символы: Список букв, специальных символов и цифр с указанием точечного состава. + &
+ Статистика: Информация о Вашей активности в приложении. + &
+ Настройки: Параметры приложения. + &
+ В настройках можно включить ещё одну кнопку - \"QR-код\". + &
+ QR-код: если у Вас есть набор карточек с текстом Брайля и + QR-кодами на обороте, по нажатию кнопки вы можете отсканировать код и проверить, + что написано на карточке. Для этого необходимо дополнительное приложение. + Если его ещё нет, по нажатию кнопки Вы будете перенаправлены на страницу для скачивания приложения. +
+ &
+ Находясь в любом разделе приложения, Вы всегда можете вызвать справку по данному разделу, + нажав экранную кнопку \"справка\" в правом верхнем углу экрана. + &
+ Из всякого раздела, кроме главного меню приложения, можно выйти в предыдущий, нажав кнопку + \"Перейти вверх\" в левом верхнем углу экрана. + ]]> +
+ + + + + Вперёд + Назад + Подсказка + К прогрессу + + Введите точки: %s + + + Выведены точки: %s + + + Урок %d ещё недоступен, пройдите сначала предыдущие уроки. \nВы остановились на уроке %d + + + Текст + Заключение + Введите точки + Введите точки + Изучение + Точки + Уроки + + + + %s + ]]> + + + + Переход к следующему шагу - кнопка \"вперёд\" справа по центру, + к предыдущему - кнопка "назад" слева по центру. + &
+ Выход в меню приложения - кнопка \"перейти вверх\" в верхней панели слева. + &
+ Вызов справки - кнопка в верхней панели справа. + &
+ \"Другие функции\" - кнопка в верхней панели рядом с кнопкой \"справка\". + Нажатие этой кнопки вызывает выпадающее меню с функциями \"навигация по курсу\", то есть + переход к другому уроку, и \"к текущему шагу\", то есть переход к последнему + непройденному шагу. + &
+ Чтобы урок стал доступен в меню \"навигация по курсу\", надо пройти все шаги в + предыдущих уроках. В уроке обычно от 20 до 30 шагов. Заголовок шага содержит номер урока, + затем номер шага после точки с запятой, затем тип шага: \"введите точки\", или \"точки\" + (то есть демонстрация символа), или \"текст\". + ]]> +
+ + + шаг с вводом символа. + &

+ В верхней половине экрана крупным шрифтом выведен символ, который нужно ввести в шеститочии + в нижней половине экрана. + &
+ Над кнопкой \"назад\" находится кнопка подсказки, она работает так же, как и в практике. + По нажатию кнопки \"подсказка\" выводится сообщение с правильным ответом, и точки + переключаются в правильное положение. Чтобы пройти то же упражнение после подсказки, нужно + нажать кнопку \"вперёд\". + &
+ Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки + 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). + ]]> +
+ + + шаг с вводом точек. +

+ Введите в шеститочии на экране точки с указанными номерами. + &
+ Если нажать кнопку \"подсказка\", расположенную над кнопкой \"назад\", выведется сообщение + с правильным ответом и точки переключатся в правильное состояние. Чтобы пройти то же + упражнение после подсказки, нажмите кнопку \"вперёд\". + &
+ Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки + 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). + ]]> +
+ + + шаг с вводом специального символа. +

+ В верхней половине экрана дано описание символа, который нужно ввести в шеститочии + в нижней половине экрана. + &
+ Если нажать кнопку \"подсказка\", расположенную над кнопкой \"назад\", выведется сообщение + с правильным ответом и точки переключатся в правильное состояние. Чтобы пройти то же + упражнение после подсказки, нажмите кнопку \"вперёд\". + &
+ Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки + 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). + ]]> +
+ + + шаг с демонстрацией символа. +

+ В верхней половине экрана крупным шрифтом выведен плоскопечатный символ, а в нижней части - + его представление точечным шрифтом. Нужно изучить точечный символ. + &
+ Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки + 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). + ]]> +
+ + + шаг с демонстрацией точек. +

+ На экран выведен точечный символ. Внимательно изучите его. + &
+ Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки + 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). + ]]> +
+ + + шаг с демонстрацией специального символа. +

+ В верхней половине экрана словами описан специальный символ, а в шеститочии в нижней части + - его точечный состав. Внимательно изучите этот символ. + &
+ Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки + 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). + ]]> +
+ + + шаг с информационным сообщением. +

+ Ознакомьтесь с сообщением, выведенном в текстовом поле по центру экрана. + ]]> +
+ + + Самый последний шаг курса! Поздравляем! +

+ Чтобы выйти в главное меню, нажмите дважды кнопку слева сверху. + ]]> +
+ + + + точка один + точка два + точка три + точка четыре + точка пять + точка шесть + + 1 + 2 + 3 + 4 + 5 + 6 + + + + Справка + К текущему шагу + Навигация по курсу + Скрыть это меню + Список колод + + + + Справка + + + + Установлено соединение с Тренажёром Брайля + Разрешение на подключение не выдано + Тренажёр Брайля отключён + Этот аппарат не поддерживается + + + + Список символов + Колода + %s: точки %s + Просмотр символа + + + + Нажмите на название группы символов в списке групп (например, "русские буквы"), чтобы + перейти к списку символов в этой группе. + &
+ Помните, что при написании чисел нужно вначале ставить цифровой знак. При написании слов на + латинице или по-гречески внутри русского текста в начале слова надо ставить признак + латинской / греческой буквы. Греческие буквы указаны в соответствии с советским (российским) + математическим стандартом. + &
+ Из этого раздела можно выйти в главное меню, нажав кнопку \"Перейти вверх\" + в левом верхнем углу экрана. + ]]> +
+ + + + Нажатие на элемент в списке (или двойное нажатие, если Вы используете средство экранного чтения) + открывает окно, где шеститочечное обозначение символа выведено на экран. + &
+ Напоминаем: при написании чисел нужно вначале ставить цифровой знак. При написании слов на + латинице или по-гречески внутри русского текста в начале слова надо ставить признак + латинской или греческой буквы соответственно. + &
+ Из этого разделела можно вернуться к предыдущему, нажав кнопку \"Перейти вверх\" + в левом верхнем углу экрана. + ]]> +
+ + + + Из этого раздела можно выйти обратно к списку символов, нажав кнопку \"Перейти вверх\" + в левом верхнем углу экрана. + ]]> + + + + + Из этого раздела можно выйти обратно к списку символов, нажав кнопку \"Перейти вверх\" + в левом верхнем углу экрана. + ]]> + + + Длительность ноты + Воспроизвести ноту + %s\n(Длительность: %s) + Выберите длительность ноты + ОК + восьмая + четвертная + половинная + целая + + + + Настройки + Всплывающие уведомления + Вибрация + Вибрация в ответ на результат выполнения задания. + Шаги с бумажным пособием + Шаги с брайлевским прибором + + Показывать шаги с обращением к бумажному пособию Голубиной в разделе "Обучение". + + + Показывать шаги для выполнения с брайлевским прибором в разделе "Обучение". + + Включить/отключить краткие всплывающие + подсказки (кроме самых важных). + Обход точек в порядке нумерации + Работает с Андроид 5.1. Если включено, + программа экранного доступа обходит точки шеститочия в порядке 1-2-3-4-5-6, + иначе 1-4-2-5-3-6 (при чтении). При письме, если включено - 4-5-6-1-2-3, иначе 4-1-5-2-6-3. + + Проверка ввода \"на лету\" + Выводить сообщение \"правильно\", + как только в шеститочии введена корректная комбинация, + не дожидаясь нажатия кнопки \"вперёд\". + + Повторять только изученное + + В разделе \"Практика\" повторять только символы, пройденные в разделе \"Обучение\". + + + Автоозвучка текстов + + + Автоматически озвучивать тексты (например, задания, справку) + средством экранного чтения, не дожидаясь фокусировки на текстовом поле. + + + Режим повышенной доступности + + + Крупный шрифт в разделе \"Обучение\". Дополнительные кнопки выхода (удобно при использовании программы + экранного доступа). Шире боковые кнопки в практике и обучении. + + + Возможность сканировать QR-код + + + Отображать в главном меню кнопку \"QR-код\" для игры с карточками Брайля. + + + При вводе столбец с точками 1, 2, 3 справа + + + При заходе в практику или шаг с вводом точек столбец с точками 1, 2, 3 находится справа, + как при письме на брайлевском приборе. + + + Режим преподавателя + + + В режиме преподавателя доступны все уроки и не ведётся учёт статистики. Для активации потребуется ввести код преподавателя. + + + Неверный код, попробуйте ещё раз + + + Введите код преподавателя + + + Ввод + + + Отмена + + + + + + + Из этого раздела можно выйти в главное меню, нажав кнопку \"Перейти вверх\" (стрелка влево) + в левом верхнем углу экрана. + ]]> + + + Статистика + Практика: + Пройдено карточек + Потребовалось подсказок + Потрачено попыток + Обучение: + Пройдено шагов + Пройдено шагов с вводом + За последние 7 дней + За последние 30 дней + Шеститочие: письмо/чтение +
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index e8b9706a..783a944a 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,92 +1,110 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/settings_hierarchy.xml b/app/src/main/res/xml/settings_hierarchy.xml index d616b703..16ae6162 100644 --- a/app/src/main/res/xml/settings_hierarchy.xml +++ b/app/src/main/res/xml/settings_hierarchy.xml @@ -89,4 +89,12 @@ android:title="@string/preference_title_additional_announcements" app:iconSpaceReserved="false" /> + + \ No newline at end of file diff --git a/app/src/test/java/com/github/braillesystems/learnbraille/data/dsl/DslTest.kt b/app/src/test/java/com/github/braillesystems/learnbraille/data/dsl/DslTest.kt index 80002691..0ab24fe0 100644 --- a/app/src/test/java/com/github/braillesystems/learnbraille/data/dsl/DslTest.kt +++ b/app/src/test/java/com/github/braillesystems/learnbraille/data/dsl/DslTest.kt @@ -86,7 +86,7 @@ private val prepopulationData by data( deck("En letters") { it is Symbol && it.type == "en" } - deck(DeckTags.all) { true } + deck(DeckTags.Grouping.All.tag) { true } // ... } } @@ -164,7 +164,7 @@ private val mats = listOf( private val decks = listOf( Deck(2, "Ru letters"), Deck(3, "En letters"), - Deck(1, DeckTags.all) + Deck(1, DeckTags.Grouping.All.tag) ) private val cards = listOf( diff --git a/app/src/test/java/com/github/braillesystems/learnbraille/data/entities/BrailleDotsTest.kt b/app/src/test/java/com/github/braillesystems/learnbraille/data/entities/BrailleDotsTest.kt index ce2e287e..5c967782 100644 --- a/app/src/test/java/com/github/braillesystems/learnbraille/data/entities/BrailleDotsTest.kt +++ b/app/src/test/java/com/github/braillesystems/learnbraille/data/entities/BrailleDotsTest.kt @@ -1,15 +1,15 @@ -package com.github.braillesystems.learnbraille.data.entities - -import org.junit.Assert.assertEquals -import org.junit.Test - -class BrailleDotsTest { - - @Test - fun spelling() { - assertEquals( - "1, 3, 6", - BrailleDots(b1 = BrailleDot.F, b3 = BrailleDot.F, b6 = BrailleDot.F).spelling - ) - } -} +package com.github.braillesystems.learnbraille.data.entities + +import org.junit.Assert.assertEquals +import org.junit.Test + +class BrailleDotsTest { + + @Test + fun spelling() { + assertEquals( + "1, 3, 6", + BrailleDots(b1 = BrailleDot.F, b3 = BrailleDot.F, b6 = BrailleDot.F).spelling + ) + } +} diff --git a/app/src/test/java/com/github/braillesystems/learnbraille/utils/UtilsTest.kt b/app/src/test/java/com/github/braillesystems/learnbraille/utils/UtilsTest.kt index c8aa7b34..171f1442 100644 --- a/app/src/test/java/com/github/braillesystems/learnbraille/utils/UtilsTest.kt +++ b/app/src/test/java/com/github/braillesystems/learnbraille/utils/UtilsTest.kt @@ -1,37 +1,37 @@ -package com.github.braillesystems.learnbraille.utils - -import kotlinx.serialization.Serializable -import org.junit.Assert.assertEquals -import org.junit.Test - -@Serializable -sealed class S { - abstract val s: String -} - -@Serializable -data class A(override val s: String) : S() - -@Serializable -data class B(val i: Long, override val s: String) : S() - -class UtilsTest { - - @Test - fun serializationBasic() { - @Serializable - data class D(val s: String, val i: Int) - - val d = D("Wow", 100500) - assertEquals(d, parse(D.serializer(), stringify(D.serializer(), d))) - } - - @Test - fun serializationVirtual() { - val s1: S = A("wow") - assertEquals(s1 as A, parse(S.serializer(), stringify(S.serializer(), s1))) - val s2: S = B(100500, "aaa") - assertEquals(s2 as B, parse(S.serializer(), stringify(S.serializer(), s2))) - assertEquals("wow", s1.s) - } -} +package com.github.braillesystems.learnbraille.utils + +import kotlinx.serialization.Serializable +import org.junit.Assert.assertEquals +import org.junit.Test + +@Serializable +sealed class S { + abstract val s: String +} + +@Serializable +data class A(override val s: String) : S() + +@Serializable +data class B(val i: Long, override val s: String) : S() + +class UtilsTest { + + @Test + fun serializationBasic() { + @Serializable + data class D(val s: String, val i: Int) + + val d = D("Wow", 100500) + assertEquals(d, parse(D.serializer(), stringify(D.serializer(), d))) + } + + @Test + fun serializationVirtual() { + val s1: S = A("wow") + assertEquals(s1 as A, parse(S.serializer(), stringify(S.serializer(), s1))) + val s2: S = B(100500, "aaa") + assertEquals(s2 as B, parse(S.serializer(), stringify(S.serializer(), s2))) + assertEquals("wow", s1.s) + } +} diff --git a/build.gradle b/build.gradle index 44d79739..4dcf30c4 100644 --- a/build.gradle +++ b/build.gradle @@ -1,38 +1,38 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - ext { - // Downgraded from '1.3.72', annotation processing error in compiler - // Then upgraded to '1.3.61' because of https://github.com/Kotlin/kotlinx.serialization/issues/576 - kotlin_version = '1.3.61' - version_navigation = '1.0.0' - version_room = "2.2.5" - version_dokka="0.9.18" - } - repositories { - google() - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:4.0.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$version_navigation" - classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" - classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:$version_dokka" - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - google() - jcenter() - maven { url "https://jitpack.io" } - } -} - -task clean(type: Delete) { - delete rootProject.buildDir -} +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + ext { + // Downgraded from '1.3.72', annotation processing error in compiler + // Then upgraded to '1.3.61' because of https://github.com/Kotlin/kotlinx.serialization/issues/576 + kotlin_version = '1.3.61' + version_navigation = '1.0.0' + version_room = "2.2.5" + version_dokka="0.9.18" + } + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:4.0.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$version_navigation" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" + classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:$version_dokka" + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + maven { url "https://jitpack.io" } + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/detekt-config.yml b/detekt-config.yml index 32c66009..86265a83 100644 --- a/detekt-config.yml +++ b/detekt-config.yml @@ -2,11 +2,11 @@ complexity: TooManyFunctions: - excludes: ['**/utils/**', '**/DotsChecker.kt'] + excludes: ['**/utils/**', '**/DotsChecker.kt', '**/TheoryRepository.kt'] LongMethod: excludes: ['**/*Fragment.kt', '**/*Application.kt', '**/LearnBrailleDatabase.kt'] ComplexMethod: - excludes: ['**/*Fragment.kt'] + excludes: ['**/*Fragment.kt', '**/LearnBrailleDatabase.kt'] LongParameterList: excludes: ['**/*Repository.kt'] # Dependency injection diff --git a/gradle.properties b/gradle.properties index 5efc49fb..23339e0d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,21 +1,21 @@ -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app's APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true -# Automatically convert third-party libraries to use AndroidX -android.enableJetifier=true -# Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9fa69657..bb82d3a3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip diff --git a/gradlew.bat b/gradlew.bat index e95643d6..f9553162 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,84 +1,84 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle index 322af9c7..a44c2430 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ -include ':app' -rootProject.name='LearnBraille' +include ':app' +rootProject.name='LearnBraille'