From 3c2ca41526ffb12778b0b0ea377e79d37fdf5ba0 Mon Sep 17 00:00:00 2001 From: zuevval Date: Tue, 26 Jan 2021 20:03:35 +0300 Subject: [PATCH 1/3] #248 normalize line endings --- .gitattributes | 10 + .gitignore | 454 +++---- CONTRIBUTING.md | 114 +- README.md | 66 +- app/.gitignore | 2 +- app/app.iml | 514 ++++---- app/build.gradle | 244 ++-- app/proguard-rules.pro | 42 +- .../data/db/LearnBrailleDatabaseTest.kt | 366 +++--- app/src/main/AndroidManifest.xml | 52 +- .../learnbraille/LearnBrailleApplication.kt | 244 ++-- .../data/db/LearnBrailleDatabase.kt | 654 +++++----- .../learnbraille/data/entities/BrailleDots.kt | 156 +-- .../learnbraille/data/entities/Lessons.kt | 68 +- .../learnbraille/data/entities/Users.kt | 66 +- .../learnbraille/ui/screens/MainActivity.kt | 74 +- .../ui/screens/exit/ExitFragment.kt | 82 +- .../ui/screens/help/HelpFragment.kt | 66 +- .../ui/screens/menu/MenuFragment.kt | 324 ++--- .../ui/screens/practice/CardFragment.kt | 384 +++--- .../ui/screens/practice/CardViewModel.kt | 300 ++--- .../ui/screens/settings/SettingsFragment.kt | 22 +- .../learnbraille/ui/views/BrailleDotsView.kt | 464 +++---- .../drawable-v24/ic_launcher_foreground.xml | 68 +- .../res/drawable/action_menu_help_button.xml | 18 +- .../res/drawable/checked_round_checkbox.xml | 14 +- .../res/drawable/ic_launcher_background.xml | 340 ++--- app/src/main/res/drawable/round_checkbox.xml | 10 +- .../res/drawable/unchecked_round_checkbox.xml | 18 +- app/src/main/res/layout/activity_main.xml | 32 +- app/src/main/res/layout/braille_dots_view.xml | 206 +-- app/src/main/res/layout/fragment_card.xml | 216 ++-- app/src/main/res/layout/fragment_exit.xml | 70 +- app/src/main/res/layout/fragment_help.xml | 28 +- app/src/main/res/layout/fragment_menu.xml | 214 ++-- .../res/mipmap-anydpi-v26/ic_launcher.xml | 8 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 8 +- app/src/main/res/navigation/navigation.xml | 496 +++---- app/src/main/res/values-sw600dp/dimens.xml | 72 +- app/src/main/res/values-sw720dp/dimens.xml | 70 +- app/src/main/res/values/colors.xml | 20 +- .../res/values/ic_launcher_background.xml | 6 +- app/src/main/res/values/strings.xml | 1134 ++++++++--------- app/src/main/res/values/styles.xml | 184 +-- .../data/entities/BrailleDotsTest.kt | 30 +- .../learnbraille/utils/UtilsTest.kt | 74 +- build.gradle | 76 +- gradle.properties | 42 +- gradlew.bat | 168 +-- settings.gradle | 4 +- 50 files changed, 4202 insertions(+), 4192 deletions(-) create mode 100644 .gitattributes 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/.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 00aff17a..3068e2e5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,122 +1,122 @@ -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 14 - versionName "1.2.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' -} - -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 14 + versionName "1.2.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' +} + +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/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/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5a937f5c..4373866c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,27 +1,27 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + \ 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..4615ba07 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 { + 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 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..75329794 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,327 @@ -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 = 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") + } +} 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/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/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/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..1488508d 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,162 @@ -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.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 + } +} 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..c457b372 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,192 @@ -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.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) + } + } +} 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..a109a759 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,11 @@ -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.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) + } +} 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/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_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/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/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/fragment_card.xml b/app/src/main/res/layout/fragment_card.xml index f5af9814..7449e68f 100644 --- a/app/src/main/res/layout/fragment_card.xml +++ b/app/src/main/res/layout/fragment_card.xml @@ -1,109 +1,109 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + \ 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_menu.xml b/app/src/main/res/layout/fragment_menu.xml index d3382c9f..e102301d 100644 --- a/app/src/main/res/layout/fragment_menu.xml +++ b/app/src/main/res/layout/fragment_menu.xml @@ -1,108 +1,108 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + \ 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..60a7fa1c 100644 --- a/app/src/main/res/navigation/navigation.xml +++ b/app/src/main/res/navigation/navigation.xml @@ -1,248 +1,248 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/colors.xml b/app/src/main/res/values/colors.xml index 74e896d7..e9ab523f 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,10 +1,10 @@ - - - #263238 - #9e3d9a - #ffffff - #ffffff - #ffffff - #000000 - #504c51 - + + + #263238 + #9e3d9a + #ffffff + #ffffff + #ffffff + #000000 + #504c51 + 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..6a670858 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,567 +1,567 @@ - - - - - - - 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 + + ΠŸΠΎΡ€ΡΠ΄ΠΎΠΊ Ρ‚ΠΎΡ‡Π΅ΠΊ: "письмо" + ΠŸΠΎΡ€ΡΠ΄ΠΎΠΊ Ρ‚ΠΎΡ‡Π΅ΠΊ: "Ρ‡Ρ‚Π΅Π½ΠΈΠ΅" + + ΠŸΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ! + ΠΠ΅ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ! + ΠŸΠΎΠ΄ΠΎΠΆΠ΄ΠΈΡ‚Π΅, Π·Π°Π΄Π°Π½ΠΈΠ΅ загруТаСтся + ΠžΡ‚Π²Π΅Ρ‚: Ρ‚ΠΎΡ‡ΠΊΠΈ %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 Π΄Π½Π΅ΠΉ + ШСститочиС: письмо/Ρ‡Ρ‚Π΅Π½ΠΈΠ΅ +
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index e8b9706a..209222ad 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,92 +1,92 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/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/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' From 9c4e9295beb1ca309a19470ffcc2a1504887095a Mon Sep 17 00:00:00 2001 From: zuevval Date: Wed, 27 Jan 2021 12:27:00 +0300 Subject: [PATCH 2/3] (#264) add side margins to text views in show/input steps --- app/src/main/res/layout/fragment_card.xml | 6 +----- .../main/res/layout/fragment_lessons_input_dots.xml | 6 +----- .../res/layout/fragment_lessons_input_marker.xml | 6 +----- .../main/res/layout/fragment_lessons_show_dots.xml | 6 +----- .../main/res/layout/fragment_lessons_show_marker.xml | 6 +----- app/src/main/res/layout/fragment_marker_view.xml | 6 +----- app/src/main/res/values/dimens.xml | 5 ----- app/src/main/res/values/styles.xml | 12 ++++++++++++ 8 files changed, 18 insertions(+), 35 deletions(-) diff --git a/app/src/main/res/layout/fragment_card.xml b/app/src/main/res/layout/fragment_card.xml index f5af9814..801f13fb 100644 --- a/app/src/main/res/layout/fragment_card.xml +++ b/app/src/main/res/layout/fragment_card.xml @@ -25,11 +25,7 @@ 270dp 0dp 18sp - 0.0 - 394dp 175dp - 0.5 - 10dp - 0.2 170sp 5dp diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index e8b9706a..ec268b1b 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -61,12 +61,24 @@ + + + + -
+
\ No newline at end of file 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