diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 00000000..07234ec8 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,62 @@ +# credit: https://habr.com/ru/post/488134/ + + +name: Android CI + +on: + push: + branches: + - master + - dev + pull_request: + +jobs: + build-and-test: # instrumental tests run on MacOS to enable hardware acceleration + runs-on: macos-latest + steps: + - uses: actions/checkout@v1 + - name: set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: grant permissions to gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew assembleDebug + - name: Run unit tests + run: ./gradlew test + - name: Run instrumental tests + # warning: it depends on "grant permission to gradlew" step + uses: reactivecircus/android-emulator-runner@v1 + with: + api-level: 29 + script: ./gradlew connectedCheck + - name: Generate documentation + run: ./gradlew dokka + - name: Upload APK to artifacts + uses: actions/upload-artifact@v1 + with: + name: learn-braille-debug + path: app/build/outputs/apk/debug/app-debug.apk + - name: Upload documentation to artifacts + uses: actions/upload-artifact@v1 + with: + name: documentation-html + path: app/build/dokka/ + - name: Upload artifact (test reports) + uses: actions/upload-artifact@v1 + with: + name: test-reports + path: app/build/reports/ + if: success() || failure() + + static-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Set up detekt # detekt gradle plugin uses newer gradle version than is used + run: curl -sSLO https://github.com/detekt/detekt/releases/download/v1.12.0-RC1/detekt && chmod a+x detekt + - name: Static check with detekt + run: ./detekt -c detekt-config.yml --build-upon-default-config + - name: Check not using developer's course + run: grep UsersCourse app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt diff --git a/.gitignore b/.gitignore index 11039858..9544a0dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# 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 diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index ce889bd5..7a748e40 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,9 +1,19 @@ + + +
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index a55e7a17..79ee123c 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,5 @@ - \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 29f4ed06..e8c8710a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,19 +8,50 @@ For building and running the app, your either need to download a virtual Android ## Git workflow -- Write short and informative commit messages. Start them with reference to issue (begins with `#`). [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/). +- 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 thing was formatted and impelemented before. -- Set up Android Studio proper kotlin code style `editor -> code style -> kotlin -> set from -> predefined -> Kotlin style guide`. +- 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 are handy DSL that allows write content in the typesafe way. +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 7ab1b0e6..4ad82ddf 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ # Learn Braille -Learn Braille is an android application for teaching Braille writing system. +[![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) -It is one of the few apps primarily designed for Russian Braille system, +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. -App can be used with -[braille trainer](https://github.com/braille-systems/braille-trainer) +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. @@ -16,14 +20,14 @@ Take a look at [wiki pages (Russian)](https://github.com/braille-systems/learn-b ## 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. +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` optimisations). -- Screen of size not less then 4 inches. +- 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. +It is pre-installed by default on a majority of devices.
+For others, it is available in the Google Play. diff --git a/app/build.gradle b/app/build.gradle index 435fb944..00aff17a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,6 +10,8 @@ apply plugin: 'androidx.navigation.safeargs' apply plugin: 'kotlinx-serialization' +apply plugin: 'org.jetbrains.dokka' + android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -24,8 +26,8 @@ android { applicationId "com.github.braillesystems.learnbraille" minSdkVersion 19 targetSdkVersion 29 - versionCode 13 - versionName "1.1.0" + versionCode 14 + versionName "1.2.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true multiDexEnabled = true @@ -38,6 +40,20 @@ android { } productFlavors { } + dokka { + outputDirectory = "$buildDir/dokka" + packageOptions { + prefix = "android" + suppress = true + } + packageOptions{ + prefix = "androidx" + suppress = true + } + } + lintOptions { + abortOnError false + } } dependencies { 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 f4e735ba..f67a9ab5 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 @@ -15,10 +15,6 @@ import org.junit.Test import org.junit.runner.RunWith import java.io.IOException - -/** - * Tests data serialization first of all. - */ @RunWith(AndroidJUnit4::class) class LearnBrailleDatabaseTest { @@ -87,7 +83,7 @@ class LearnBrailleDatabaseTest { id = 3, data = ShowDots( text = "Перед Вами полное шеститочие", - dots = BrailleDots(F, F, F, F, F, F) + brailleDots = BrailleDots(F, F, F, F, F, F) ), lessonId = 1, courseId = 1 ), @@ -95,7 +91,7 @@ class LearnBrailleDatabaseTest { id = 4, data = InputDots( text = "Введите все шесть точек", - dots = BrailleDots(F, F, F, F, F, F) + brailleDots = BrailleDots(F, F, F, F, F, F) ), lessonId = 2, courseId = 1 ), @@ -157,30 +153,30 @@ class LearnBrailleDatabaseTest { @Test fun testUsers() = runBlocking { - assertEquals("default", db.userDao.getUser(1)!!.login) + assertEquals("default", db.userDao.user(1)!!.login) } @Test fun testMaterials() = runBlocking { - val data = db.materialDao.getMaterial(1)!!.data + 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.getDeck(1)!!.tag) + assertEquals("Ru letters", db.deckDao.deck(1)!!.tag) } @Test fun testCourses() = runBlocking { - assertEquals("Super course", db.courseDao.getCourse(1)!!.name) + assertEquals("Super course", db.courseDao.course(1)!!.name) } @Test fun testSteps() = runBlocking { for ((i, step) in steps.withIndex()) { - val fromDb = db.stepDao.getStep(i + 1L)!! + val fromDb = db.stepDao.step(i + 1L)!! assertEquals(step, fromDb) } } diff --git a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepositoryTest.kt b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepositoryTest.kt index 5f9553f7..4109b78b 100644 --- a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepositoryTest.kt +++ b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepositoryTest.kt @@ -55,11 +55,11 @@ class ActionsRepositoryTest { @Test fun getAll() = runBlocking { - assertEquals(actions.toList(), repo.getActionsFrom(Days(100))) + assertEquals(actions.toList(), repo.actionsFrom(Days(100))) } @Test - fun rejectLast() = runBlocking { - assertEquals(listOf(actions.first()), repo.getActionsFrom(Days(10))) + fun getSince() = runBlocking { + assertEquals(listOf(actions.first()), repo.actionsFrom(Days(25))) } } diff --git a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepositoryTest.kt b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepositoryTest.kt new file mode 100644 index 00000000..22dac0f2 --- /dev/null +++ b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepositoryTest.kt @@ -0,0 +1,241 @@ +package com.github.braillesystems.learnbraille.data.repository + +import androidx.room.Room +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.github.braillesystems.learnbraille.data.db.LearnBrailleDatabase +import com.github.braillesystems.learnbraille.data.entities.* +import com.github.braillesystems.learnbraille.data.entities.BrailleDot.E +import com.github.braillesystems.learnbraille.data.entities.BrailleDot.F +import com.github.braillesystems.learnbraille.res.MarkerType +import com.github.braillesystems.learnbraille.res.SymbolType +import com.github.braillesystems.learnbraille.utils.unreachable +import kotlinx.coroutines.runBlocking +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.io.IOException + +@RunWith(AndroidJUnit4::class) +class MaterialsRepositoryTest { + + private lateinit var db: LearnBrailleDatabase + private lateinit var repo: MaterialsRepository + + 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 + ) + ), + Material( + 2, + MarkerSymbol( + type = MarkerType.GreekCapital, + brailleDots = BrailleDots(F, F, F, E, E, E) + ) + ), + Material( + 3, + Symbol( + char = 'B', + brailleDots = BrailleDots(F, F, F, E, E, F), + type = SymbolType.digit + ) + ) + ) + + private val knownMaterials = listOf( + KnownMaterial(1, 2), + KnownMaterial(1, 3) + ) + + private val decks = listOf( + Deck( + id = 1, + tag = "Ru letters" + ), + Deck( + id = 2, + tag = "Another useless deck" + ) + ) + + private val cards = listOf( + Card( + deckId = 1, + materialId = 1 + ), + Card( + deckId = 2, + materialId = 1 + ), + Card( + deckId = 1, + materialId = 2 + ) + ) + + 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 + ) + ) + + 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) + knownMaterialDao.insert(knownMaterials) + deckDao.insert(decks) + cardDao.insert(cards) + courseDao.insert(courses) + lessonDao.insert(lessons) + stepDao.insert(steps) + stepAnnotationDao.insert(annotations) + stepHasAnnotationDao.insert(stepAnnotations) + } + } + + repo = MaterialsRepositoryImpl( + db.deckDao, db.cardDao, + object : PreferenceRepository { + override val buzzEnabled: Boolean + get() = unreachable + override val toastsEnabled: Boolean + get() = unreachable + override val golubinaBookStepsEnabled: Boolean + get() = unreachable + override val slateStylusStepsEnabled: Boolean + get() = unreachable + override val traverseDotsInEnumerationOrder: Boolean + get() = unreachable + override val inputOnFlyCheck: Boolean + get() = unreachable + override val additionalAnnouncementsEnabled: Boolean + get() = unreachable + override val practiceUseOnlyKnownMaterials: Boolean + get() = true + override val extendedAccessibilityEnabled: Boolean + get() = unreachable + override val additionalQrCodeButtonEnabled: Boolean + get() = unreachable + override val isWriteModeFirst: Boolean + get() = unreachable + override val currentUserId: DBid + get() = 1 + + override suspend fun getCurrentUser(): User = users.first() + } + ) + } + + @After + @Throws(IOException::class) + fun closeDB() { + db.close() + } + + @Test + fun randomMaterialFromDeck() = runBlocking { + val material = repo.randomMaterialFromDeck(2) + assertTrue(material in materials.filter { Card(2, it.id) in cards }) + } + + @Test + fun randomKnownMaterialFromDeck() = runBlocking { + val material = repo.randomKnownMaterialFromDeck(1) + assertTrue(material in materials.filter { Card(1, it.id) in cards }) + assertTrue(material!!.id in knownMaterials.map { it.materialId }) + } + + @Test + fun allMaterialsFromDeck() = runBlocking { + assertEquals( + materials.filter { Card(2, it.id) in cards }, + repo.allMaterialsFromDeck(2) + ) + } + + @Test + fun allDecks() = runBlocking { + assertEquals( + decks, + repo.allDecks() + ) + } + + @Test + fun availableDecks() = runBlocking { + assertEquals(listOf(decks.first()), repo.availableDecks()) + } + + @Test + fun allDecksWithAvailability() = runBlocking { + assertEquals( + listOf( + DeckWithAvailability(decks[0], true), + DeckWithAvailability(decks[1], false) + ), + repo.allDecksWithAvailability() + ) + } +} diff --git a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepositoryTest.kt b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepositoryTest.kt deleted file mode 100644 index 074a6d5a..00000000 --- a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepositoryTest.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.braillesystems.learnbraille.data.repository - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class PracticeRepositoryTest { - - @Test - fun practiceRepo() { - // TODO - } -} \ No newline at end of file diff --git a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepositoryTest.kt b/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepositoryTest.kt deleted file mode 100644 index 0a69983e..00000000 --- a/app/src/androidTest/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepositoryTest.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.braillesystems.learnbraille.data.repository - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class TheoryRepositoryTest { - - @Test - fun theoryRepo() { - // TODO - } -} diff --git a/app/src/androidTest/java/com/github/braillesystems/learnbraille/res/ResourceTest.kt b/app/src/androidTest/java/com/github/braillesystems/learnbraille/res/ResourceTest.kt new file mode 100644 index 00000000..d6168c9a --- /dev/null +++ b/app/src/androidTest/java/com/github/braillesystems/learnbraille/res/ResourceTest.kt @@ -0,0 +1,49 @@ +package com.github.braillesystems.learnbraille.res + +import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.github.braillesystems.learnbraille.utils.get +import org.junit.Assert.assertNotNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ResourceTest { + + private lateinit var context: Context + + @Before + fun before() { + context = InstrumentationRegistry.getInstrumentation().targetContext + } + + @Test + fun inputSymbolPrintRulesTest() { + content.symbols.keys.forEach { + assertNotNull(context.inputSymbolPrintRules[it]) + } + } + + @Test + fun showSymbolPrintRulesTest() { + content.symbols.keys.forEach { + assertNotNull(context.showSymbolPrintRules[it]) + } + } + + @Test + fun inputMarkerPrintRulesTest() { + content.markers.keys.forEach { + assertNotNull(context.inputMarkerPrintRules[it]) + } + } + + @Test + fun showMarkerPrintRulesTest() { + content.markers.keys.forEach { + assertNotNull(context.showMarkerPrintRules[it]) + } + } +} diff --git a/app/src/androidTest/java/com/github/braillesystems/learnbraille/ui/screens/DotsCheckerTest.kt b/app/src/androidTest/java/com/github/braillesystems/learnbraille/ui/screens/DotsCheckerTest.kt deleted file mode 100644 index b6036631..00000000 --- a/app/src/androidTest/java/com/github/braillesystems/learnbraille/ui/screens/DotsCheckerTest.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.braillesystems.learnbraille.ui.screens - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class DotsCheckerTest { - - @Test - fun dotsChecker() { - // TODO - } -} diff --git a/app/src/androidTest/java/com/github/braillesystems/learnbraille/utils/_KotlinTest.kt b/app/src/androidTest/java/com/github/braillesystems/learnbraille/utils/_KotlinTest.kt deleted file mode 100644 index dc541e55..00000000 --- a/app/src/androidTest/java/com/github/braillesystems/learnbraille/utils/_KotlinTest.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.github.braillesystems.learnbraille.utils - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith - - -@RunWith(AndroidJUnit4::class) -class KotlinTest { - - private fun getNextNumber(currentNumber: Int, nextNumbers: Array, repeat: Int): Int? { - var i = 0 - fun getNextNumber(): Int { - return nextNumbers[i++] - } - - fun ifNumberSameAsCurrent(number: Int) = number == currentNumber - - return tryN(repeat, { !ifNumberSameAsCurrent(it) }, { getNextNumber() }) - } - - @Test - fun tryNTest() { - // testing how tryN helps to find first number in a sequence different from specified number - assertEquals(1, getNextNumber(0, arrayOf(1, 1, 1), 2)) - assertEquals(2, getNextNumber(1, arrayOf(1, 2, 3), 2)) - assertEquals(2, getNextNumber(1, arrayOf(1, 2, 3), 3)) - assertEquals(null, getNextNumber(1, arrayOf(1, 2, 3), 1)) - assertEquals(5, getNextNumber(0, arrayOf(0, 0, 0, 5, 1, 0, 4), 4)) - - } -} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 18285f0c..5a937f5c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,8 +2,6 @@ - - - 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 a152f586..51dc64a5 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt @@ -6,6 +6,8 @@ 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 @@ -43,33 +45,36 @@ class LearnBrailleApplication : Application() { ) } + factory { + val db = get() + MaterialsRepositoryImpl(db.deckDao, db.cardDao, get()) + } + factory { val db = get() PracticeRepositoryImpl( this@LearnBrailleApplication, - db.deckDao, db.cardDao, get() + db.deckDao, get(), get() ) } factory { val db = get() PracticeRepositoryImpl( this@LearnBrailleApplication, - db.deckDao, db.cardDao, get() + db.deckDao, get(), get() ) } factory { - val db = get() BrowserRepositoryImpl( this@LearnBrailleApplication, - get(), db.deckDao, db.cardDao + get(), get() ) } factory { - val db = get() BrowserRepositoryImpl( this@LearnBrailleApplication, - get(), db.deckDao, db.cardDao + get(), get() ) } @@ -94,7 +99,7 @@ class LearnBrailleApplication : Application() { factory { (getEnteredDots: () -> BrailleDots) -> CardViewModelFactory( - get(), get(), + get(), get(), get(), this@LearnBrailleApplication, getEnteredDots ) @@ -105,10 +110,13 @@ class LearnBrailleApplication : Application() { androidContext(this@LearnBrailleApplication) modules(koinModule) }.koin + + // Touch database to force it's preparation + get().devnull } } lateinit var koin: Koin private set -val COURSE = UsersCourse(2L) +val COURSE = UsersCourse diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/db/LearnBrailleDatabase.kt similarity index 60% rename from app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt rename to app/src/main/java/com/github/braillesystems/learnbraille/data/db/LearnBrailleDatabase.kt index 1c997316..3f52dad7 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/db/LearnBrailleDatabase.kt @@ -31,7 +31,7 @@ import timber.log.Timber CurrentStep::class, LastCourseStep::class, LastLessonStep::class, Action::class ], - version = 18, + version = 19, exportSchema = true ) @TypeConverters( @@ -68,14 +68,12 @@ abstract class LearnBrailleDatabase : RoomDatabase(), KoinComponent { /** * Android Room prepopulation and migrations are lazy, * they will start with the first request, blocking it. - * - * TODO add reference to docs */ 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.getUser(1).devnull + userDao.user(1).devnull Timber.i("Finnish database preparation") } } @@ -88,6 +86,10 @@ abstract class LearnBrailleDatabase : RoomDatabase(), KoinComponent { 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, @@ -131,7 +133,8 @@ abstract class LearnBrailleDatabase : RoomDatabase(), KoinComponent { }) .addMigrations( MIGRATION_16_17, - MIGRATION_17_18 + MIGRATION_17_18, + MIGRATION_18_19 ) .build() .init() @@ -198,10 +201,127 @@ private val MIGRATION_16_17 = object : Migration(16, 17), KoinComponent { } } -private val MIGRATION_17_18 = object : Migration(17, 18), KoinComponent { +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/dsl/Courses.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Courses.kt index 7ae7c979..fed42911 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Courses.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Courses.kt @@ -1,21 +1,16 @@ package com.github.braillesystems.learnbraille.data.dsl import com.github.braillesystems.learnbraille.data.entities.Course +import com.github.braillesystems.learnbraille.data.entities.CourseDesc +import com.github.braillesystems.learnbraille.data.entities.CourseName +import com.github.braillesystems.learnbraille.data.entities.DBid import com.github.braillesystems.learnbraille.utils.side -sealed class CourseID(val id: Long) +sealed class CourseID(val id: DBid) + object DevelopersCourse : CourseID(1) -class UsersCourse(id: Long) : CourseID(id) { - init { - require(id > 1) { - "id == 1 stands for developers course" - } - } -} +object UsersCourse : CourseID(2) -/** - * Provided values have no ID - */ @DataBuilderMarker class CoursesBuilder(block: CoursesBuilder.() -> Unit) { @@ -27,14 +22,16 @@ class CoursesBuilder(block: CoursesBuilder.() -> Unit) { block() } - fun course(name: String, description: String, block: LessonAccumulatorBuilder.() -> Unit) = - LessonAccumulatorBuilder(block).side { - val course = Course(DEFAULT_ID, name, description) - _data[course] = it.lessons - } + fun course( + name: CourseName, description: CourseDesc, + block: LessonAccumulatorBuilder.() -> Unit + ) = LessonAccumulatorBuilder(block).side { + val course = Course(UNDEFINED_ID, name, description) + _data[course] = it.lessons + } - fun course(name: String, description: String, lessons: LessonsBuilder) { - val course = Course(DEFAULT_ID, name, description) + fun course(name: CourseName, description: CourseDesc, lessons: LessonsBuilder) { + val course = Course(UNDEFINED_ID, name, description) _data[course] = lessons.lessons } } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/DataStorage.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/DataStorage.kt index 9d8e29d7..23f71f28 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/DataStorage.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/DataStorage.kt @@ -5,7 +5,6 @@ import com.github.braillesystems.learnbraille.res.DeckTags import com.github.braillesystems.learnbraille.utils.side import kotlin.reflect.KProperty - interface DataStorage { val users: List? val materials: List? @@ -19,35 +18,24 @@ interface DataStorage { val knownMaterials: List? } - -const val DEFAULT_ID = -1L -const val ALL_CARDS_DECK_ID = 1L - -typealias StepAnnotationName = String typealias StepWithAnnotations = Pair> typealias LessonWithSteps = Pair> - -@DslMarker -annotation class DataBuilderMarker - - class data( private val materials: MaterialsBuilder, - private val stepAnnotations: List, + private val stepAnnotationNames: List, private val knownMaterials: List, private val block: DataBuilder.() -> Unit ) { internal operator fun getValue(thisRef: Any?, property: KProperty<*>): DataStorage = - DataBuilder(materials, stepAnnotations, knownMaterials, block) + DataBuilder(materials, stepAnnotationNames, knownMaterials, block) } - @DataBuilderMarker class DataBuilder( - private val _materials: MaterialsBuilder, + materials: MaterialsBuilder, private val stepAnnotationNames: List, - private val _knownMaterials: List, + knownMaterials: List, block: DataBuilder.() -> Unit ) : DataStorage { @@ -55,6 +43,7 @@ class DataBuilder( override val users: List get() = _users + private val _materials: MaterialsBuilder = materials override val materials: List get() = _materials.materials @@ -86,6 +75,7 @@ class DataBuilder( override val stepsHasAnnotations: List get() = _stepsHasAnnotations + private val _knownMaterials: List = knownMaterials override val knownMaterials: List by lazy { _knownMaterials.map { it.copy(userId = 1) } } @@ -128,7 +118,9 @@ class DataBuilder( stepsWithAnnotations.forEachIndexed { iStep, (step, stepAnnotationNames) -> val stepId = iStep + 1L - _steps += step.copy(id = stepId, courseId = courseId, lessonId = lessonId) + _steps += step + .copy(id = stepId, courseId = courseId, lessonId = lessonId) + .interpolateText(course.name) stepAnnotationNames.forEach { val stepAnnotation = annotationByName[it]?.id @@ -162,7 +154,6 @@ class DataBuilder( } } - @DataBuilderMarker class DecksBuilder(block: DecksBuilder.() -> Unit) { @@ -175,7 +166,26 @@ class DecksBuilder(block: DecksBuilder.() -> Unit) { } fun deck(tag: String, entryCriterion: (MaterialData) -> Boolean) { - val deck = Deck(DEFAULT_ID, tag) + val deck = Deck(UNDEFINED_ID, tag) _deckToPredicate[deck] = entryCriterion } } + +private fun Step.interpolateText(courseName: CourseName): Step = + this.copy( + data = + if (data !is BaseInfo) data + else { + when (data) { + is FirstInfo -> FirstInfo(interpolateTextHelper(data.text, courseName)) + is LastInfo -> LastInfo(interpolateTextHelper(data.text, courseName)) + is Info -> Info(interpolateTextHelper(data.text, courseName)) + } + } + ) + +private fun Step.interpolateTextHelper(text: HtmlText, courseName: CourseName): HtmlText = + text + .replace(InfoInterpolation.iStep, id.toString()) + .replace(InfoInterpolation.iLesson, lessonId.toString()) + .replace(InfoInterpolation.courseName, courseName) diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Defs.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Defs.kt new file mode 100644 index 00000000..6250ffeb --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Defs.kt @@ -0,0 +1,13 @@ +package com.github.braillesystems.learnbraille.data.dsl + +const val UNDEFINED_ID = -1L +const val ALL_CARDS_DECK_ID = 1L + +object InfoInterpolation { + const val iStep = "#istep" + const val iLesson = "#ilesson" + const val courseName = "#courseName" +} + +@DslMarker +internal annotation class DataBuilderMarker diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Lessons.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Lessons.kt index 0e4ff4c7..b1abba54 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Lessons.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Lessons.kt @@ -22,7 +22,7 @@ class LessonsBuilder(block: LessonsBuilder.() -> Unit) { fun lesson(name: String, description: String = "", block: StepsBuilder.() -> Unit) = StepsBuilder(block).side { _lessons += Pair( - Lesson(DEFAULT_ID, DEFAULT_ID, name, description), + Lesson(UNDEFINED_ID, UNDEFINED_ID, name, description), it.steps ) } @@ -41,7 +41,7 @@ class StepsBuilder(block: StepsBuilder.() -> Unit) { operator fun StepData.unaryPlus() { _steps += Pair( - Step(DEFAULT_ID, DEFAULT_ID, DEFAULT_ID, this), + Step(UNDEFINED_ID, UNDEFINED_ID, UNDEFINED_ID, this), listOf() ) } @@ -51,7 +51,7 @@ class StepsBuilder(block: StepsBuilder.() -> Unit) { "First and Last steps of the course should not be annotated" } _steps += Pair( - Step(DEFAULT_ID, DEFAULT_ID, DEFAULT_ID, first), + Step(UNDEFINED_ID, UNDEFINED_ID, UNDEFINED_ID, first), second ) } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Materials.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Materials.kt index 3fbec36f..031dd796 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Materials.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Materials.kt @@ -1,13 +1,10 @@ package com.github.braillesystems.learnbraille.data.dsl -import com.github.braillesystems.learnbraille.data.entities.BrailleDots -import com.github.braillesystems.learnbraille.data.entities.KnownMaterial -import com.github.braillesystems.learnbraille.data.entities.Material -import com.github.braillesystems.learnbraille.data.entities.Symbol +import com.github.braillesystems.learnbraille.data.entities.* +import com.github.braillesystems.learnbraille.res.MarkerType import com.github.braillesystems.learnbraille.res.content import kotlin.reflect.KProperty - open class materials(private val block: MaterialsBuilder.() -> Unit) { private var materials: MaterialsBuilder? = null @@ -27,15 +24,27 @@ class MaterialsBuilder(block: MaterialsBuilder.() -> Unit) { val symbols: Map get() = _symbols + private val _markers = mutableMapOf() + val markers: Map + get() = _markers + init { block() _materials = materials.mapIndexed { index, material -> material.copy(id = index + 1L).apply { - if (data is Symbol) { - require(!_symbols.contains(data.char)) { - "Symbol (symbol = ${data.char}, type = ${data.type}) already exists" + when (data) { + is Symbol -> { + require(!_symbols.contains(data.char)) { + "Symbol (symbol = ${data.char}, type = ${data.type}) already exists" + } + _symbols[data.char] = this + } + is MarkerSymbol -> { + require(!_markers.contains(data.type)) { + "Symbol (type = ${data.type}" + } + _markers[data.type] = this } - _symbols[data.char] = this } } }.toMutableList() @@ -43,11 +52,16 @@ class MaterialsBuilder(block: MaterialsBuilder.() -> Unit) { operator fun SymbolsBuilder.unaryPlus() { symbols.forEach { symbol -> - this@MaterialsBuilder._materials.add(Material(DEFAULT_ID, symbol)) + this@MaterialsBuilder._materials.add(Material(UNDEFINED_ID, symbol)) } } -} + operator fun MarkersBuilder.unaryPlus() { + markers.forEach { marker -> + this@MaterialsBuilder._materials.add(Material(UNDEFINED_ID, marker)) + } + } +} class symbols(private val symbolType: String, private val block: SymbolsBuilder.() -> Unit) { @@ -77,6 +91,31 @@ class SymbolsBuilder(private val symbolType: String, block: SymbolsBuilder.() -> } } +class markers(private val block: MarkersBuilder.() -> Unit) { + + private var markers: MarkersBuilder? = null + + operator fun getValue(thisRef: Any?, property: KProperty<*>) = + markers ?: MarkersBuilder(block).also { markers = it } +} + +class MarkersBuilder(private val block: MarkersBuilder.() -> Unit) { + + private val _map = mutableMapOf() + val map: Map get() = _map + internal val markers: List + get() = _map.values.toList() + + init { + block() + } + + operator fun get(type: MarkerType): MarkerSymbol? = _map[type] + + fun marker(type: MarkerType, brailleDots: BrailleDots) { + _map[type] = MarkerSymbol(type, brailleDots) + } +} class known(vararg chars: Char) { @@ -84,7 +123,8 @@ class known(vararg chars: Char) { private var knownMaterials: List? = null operator fun getValue(thisRef: Any?, property: KProperty<*>): List = - knownMaterials ?: cs.map { - KnownMaterial(DEFAULT_ID, content.symbols.getValue(it).id) - }.also { knownMaterials = it } + knownMaterials + ?: cs + .map { KnownMaterial(UNDEFINED_ID, content.symbols.getValue(it).id) } + .also { knownMaterials = it } } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Users.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Users.kt index b6ae72eb..a2633ff0 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Users.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Users.kt @@ -1,7 +1,8 @@ package com.github.braillesystems.learnbraille.data.dsl import com.github.braillesystems.learnbraille.data.entities.User - +import com.github.braillesystems.learnbraille.data.entities.UserLogin +import com.github.braillesystems.learnbraille.data.entities.UserName @DataBuilderMarker class UsersBuilder(block: UsersBuilder.() -> Unit) { @@ -14,7 +15,7 @@ class UsersBuilder(block: UsersBuilder.() -> Unit) { block() } - fun user(login: String, name: String) { + fun user(login: UserLogin, name: UserName) { _users += User( login = login, name = name diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Actions.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Actions.kt index 7c13bd6f..b2f0aa5c 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Actions.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Actions.kt @@ -9,7 +9,7 @@ import java.util.* @Entity(tableName = "actions") data class Action( @PrimaryKey(autoGenerate = true) - var id: Long = 0, + var id: DBid = 0, val type: ActionType, val date: Date = Date() ) { @@ -31,7 +31,7 @@ interface ActionDao { suspend fun insert(vararg actions: Action) @Query("select * from actions where date >= :date") - suspend fun getAllActionsSince(date: Long): List + suspend fun allActionsSince(date: Long): List @Query("delete from actions") suspend fun clear() 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 0bc3e7f9..e62f0bd0 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 @@ -5,7 +5,6 @@ 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. */ @@ -19,7 +18,6 @@ enum class BrailleDot { } } - /** * Combination on Braille dots for one symbol in 6-dots notation. */ @@ -70,7 +68,6 @@ val BrailleDots.filled: List operator fun BrailleDots.contains(i: Int) = i in filled - class BrailleDotsConverters { @TypeConverter diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Cards.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Cards.kt index f2d1574b..e788ca27 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Cards.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Cards.kt @@ -2,13 +2,12 @@ package com.github.braillesystems.learnbraille.data.entities import androidx.room.* - @Entity(tableName = "cards", primaryKeys = ["deck_id", "material_id"]) data class Card( @ColumnInfo(name = "deck_id") - val deckId: Long, + val deckId: DBid, @ColumnInfo(name = "material_id") - val materialId: Long + val materialId: DBid ) @Dao @@ -26,7 +25,7 @@ interface CardDao { order by RANDOM() limit 1 """ ) - suspend fun getRandomMaterialFromDeck(deckId: Long): Material? + suspend fun randomMaterialFromDeck(deckId: DBid): Material? @Query( """ @@ -40,7 +39,7 @@ interface CardDao { order by RANDOM() limit 1 """ ) - suspend fun getRandomKnownMaterialFromDeck(userId: Long, deckId: Long): Material? + suspend fun randomKnownMaterialFromDeck(userId: DBid, deckId: DBid): Material? @Query( """ @@ -51,7 +50,7 @@ interface CardDao { order by m.id """ ) - suspend fun getAllMaterialsFromDeck(id: Long): List + suspend fun allMaterialsFromDeck(id: DBid): List @Query("delete from cards") suspend fun clear() diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Common.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Common.kt new file mode 100644 index 00000000..fc836e2a --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Common.kt @@ -0,0 +1,3 @@ +package com.github.braillesystems.learnbraille.data.entities + +typealias DBid = Long diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/CoursePositions.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/CoursePositions.kt index df1912f5..05679bf4 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/CoursePositions.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/CoursePositions.kt @@ -2,20 +2,19 @@ package com.github.braillesystems.learnbraille.data.entities import androidx.room.* - /** * `Current` means: last not passed. */ @Entity(tableName = "current_step", primaryKeys = ["user_id", "course_id"]) data class CurrentStep( @ColumnInfo(name = "user_id") - val userId: Long, + val userId: DBid, @ColumnInfo(name = "course_id") - val courseId: Long, + val courseId: DBid, @ColumnInfo(name = "lesson_id") - val lessonId: Long, + val lessonId: DBid, @ColumnInfo(name = "step_id") - val stepId: Long + val stepId: DBid ) @Dao @@ -28,20 +27,19 @@ interface CurrentStepDao { suspend fun update(currentStep: CurrentStep) } - /** * `Last` means: last seen. */ @Entity(tableName = "last_course_step", primaryKeys = ["user_id", "course_id"]) data class LastCourseStep( @ColumnInfo(name = "user_id") - val userId: Long, + val userId: DBid, @ColumnInfo(name = "course_id") - val courseId: Long, + val courseId: DBid, @ColumnInfo(name = "lesson_id") - val lessonId: Long, + val lessonId: DBid, @ColumnInfo(name = "step_id") - val stepId: Long + val stepId: DBid ) @Dao @@ -54,20 +52,19 @@ interface LastCourseStepDao { suspend fun update(lastCourseStep: LastCourseStep) } - /** * Last step, what user visited in lesson */ @Entity(tableName = "last_lesson_step", primaryKeys = ["user_id", "course_id", "lesson_id"]) data class LastLessonStep( @ColumnInfo(name = "user_id") - val userId: Long, + val userId: DBid, @ColumnInfo(name = "course_id") - val courseId: Long, + val courseId: DBid, @ColumnInfo(name = "lesson_id") - val lessonId: Long, + val lessonId: DBid, @ColumnInfo(name = "step_id") - val stepId: Long + val stepId: DBid ) @Dao diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Courses.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Courses.kt index a111346c..a3d54a60 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Courses.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Courses.kt @@ -2,12 +2,14 @@ package com.github.braillesystems.learnbraille.data.entities import androidx.room.* +typealias CourseName = String +typealias CourseDesc = String @Entity(tableName = "courses") data class Course( - @PrimaryKey val id: Long, - val name: String, - val description: String + @PrimaryKey val id: DBid, + val name: CourseName, + val description: CourseDesc ) @Dao @@ -17,7 +19,7 @@ interface CourseDao { suspend fun insert(courses: List) @Query("select * from courses where id = :id") - suspend fun getCourse(id: Long): Course? + suspend fun course(id: DBid): Course? @Query("delete from courses") suspend fun clear() diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Decks.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Decks.kt index 7125baf0..5cae21bc 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Decks.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Decks.kt @@ -2,11 +2,12 @@ package com.github.braillesystems.learnbraille.data.entities import androidx.room.* +typealias DeckTag = String @Entity(tableName = "decks") data class Deck( - @PrimaryKey val id: Long, - val tag: String + @PrimaryKey val id: DBid, + val tag: DeckTag ) @Dao @@ -16,10 +17,10 @@ interface DeckDao { suspend fun insert(decks: List) @Query("select * from decks where id = :id") - suspend fun getDeck(id: Long): Deck? + suspend fun deck(id: DBid): Deck? @Query("select * from decks") - suspend fun getAllDecks(): List + suspend fun allDecks(): List @Query( """ @@ -32,7 +33,7 @@ interface DeckDao { order by d.id """ ) - suspend fun getAvailableDecks(userId: Long): List + suspend fun availableDecks(userId: DBid): List @Query("delete from decks") suspend fun clear() diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/KnownMaterials.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/KnownMaterials.kt index d703a3d0..1a8fcfb6 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/KnownMaterials.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/KnownMaterials.kt @@ -2,13 +2,12 @@ package com.github.braillesystems.learnbraille.data.entities import androidx.room.* - @Entity(tableName = "known_materials", primaryKeys = ["user_id", "material_id"]) data class KnownMaterial( @ColumnInfo(name = "user_id") - val userId: Long, + val userId: DBid, @ColumnInfo(name = "material_id") - val materialId: Long + val materialId: DBid ) @Dao 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 25e5571b..15fa45c2 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 @@ -2,14 +2,16 @@ 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: Long, + val id: DBid, @ColumnInfo(name = "course_id") - val courseId: Long, - val name: String, - val description: String + val courseId: DBid, + val name: LessonName, + val description: LessonDesc ) @Dao @@ -25,7 +27,7 @@ interface LessonDao { order by id """ ) - suspend fun getAllCourseLessons(courseId: Long): List + 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/MaterialData.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/MaterialData.kt index d5d800b3..a1ea408f 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/MaterialData.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/MaterialData.kt @@ -1,12 +1,12 @@ package com.github.braillesystems.learnbraille.data.entities import androidx.room.TypeConverter +import com.github.braillesystems.learnbraille.res.MarkerType import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.UnstableDefault import kotlinx.serialization.json.Json - @Serializable sealed class MaterialData @@ -21,11 +21,24 @@ class MaterialDataTypeConverters { fun from(s: String): MaterialData = Json.parse(MaterialData.serializer(), s) } +typealias SymbolType = String + +@Serializable +sealed class OneBrailleSymbol : MaterialData() { + abstract val brailleDots: BrailleDots +} @Serializable data class Symbol( val char: Char, - val brailleDots: BrailleDots, + override val brailleDots: BrailleDots, @SerialName("symbol_type") - val type: String -) : MaterialData() + val type: SymbolType +) : OneBrailleSymbol() + +@Serializable +data class MarkerSymbol( + @SerialName("marker_type") + val type: MarkerType, + override val brailleDots: BrailleDots +) : OneBrailleSymbol() diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Materials.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Materials.kt index d2a5ff11..34482385 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Materials.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Materials.kt @@ -3,11 +3,10 @@ package com.github.braillesystems.learnbraille.data.entities import androidx.room.* import kotlinx.serialization.Serializable - @Entity(tableName = "materials") @Serializable data class Material( - @PrimaryKey val id: Long, + @PrimaryKey val id: DBid, val data: MaterialData ) @@ -18,10 +17,10 @@ interface MaterialDao { suspend fun insert(materials: List) @Query("select * from materials where id = :id") - suspend fun getMaterial(id: Long): Material? + suspend fun material(id: DBid): Material? @Query("select * from materials order by RANDOM() limit 1") - suspend fun getRandomMaterial(): Material? + suspend fun randomMaterial(): Material? @Query("Delete from materials") suspend fun clear() diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepAnnotations.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepAnnotations.kt index 307e7806..f9d36d80 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepAnnotations.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepAnnotations.kt @@ -2,11 +2,12 @@ package com.github.braillesystems.learnbraille.data.entities import androidx.room.* +typealias StepAnnotationName = String @Entity(tableName = "step_annotations", indices = [Index(value = ["name"], unique = true)]) data class StepAnnotation( - @PrimaryKey val id: Long, - val name: String + @PrimaryKey val id: DBid, + val name: StepAnnotationName ) @Dao diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepData.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepData.kt index 4a34db54..ede280b8 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepData.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepData.kt @@ -6,13 +6,10 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.UnstableDefault import kotlinx.serialization.json.Json - /** * There are specific step types in the app. * They differs by visual representation and by the data they contain. * - * Step type can be determined by `is` check. - * * All texts are in html format. Use .parseAsHtml android extension function. */ @Serializable @@ -29,12 +26,15 @@ class StepDataConverters { fun from(string: String) = Json.parse(StepData.serializer(), string) } +typealias HtmlText = String /** * Represents step types with information. */ @Serializable -sealed class BaseInfo : StepData() +sealed class BaseInfo : StepData() { + abstract val text: HtmlText +} /** * Step displays information text for the user. @@ -42,7 +42,7 @@ sealed class BaseInfo : StepData() @Serializable data class Info( @SerialName("info") - val text: String + override val text: HtmlText ) : BaseInfo() /** @@ -51,7 +51,7 @@ data class Info( @Serializable data class FirstInfo( @SerialName("info") - val text: String + override val text: HtmlText ) : BaseInfo() /** @@ -60,12 +60,14 @@ data class FirstInfo( @Serializable data class LastInfo( @SerialName("info") - val text: String + override val text: HtmlText ) : BaseInfo() @Serializable -sealed class BaseInput : StepData() +sealed class BaseInput : StepData() { + abstract val brailleDots: BrailleDots +} /** * Step prompts the user to enter something. @@ -73,7 +75,13 @@ sealed class BaseInput : StepData() @Serializable data class Input( val material: Material -) : BaseInput() +) : BaseInput() { + + override val brailleDots: BrailleDots + get() = when (material.data) { + is OneBrailleSymbol -> material.data.brailleDots + } +} /** * Step prompts the user to enter dots with specific numbers. @@ -83,13 +91,16 @@ data class Input( */ @Serializable data class InputDots( - val text: String?, - val dots: BrailleDots + val text: HtmlText?, + @SerialName("dots") // backward compatibility + override val brailleDots: BrailleDots ) : BaseInput() @Serializable -sealed class BaseShow : StepData() +sealed class BaseShow : StepData() { + abstract val brailleDots: BrailleDots +} /** * Step shows something. @@ -97,7 +108,13 @@ sealed class BaseShow : StepData() @Serializable data class Show( val material: Material -) : BaseShow() +) : BaseShow() { + + override val brailleDots: BrailleDots + get() = when (material.data) { + is OneBrailleSymbol -> material.data.brailleDots + } +} /** * Step shows Braille dots with specific numbers. @@ -107,6 +124,7 @@ data class Show( */ @Serializable data class ShowDots( - val text: String?, - val dots: BrailleDots + val text: HtmlText?, + @SerialName("dots") + override val brailleDots: BrailleDots ) : BaseShow() diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepHasAnnotations.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepHasAnnotations.kt index 0116128e..a7e06fea 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepHasAnnotations.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/StepHasAnnotations.kt @@ -2,20 +2,19 @@ package com.github.braillesystems.learnbraille.data.entities import androidx.room.* - @Entity( tableName = "step_has_annotations", primaryKeys = ["course_id", "lesson_id", "step_id", "annotation_id"] ) data class StepHasAnnotation( @ColumnInfo(name = "course_id") - val courseId: Long, + val courseId: DBid, @ColumnInfo(name = "lesson_id") - val lessonId: Long, + val lessonId: DBid, @ColumnInfo(name = "step_id") - val stepId: Long, + val stepId: DBid, @ColumnInfo(name = "annotation_id") - val annotationId: Long + val annotationId: DBid ) @Dao diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Steps.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Steps.kt index 5623437e..fe68006c 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Steps.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Steps.kt @@ -1,139 +1,133 @@ -package com.github.braillesystems.learnbraille.data.entities - -import androidx.room.* -import com.github.braillesystems.learnbraille.data.dsl.StepAnnotationName -import kotlinx.serialization.Serializable - - -@Entity(tableName = "steps", primaryKeys = ["id", "course_id", "lesson_id"]) -@Serializable -data class Step( - val id: Long, - @ColumnInfo(name = "course_id") - val courseId: Long, - @ColumnInfo(name = "lesson_id") - val lessonId: Long, - val data: StepData -) : Comparable { - - override operator fun compareTo(other: Step): Int { - require(other.courseId == courseId) { - "Only steps of the same course are comparable" - } - if (this == other) return 0 - return if (lessonId < other.lessonId || - (lessonId == other.lessonId && id < other.id) - ) { - -1 - } else { - 1 - } - } -} - -/** - * Be careful: there are duplications of some SQL code. - */ -@Dao -interface StepDao { - - @Insert(onConflict = OnConflictStrategy.ABORT) - suspend fun insert(steps: List) - - @Query("select * from steps where id = :id") - suspend fun getStep(id: Long): Step? - - @Query( - """ - select steps.* from current_step as cs - inner join steps on steps.course_id = cs.course_id - and steps.lesson_id = cs.lesson_id and steps.id = cs.step_id - where cs.user_id = :userId and cs.course_id = :courseId - """ - ) - suspend fun getCurrentStep(userId: Long, courseId: Long): Step? - - @Query( - """ - select steps.* from last_course_step as ls - inner join steps on steps.course_id = ls.course_id - and steps.lesson_id = ls.lesson_id and steps.id = ls.step_id - where ls.user_id = :userId and ls.course_id = :courseId - """ - ) - suspend fun getLastStep(userId: Long, courseId: Long): Step? - - @Query( - """ - select steps.* from last_lesson_step as ls - inner join steps on steps.course_id = ls.course_id - and steps.lesson_id = ls.lesson_id and steps.id = ls.step_id - where ls.user_id = :userId and ls.course_id = :courseId and ls.lesson_id = :lessonId - """ - ) - suspend fun getLastStep(userId: Long, courseId: Long, lessonId: Long): Step? - - @Query( - """ - select steps.* from steps - where steps.course_id = :courseId - order by steps.lesson_id, steps.id limit 1 - """ - ) - suspend fun getFirstCourseStep(courseId: Long): Step? - - @Query( - """ - select steps.* from steps - where course_id = :courseId - and ( - (id > :thisStepId and lesson_id = :thisLessonId) - or (lesson_id > :thisLessonId) - ) - and not exists( - select * from step_has_annotations as sha - inner join step_annotations as sa on sha.annotation_id = sa.id - where sha.course_id = :courseId - and sha.lesson_id = steps.lesson_id and sha.step_id = steps.id - and sa.name in (:proscribedAnnotations) - ) - order by lesson_id, id - limit 1 - """ - ) - suspend fun getNextStep( - courseId: Long, - thisLessonId: Long, - thisStepId: Long, - proscribedAnnotations: List = listOf() - ): Step? - - @Query( - """ - select steps.* from steps - where course_id = :courseId - and ( - (id < :thisStepId and lesson_id = :thisLessonId) - or (lesson_id < :thisLessonId) - ) - and not exists( - select * from step_has_annotations as sha - inner join step_annotations as sa on sha.annotation_id = sa.id - where sha.course_id = :courseId - and sha.lesson_id = steps.lesson_id and sha.step_id = steps.id - and sa.name in (:proscribedAnnotations) - ) - order by lesson_id desc, id desc - limit 1 - """ - ) - suspend fun getPrevStep( - courseId: Long, - thisLessonId: Long, - thisStepId: Long, - proscribedAnnotations: List = listOf() - ): Step? - - @Query("delete from steps") - suspend fun clear() -} +package com.github.braillesystems.learnbraille.data.entities + +import androidx.room.* +import com.github.braillesystems.learnbraille.utils.compareTo +import kotlinx.serialization.Serializable + +@Entity(tableName = "steps", primaryKeys = ["id", "course_id", "lesson_id"]) +@Serializable +data class Step( + val id: DBid, + @ColumnInfo(name = "course_id") + val courseId: DBid, + @ColumnInfo(name = "lesson_id") + val lessonId: DBid, + val data: StepData +) : Comparable { + + override operator fun compareTo(other: Step): Int { + require(other.courseId == courseId) { + "Only steps of the same course are comparable" + } + val id1 = lessonId to id + val id2 = other.run { lessonId to id } + return id1.compareTo(id2) + } +} + +/** + * Be careful: there are duplications of some SQL code. + */ +@Dao +interface StepDao { + + @Insert(onConflict = OnConflictStrategy.ABORT) + suspend fun insert(steps: List) + + @Query("select * from steps where id = :id") + suspend fun step(id: DBid): Step? + + @Query( + """ + select steps.* from current_step as cs + inner join steps on steps.course_id = cs.course_id + and steps.lesson_id = cs.lesson_id and steps.id = cs.step_id + where cs.user_id = :userId and cs.course_id = :courseId + """ + ) + suspend fun currentStep(userId: DBid, courseId: DBid): Step? + + @Query( + """ + select steps.* from last_course_step as ls + inner join steps on steps.course_id = ls.course_id + and steps.lesson_id = ls.lesson_id and steps.id = ls.step_id + where ls.user_id = :userId and ls.course_id = :courseId + """ + ) + suspend fun lastStep(userId: DBid, courseId: DBid): Step? + + @Query( + """ + select steps.* from last_lesson_step as ls + inner join steps on steps.course_id = ls.course_id + and steps.lesson_id = ls.lesson_id and steps.id = ls.step_id + where ls.user_id = :userId and ls.course_id = :courseId and ls.lesson_id = :lessonId + """ + ) + suspend fun lastStep(userId: DBid, courseId: DBid, lessonId: DBid): Step? + + @Query( + """ + select steps.* from steps + where steps.course_id = :courseId + order by steps.lesson_id, steps.id limit 1 + """ + ) + suspend fun firstCourseStep(courseId: DBid): Step? + + @Query( + """ + select steps.* from steps + where course_id = :courseId + and ( + (id > :thisStepId and lesson_id = :thisLessonId) + or (lesson_id > :thisLessonId) + ) + and not exists( + select * from step_has_annotations as sha + inner join step_annotations as sa on sha.annotation_id = sa.id + where sha.course_id = :courseId + and sha.lesson_id = steps.lesson_id and sha.step_id = steps.id + and sa.name in (:proscribedAnnotations) + ) + order by lesson_id, id + limit 1 + """ + ) + suspend fun nextStep( + courseId: DBid, + thisLessonId: DBid, + thisStepId: DBid, + proscribedAnnotations: List = listOf() + ): Step? + + @Query( + """ + select steps.* from steps + where course_id = :courseId + and ( + (id < :thisStepId and lesson_id = :thisLessonId) + or (lesson_id < :thisLessonId) + ) + and not exists( + select * from step_has_annotations as sha + inner join step_annotations as sa on sha.annotation_id = sa.id + where sha.course_id = :courseId + and sha.lesson_id = steps.lesson_id and sha.step_id = steps.id + and sa.name in (:proscribedAnnotations) + ) + order by lesson_id desc, id desc + limit 1 + """ + ) + suspend fun prevStep( + courseId: DBid, + thisLessonId: DBid, + thisStepId: DBid, + proscribedAnnotations: List = listOf() + ): Step? + + @Query("delete from steps") + 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 e997762d..995c0ea4 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 @@ -2,12 +2,15 @@ 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: Long = 0, - val login: String, - val name: String + var id: DBid = 0, + val login: UserLogin, + val name: UserName ) @Dao @@ -20,10 +23,10 @@ interface UserDao { suspend fun insert(users: List) @Query("select * from users where :login = login limit 1") - suspend fun getUser(login: String): User? + suspend fun user(login: UserLogin): User? @Query("select * from users where :id = id limit 1") - suspend fun getUser(id: Long): User? + suspend fun user(id: DBid): User? @Query("delete from users") suspend fun clear() diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepository.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepository.kt index 91ec3e8c..83dd9011 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepository.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/ActionsRepository.kt @@ -9,8 +9,10 @@ import com.github.braillesystems.learnbraille.utils.scope import kotlinx.coroutines.launch import java.util.* +typealias Actions = List + interface ActionsRepository { - suspend fun getActionsFrom(days: Days): Actions + suspend fun actionsFrom(days: Days): Actions } interface MutableActionsRepository : ActionsRepository { @@ -31,14 +33,12 @@ class ActionsRepositoryImpl( override suspend fun clearAllStats() = actionsDao.clear() - override suspend fun getActionsFrom(days: Days): Actions = + override suspend fun actionsFrom(days: Days): Actions = actionsDao - .getAllActionsSince((getCurrDate() - days).time) + .allActionsSince((getCurrDate() - days).time) .also { scope().launch { actionsDao.removeAllActionsBefore((getCurrDate() - keepActionsTime).time) } } } - -typealias Actions = List diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/BrowserRepository.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/BrowserRepository.kt index 25e87dc6..3bf4265f 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/BrowserRepository.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/BrowserRepository.kt @@ -2,30 +2,28 @@ package com.github.braillesystems.learnbraille.data.repository import android.content.Context import com.github.braillesystems.learnbraille.data.dsl.ALL_CARDS_DECK_ID -import com.github.braillesystems.learnbraille.data.entities.CardDao -import com.github.braillesystems.learnbraille.data.entities.DeckDao +import com.github.braillesystems.learnbraille.data.entities.DBid import com.github.braillesystems.learnbraille.utils.preferences interface BrowserRepository : MaterialsRepository { - val currentDeckId: Long + val currentDeckId: DBid } interface MutableBrowserRepository : BrowserRepository { - override var currentDeckId: Long + override var currentDeckId: DBid } class BrowserRepositoryImpl( private val context: Context, private val preferenceRepository: PreferenceRepository, - deckDao: DeckDao, - cardDao: CardDao -) : MaterialsRepositoryImpl(deckDao, cardDao), - MutableBrowserRepository { + private val materialsRepository: MaterialsRepository +) : MutableBrowserRepository, + MaterialsRepository by materialsRepository { private val currDeckPreference: String get() = "browser_curr_deck_${preferenceRepository.currentUserId}" - override var currentDeckId: Long + override var currentDeckId: DBid get() = context.preferences.getLong( currDeckPreference, ALL_CARDS_DECK_ID ) diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepository.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepository.kt index 54eaa064..79654363 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepository.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/MaterialsRepository.kt @@ -1,26 +1,47 @@ package com.github.braillesystems.learnbraille.data.repository -import com.github.braillesystems.learnbraille.data.entities.CardDao -import com.github.braillesystems.learnbraille.data.entities.Deck -import com.github.braillesystems.learnbraille.data.entities.DeckDao -import com.github.braillesystems.learnbraille.data.entities.Material +import com.github.braillesystems.learnbraille.data.entities.* + +data class DeckWithAvailability( + val deck: Deck, + val containsCards: Boolean +) interface MaterialsRepository { - suspend fun getRandomMaterialFromDeck(id: Long): Material? - suspend fun getAllMaterialsFromDeck(id: Long): List - suspend fun getAllDecks(): List + suspend fun randomMaterialFromDeck(id: DBid): Material? + suspend fun randomKnownMaterialFromDeck(id: DBid): Material? + suspend fun allMaterialsFromDeck(id: DBid): List + suspend fun allDecks(): List + suspend fun availableDecks(): List + suspend fun allDecksWithAvailability(): List } open class MaterialsRepositoryImpl( private val deckDao: DeckDao, - private val cardDao: CardDao + private val cardDao: CardDao, + private val preferenceRepository: PreferenceRepository ) : MaterialsRepository { - override suspend fun getRandomMaterialFromDeck(id: Long): Material? = - cardDao.getRandomMaterialFromDeck(id) + override suspend fun randomMaterialFromDeck(id: DBid): Material? = + cardDao.randomMaterialFromDeck(id) + + override suspend fun randomKnownMaterialFromDeck(id: DBid): Material? = + cardDao.randomKnownMaterialFromDeck(preferenceRepository.currentUserId, id) + + override suspend fun allMaterialsFromDeck(id: DBid): List = + cardDao.allMaterialsFromDeck(id) + + override suspend fun allDecks(): List = deckDao.allDecks() - override suspend fun getAllMaterialsFromDeck(id: Long): List = - cardDao.getAllMaterialsFromDeck(id) + override suspend fun availableDecks(): List = + deckDao.availableDecks(preferenceRepository.currentUserId) - override suspend fun getAllDecks(): List = deckDao.getAllDecks() + override suspend fun allDecksWithAvailability(): List = + if (preferenceRepository.practiceUseOnlyKnownMaterials) { + val all = allDecks() + val available = availableDecks() + all.map { DeckWithAvailability(it, available.contains(it)) } + } else { + deckDao.allDecks().map { DeckWithAvailability(it, true) } + } } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt index e6ae0628..5b666efb 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt @@ -2,40 +2,35 @@ package com.github.braillesystems.learnbraille.data.repository import android.content.Context import com.github.braillesystems.learnbraille.data.dsl.ALL_CARDS_DECK_ID -import com.github.braillesystems.learnbraille.data.entities.CardDao +import com.github.braillesystems.learnbraille.data.entities.DBid import com.github.braillesystems.learnbraille.data.entities.Deck import com.github.braillesystems.learnbraille.data.entities.DeckDao import com.github.braillesystems.learnbraille.data.entities.Material import com.github.braillesystems.learnbraille.utils.preferences -data class DeckNotEmpty( - val deck: Deck, - val containsCards: Boolean -) - -interface PracticeRepository { - val currentDeckId: Long - suspend fun getNextMaterial(): Material? - suspend fun getCurrDeck(): Deck - suspend fun getAllDecks(): List +interface PracticeRepository : MaterialsRepository { + val currentDeckId: DBid + suspend fun currentDeck(): Deck + suspend fun randomMaterial(): Material? + suspend fun randomKnownMaterial(): Material? } interface MutablePracticeRepository : PracticeRepository { - override var currentDeckId: Long - suspend fun getNextMaterialNotNull(): Material + override var currentDeckId: DBid } class PracticeRepositoryImpl( private val context: Context, private val deckDao: DeckDao, - private val cardDao: CardDao, - private val preferenceRepository: PreferenceRepository -) : MutablePracticeRepository { + private val preferenceRepository: PreferenceRepository, + private val materialsRepository: MaterialsRepository +) : MutablePracticeRepository, + MaterialsRepository by materialsRepository { private val currDeckPreference: String get() = "practice_curr_deck_${preferenceRepository.currentUserId}" - override var currentDeckId: Long + override var currentDeckId: DBid get() = context.preferences.getLong( currDeckPreference, ALL_CARDS_DECK_ID ) @@ -46,42 +41,12 @@ class PracticeRepositoryImpl( } } - override suspend fun getNextMaterial(): Material? = - if (preferenceRepository.practiceUseOnlyKnownMaterials) { - cardDao.getRandomKnownMaterialFromDeck( - preferenceRepository.currentUserId, - currentDeckId - ) - } else { - cardDao.getRandomMaterialFromDeck(currentDeckId) - } + override suspend fun currentDeck(): Deck = + deckDao.deck(currentDeckId) ?: error("Wrong currentDeckId") - /** - * Deck changes automatically if `use only known materials` enabled - * and previous `currentDeck` became not available. - */ - override suspend fun getNextMaterialNotNull(): Material = getNextMaterial() ?: run { - if (preferenceRepository.practiceUseOnlyKnownMaterials) { - currentDeckId = ALL_CARDS_DECK_ID - getNextMaterial() - ?: error("Some materials are expected to be added as known by default") - } else { - error( - "Materials are expected to be prepopulated and " + - "user should not have possibilities to access empty deck" - ) - } - } + override suspend fun randomMaterial(): Material? = + materialsRepository.randomMaterialFromDeck(currentDeckId) - override suspend fun getCurrDeck(): Deck = - deckDao.getDeck(currentDeckId) ?: error("Current deck should always exist") - - override suspend fun getAllDecks(): List = - if (preferenceRepository.practiceUseOnlyKnownMaterials) { - val all = deckDao.getAllDecks() - val available = deckDao.getAvailableDecks(preferenceRepository.currentUserId) - all.map { DeckNotEmpty(it, available.contains(it)) } - } else { - deckDao.getAllDecks().map { DeckNotEmpty(it, true) } - } + override suspend fun randomKnownMaterial(): Material? = + materialsRepository.randomKnownMaterialFromDeck(currentDeckId) } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt index 159f70db..3ce51ad4 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt @@ -3,6 +3,7 @@ package com.github.braillesystems.learnbraille.data.repository import android.content.Context import android.widget.Toast import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.DBid import com.github.braillesystems.learnbraille.data.entities.User import com.github.braillesystems.learnbraille.data.entities.UserDao import com.github.braillesystems.learnbraille.utils.BuzzPattern @@ -10,22 +11,22 @@ import com.github.braillesystems.learnbraille.utils.logged import com.github.braillesystems.learnbraille.utils.preferences import timber.log.Timber - interface PreferenceRepository { val buzzEnabled: Boolean val toastsEnabled: Boolean val brailleTrainerEnabled: Boolean get() = false // Uncomment in android manifest when set true - val speechRecognitionEnabled: Boolean val golubinaBookStepsEnabled: Boolean + val slateStylusStepsEnabled: Boolean val traverseDotsInEnumerationOrder: Boolean val inputOnFlyCheck: Boolean val additionalAnnouncementsEnabled: Boolean val practiceUseOnlyKnownMaterials: Boolean val extendedAccessibilityEnabled: Boolean val additionalQrCodeButtonEnabled: Boolean + val isWriteModeFirst: Boolean - val currentUserId: Long + val currentUserId: DBid suspend fun getCurrentUser(): User val toastDuration get() = Toast.LENGTH_SHORT @@ -58,16 +59,16 @@ class PreferenceRepositoryImpl( ) } - override val speechRecognitionEnabled: Boolean by logged { + override val golubinaBookStepsEnabled: Boolean by logged { context.preferences.getBoolean( - context.getString(R.string.preference_speech_recognition_enabled), - false // To enable, set to `true` and uncomment permission in AndroidManifest + context.getString(R.string.preference_golubina_book_steps_enabled), + true ) } - override val golubinaBookStepsEnabled: Boolean by logged { + override val slateStylusStepsEnabled: Boolean by logged { context.preferences.getBoolean( - context.getString(R.string.preference_golubina_book_steps_enabled), + context.getString(R.string.preference_slate_stylus_steps_enabled), true ) } @@ -114,14 +115,21 @@ class PreferenceRepositoryImpl( ) } - override val currentUserId: Long by logged { + override val isWriteModeFirst: Boolean by logged { + context.preferences.getBoolean( + context.getString(R.string.preferences_is_write_mode_first), + true + ) + } + + override val currentUserId: DBid by logged { context.preferences.getLong( context.getString(R.string.preference_current_user), 1 ) } override suspend fun getCurrentUser(): User = - userDao.getUser(currentUserId) + userDao.user(currentUserId) ?.also { Timber.i("Current user = $it") } ?: error("Current user should always exist") } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepository.kt b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepository.kt index fb142a80..c3582605 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepository.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepository.kt @@ -7,16 +7,16 @@ import com.github.braillesystems.learnbraille.utils.scope import kotlinx.coroutines.launch interface TheoryRepository { - suspend fun getCurrentStep(courseId: Long): Step - suspend fun getLastCourseStep(courseId: Long): Step - suspend fun getAllCourseLessons(courseId: Long): List + suspend fun currentStep(courseId: DBid): Step + suspend fun lastCourseStep(courseId: DBid): Step + suspend fun allCourseLessons(courseId: DBid): List } interface MutableTheoryRepository : TheoryRepository { - suspend fun getNextStepAndUpdate(thisStep: Step, markThisAsPassed: Boolean = false): Step? - suspend fun getPrevStepAndUpdate(thisStep: Step): Step? - suspend fun getCurrentStepAndUpdate(courseId: Long): Step - suspend fun getLastLessonOrCurrentStepAndUpdate(courseId: Long, lessonId: Long): Step + suspend fun nextStepAndMove(thisStep: Step, markThisAsPassed: Boolean = false): Step? + suspend fun prevStepAndMove(thisStep: Step): Step? + suspend fun currentStepAndMove(courseId: DBid): Step + suspend fun lastLessonOrCurrentStepAndMove(courseId: DBid, lessonId: DBid): Step } class TheoryRepositoryImpl( @@ -30,22 +30,24 @@ class TheoryRepositoryImpl( private val actionsRepository: MutableActionsRepository ) : MutableTheoryRepository { - private val proscribedAnnotations + private val proscribedAnnotations: List get() = listOfNotNull( if (preferenceRepository.golubinaBookStepsEnabled) null - else StepAnnotation.golubinaBookRequired + else StepAnnotation.golubinaBookRequired, + if (preferenceRepository.slateStylusStepsEnabled) null + else StepAnnotation.slateStylusRequired ) - override suspend fun getNextStepAndUpdate(thisStep: Step, markThisAsPassed: Boolean): Step? { + @Suppress("ReturnCount") + override suspend fun nextStepAndMove(thisStep: Step, markThisAsPassed: Boolean): Step? { val next = stepDao - .getNextStep( + .nextStep( thisStep.courseId, thisStep.lessonId, thisStep.id, proscribedAnnotations ) ?: return null - val curr = requireNotNull( - stepDao.getCurrentStep(preferenceRepository.currentUserId, thisStep.courseId) - ) + val curr = stepDao + .currentStep(preferenceRepository.currentUserId, thisStep.courseId)!! val updateAndReturn = { updateLast(next) @@ -69,9 +71,9 @@ class TheoryRepositoryImpl( return updateAndReturn() } - override suspend fun getPrevStepAndUpdate(thisStep: Step): Step? = + override suspend fun prevStepAndMove(thisStep: Step): Step? = stepDao - .getPrevStep( + .prevStep( thisStep.courseId, thisStep.lessonId, thisStep.id, @@ -79,37 +81,37 @@ class TheoryRepositoryImpl( ) ?.also { updateLast(it) } - override suspend fun getCurrentStepAndUpdate(courseId: Long): Step = - getCurrentStep(courseId).also { updateLast(it) } + override suspend fun currentStepAndMove(courseId: DBid): Step = + currentStep(courseId).also { updateLast(it) } /** * LessonId is supposed to exist for this courseId. */ - override suspend fun getLastLessonOrCurrentStepAndUpdate(courseId: Long, lessonId: Long): Step = + override suspend fun lastLessonOrCurrentStepAndMove(courseId: DBid, lessonId: DBid): Step = stepDao - .getLastStep(preferenceRepository.currentUserId, courseId, lessonId) + .lastStep(preferenceRepository.currentUserId, courseId, lessonId) ?.also { updateLast(it) } ?: error( "No such lessonId ($lessonId) exists for course ($courseId) " + "or current step is behind lesson with such lessonId" ) - override suspend fun getCurrentStep(courseId: Long): Step = + override suspend fun currentStep(courseId: DBid): Step = stepDao - .getCurrentStep(preferenceRepository.currentUserId, courseId) + .currentStep(preferenceRepository.currentUserId, courseId) ?: initCoursePos(courseId) - override suspend fun getLastCourseStep(courseId: Long): Step = + override suspend fun lastCourseStep(courseId: DBid): Step = stepDao - .getLastStep(preferenceRepository.currentUserId, courseId) + .lastStep(preferenceRepository.currentUserId, courseId) ?: initCoursePos(courseId) - override suspend fun getAllCourseLessons(courseId: Long): List = - lessonDao.getAllCourseLessons(courseId) + override suspend fun allCourseLessons(courseId: DBid): List = + lessonDao.allCourseLessons(courseId) - private suspend fun initCoursePos(courseId: Long): Step = + private suspend fun initCoursePos(courseId: DBid): Step = stepDao - .getFirstCourseStep(courseId) + .firstCourseStep(courseId) ?.also { first -> updateLast(first) currentStepDao.update( @@ -137,8 +139,8 @@ class TheoryRepositoryImpl( }.devnull private fun updateKnown(step: Step): Unit = scope().launch { - when (step.data) { - is Input -> knownMaterialDao.insert( + if (step.data is Input) { + knownMaterialDao.insert( KnownMaterial( preferenceRepository.currentUserId, step.data.material.id diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt b/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt index c78823a7..578dcf10 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt @@ -4,32 +4,22 @@ import android.content.Context import androidx.fragment.app.Fragment import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.data.dsl.data +import com.github.braillesystems.learnbraille.data.entities.MarkerSymbol import com.github.braillesystems.learnbraille.data.entities.Symbol +import com.github.braillesystems.learnbraille.utils.contextNotNull import com.github.braillesystems.learnbraille.utils.lazyWithContext - -/** - * Do not change name of this property, it is used for prepopulation. - * - * Use `DslTest.kt` file as DSL tutorial. - * - * Text in steps is parsed as HTML. - * - * If using `content.symbols.getValue`, `content` should be added to `data` as `materials`. - * It is better to simply have only one value declared as `by materials`. - * - * Correctness of all information should be checked in compile time or in runtime. - * If you need some additional info, do not hardcode it. Just make request to the new DSL feature. - */ val prepopulationData by data( materials = content, - stepAnnotations = listOf( - StepAnnotation.golubinaBookRequired + stepAnnotationNames = listOf( + StepAnnotation.golubinaBookRequired, + StepAnnotation.slateStylusRequired ), knownMaterials = knownMaterials ) { users { + // Default user should always exist and be first in the list of users user( login = "default", name = "John Smith" @@ -37,7 +27,7 @@ val prepopulationData by data( } courses { - // This course should be always be first + // Dev course should be always be first course( name = "Test course for developers", description = "Small course for tests during development", @@ -55,40 +45,71 @@ val prepopulationData by data( } decks { - deck(DeckTags.all) { true } // This deck should always exist + // All cards deck should always exist and be first in the list + deck(DeckTags.all) { true } + + deck(DeckTags.allWithRus) { data -> + val isNative = data is Symbol + && data.type != SymbolType.greek + && data.type != SymbolType.latin + isNative || data !is Symbol + } deck(DeckTags.ruLetters) { data -> data is Symbol && data.type == SymbolType.ru } + deck(DeckTags.latinLetters) { data -> + data is Symbol && data.type == SymbolType.latin + } + deck(DeckTags.greekLetters) { data -> + data is Symbol && data.type == SymbolType.greek + } deck(DeckTags.special) { data -> data is Symbol && data.type == SymbolType.special } + deck(DeckTags.markers) { data -> + data is MarkerSymbol + } deck(DeckTags.digits) { data -> data is Symbol && data.type == SymbolType.digit } + deck(DeckTags.math) { data -> + data is Symbol && data.type == SymbolType.math + } } } object StepAnnotation { const val golubinaBookRequired = "golubina_book_required" + const val slateStylusRequired = "brl_slate_stylus_required" } object DeckTags { const val all = "all" + const val allWithRus = "all_with_rus" const val ruLetters = "ru_letters" + const val latinLetters = "latin_letters" + const val greekLetters = "greek_letters" const val digits = "digits" + const val markers = "markers" const val special = "special" + const val math = "math" } val Context.deckTagToName: Map by lazyWithContext { DeckTags.run { mapOf( all to getString(R.string.deck_name_all), + allWithRus to getString(R.string.deck_name_all_but_foreign), ruLetters to getString(R.string.deck_name_ru_letters), + latinLetters to getString(R.string.deck_name_latin_letters), + greekLetters to getString(R.string.deck_name_greek_letters), digits to getString(R.string.deck_name_digits), - special to getString(R.string.deck_name_special_symbols) + markers to getString(R.string.deck_name_markers), + special to getString(R.string.deck_name_punctuation), + math to getString(R.string.deck_name_math) ) } } val Fragment.deckTagToName - get() = context?.deckTagToName ?: error("Fragment should have context") + get() = contextNotNull.deckTagToName diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt b/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt index 3caca8af..d0c51b4e 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt @@ -1,10 +1,12 @@ package com.github.braillesystems.learnbraille.res +import com.github.braillesystems.learnbraille.data.dsl.InfoInterpolation import com.github.braillesystems.learnbraille.data.dsl.annotate import com.github.braillesystems.learnbraille.data.dsl.lessons 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.PunctuationSigns.Hyphen internal val golubinaIntroLessons by lessons { @@ -12,282 +14,329 @@ internal val golubinaIntroLessons by lessons { name = "Знакомство с шеститочием" ) { +FirstInfo( - """Перед Вами пошаговый курс для обучения с нуля системе Луи Брайля. -

- Курс построен на основе книги В. В. Голубиной "Пособие по изучению системы Л. Брайля". - В течение курса Вы ознакомитесь с обозначениями букв, цифр и специальных символов. - В некоторых шагах предлагается выполнять задания, используя книгу Голубиной. + """ + Перед Вами пошаговый курс для обучения с нуля системе Луи Брайля. Курс предназначен + для взрослых незрячих и детей старшего школьного возраста, недавно потерявших зрение, + а также для самостоятельного освоения рельефно-точечного шрифта родственниками + инвалидов по зрению, специалистами по реабилитации и студентами-тифлопедагогами. +
+ Курс построен на основе учебного пособия по изучению системы Л. Брайля. + Автор-составитель - тифлопедагог В. В. Голубина. Методическое консультирование + осуществлялось тифлопедагогом В. Н. Воробьёвой. +
+ В течение курса Вы ознакомитесь с обозначениями букв (русских, латинских и греческих), + цифр, знаков препинания и специальных символов (цифровой знак и другие). + В некоторых шагах предлагается выполнять задания, используя указанное пособие. Если Вам будет непонятно, как пользоваться курсом, Вы в любой момент можете вызвать справку по разделу, нажав кнопку "справка" в правом верхнем углу экрана. """ ) - +Info( + +Info(InfoInterpolation.run { """ - Урок 1. Тренировка чтения и ввода отдельных комбинаций. + Урок $iLesson. Тренировка чтения и ввода отдельных комбинаций.

- В азбуке Брайля любой символ - это шеститочие. + В азбуке Брайля основой любого символа является шеститочие, в котором точки + расположены в два столбца по три точки. Каждая точка имеет определённый порядковый + номер. +
+ При письме точки в правом столбце имеют номера 1, 2, 3 сверху вниз, в левом - + 4, 5, 6 сверху вниз. При чтении лист переворачивается, и точки 1, 2, 3 оказываются слева.
- Точки расположены в два столбца по три. -
- Точки в первом столбце имеют номера 1, 2, 3 сверху вниз. - Во втором столбце - 4, 5, 6 сверху вниз. + В приложении при демонстрации символов рядом с точками 1, 2, 3 мы ставим вертикальную черту. + Если символ выведен "как при чтении", эта черта слева от шеститочия, если "как при письме", то справа. """ + }) + +ShowDots( + text = "Перед Вами полное шеститочие:
точки 1, 2, 3, 4, 5, 6", + brailleDots = BrailleDots(F, F, F, F, F, F) ) + +InputDots(text = "Введите все шесть точек", brailleDots = BrailleDots(F, F, F, F, F, F)) +ShowDots( - text = "Перед Вами символ Брайля -
" + + text = "Перед Вами символ шрифта Брайля -
" + " точка номер один", - dots = BrailleDots(F, E, E, E, E, E) + brailleDots = BrailleDots(F, E, E, E, E, E) ) - +InputDots( - text = "Введите точку один", - dots = BrailleDots(F, E, E, E, E, E) + +InputDots(text = "Введите точку один", brailleDots = BrailleDots(F, E, E, E, E, E)) + +Info( + """ + В шаге с демонстрацией столбец с точками 1, 2, 3 находится слева, + а в шаге с вводом - справа, потому что буквы пишутся на обороте листа, а для чтения + лист переворачивается. +
+ Впрочем, порядок столбцов в этом приложении можно изменить кнопкой "Режим письмо/чтение" + справа на экране над кнопкой "Вперёд". +
+ В настройках можно отключить "При вводе столбец с точками 1, 2, 3 справа". Тогда + при заходе в любой шаг точки 1, 2, 3 будут слева, но это по-прежнему можно будет изменить, + нажав кнопку "Режим письмо/чтение". + """ ) +ShowDots( - text = "Точка номер два", - dots = BrailleDots(E, F, E, E, E, E) - ) - +InputDots( - text = "Введите точку два", - dots = BrailleDots(E, F, E, E, E, E) + text = "Перед Вами символ шрифта Брайля -
" + + " точка номер три", + brailleDots = BrailleDots(E, E, F, E, E, E) ) + +InputDots(text = "Введите точку три", brailleDots = BrailleDots(E, E, F, E, E, E)) +ShowDots( - text = "Комбинация точек 1 и 2", - dots = BrailleDots(F, F, E, E, E, E) - ) - +InputDots( - text = "Введите комбинацию: точки 1 и 2", - dots = BrailleDots(F, F, E, E, E, E) + text = "Перед Вами символ шрифта Брайля -
" + + " точка номер пять", + brailleDots = BrailleDots(E, E, E, E, F, E) ) + +InputDots(text = "Введите точку пять", brailleDots = BrailleDots(E, E, E, E, F, E)) +InputDots( - text = "Введите комбинацию точек 1 и 3", - dots = BrailleDots(F, E, F, E, E, E) + text = """Теперь выполним пять практических заданий без обучающей картинки. +
+ Введите комбинацию точек 1 и 3""", + brailleDots = BrailleDots(F, E, F, E, E, E) ) +InputDots( - text = "Введите комбинацию точек 1, 3 и 4", - dots = BrailleDots(F, E, F, F, E, E) + text = "Введите комбинацию: точки 4 и 6", + brailleDots = BrailleDots(E, E, E, F, E, F) ) +InputDots( - // repeat = 3 - text = "Введите комбинацию точек 1, 3 и 6", - dots = BrailleDots(F, E, F, E, E, F) + text = "Введите комбинацию точек 1, 3, 4 и 6", + brailleDots = BrailleDots(F, E, F, F, E, F) ) +InputDots( - // repeat = 3 - text = "Введите комбинацию точек 1, 5 и 6", - dots = BrailleDots(F, E, E, E, F, F) - ) - +InputDots( - // repeat = 3 - text = "Введите комбинацию точек 2, 3, 4 и 5", - dots = BrailleDots(E, F, F, F, F, E) - ) - +ShowDots( - text = "Перед Вами полное шеститочие:
" + - "точки 1, 2, 3, 4, 5, 6", - dots = BrailleDots(F, F, F, F, F, F) + text = "Введите комбинацию точек 1, 2, 3, 4 и 5", + brailleDots = BrailleDots(F, F, F, F, F, E) ) +InputDots( - // repeat = 3 - text = "Введите все шесть точек", - dots = BrailleDots(F, F, F, F, F, F) + text = "Введите комбинацию точек 1, 2, 3, 4 и 6", + brailleDots = BrailleDots(F, F, F, F, E, F) ) +Info( - """Откройте букварь на странице 12. - В верхней строке 14 раз повторён символ полного шеститочия.""" + """Откройте пособие на странице 12. + В верхней строке 14 раз повторён символ полного шеститочия. +
+ Начиная со второй строки - тренировочные упражнения. + Самостоятельно потренируйтесь читать отдельные комбинации точек.""" ).annotate(StepAnnotation.golubinaBookRequired) - +Info( - """Первый урок закончен. В следующем уроке мы изучим буквы А, Б, Ц, Д, Е.""" - ) + +InputDots( + text = """Введите "средние точки" 2 и 5""", + brailleDots = BrailleDots(E, F, E, E, F, E) + ) + +Info( + """Запишите "средние точки" 2 и 5 на брайлевском приборе. +
+ Совет: если у Вас нет возможности писать на брайлевском приборе, в разделе "Настройки" можно отключить + шаги, требующие обращения к брайлевскому прибору. + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """Урок $iLesson закончен. В следующем уроке мы изучим буквы А, Б, Ц, Д, Е.""" + }) } lesson( name = "Русские буквы А, Б, Ц, Д, Е" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 2. Буквы А, Б, Ц, Д, Е. + Урок $iLesson. Буквы А, Б, Ц, Д, Е.

- Некоторые символы Брайля обозначают как букву, так и цифру. Это буквы А, Б, - Ц и так далее. С добавлением цифрового знака, который мы изучим в следующем - уроке, из них получаются цифры 1, 2, 3 и так далее.""" - ) + В нашем курсе буквы изучаются не в порядке русского алфавита, а в порядке + "ключа Брайля": сперва те, что состоят из двух верхних рядов точек, затем + остальные. Этот порядок пришёл к нам из латинского алфавита Луи Брайля. """ + }) +Info( """Буква А обозначается одной точкой, точкой номер один. Ознакомьтесь с ней в следующем шаге.""" ) +Show(content.symbols.getValue('А')) +Info( - """Откройте букварь на странице 13. Вверху слева рельефно-графическое + """Откройте пособие на странице 13. Вверху слева рельефно-графическое изображение буквы А. Рядом после полного шеститочия пять раз повторена буква А точечным шрифтом.""" ).annotate(StepAnnotation.golubinaBookRequired) - +Input(content.symbols.getValue('А')/*, repeat = 5*/) + +Input(content.symbols.getValue('А')) + slateStylusLine('А') +Show(content.symbols.getValue('Б')) +Info( - """Снова изучим страницу 13 в букваре. Под строкой с буквой А - + """Снова изучим страницу 13 в пособии. Под строкой с буквой А - такая же с буквой Б.""" ).annotate(StepAnnotation.golubinaBookRequired) +Input(content.symbols.getValue('Б')) + slateStylusLine('Б') +Info( """ Буква Ц: точки 1 и 4

- Ознакомьтесь с буквой Ц на странице 13 букваря. + Ознакомьтесь с буквой Ц на странице 13 пособия. Строка с буквой Ц находится под строкой с буквой Б.""" ).annotate(StepAnnotation.golubinaBookRequired) +Show(content.symbols.getValue('Ц')) - +Input(content.symbols.getValue('Ц')/*, repeat = 5*/) + +Input(content.symbols.getValue('Ц')) +Show(content.symbols.getValue('Д')) - +Input(content.symbols.getValue('Д')/*, repeat = 5*/) + +Input(content.symbols.getValue('Д')) +Info( """Последняя буква в этом уроке - буква Е. Она обозначается двумя точками: номер 1 и 5.""" ) +Show(content.symbols.getValue('Е')) - +Input(content.symbols.getValue('Е')/*, repeat = 5*/) + +Input(content.symbols.getValue('Е')) + +Info("""В следующих трёх шагах нужно ввести буквы Б, А, Ц (вместе это слово БАЦ).""") + inputChars("БАЦ") + +Info("""Теперь наберите в следующих трёх шагах, вводя букву за буквой, слово ДЕД.""") + inputChars("ДЕД") + +Info("""Далее в следующих четырёх шагах введите слово БАБА.""") + inputChars("БАБА") +Info( - """Урок 2 закончен. В следующем занятии займёмся повторением букв А, Б, Ц, Д, Е - и выучим буквы Ф, Г. Рекомендуем в дополнение к уроку потренироваться - вводить изученные буквы в разделе "Практика".""" - ) + """Запишите на брайлевском приборе три строки: из букв Ц, Д и Е. +
+ После этого запишите через пробел слова: БАЦ, ДЕД, БАБА. + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """Урок $iLesson закончен. В следующем занятии займёмся повторением букв А, Б, Ц, Д, Е + и потренируемся писать цифры от 1 до 5, образуемые с помощью этих букв. Рекомендуем + в дополнение к уроку потренироваться вводить изученные буквы в разделе "Практика".""" + }) } + lesson( - name = "Буквы Ф, Г" + name = "Цифры от 1 до 5" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 3. Буквы Ф, Г. + Урок $iLesson. Цифры от 1 до 5.

- Перед прохождением нового материала повторим пройденное. - В следующих трёх шагах нужно ввести буквы Б, А, Ц (вместе это слово БАЦ).""" - ) - inputChars("БАЦ") + В этом уроке мы начнём изучать цифры. Но для начала повторим пройденное. + В следующих шагах введите буквы, изученные в прошлом уроке.""" + }) + inputChars("АБЦДЕ") +Info( """ - Теперь познакомимся с буквами Ф и Г. -
- Буква Ф обозначается точками 1, 2 и 4. + Переходим к изучению цифр.
- Буква Г - точки 1, 2, 4 и 5. + Запомним, 10 символов первой строки «Ключа Брайля» обозначают 10 латинских и + русских букв, а также арабские цифры.
- В следующих шагах нужно ознакомиться с буквами и ввести их.""" + Для того, чтобы букву преобразовать в цифру, перед ней ставится + «Цифровой знак». Он обозначается четырьмя точками: номер 3, 4, 5 и 6. + """ ) - +Show(content.symbols.getValue('Ф')) + +Show(content.markers.getValue(MarkerType.NumberSign)) +Info( """ - Откройте страницу 15 в букваре. Вверху страницы найдите рельефно-графическое - изображение буквы Ф и рядом букву Ф, пять раз написанную релефно-точечным шрифтом - """ + В пособии на странице 14, после страницы с буквами от А до Е - цифровой знак. + Найдите четвёртую сверху строчку. В ней цифровой знак повторён пять раз. + """ ).annotate(StepAnnotation.golubinaBookRequired) - +Input(content.symbols.getValue('Ф')/*, repeat = 5*/) - +Show(content.symbols.getValue('Г')) + +Input(content.markers.getValue(MarkerType.NumberSign)) +Info( """ - В букваре под буквой Ф - буква Г. Изучите её графическое представление - и точечный состав. + В следующих шагах изучим цифры 1, 2, 3, 4, 5. + Они получаются из букв А, Б, Ц, Д, Е добавлением цифрового знака. + Например, цифра 3, как и число 3 - это цифровой знак + Ц. + Число двадцать четыре - это цифровой знак, затем буквы Б и Д. + В уроках мы для краткости не будем всякий раз ставить цифровой знак.""" + ) + showAndInputChars("12345") + +Info( + """ + На пятой сверху строчке на странице 14 пособия, под строкой с цифровым знаком, + написаны цифры от одного до пяти. + Изучите каждую цифру. """ ).annotate(StepAnnotation.golubinaBookRequired) - +Input(content.symbols.getValue('Г')/*, repeat = 5*/) +Info( """ - В следующих трёх шагах введите по буквам слово БЕГ.""" - ) - inputChars("БЕГ") - +Info( - """Поздравляем! Третий урок пройден. В следующем занятии мы узнаем, - как с помощью букв А, Б, Ц, Д, Е, Ф, Г составить цифры 1, 2, 3, 4, 5 и 6.""" - ) + Запишите, используя брайлевский прибор, цифровой знак и за ним изученные пять цифр. + Получится число "двенадцать тысяч триста сорок пять". + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """ + Урок $iLesson пройден! Рекомендуем самостоятельно изучить цифры и числа на странице 15 + в пособии (внизу страницы). +
+ Следующий урок будет посвящён буквам Ф, Г и цифрам 6, 7.""" + }) } lesson( - name = "Цифры от 1 до 6" + name = "Буквы Ф, Г и цифры 6, 7" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 4. Цифры от 1 до 6. + Урок $iLesson. Буквы Ф, Г и цифры 6, 7.

- В этом уроке мы начнём изучать цифры. Но для начала повторим пройденное. - В следующих шагах введите буквы, изученные в прошлых двух уроках.""" - ) - inputChars("АБЦДЕФГ") + Перед прохождением нового материала повторим пройденное. + В следующих пяти шагах нужно ввести буквы Б, E, Д, А (вместе это слово БЕДА).""" + }) + inputChars("БЕДА") +Info( """ - Переходим к изучению цифр. + Теперь познакомимся с буквами Ф и Г.
- Чтобы отличить цифры от букв, перед числами и цифрами ставится цифровой знак. - Этот символ обозначается точками 3, 4, 5, 6. - Когда нужно обозначить цифровой знак в обычном, плоскопечатном письме, - как правило, пользуются символом правой квадратной скобки: ]""" + Буква Ф обозначается точками 1, 2 и 4. +
+ Буква Г - точки 1, 2, 4 и 5. +
+ В следующих шагах нужно ознакомиться с буквами и ввести их.""" ) - +Show(content.symbols.getValue(']')) + +Show(content.symbols.getValue('Ф')) +Info( """ - В букваре на странице 14, перед страницей с буквами Ф и Г - цифровой знак. - Найдите четвёртую сверху строчку. В ней цифровой знак повторён пять раз. - """ + Откройте страницу 15 в пособии. Вверху страницы найдите рельефно-графическое + изображение буквы Ф и рядом букву Ф, пять раз написанную рельефно-точечным шрифтом. + """ ).annotate(StepAnnotation.golubinaBookRequired) - +Input(content.symbols.getValue(']')/*, repeat = 3*/) + +Input(content.symbols.getValue('Ф')) + slateStylusLine('Ф') + +Show(content.symbols.getValue('Г')) +Info( """ - В следующих шагах изучим цифры 1, 2, 3, 4, 5, 6, 7. - Они получаются из букв А, Б, Ц, Д, Е, Ф, Г добавлением цифрового знака. - Например, цифра и число 3 - это цифровой знак + Ц. - Число двадцать семь - это цифровой знак, затем буквы Б и Г. - В уроках мы для краткости не будем всякий раз ставить цифровой знак.""" - ) - +Show(content.symbols.getValue('1')) - +Input(content.symbols.getValue('1')) - +Show(content.symbols.getValue('2')) - +Input(content.symbols.getValue('2')) - +Show(content.symbols.getValue('3')) - +Input(content.symbols.getValue('3')) - +Show(content.symbols.getValue('4')) - +Input(content.symbols.getValue('4')) - +Show(content.symbols.getValue('5')) - +Input(content.symbols.getValue('5')) - +Show(content.symbols.getValue('6')) - +Input(content.symbols.getValue('6')) - +Show(content.symbols.getValue('7')) - +Input(content.symbols.getValue('7')) - +Info( - """ - На пятой сверху строчке на странице 14 букваря, под строкой с цифровым знаком, - написаны цифры от одного до пяти. - Изучите каждую цифру. + В пособии под буквой Ф - буква Г. Изучите её графическое представление + и точечный состав. """ ).annotate(StepAnnotation.golubinaBookRequired) + +Input(content.symbols.getValue('Г')) + slateStylusLine('Г') +Info( """ - Урок 4 пройден! Рекомендуем самостоятельно изучить цифры и числа на странице 15 - в букваре (внизу страницы). + В следующих трёх шагах введите по буквам слово БЕГ.""" + ) + inputChars("БЕГ") + +Info( + """ + Цифровой знак и буква Ф за ним обозначают арабскую цифру 6 или число 6. +
+ Цифровой знак и буква Г за ним - это семёрка.
- Следующий урок будет посвящён повторению, а также мы изучим - букву Х и цифру 8.""" + В следующих трёх шагах введите число ШЕСТЬДЕСЯТ СЕМЬ.""" ) + +Input(content.markers.getValue(MarkerType.NumberSign)) + inputChars("67") + +Info( + """Напишите слово БЕГ и число "ШЕСТДЕСЯТ СЕМЬ" при помощи брайлевского прибора.""" + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """Поздравляем! Урок $iLesson пройден.""" + }) } lesson( name = "Буква Х и цифра 8" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 5. Буква Х и цифра 8. + Урок $iLesson. Буква Х и цифра 8.

По окончании этого урока Вы узнаете букву Х и цифру 8. - Но перед изучением нового повторим пройденное. Введите по буквам слово ФЕБ. """ - ) + Но перед изучением нового повторим пройденное. Введите по буквам слово ФЕБ.""" + }) inputChars("ФЕБ") - +Info( - """ - В следующих шести шагах введите по буквам слово БАГДАД.""" - ) + +Info("""В следующих шести шагах введите по буквам слово БАГДАД.""") inputChars("БАГДАД") +Info( """Наберите в следующих четырёх шагах число СТО ДВАДЦАТЬ ТРИ, поставив перед ним цифровой знак. """ ) - inputChars("]123") + +Input(content.markers.getValue(MarkerType.NumberSign)) + inputChars("123") +Info( """ Буква Х обозначается точками 1, 2 и 5. @@ -299,44 +348,37 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Х')) +Info( """ - Откройте страницу 16 в букваре Голубиной. В верхней части страницы найдите + Откройте страницу 16 в пособии Голубиной. В верхней части страницы найдите рельефно-графическую букву Х и рядом строку из пяти брайлевских букв Х. """ ).annotate(StepAnnotation.golubinaBookRequired) +Input(content.symbols.getValue('Х')) +Input(content.symbols.getValue('8')) - +Info( - """ - В следующих шагах введите по буквам слово ЦЕХ - """ - ) + slateStylusLine('Х') + +Info("""В следующих шагах введите по буквам слово ЦЕХ""") inputChars("ЦЕХ") + +Info("""Наберите восклицание АХ""") + inputChars("АХ") + +Info("""Далее введите, поставив цифровой знак, число "ВОСЕМЬСОТ СЕМЬДЕСЯТ ШЕСТЬ" """) + +Input(content.markers.getValue(MarkerType.NumberSign)) + inputChars("876") +Info( + """Введите на брайлевском приборе слова ЦЕХ, АХ и число ВОСЕМЬСОТ СЕМЬДЕСЯТ ШЕСТЬ.""" + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { """ - Наберите слово БАЦ - """ - ) - inputChars("БАЦ") - +Info( - """ - Далее введите, поставив цифровой знак, число "Восемьсот семдесят шесть" - """ - ) - inputChars("]876") - +Info( - """ - Урок 5 пройден! + Урок $iLesson пройден!
На следующем занятии мы освоим буквы И и Ж, а также цифры 9 и 0""" - ) + }) } lesson( name = "Буквы Ж, И и цифры 9, 0" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 6. Буквы Ж, И и цифры 9, 0 + Урок $iLesson. Буквы Ж, И и цифры 9, 0

Буква И - это точки 2 и 4. Этот же символ с добавлением цифрового знака обозначает цифру 9. @@ -344,13 +386,15 @@ internal val golubinaIntroLessons by lessons { Буква Ж - это точки 2, 4 и 5. Цифровой знак и буква Ж образуют цифру 0. """ - ) + }) +Info( """ - Раскройте букварь на странице 17. В верхней части листа найдите рельефно-графические + Раскройте пособие на странице 17. В верхней части листа найдите рельефно-графические и рельефно-точечные изображения букв Ж и И. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('Ж') + slateStylusLine('И') +Info( """Введите буквы, составляющие слово БАГАЖ""" ) @@ -371,22 +415,30 @@ internal val golubinaIntroLessons by lessons { +Info( """Введите по символам число 850""" ) - inputChars("]850") + +Input(content.markers.getValue(MarkerType.NumberSign)) + inputChars("850") +Info( """ - Урок 6 закончен. Теперь Вы, помимо букв, знаете все цифры. - Примеры слов и чисел к этому уроку можно найти в букваре внизу страницы 17. + Напишите с помощью брайлевского прибора слова, набранные до этого на экране: +
+ БАГДАД, ЖАЖДА, ГИД, ИЖИЦА и число ВОСЕМЬСОТ ПЯТЬДЕСЯТ. + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """ + Урок $iLesson закончен. Теперь Вы, помимо букв, знаете все цифры. + Примеры слов и чисел к этому уроку можно найти в пособии внизу страницы 17.
Следующий урок посвящён буквам К, Л и М.""" - ) + }) } lesson( name = "Буквы К, Л, М" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 7. Буквы К, Л, М + Урок $iLesson. Буквы К, Л, М

До сих пор мы изучали только буквы, образованные верхними четырьмя точками, то есть точками 1, 2, 4, 5. В нескольких следующих уроках будут рассмотрены @@ -394,7 +446,7 @@ internal val golubinaIntroLessons by lessons {
Перед занятием повторим пройденное. Введите по буквам слово ЕЖИХА. """ - ) + }) inputChars("ЕЖИХА") +Info( """ @@ -411,10 +463,12 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Л')) +Info( """ - Сейчас предлагаем Вам найти страницу 19 в букваре, где вверху изображена зрячая + Сейчас предлагаем Вам найти страницу 19 в пособии, где вверху изображена рельефная буква К и рядом с ней строка из пяти брайлевских букв К. Ниже находится буква Л. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('К') + slateStylusLine('Л') +Info( """Введите по буквам слово КЛАД""" ) @@ -436,10 +490,11 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('М')) +Info( """ - На той же странице 19 в букваре найдите и прочтите строку с буквой М + На той же странице 19 в пособии найдите и прочтите строку с буквой М под строками с буквами К, Л. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('М') +Info( """В следующих трёх шагах ведите по буквам слово МЕЛ""" ) @@ -454,25 +509,32 @@ internal val golubinaIntroLessons by lessons { inputChars("ФИАЛКА") +Info( """ - Мы подошли к концу урока 7. + Напишите на брайлевском приборе пройденные в уроке слова: +
+ КЛАД, БЕЛКА, МЕЛ, МАК, ФИАЛКА. + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """ + Мы подошли к концу урока $iLesson.
- Буквам К, Л, М посвящены страницы 19 и 20 букваря; желательно + Буквам К, Л, М посвящены страницы 19 и 20 пособия; желательно повторить материал, прочитав слова и числа на этих страницах.
На следующем занятии мы узнаем, как обозначается буква Н.""" - ) + }) } lesson( name = "Буква Н" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 8. Буква Н. + Урок $iLesson. Буква Н.

Начнём занятие с повторения. Введите по буквам слово КАМБАЛА. """ - ) + }) inputChars("КАМБАЛА") +Info( """ @@ -487,10 +549,11 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Н')) +Info( """ - Раскройте страницу 21 букваря и найдите вверху букву Н. + Раскройте страницу 21 пособия и найдите вверху букву Н. """ ).annotate(StepAnnotation.golubinaBookRequired) +Input(content.symbols.getValue('Н')) + slateStylusLine('Н') +Info( """Введите по буквам слово БАНАН""" ) @@ -505,24 +568,31 @@ internal val golubinaIntroLessons by lessons { inputChars("БЛАНК") +Info( """ - На этом урок 8 завершается. Для закрепления материала полезно прочесть - текст на страницах 21 и 22 в букваре. + С помощью брайлевского прибора запишите введённые ранее слова: +
+ БАНАН, ЦЕНА, БЛАНК. + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """ + На этом урок $iLesson завершается. Для закрепления материала полезно прочесть + текст на страницах 21 и 22 в пособии.
В следующий раз изучим букву О и знак препинания ЗАПЯТАЯ. """ - ) + }) } lesson( name = "Буква О и знак препинания 'Запятая'" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 9. Буква О и знак препинания "Запятая". + Урок $iLesson. Буква О и знак препинания "Запятая".

Как обычно, в начале занятия повторим пройденное. Введите по буквам слово НАДЕЖДА. """ - ) + }) inputChars("НАДЕЖДА") +Info( """ @@ -535,9 +605,10 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('О')) +Info( """ - Возьмите букварь и откройте страницу 23. Сверху найдите букву О. + Возьмите пособие и откройте страницу 23. Сверху найдите букву О. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('О') +Info( """Наберите по буквам слово ОБЛАКО""" @@ -565,22 +636,32 @@ internal val golubinaIntroLessons by lessons { ) inputChars("ГЕОЛОГ,") +Info( - """Урок 9 подходит к концу. В следующем уроке нас ждёт изучение буквы П. - Материалы к этому занятию можно прочесть на страницах 23-24 в букваре. """ - ) + Используя брайлевский прибор, запишите фразу: +
+ БЕЛОЕ ОБЛАКО, БЛЕДНОЕ ЛИЦО, БЕЛОЕ МОЛОКО +
+ разделяя перечисляемые элементы запятыми. Затем откройте страницу 24 пособия, + где записана эта фраза, и проверьте себя. + """ + ).annotate(StepAnnotation.golubinaBookRequired, StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """Урок $iLesson подходит к концу. В следующем уроке нас ждёт изучение буквы П. + Материалы к этому занятию можно прочесть на страницах 23-24 в пособии. + """ + }) } lesson( name = "Буква П" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 10. Буква П. + Урок $iLesson. Буква П.

В виде разминки перед уроком повторим изученные ранее символы. Введите по буквам слово МОЛОКО и поставьте после него запятую. """ - ) + }) inputChars("МОЛОКО,") +Info( """ @@ -593,9 +674,10 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('П')) +Info( """ - Найдём букву П в букваре. Для этого раскройте страницу 25 и изучите верхнюю строку. + Найдём букву П в пособии. Для этого раскройте страницу 25 и изучите верхнюю строку. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('П') +Info( """Введите отдельными символами слово ПЕНА""" ) @@ -612,30 +694,31 @@ internal val golubinaIntroLessons by lessons { """И последнее в этом уроке: введите по буквам слово КАПКАН""" ) inputChars("КАПКАН") - +Info( + +Info(InfoInterpolation.run { """ - Вот и пройден десятый урок. Следующее занятие отведено для изучения буквы Ч. + Вот и пройден урок $iLesson. Следующее занятие отведено для изучения буквы Ч. Изученный урок полезно закрепить, прочтя слова на странице 25 в книге Голубиной. """ - ) + }) } lesson( name = "Буква Ч" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 11. Буква Ч. + Урок $iLesson. Буква Ч.

Перед началом работы немного повторим прошлые уроки. Введите по буквам слово ХЛОПОК. """ - ) + }) inputChars("ХЛОПОК") +Info( """Введите отдельными символами число 215, поставив перед ним цифровой знак""" ) - inputChars("]215") + +Input(content.markers.getValue(MarkerType.NumberSign)) + inputChars("215") +Info( """ @@ -669,24 +752,31 @@ internal val golubinaIntroLessons by lessons { inputChars("БОЧОНОК") +Info( """ - Урок 11, в котором мы изучали букву 'Ч', позади. + Запишите на брайлевском приборе изученные слова, разделяя их запятыми: +
+ ЧЕК, ОЧКИ, КОЧАН, БОЧОНОК + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """ + Урок $iLesson, в котором мы изучали букву 'Ч', позади. А в следующий раз мы будем заниматься буквой Р. """ - ) + }) } lesson( name = "Буква Р" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 12. Буква Р. + Урок $iLesson. Буква Р.

- Этому уроку соответствует страницы 27 и 28 букваря. + Этому уроку соответствует страницы 27 и 28 пособия.
Перед стартом повторим материал прошлых уроков. Введите по символам слово ПЧЕЛА. """ - ) + }) inputChars("ПЧЕЛА") +Info( """ @@ -699,9 +789,10 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Р')) +Info( """ - Раскройте страницу 27 в букваре и ознакомьтесь с бувой Р вверху листа. + Раскройте страницу 27 в пособии и ознакомьтесь с буквой Р вверху листа. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('Р') +Info( """Наберите последовательно, по буквам слово РЕКА""" ) @@ -724,22 +815,29 @@ internal val golubinaIntroLessons by lessons { inputChars("ГАРАЖ") +Info( """ - Урок 12 завершён. Теперь мы, помимо прочего, знаем точечный состав буквы 'Р'. + Возьмите брайлевский прибор и запишите через запятую пройденные слова: +
+ РЕКА, КРАН, КРЕМ, ФАРА, ГАРАЖ + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """ + Урок $iLesson завершён. Теперь мы, помимо прочего, знаем точечный состав буквы 'Р'. За этим уроком следует занятие, посвящённое букве С. """ - ) + }) } lesson( name = "Буква С" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 13. Буква С. + Урок $iLesson. Буква С.

Перед уроком вспомним старый материал. Введите по буквам слово МАРАФОН """ - ) + }) inputChars("МАРАФОН") +Info( """ @@ -753,9 +851,10 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('С')) +Info( """ - Перейдите в букваре Голубиной к странице 29 и прочтите сверху строку с буквой С. + Перейдите в пособии Голубиной к странице 29 и прочтите сверху строку с буквой С. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('С') +Info( """В следующих шагах наберите слово САЖА""" ) @@ -778,27 +877,35 @@ internal val golubinaIntroLessons by lessons { inputChars("ЧЕСНОК") +Info( """ - Мы подошли к финалу урока 13. К этому моменту мы уже изучили 19 букв, + Осталось немного поработать с брайлевским прибором. Запишите на нём слова: +
+ САЖА, СЛЕД, ФАСАД, ДОСКА, ЧЕСНОК + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """ + Мы подошли к финалу урока $iLesson. К этому моменту мы уже изучили 19 букв, знак препинания 'ЗАПЯТАЯ' и цифры. В следующем уроке изучим букву Т. """ - ) + }) } lesson( name = "Буква Т и знак 'Дефис'" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 14. Буква Т и знак 'Дефис'. + Урок $iLesson. Буква Т и знак 'Дефис'.

Как обычно, предварим урок повторением пройденного. Введите символы, составляющие слово СОЛНЦЕ """ - ) + }) inputChars("СОЛНЦЕ") +Info( """Теперь наберите число 870, сначала поставив цифровой знак""" ) - inputChars("]870") + +Input(content.markers.getValue(MarkerType.NumberSign)) + inputChars("870") +Info( """ Теперь рассмотрим букву 'Т'. @@ -811,7 +918,7 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Т')) +Info( """ - В букваре В. В. Голубиной найдите букву Т на странице 30 + В пособии В. В. Голубиной найдите букву Т на странице 30 """ ).annotate(StepAnnotation.golubinaBookRequired) +Info( @@ -828,24 +935,31 @@ internal val golubinaIntroLessons by lessons { Прямо сейчас перейдём к изучению дефиса.
Знак 'Дефис' обозначается точками 3 и 6. Не нужно путать его с буквой Ц: - точки буквы Ц - в верхней трети шеститочия, точки дефиса - в самой нижней. + точки буквы Ц — в верхней трети шеститочия, точки дефиса — в самой нижней. """ ) - +Show(content.symbols.getValue('-')) + +Show(content.symbols.getValue(Hyphen.c)) +Info( """ - Переверните страницу в букваре. На странице 31 найдите по центру знак "дефис", + Переверните страницу в пособии. На странице 31 найдите по центру знак "дефис", повторённый пять раз. """ ).annotate(StepAnnotation.golubinaBookRequired) +Info( """Введите по символам слово ПОЛ-ЛИМОНА""" ) - inputChars("ПОЛ-ЛИМОНА") + inputChars("ПОЛ${Hyphen.c}ЛИМОНА") +Info( - """И последнее слово, которое нужно ввести в этом уроке - местоимение ГДЕ-ЛИБО""" + """И последнее слово, которое нужно ввести в этом уроке — местоимение ГДЕ-ЛИБО""" ) - inputChars("ГДЕ-ЛИБО") + inputChars("ГДЕ${Hyphen.c}ЛИБО") + +Info( + """ + Теперь рекомендуем Вам записать на брайлевском приборе изученные слова: +
+ ПОЛ—ЛИМОНА, ГДЕ—ЛИБО + """ + ).annotate(StepAnnotation.slateStylusRequired) +Info( """ На этом урок 14 закончен. Буква Т, которой он был посвящён, стала двадцатой @@ -857,14 +971,14 @@ internal val golubinaIntroLessons by lessons { lesson( name = "Буква У" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 15. Буква У. + Урок $iLesson. Буква У.

Вспомним для начала материал прошлых уроков. На следующих этапах введите по буквам слово ТЕЛЕФОН """ - ) + }) inputChars("ТЕЛЕФОН") +Info( """ @@ -881,6 +995,7 @@ internal val golubinaIntroLessons by lessons { Откройте эту страницу и прочтите верхнюю строку. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('У') +Info( """В следующих шагах введите, пожалуйста, слово УГОЛ""" ) @@ -898,28 +1013,35 @@ internal val golubinaIntroLessons by lessons { ) inputChars("ТРУД") +Info( - """Последнее, что нужно сделать в этом уроке - введите слово ЧУДЕСА""" + """Последнее, что нужно набрать в этом уроке — слово ЧУДЕСА""" ) inputChars("ЧУДЕСА") +Info( """ - Урок 15 окончен. Мы изучили букву У - первую букву, где нам встретилась точка + Набранные в этом уроке слова запишем и на брайлевском приборе через запятую: +
+ УГОЛ, ЛУНА, КРУГ, ТРУД, ЧУДЕСА + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """ + Урок $iLesson окончен. Мы изучили букву У — первую букву, где нам встретилась точка номер 6 (но не первый символ: ранее мы сталкивались с ним, изучая цифровой знак и знак 'Дефис'). В следующем занятии нас ждёт буква Щ. """ - ) + }) } lesson( name = "Буква Щ" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 16. Буква Щ. + Урок $iLesson. Буква Щ.

В виде разминки повторим ранее изученное. Введите буквы, которые сложатся в слово ХУДОЖНИК """ - ) + }) inputChars("ХУДОЖНИК") +Info( """ @@ -930,9 +1052,10 @@ internal val golubinaIntroLessons by lessons { """ ) +Show(content.symbols.getValue('Щ')) + slateStylusLine('Щ') +Info( """ - Букварь содержит урок с буквой Щ на странице 33. + Пособие содержит урок с буквой Щ на странице 33.
Найдите эту страницу и изучите самую верхнюю строку. """ @@ -954,27 +1077,34 @@ internal val golubinaIntroLessons by lessons { ) inputChars("ЩЕПКА") +Info( - """Перед тем, как мы закончим занятие, введите слово ОЩУЩЕНИЕ""" + """И последнее: введите слово ОЩУЩЕНИЕ""" ) inputChars("ОЩУЩЕНИЕ") +Info( """ - Урок 16 - буква Щ - подходит к завершению. Теперь нам известны 22 буквы русского + Перед тем, как мы закончим занятие, запишите на брайлевском приборе все пройденные слова: +
+ ЩЕГОЛ, ПЛАЩ, КЛЕЩ, ЩЕПКА, ОЩУЩЕНИЕ + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """ + Урок $iLesson - буква Щ - подходит к завершению. Теперь нам известны 22 буквы русского алфавита. Следующее занятие будет на тему буквы З. """ - ) + }) } lesson( name = "Буква З" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 17. Буква З. + Урок $iLesson. Буква З.

Новый материал мы будем изучать после краткого повторения. Далее введите буквы слова ЩЕПОТКА """ - ) + }) inputChars("ЩЕПОТКА") +Info( """ @@ -991,6 +1121,7 @@ internal val golubinaIntroLessons by lessons { Прочтите первую строку на этой странице. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('З') +Info( """Последовательно, по буквам введите слово ЗАКАЗ""" ) @@ -1008,32 +1139,40 @@ internal val golubinaIntroLessons by lessons { ) inputChars("АРБУЗ") +Info( - """И последнее, что сегодня осталось сделать: введите слово ЗАДАЧА""" + """И последнее, что сегодня осталось ввести: слово ЗАДАЧА""" ) inputChars("ЗАДАЧА") +Info( + """ + Для закрепления материала напишите слова на брайлевском приборе: +
+ ЗАКАЗ, ЗЕРНО, АЗБУКА, АРБУЗ, ЗАДАЧА + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { """ - Вы успешно окончили урок 17 - буква З. На следующем занятии перейдём к освоению + Вы успешно окончили урок $iLesson - буква З. На следующем занятии перейдём к освоению буквы И краткое. """ - ) + }) } lesson( name = "Буква Й" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 18. Буква "И краткое". + Урок $iLesson. Буква "И краткое".

Знакомство с новой буквой начнём после небольшой ревизии ранее пройденного. Пожалуйста, введите буквы, образующие слово МИМОЗА """ - ) + }) inputChars("МИМОЗА") +Info( """Также в качестве повторения введите цифровой знак, а за ним число 964""" ) - inputChars("]964") + +Input(content.markers.getValue(MarkerType.NumberSign)) + inputChars("964") +Info( """ Переходим к главному в сегодняшнем уроке: ознакомимся с буквой 'И краткое'. @@ -1046,11 +1185,12 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Й')) +Info( """ - Букварь Голубиной содержит материал на тему буквы Й на странице 35. + Пособие под редакцией В. Голубиной содержит материал на тему буквы Й на странице 35.
Ознакомьтесь с рельефно-графическим и рельефно-точечным изображением буквы "Й" на первой строке. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('Й') +Info( """Буква за буквой, введите точечным шрифтом слово КЛЕЙ""" ) @@ -1072,26 +1212,33 @@ internal val golubinaIntroLessons by lessons { ) inputChars("РАЙОН") +Info( + """ + Все пройденные слова следует записать на брайлевском приборе. Напомним, это слова +
+ КЛЕЙ, УЛЕЙ, РЕЙС, ЗНОЙ, РАЙОН + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { """ - Урок 18 - буква 'И краткое' - окончен. Теперь мы знаем уже 4 буквы, содержащие + Урок $iLesson - буква 'И краткое' - окончен. Теперь мы знаем уже 4 буквы, содержащие точку номер 6. Предлагаем Вам самостоятельно вспомнить, какие именно.
До встречи на следующем уроке, где мы будем изучать твёрдый знак и знак препинания 'Литературная точка'. """ - ) + }) } lesson( name = "Твёрдый знак и литературная точка" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 19. Твёрдый знак и литературная точка. + Урок $iLesson. Твёрдый знак и литературная точка.

Вначале повторим ранее изученные буквы. Введите по буквам прилагательное ОСЕННИЙ """ - ) + }) inputChars("ОСЕННИЙ") +Info( """ @@ -1104,11 +1251,12 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Ъ')) +Info( """ - Откройте раздел букваря, где изучается твёрдый знак, на странице 36. + Откройте раздел пособия, где изучается твёрдый знак, на странице 36.
Прочтите верхнюю строку, которая содержит пять повторов этого символа. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('Ъ') +Info( """Введите по буквам слово СЪЕЗД""" ) @@ -1128,43 +1276,54 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('.')) +Info( """ - В середине страницы 36 букваря, где мы изучали твёрдый знак, найдите и прочтите + В середине страницы 36 пособия, где мы изучали твёрдый знак, найдите и прочтите строку с изображением литературной точки. """ ).annotate(StepAnnotation.golubinaBookRequired) +Input(content.symbols.getValue('.')) + +Info( + "Используя брайлевский прибор, запишите строку из одних точек." + ).annotate(StepAnnotation.slateStylusRequired) +Info( """ - В следующих шагах введите по буквам целое предложение: "Ленинград - город-герой". + В следующих шагах введите по буквам целое предложение: "Ленинград — город-герой".
В конце поставьте литературную точку. """ ) - inputChars("ЛЕНИНГРАД-ГОРОД-ГЕРОЙ") + inputChars("ЛЕНИНГРАД${Hyphen.c}ГОРОД${Hyphen.c}ГЕРОЙ.") +Info( + """ + Теперь запишите на брайлевском приборе то же самое предложение: +
+ ЛЕНИНГРАД — ГОРОД—ГЕРОЙ. + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { """ - На этом мы завершаем занятие номер 19. Осталось ещё семь уроков, после чего + На этом мы завершаем занятие номер $iLesson. Осталось ещё семь уроков, после чего мы будем знать все русские буквы. В следующем уроке узнаем точечный состав буквы Ы. """ - ) + }) } lesson( name = "Буква Ы" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 20. Буква Ы. + Урок $iLesson. Буква Ы.

Ради закрепления пройденного введите по символам слово ОБЪЕДИНЕНИЕ """ - ) + }) inputChars("ОБЪЕДИНЕНИЕ") +Info( """Также повторим специальные символы. Введите цифровой знак, запятую, дефис и точку""" ) - inputChars("],-.") + +Input(content.markers.getValue(MarkerType.NumberSign)) + inputChars(",${Hyphen.c}.") +Info( """ Переходим к изучению точечного состава буквы Ы. @@ -1176,10 +1335,11 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Ы')) +Info( """ - Теперь посетим страницу 37 букваря, где нужно найти и прочесть верхнюю строку - + Теперь посетим страницу 37 пособия, где нужно найти и прочесть верхнюю строку - рельефно-графические и рельефно-точечное изображение буквы Ы. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('Ы') +Info( """Набирая буквы одна за одной, введите слово МЫЛО""" ) @@ -1201,24 +1361,31 @@ internal val golubinaIntroLessons by lessons { ) inputChars("КОЗЫ") +Info( + """ + Все изученные слова, пожалуйста, запишите и на брайлевском приборе: +
+ МЫЛО, ЛЫЖИ, КЛЫК, МУЗЫКА, КОЗЫ + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { """ - Урок 20 - буква 'Ы' - подходит к завершению. - Остались неизученными семь букв русского алфавита; для них отведены оставшиеся + Урок $iLesson - буква 'Ы' - подходит к завершению. + Остались неизученными семь букв русского алфавита; для них отведены последующие шесть уроков. На следующем занятии будем осваивать мягкий знак. """ - ) + }) } lesson( name = "Мягкий знак" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 21. Мягкий знак. + Урок $iLesson. Мягкий знак.

Как и в прошлые разы, перед занятием повторим ранее изученное. Введите, набирая отдельные буквы, прилагательное РЫЖИЙ """ - ) + }) inputChars("РЫЖИЙ") +Info( """ @@ -1231,12 +1398,13 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Ь')) +Info( """ - Откройте страницу 39 в букваре Голубиной. Изучите первую строку, где содержится + Откройте страницу 39 в пособии Голубиной. Изучите первую строку, где содержится мягкий знак.
После урока рекомендуем прочесть слова на страницах 39 и 40. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('Ь') +Info( """Введите по буквам существительное СОЛЬ""" ) @@ -1259,23 +1427,30 @@ internal val golubinaIntroLessons by lessons { ) inputChars("СТАЛЬ") +Info( + """ + Как обычно, напишите на брайлевском приборе изученные слова: +
+ СОЛЬ, МЕЛЬ, КОНЬ, РУЧЬИ, СТАЛЬ + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { """ - Урок 21, в котором мы изучили мягкий знак, пройден. + Урок $iLesson, в котором мы изучили мягкий знак, пройден. В течение следующего занятия мы изучим сразу две буквы: Ё и Ш. """ - ) + }) } lesson( name = "Буквы Ё и Ш" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 22. Буквы Ё и Ш. + Урок $iLesson. Буквы Ё и Ш.

Перед уроком будет полезно вспомнить буквы, пройденные на прошлых занятиях. Введите шаг за шагом слово АПЕЛЬСИН """ - ) + }) inputChars("АПЕЛЬСИН") +Info( """ @@ -1289,11 +1464,12 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Ё')) +Info( """ - Вводный текст о буквах Ё и Ш можно найти в букваре на страницах 41 и 42. + Вводный текст о буквах Ё и Ш можно найти в пособии на страницах 41 и 42.
Откройте страницу 41 и прочтите верхнюю строку с буквой Ё. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('Ё') +Info( """Шаг за шагом введите слово ЁЛКА""" ) @@ -1313,9 +1489,10 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Ш')) +Info( """ - Прочтите строку с буквой Ш в середине страницы 41 букваря. + Прочтите строку с буквой Ш в середине страницы 41 пособия. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('Ш') +Info( """Наберите отдельными символами слово ШЁЛК""" ) @@ -1333,25 +1510,32 @@ internal val golubinaIntroLessons by lessons { ) inputChars("КОШКА") +Info( + """ + Используя брайлевский прибор, запишите изученные в этом уроке существительные: +
+ ЁЛКА, КЛЁН, ШЁЛК, ШЕСТ, ШАРФ, КОШКА + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { """ - Урок 22, посвящённый буквам Ё и Ш, завершён. + Урок $iLesson, посвящённый буквам Ё и Ш, завершён.
До окончания изучения всего алфавита осталось 4 урока и 4 буквы: В, Э, Ю, Я. В следующем уроке введём букву Я. """ - ) + }) } lesson( name = "Буква Я" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 23. Буква 'Я'. + Урок $iLesson. Буква 'Я'.

Мы выучили уже много букв, и перед занятием повторим часть из них. Введите по буквам слово МИШЕНЬ """ - ) + }) inputChars("МИШЕНЬ") +Info( """ @@ -1365,11 +1549,12 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Я')) +Info( """ - Изучению буквы 'Я' отведены страницы 43 и 44 в букваре Голубиной. + Изучению буквы 'Я' отведены страницы 43 и 44 в пособии под редакцией В. Голубиной.
Откройте страницу 43 и прочтите верхнюю строку - пять букв 'Я' подряд. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('Я') +Info( """Введите брайлевские буквы, которые составляют слово ЯБЛОКО""" ) @@ -1394,22 +1579,29 @@ internal val golubinaIntroLessons by lessons { inputChars("ЗЕМЛЯ") +Info( """ - Урок 23 - буква Я - успешно завершён. + Запишите на брайлевском приборе изученные слова с буквой Я: +
+ ЯБЛОКО, ЯГОДА, ПЯТНО, ЦАПЛЯ, ЗЕМЛЯ + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """ + Урок $iLesson - буква Я - успешно завершён. В следующий раз мы будем осваивать букву Ю. """ - ) + }) } lesson( name = "Буква Ю" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 24. Буква 'Ю'. + Урок $iLesson. Буква 'Ю'.

Начнём с ревизии уже изученных букв и специальных символов. Введите, набирая отдельные буквы, слово ПОЛЯНА и поставьте после него запятую """ - ) + }) inputChars("ПОЛЯНА,") +Info( """ @@ -1422,11 +1614,12 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('Ю')) +Info( """ - Буква 'Ю' и слова с ней находятся на страницах 45 и 46 в букваре. + Буква 'Ю' и слова с ней находятся на страницах 45 и 46 в пособии.
Найдите сорок пятую страницу и ознакомьтесь с буквой Ю в самой верхней строке. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('Ю') +Info( """Наберите по символам слово с новой буквой: ЮБКА""" ) @@ -1454,31 +1647,39 @@ internal val golubinaIntroLessons by lessons { inputChars("СЮЖЕТ") +Info( """ - Урок 24 - русская буква Ю - закончен. Нам осталось пройти всего два занятия, + Снова возьмите брайлевский прибор и напишите пройденные слова с буквами Ю, Т: +
+ ЮБКА, УТЮГ, ТЮБИК, ТРЮК, СЮЖЕТ + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """ + Урок $iLesson - русская буква Ю - закончен. Нам осталось пройти всего два занятия, после чего весь алфавит будет изучен.
- В нашей предпоследней встрече мы освоим букву Э. + В нашей предпоследней встрече, связанной с русским алфавитом, мы освоим букву Э. """ - ) + }) } lesson( name = "Буква Э" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 25. Буква 'Э'. + Урок $iLesson. Буква 'Э'.

Перед погружением в новый материал повторим пройденное. Введите по буквам слово ИНТЕРЕСНЫЙ """ - ) + }) inputChars("ИНТЕРЕСНЫЙ") +Info( """Ещё перед началом урока мы чуть-чуть повторим числа. Введите цифровой знак и за ним число 1984 """ ) - inputChars("]1984") + +Input(content.markers.getValue(MarkerType.NumberSign)) + inputChars("1984") +Info( """ Давайте изучим букву 'Э'. @@ -1491,11 +1692,12 @@ internal val golubinaIntroLessons by lessons { +Info( """ Для развития памяти пальцев рекомендуется в дополнение к этому уроку - просмотреть материал о букве Э на страницах 47 и 48 в букваре. + просмотреть материал о букве Э на страницах 47 и 48 в пособии.
Сейчас откройте страницу 47 и прочтите первую строку, где написана буква 'Э'. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('Э') +Info( """Вводя отдельные буквы, составьте слово ЭХО""" ) @@ -1524,23 +1726,30 @@ internal val golubinaIntroLessons by lessons { inputChars("ЭСТРАДА") +Info( """ - Урок 25, в ходе которого мы выучили букву Э, закончен. + Теперь запишите введённые ранее слова на брайлевском приборе: +
+ ЭХО, ПОЭМА, ЭКРАН, ЭПИЗОД, ЭСТРАДА + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """ + Урок $iLesson, в ходе которого мы выучили букву Э, закончен.
- В следующем уроке, который станет последним в нашем курсе, изучим букву В. + В следующем уроке изучим букву В, после чего весь алфавит будет освоен. """ - ) + }) } lesson( name = "Буква В" ) { - +Info( + +Info(InfoInterpolation.run { """ - Урок 26. Буква 'В'. + Урок $iLesson. Буква 'В'.

В начале этого занятия вспомним материал, ранее пройденный в курсе. Пожалуйста, наберите буквы, составляющие слово ЭНЦИКЛОПЕДИЯ """ - ) + }) inputChars("ЭНЦИКЛОПЕДИЯ") +Info( """ @@ -1553,13 +1762,14 @@ internal val golubinaIntroLessons by lessons { +Show(content.symbols.getValue('В')) +Info( """ - Для более тщательного изучения буквы 'В' мы рекомендуем обратиться к букварю + Для более тщательного изучения буквы 'В' мы рекомендуем обратиться к пособию В. Голубиной, страницы 49 и 50.
В данный момент предлагаем Вам раскрыть страницу 49 и прочесть верхнюю строку, где написана буква 'В'. """ ).annotate(StepAnnotation.golubinaBookRequired) + slateStylusLine('В') +Info( """Наберите первое слово с буквой 'В' - слово ВОР""" ) @@ -1586,9 +1796,454 @@ internal val golubinaIntroLessons by lessons { """Заключительное упражнение: наберите слово КОРОВА""" ) inputChars("КОРОВА") - +LastInfo( + +Info( + """ + Не забудьте для тренировки написать на брайлевском приборе набранные слова: +
+ ВОР, ВРАЧ, ВОРОНА, СЕВЕР, КОРОВА. + """ + ).annotate(StepAnnotation.slateStylusRequired) + +Info(InfoInterpolation.run { + """ + Урок $iLesson - буква В - закончен. +
+ Теперь Вам знаком весь русский алфавит и цифры, а также некоторые знаки препинания. + В следующих занятиях мы пополним наш запас знаков препинания. + """ + }) + } + lesson("Двоеточие и точка с запятой") { // Golubina pp 53-54 + +Info(InfoInterpolation.run { + """ + Урок $iLesson: Двоеточие и точка с запятой. +

+ До сих пор мы знали три знака препинания - точка, запятая и дефис (тире). + В этом уроке будут введены ещё два. +
+ В виде разминки перед уроком введите по буквам слово ДВОЕТОЧИЕ + """ + }) + inputChars("ДВОЕТОЧИЕ") + +Info( + """ + В рельефно-точечном алфавите точка с запятой обозначается точками с номерами 2 + и 3. Напоминает букву Б, только точки сдвинуты на ряд вниз. +
+ В следующих шагах ознакомьтесь с символом "Точка с запятой". + """ + ) + showAndInputChars(";") + +Info( + """ + Раскройте пособие В. Голубиной на странице 53. +
+ Прочтите шестую сверху строку, где после полного шеститочия приведено обозначение + знака "точка с запятой". Затем прочтите цитату М. Шолохова в строках ниже. + """ + ).annotate(StepAnnotation.golubinaBookRequired) + +Info( + """ + Точка с запятой используется для разделения частей сложного предложения. +
+ В следующих шагах введите слово РАЗДЕЛЕНИЕ; и поставьте в конце точку с запятой. + """ + ) + inputChars("РАЗДЕЛЕНИЕ;") + +Info( + """ + Двоеточие записывается в азбуке Брайля с помощью точек 2 и 5. Похоже на дефис, + но точки расположены на ряд выше. +
+ Далее прочтите, а затем введите этот знак. + """ + ) + showAndInputChars(":") + +Info( + """ + В пособии В. В. Голубиной перейдите к странице 54. +
+ На этой странице прочтите строку 4 сверху, где записан брайлевский знак двоеточия. +
+ Прочтите и остальное содержание страницы 54 - предложение о Летнем Саде, а также + цитаты Пушкина и Горького. + """ + ).annotate(StepAnnotation.golubinaBookRequired) + +Info( + """Введите по буквам слово ВЫВОД: и поставьте в конце двоеточие""" + ) + inputChars("ВЫВОД:") + +Info( + """ + Перепишите, используя брайлевский прибор, предложение со страницы 54 пособия: +
+ Летний Сад омывается реками: Невой, Мойкой, Фонтанкой и Лебяжьей Канавкой. + """ + ).annotate(StepAnnotation.slateStylusRequired, StepAnnotation.golubinaBookRequired) + +Info(InfoInterpolation.run { + """ + Урок $iLesson - двоеточие и точка с запятой - подошёл к концу. +
+ Следующий урок посвящён не менее важным символам - вопросительному знаку и + восклицательному знаку. + """ + }) + } + lesson("Вопросительный и восклицательный знак") { + +Info(InfoInterpolation.run { + """ + Урок $iLesson. Вопросительный и восклицательный знак. +

+ В этом уроке мы пополним коллекцию известных нам знаков препинания. +
+ Перед стартом немного повторения. Введите слово "ВОСКЛИЦАНИЕ" + """ + }) + inputChars("ВОСКЛИЦАНИЕ") + +Info( + """ + Вопросительный знак - точки 2 и 6. Как буква Е, но точки сдвинуты на один ряд вниз. +
+ В следующих шагах ознакомимся с этим символом. + """ + ) + showAndInputChars("?") + +Info( + """ + Раскройте страницу 55 пособия. +
+ В четвёртой снизу строчке вводится вопросительный знак. Полное шеститочие перед + ним приведено лишь в качестве ориентира. +
+ В строках ниже прочтите примеры с вопросительным знаком. + """ + ).annotate(StepAnnotation.golubinaBookRequired) + +Info( + """Введите вопрос: ГДЕ?""" + ) + inputChars("ГДЕ?") + +Info("""Наберите по буквам вопрос: КОГДА?""") + inputChars("КОГДА?") + + +Info( + """ + Восклицательный знак в азбуке Брайля - точки 2, 3 и 5. Не перепутайте с буквой Ф: + точки расположены похожим образом, но каждая сдвинута на ряд ниже. + """ + ) + showAndInputChars("!") + +Info( + """ + Найдите в обучающем пособии страницу 56. +
+ В третьей сверху строке описан восклицательный знак. Прочтите эту строку. +
+ Затем прочтите приведённые в следующих строках цитаты А. С. Пушкина и Н. А. + Некрасова. + """ + ).annotate(StepAnnotation.golubinaBookRequired) + +Info("""В завершение урока введите слово с восклицательным знаком на конце: ЧУДЕСНО!""") + inputChars("ЧУДЕСНО!") + +Info(InfoInterpolation.run { + """ + Поздравляем. Урок $iLesson - восклицательный и вопросительный знак - завершён. +
+ В следующем уроке мы изучим скобки, кавычки и знак "Звёздочка". + """ + }) + } + lesson("""Скобки, кавычки и знак "*" """) { // Golubina pp 57-58 + +Info(InfoInterpolation.run { + """ + Урок $iLesson: Скобки, кавычки и знак "Звёздочка". +

+ В результате этого занятия мы изучим парные знаки препинания - скобки и кавычки, + а также узнаем, как в рельефно-точечном алфавите обозначается символ "Звёздочка". +
+ Перед уроком закрепим пройденное. Введите слово "ВНИМАНИЕ!" и поставьте на конце + восклицательный знак. + """ + }) + inputChars("ВНИМАНИЕ!") + +Info( + """ + Левая открывающая скобка обозначается точками с номерами 1, 2 и 6, правая - 3, + 4 и 5. Заметьте, правая является зеркальным отражением левой. +
+ Скобки при письме примыкают к выделяемой части предложения. + """ + ) + showAndInputChars("()") + +Info( + """ + Найдите страницу 57 в обучающем пособии. +
+ Изучите три верхние строки - обозначение скобок и комментарий. Также прочтите + на следующих трёх строках предложение о Русском музее с примером употребления + скобок. + """ + ).annotate(StepAnnotation.golubinaBookRequired) + +Info("""Введите слово (ВЕРОЯТНО), заключив его в скобки""") + inputChars("(ВЕРОЯТНО)") + +Info( + """ + Теперь изучим кавычки. Левая (открывающая) кавычка - это точки 2, 3 и 6, правая + (закрывающая) - точки 3, 5 и 6. +
+ Как и скобки, кавычки примыкают к выделяемой части. Открывающая и закрывающая + кавычки являются зеркальным отражением друг друга. + """ + ) + showAndInputChars("«»") + +Info("""Введите название повести Тургенева - «НАКАНУНЕ», заключив его в кавычки""") + inputChars("«НАКАНУНЕ»") + +Info( + """ + Нам осталось изучить ещё один символ в этом уроке - знак "Звёздочка". +
+ Звёздочка обозначается двумя точками с номерами 3 и 5. + """ + ) + showAndInputChars("*") + +Info( + """ + На той же странице 57 пособия, где мы изучали скобки, прочтите шесть нижних строк. + В них демонстрируются символы открывающей скобки, звёздочки и закрывающей скобки. +
+ После этого переверните страницу и прочтите шесть верхних строк, где приведены + примеры использования кавычек. + """ + ).annotate(StepAnnotation.golubinaBookRequired) + +Info(InfoInterpolation.run { + """ + Урок $iLesson - cкобки, кавычки и знак "Звёздочка" - подходит к завершению. +
+ Следующие уроки покрывают материал, который встречается в брайлевских книгах + не так часто, поэтому упражнений будет меньше и изложение будет носить скорее + справочный характер. + """ + }) + } + lesson("Специальные символы") { // Golubina: pp 59-62 + +Info(InfoInterpolation.run { + """ + Урок $iLesson: Специальные символы. +

+ Этот урок посвящён литературным знакам и знакам выделения шрифтов, которые, + за исключением, разве что, знака ударения, не имеют аналогов в плоскопечатном + письме. +
+ До сих пор мы знали один такой знак - цифровой. Теперь познакомимся и с остальными. + """ + }) + +Info( + """ + Знак ударения обозначается точкой 4 и ставится в клетке перед ударной гласной. +
+ Например, в слове МОСКВА знак ударения будет стоять перед буквой А. + """ + ) + showAndInputChars("\'") + +Info( + """ + Раскройте пособие В. Голубиной на странице 59. +
+ Прочтите вторую сверху строку, где вводится знак ударения. Также прочтите + примеры употребления этого знака в шестой и седьмой строках сверху. + """ + ).annotate(StepAnnotation.golubinaBookRequired) + +Info( + """ + В брайлевских текстах перед именами и названиями иногда ставят признак + большой буквы. Он обозначается точками 4 и 5. + """ + ) + +Show(content.markers.getValue(MarkerType.RussianCapital)) + +Input(content.markers.getValue(MarkerType.RussianCapital)) + +Info( + """ + На странице 59 в пособии прочтите четыре нижние строки, где приводится признак + большой русской буквы и предложение с его использованием. + """ + ).annotate(StepAnnotation.golubinaBookRequired) + +Info( + """ + Если в текст на русском языке вставлено слово на латинице (например, + на английском), перед ним непременно ставится признак большой латинской буквы + или признак малой латинской буквы. +
+ Признак большой буквы латинского алфавита - точки 4 и 6. +
+ Признак малой буквы латинского алфавита обозначается одной точкой номер 6. + """ + ) + +Show(content.markers.getValue(MarkerType.LatinCapital)) + +Input(content.markers.getValue(MarkerType.LatinCapital)) + +Info( + """ + Перед греческими буквами в русских текстах ставят признак большой буквы + греческого алфавита. Он обозначается точками 4, 5 и 6. Не перепутайте + с буквой "Л". + """ + ) + +Info( + """ + Переверните страницу в пособии и прочтите содержание страницы 60, где вводятся + признаки букв греческого и латинского алфавита. + """ + ).annotate(StepAnnotation.golubinaBookRequired) + +Info( + """ + Иногда для смыслового выделения используют жирный или курсивный шрифт. Для них + в брайлевских текстах есть специальные знаки, которые ставятся с обеих сторон + выделяемого слова или фразы, причём примыкают вплотную к тексту, как скобки + или кавычки. +
+ Знак курсивного шрифта обозначается точками 4, 5, 6 (совпадает с признаком большой + латинской буквы). +
+ Знак жирного шрифта - точки 1, 2, 4, 5, 6 (то есть все, кроме третьей). + """ + ) + +Show(content.markers.getValue(MarkerType.ItalicFont)) + +Input(content.markers.getValue(MarkerType.ItalicFont)) + +Show(content.markers.getValue(MarkerType.BoldFont)) + +Input(content.markers.getValue(MarkerType.BoldFont)) + +Info( + """ + Прочтите первые пять строк на странице 61. Там рассказывается о знаках жирного + и курсивного шрифта. При желании прочтите и остальной материал на странице, где + рассказывается о знаках разрядки и выделения врезки. Мы не будем изучать эти знаки + в данном курсе. + """ + ).annotate(StepAnnotation.golubinaBookRequired) + +Info(InfoInterpolation.run { + """ + Урок $iLesson - специальные символы - закончен. +
+ Мы привели лишь наиболее часто употребимые литературные знаки. Полный + список доступен в интернете на сайте chtenie.spb.ru/komissia.htm +
+ На следующем занятии мы познакомимся с обозначениями основных арифметических знаков. + """ + }) + } + + lesson("Математические символы") { // Golubina: p 65 + +Info(InfoInterpolation.run { + """ + Урок $iLesson: Математические символы. +

+ В данном уроке мы изучим запись арифметических операций: сложение, умножение, + вычитание и деление, а также равенство. В плоскопечатном шрифте для умножения и + деления есть два варианта записи; оба варианта имеют аналоги в шрифте Брайля. +
+ В следующих шагах ознакомьтесь с обозначениями знаков арифметических действий + и введите их на экране. + """ + }) + showAndInputChars("+-✕·/÷=") + +Info( + """ + Раскройте книгу В. В. Голубиной на странице 65 и прочтите её содержание. Там + приведены обозначения арифметических операций, математические скобки (здесь мы + не будем их изучать) и записано несколько арифметических примеров. + """ + ).annotate(StepAnnotation.golubinaBookRequired) + + +Info("""В следующих шагах запишите: ДВА ПЛЮС ДВА РАВНО ЧЕТЫРЕ""") + +Input(content.markers.getValue(MarkerType.NumberSign)) + inputChars("2+") + +Input(content.markers.getValue(MarkerType.NumberSign)) + inputChars("2=4") + +Info( """ - Урок 26 - буква В - закончен. + Запишите на брайлевском приборе примеры: +
+ ОДИН ПЛЮС ОДИН РАВНО ДВА +
+ ТРИ МИНУС ОДИН РАВНО ДВА +
+ после чего проверьте себя по третьей снизу строке на странице 65 в пособии. + Обратите внимание: перед знаками арифметических действий надо ставить пробел, + но после них пробел не делается. + """ + ).annotate(StepAnnotation.golubinaBookRequired, StepAnnotation.slateStylusRequired) + + +Info(InfoInterpolation.run { + """ + Урок $iLesson - математические символы - окончен. +
+ В следующих двух уроках мы бегло ознакомимся с обозначениями латинских и греческих + букв. + """ + }) + } + lesson("Латинский алфавит") { + +Info(InfoInterpolation.run { + """ + Урок $iLesson: латинский алфавит. +

+ В этом уроке собраны все буквы латинского алфавита. Каждую из них нужно сперва + прочесть, а затем ввести в шеститочии на экране. +
+ Слова на латинице внутри русского текста выделяются специальным символом + "Признак большой буквы латинского алфавита" (точки 4 и 6) или + "Признак малой буквы латинского алфавита" (точка 6). + """ + }) + +Info( + """ + Латинский алфавит в книге под редакцией В. В. Голубиной изучается на страницах + 85-91. Для каждой буквы в пособии приведена русская транскрипция, английское + написание шрифтом Брайля (вместе с признаком заглавной латинской буквы) + и рельефно-графическое изображение. + """ + ).annotate(StepAnnotation.golubinaBookRequired) + +Show(content.markers.getValue(MarkerType.LatinCapital)) + +Input(content.markers.getValue(MarkerType.LatinCapital)) + +Show(content.markers.getValue(MarkerType.LatinSmall)) + +Input(content.markers.getValue(MarkerType.LatinSmall)) + showAndInputChars("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + +Info( + """ + В следующих шагах введите по буквам фамилию Луи Брайля, как она пишется во + французском и английском языках: BRAILLE, поставив перед этим признак большой + латинской буквы. + """ + ) + +Input(content.markers.getValue(MarkerType.LatinCapital)) + inputChars("BRAILLE") + +Info(InfoInterpolation.run { + """ + На этом урок $iLesson завершается. В следующем занятии аналогичным образом + мы кратко ознакомимся с греческим алфавитом. + """ + }) + } + lesson("Греческий алфавит") { + +Info(InfoInterpolation.run { + """ + Урок $iLesson: греческий алфавит. +

+ Последний урок в нашем курсе посвящён греческому алфавиту. При вставке заглавных + греческих букв в русском тексте ставится специальный символ "Признак большой + буквы греческого алфавита". + """ + }) + +Show(content.markers.getValue(MarkerType.GreekCapital)) + +Input(content.markers.getValue(MarkerType.GreekCapital)) + +Info( + """ + Пособие под редакцией В. Голубиной включает и греческий алфавит. Его можно найти + на страницах 93-99. Для каждой буквы приведено её название (на русском языке), + обозначение в азбуке Брайля (вместе с признаком заглавной буквы), а также + рельефно-графическое изображение. + """ + ).annotate(StepAnnotation.golubinaBookRequired) + showAndInputChars("ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ") + +LastInfo(InfoInterpolation.run { + """ + Урок $iLesson закончен.
Вы дошли до конца курса. Спасибо, что воспользовались нашим обучающим приложением! При желании можно вернутся к ранее пройденному материалу @@ -1598,6 +2253,6 @@ internal val golubinaIntroLessons by lessons { Мы советуем Вам читать различные книги рельефно-точечным шрифтом. Желаем вам приятного опыта использования алфавита Луи Брайля! """ - ) + }) } } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt b/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt index af98f675..a6a2c6f8 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt @@ -1,15 +1,41 @@ package com.github.braillesystems.learnbraille.res import android.content.Context +import androidx.fragment.app.Fragment import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.data.dsl.known +import com.github.braillesystems.learnbraille.data.dsl.markers import com.github.braillesystems.learnbraille.data.dsl.materials import com.github.braillesystems.learnbraille.data.dsl.symbols import com.github.braillesystems.learnbraille.data.entities.BrailleDot.E import com.github.braillesystems.learnbraille.data.entities.BrailleDot.F import com.github.braillesystems.learnbraille.data.entities.BrailleDots +import com.github.braillesystems.learnbraille.data.entities.Symbol +import com.github.braillesystems.learnbraille.res.MathSigns.* +import com.github.braillesystems.learnbraille.res.PunctuationSigns.* +import com.github.braillesystems.learnbraille.utils.contextNotNull import com.github.braillesystems.learnbraille.utils.rules +import kotlinx.serialization.Serializable +object SymbolType { + const val ru = "ru" + const val digit = "digit" + const val latin = "Latin" + const val greek = "greek" + const val special = "special" + const val math = "math" +} + +@Serializable +enum class MarkerType { + RussianCapital, + GreekCapital, + LatinCapital, + LatinSmall, + BoldFont, + ItalicFont, + NumberSign +} /** * Do not forget to register print rules below for the new types of symbols. @@ -18,14 +44,179 @@ import com.github.braillesystems.learnbraille.utils.rules */ val content by materials { +ruSymbols - +specialSymbols + +punctuationSigns +uebDigits + +latinLetters + +greekLetters + +mathSigns + +ms } val knownMaterials by known( 'А', 'Б', 'Ц', 'Д', 'Е', 'Ф', 'Г' ) + +private val ruSymbols by symbols(SymbolType.ru) { + // UTF-16: 0410-042F + symbol(char = 'А', brailleDots = BrailleDots(F, E, E, E, E, E)) + symbol(char = 'Б', brailleDots = BrailleDots(F, F, E, E, E, E)) + symbol(char = 'В', brailleDots = BrailleDots(E, F, E, F, F, F)) + symbol(char = 'Г', brailleDots = BrailleDots(F, F, E, F, F, E)) + symbol(char = 'Д', brailleDots = BrailleDots(F, E, E, F, F, E)) + symbol(char = 'Е', brailleDots = BrailleDots(F, E, E, E, F, E)) + symbol(char = 'Ё', brailleDots = BrailleDots(F, E, E, E, E, F)) + symbol(char = 'Ж', brailleDots = BrailleDots(E, F, E, F, F, E)) + symbol(char = 'З', brailleDots = BrailleDots(F, E, F, E, F, F)) + symbol(char = 'И', brailleDots = BrailleDots(E, F, E, F, E, E)) + symbol(char = 'Й', brailleDots = BrailleDots(F, F, F, F, E, F)) + symbol(char = 'К', brailleDots = BrailleDots(F, E, F, E, E, E)) + symbol(char = 'Л', brailleDots = BrailleDots(F, F, F, E, E, E)) + symbol(char = 'М', brailleDots = BrailleDots(F, E, F, F, E, E)) + symbol(char = 'Н', brailleDots = BrailleDots(F, E, F, F, F, E)) + symbol(char = 'О', brailleDots = BrailleDots(F, E, F, E, F, E)) + symbol(char = 'П', brailleDots = BrailleDots(F, F, F, F, E, E)) + symbol(char = 'Р', brailleDots = BrailleDots(F, F, F, E, F, E)) + symbol(char = 'С', brailleDots = BrailleDots(E, F, F, F, E, E)) + symbol(char = 'Т', brailleDots = BrailleDots(E, F, F, F, F, E)) + symbol(char = 'У', brailleDots = BrailleDots(F, E, F, E, E, F)) + symbol(char = 'Ф', brailleDots = BrailleDots(F, F, E, F, E, E)) + symbol(char = 'Х', brailleDots = BrailleDots(F, F, E, E, F, E)) + symbol(char = 'Ц', brailleDots = BrailleDots(F, E, E, F, E, E)) + symbol(char = 'Ч', brailleDots = BrailleDots(F, F, F, F, F, E)) + symbol(char = 'Ш', brailleDots = BrailleDots(F, E, E, E, F, F)) + symbol(char = 'Щ', brailleDots = BrailleDots(F, E, F, F, E, F)) + symbol(char = 'Ъ', brailleDots = BrailleDots(F, F, F, E, F, F)) + symbol(char = 'Ы', brailleDots = BrailleDots(E, F, F, F, E, F)) + symbol(char = 'Ь', brailleDots = BrailleDots(E, F, F, F, F, F)) + symbol(char = 'Э', brailleDots = BrailleDots(E, F, E, F, E, F)) + symbol(char = 'Ю', brailleDots = BrailleDots(F, F, E, E, F, F)) + symbol(char = 'Я', brailleDots = BrailleDots(F, F, E, F, E, F)) +} + +enum class PunctuationSigns(val c: Char) { + Hyphen('—'), + LeftQuotation('«'), + RightQuotation('»') +} + +private val punctuationSigns by symbols(SymbolType.special) { + symbol(char = ',', brailleDots = BrailleDots(E, F, E, E, E, E)) // Comma + symbol(char = Hyphen.c, brailleDots = BrailleDots(E, E, F, E, E, F)) // Hyphen + symbol(char = '.', brailleDots = BrailleDots(E, F, E, E, F, F)) // Dot + symbol(char = '!', brailleDots = BrailleDots(E, F, F, E, F, E)) // Exclamation mark + symbol(char = '?', brailleDots = BrailleDots(E, F, E, E, E, F)) // Question mark + symbol(char = LeftQuotation.c, brailleDots = BrailleDots(E, F, F, E, E, F)) + symbol(char = RightQuotation.c, brailleDots = BrailleDots(E, E, F, E, F, F)) + symbol(char = '(', brailleDots = BrailleDots(F, F, E, E, E, F)) // Left parenthesis + symbol(char = ')', brailleDots = BrailleDots(E, E, F, F, F, E)) // Right parenthesis + symbol(char = '*', brailleDots = BrailleDots(E, E, F, E, F, E)) // Asterisk + symbol(char = ':', brailleDots = BrailleDots(E, F, E, E, F, E)) // Colon + symbol(char = ';', brailleDots = BrailleDots(E, F, F, E, E, E)) // Semicolon + symbol(char = '\'', brailleDots = BrailleDots(E, E, E, F, E, E)) // Stress +} + +enum class MathSigns(val c: Char) { + DotMul('·'), + CrossMul('✕'), + Div('÷') +} + +private val mathSigns by symbols(SymbolType.math) { + symbol(char = '+', brailleDots = BrailleDots(E, F, F, E, F, E)) // Plus + symbol(char = '-', brailleDots = BrailleDots(E, E, F, E, E, F)) // Minus + symbol(char = DotMul.c, brailleDots = BrailleDots(E, E, F, E, E, E)) // Dot multiplication + symbol(char = CrossMul.c, brailleDots = BrailleDots(E, F, F, E, E, F)) // Cross multiplication + symbol(char = '/', brailleDots = BrailleDots(E, E, F, E, F, F)) // Division (fraction) + symbol(char = Div.c, brailleDots = BrailleDots(E, F, E, E, F, F)) // Division + symbol(char = '=', brailleDots = BrailleDots(E, F, F, E, F, F)) // Equality +} + +private val uebDigits by symbols(SymbolType.digit) { + // UTF-16: 0030-0039 + symbol(char = '0', brailleDots = BrailleDots(E, F, E, F, F, E)) + symbol(char = '1', brailleDots = BrailleDots(F, E, E, E, E, E)) + symbol(char = '2', brailleDots = BrailleDots(F, F, E, E, E, E)) + symbol(char = '3', brailleDots = BrailleDots(F, E, E, F, E, E)) + symbol(char = '4', brailleDots = BrailleDots(F, E, E, F, F, E)) + symbol(char = '5', brailleDots = BrailleDots(F, E, E, E, F, E)) + symbol(char = '6', brailleDots = BrailleDots(F, F, E, F, E, E)) + symbol(char = '7', brailleDots = BrailleDots(F, F, E, F, F, E)) + symbol(char = '8', brailleDots = BrailleDots(F, F, E, E, F, E)) + symbol(char = '9', brailleDots = BrailleDots(E, F, E, F, E, E)) +} + +private val latinLetters by symbols(SymbolType.latin) { + // UTF-16: 0041-005A + symbol(char = 'A', brailleDots = BrailleDots(F, E, E, E, E, E)) + symbol(char = 'B', brailleDots = BrailleDots(F, F, E, E, E, E)) + symbol(char = 'C', brailleDots = BrailleDots(F, E, E, F, E, E)) + symbol(char = 'D', brailleDots = BrailleDots(F, E, E, F, F, E)) + symbol(char = 'E', brailleDots = BrailleDots(F, E, E, E, F, E)) + symbol(char = 'F', brailleDots = BrailleDots(F, F, E, F, E, E)) + symbol(char = 'G', brailleDots = BrailleDots(F, F, E, F, F, E)) + symbol(char = 'H', brailleDots = BrailleDots(F, F, E, E, F, E)) + symbol(char = 'I', brailleDots = BrailleDots(E, F, E, F, E, E)) + symbol(char = 'J', brailleDots = BrailleDots(E, F, E, F, F, E)) + symbol(char = 'K', brailleDots = BrailleDots(F, E, F, E, E, E)) + symbol(char = 'L', brailleDots = BrailleDots(F, F, F, E, E, E)) + symbol(char = 'M', brailleDots = BrailleDots(F, E, F, F, E, E)) + symbol(char = 'N', brailleDots = BrailleDots(F, E, F, F, F, E)) + symbol(char = 'O', brailleDots = BrailleDots(F, E, F, E, F, E)) + symbol(char = 'P', brailleDots = BrailleDots(F, F, F, F, E, E)) + symbol(char = 'Q', brailleDots = BrailleDots(F, F, F, F, F, E)) + symbol(char = 'R', brailleDots = BrailleDots(F, F, F, E, F, E)) + symbol(char = 'S', brailleDots = BrailleDots(E, F, F, F, E, E)) + symbol(char = 'T', brailleDots = BrailleDots(E, F, F, F, F, E)) + symbol(char = 'U', brailleDots = BrailleDots(F, E, F, E, E, F)) + symbol(char = 'V', brailleDots = BrailleDots(F, F, F, E, E, F)) + symbol(char = 'W', brailleDots = BrailleDots(E, F, E, F, F, F)) + symbol(char = 'X', brailleDots = BrailleDots(F, E, F, F, E, F)) + symbol(char = 'Y', brailleDots = BrailleDots(F, E, F, F, F, F)) + symbol(char = 'Z', brailleDots = BrailleDots(F, E, F, E, F, F)) +} + +private val greekLetters by symbols(SymbolType.greek) { + // Note: A - Alpha (Unicode char 0391); A - English (0041); А - Russian (0410) + // You may check the code by copying and pasting at https://r12a.github.io/app-conversion/ + // Capital greek letters in UTF-16: 0391-03A9 + symbol(char = 'Α', brailleDots = BrailleDots(F, E, E, E, E, E)) + symbol(char = 'Β', brailleDots = BrailleDots(F, F, E, E, E, E)) + symbol(char = 'Γ', brailleDots = BrailleDots(F, F, E, F, F, E)) + symbol(char = 'Δ', brailleDots = BrailleDots(F, E, E, F, F, E)) + symbol(char = 'Ε', brailleDots = BrailleDots(F, E, E, E, F, E)) + symbol(char = 'Ζ', brailleDots = BrailleDots(F, E, F, E, F, F)) + symbol(char = 'Η', brailleDots = BrailleDots(E, F, E, F, F, E)) + symbol(char = 'Θ', brailleDots = BrailleDots(F, F, E, E, F, E)) + symbol(char = 'Ι', brailleDots = BrailleDots(E, F, E, F, E, E)) + symbol(char = 'Κ', brailleDots = BrailleDots(F, E, F, E, E, E)) + symbol(char = 'Λ', brailleDots = BrailleDots(F, F, F, E, E, E)) + symbol(char = 'Μ', brailleDots = BrailleDots(F, E, F, F, E, E)) + symbol(char = 'Ν', brailleDots = BrailleDots(F, E, F, F, F, E)) + symbol(char = 'Ξ', brailleDots = BrailleDots(F, E, F, F, E, F)) + symbol(char = 'Ο', brailleDots = BrailleDots(F, E, F, E, F, E)) + symbol(char = 'Π', brailleDots = BrailleDots(F, F, F, F, E, E)) + symbol(char = 'Ρ', brailleDots = BrailleDots(F, F, F, E, F, E)) + symbol(char = 'Σ', brailleDots = BrailleDots(E, F, F, F, E, E)) + symbol(char = 'Τ', brailleDots = BrailleDots(E, F, F, F, F, E)) + symbol(char = 'Υ', brailleDots = BrailleDots(F, E, F, E, E, F)) + symbol(char = 'Φ', brailleDots = BrailleDots(F, F, E, F, E, E)) + symbol(char = 'Χ', brailleDots = BrailleDots(F, E, E, F, E, E)) + symbol(char = 'Ψ', brailleDots = BrailleDots(F, E, F, F, F, F)) + symbol(char = 'Ω', brailleDots = BrailleDots(E, F, E, F, F, F)) +} + +private val ms by markers { + marker(MarkerType.GreekCapital, BrailleDots(E, E, E, F, F, F)) + marker(MarkerType.LatinCapital, BrailleDots(E, E, E, F, E, F)) + marker(MarkerType.LatinSmall, BrailleDots(E, E, E, E, E, F)) + marker(MarkerType.RussianCapital, BrailleDots(E, E, E, F, F, E)) + marker(MarkerType.BoldFont, BrailleDots(F, F, E, F, F, F)) + marker(MarkerType.ItalicFont, BrailleDots(E, E, E, F, F, F)) + marker(MarkerType.NumberSign, BrailleDots(E, E, F, F, F, F)) +} + + /* * Add here rules, how to display hints for symbols. * @@ -33,6 +224,12 @@ val knownMaterials by known( * so use `Fragment.getString` outside of lambdas. */ +val Fragment.inputSymbolPrintRules get() = contextNotNull.inputSymbolPrintRules +val Fragment.showSymbolPrintRules get() = contextNotNull.showSymbolPrintRules +val Fragment.captionRules get() = contextNotNull.captionRules +val Fragment.inputMarkerPrintRules get() = contextNotNull.inputMarkerPrintRules +val Fragment.showMarkerPrintRules get() = contextNotNull.showMarkerPrintRules + val Context.inputSymbolPrintRules by rules( { val t = getString(R.string.input_letter_intro_template) @@ -45,18 +242,67 @@ val Context.inputSymbolPrintRules by rules( }, { - val other = getString(R.string.input_special_intro_template) - val numSign = getString(R.string.input_special_intro_num_sign) + val t = getString(R.string.input_latin_letter_intro_template) + latinLetters.map::containsKey to { c: Char -> t.format(c) } + }, + + { + val t = getString(R.string.input_greek_letter_intro_template) + greekLetters.map::containsKey to { c: Char -> t.format(c) } + }, + + { val dotIntro = getString(R.string.input_special_intro_dot) val commaIntro = getString(R.string.input_special_intro_comma) val hyphenIntro = getString(R.string.input_special_intro_hyphen) - specialSymbols.map::containsKey to { c: Char -> + val exclamationIntro = getString(R.string.input_special_intro_exclamation) + val questionIntro = getString(R.string.input_special_intro_question) + val quotationLeftIntro = getString(R.string.input_special_intro_quotation_left) + val quotationRightIntro = getString(R.string.input_special_intro_quotation_right) + val parenthesisLeftIntro = getString(R.string.input_special_intro_parenthesis_left) + val parenthesisRightIntro = getString(R.string.input_special_intro_parenthesis_right) + val asteriskIntro = getString(R.string.input_special_intro_asterisk) + val colonIntro = getString(R.string.input_special_intro_colon) + val semicolonIntro = getString(R.string.input_special_intro_semicolon) + val stressIntro = getString(R.string.input_special_intro_stress) + punctuationSigns.map::containsKey to { c: Char -> when (c) { - ']' -> numSign '.' -> dotIntro ',' -> commaIntro - '-' -> hyphenIntro - else -> other.format(c) + Hyphen.c -> hyphenIntro + '!' -> exclamationIntro + '?' -> questionIntro + LeftQuotation.c -> quotationLeftIntro + RightQuotation.c -> quotationRightIntro + '(' -> parenthesisLeftIntro + ')' -> parenthesisRightIntro + '*' -> asteriskIntro + ':' -> colonIntro + ';' -> semicolonIntro + '\'' -> stressIntro + else -> error("Undefined symbol: $c") + } + } + }, + + { + val plusIntro = getString(R.string.input_math_plus) + val minusIntro = getString(R.string.input_math_minus) + val dotMultIntro = getString(R.string.input_math_dot_mult) + val crossMultIntro = getString(R.string.input_math_cross_mult) + val fracDivisionIntro = getString(R.string.input_math_division_fraction) + val divisionIntro = getString(R.string.input_math_division) + val equalityIntro = getString(R.string.input_math_equality) + mathSigns.map::containsKey to { c: Char -> + when (c) { + '+' -> plusIntro + '-' -> minusIntro + DotMul.c -> dotMultIntro + CrossMul.c -> crossMultIntro + '/' -> fracDivisionIntro + Div.c -> divisionIntro + '=' -> equalityIntro + else -> error("Undefined symbol: $c") } } } @@ -74,82 +320,202 @@ val Context.showSymbolPrintRules by rules( }, { - val other = getString(R.string.show_special_intro_template) - val numSign = getString(R.string.show_special_intro_num_sign) + val t = getString(R.string.show_latin_letter_intro_template) + latinLetters.map::containsKey to { c: Char -> t.format(c) } + }, + + { + val t = getString(R.string.show_greek_letter_intro_template) + greekLetters.map::containsKey to { c: Char -> t.format(c) } + }, + + { val dotIntro = getString(R.string.show_special_intro_dot) val commaIntro = getString(R.string.show_special_intro_comma) val hyphenIntro = getString(R.string.show_special_intro_hyphen) - specialSymbols.map::containsKey to { c: Char -> + val exclamationIntro = getString(R.string.show_special_intro_exclamation) + val questionIntro = getString(R.string.show_special_intro_question) + val quotationLeftIntro = getString(R.string.show_special_intro_quotation_left) + val quotationRightIntro = getString(R.string.show_special_intro_quotation_right) + val parenthesisLeftIntro = getString(R.string.show_special_intro_parenthesis_left) + val parenthesisRightIntro = getString(R.string.show_special_intro_parenthesis_right) + val asteriskIntro = getString(R.string.show_special_intro_asterisk) + val colonIntro = getString(R.string.show_special_intro_colon) + val semicolonIntro = getString(R.string.show_special_intro_semicolon) + val stressIntro = getString(R.string.show_special_intro_stress) + punctuationSigns.map::containsKey to { c: Char -> when (c) { - ']' -> numSign '.' -> dotIntro ',' -> commaIntro - '-' -> hyphenIntro - else -> other.format(c) + Hyphen.c -> hyphenIntro + '!' -> exclamationIntro + '?' -> questionIntro + LeftQuotation.c -> quotationLeftIntro + RightQuotation.c -> quotationRightIntro + '(' -> parenthesisLeftIntro + ')' -> parenthesisRightIntro + '*' -> asteriskIntro + ':' -> colonIntro + ';' -> semicolonIntro + '\'' -> stressIntro + else -> error("Undefined symbol: $c") + } + } + }, + + { + val plusIntro = getString(R.string.show_math_plus) + val minusIntro = getString(R.string.show_math_minus) + val dotMultIntro = getString(R.string.show_math_dot_mult) + val crossMultIntro = getString(R.string.show_math_cross_mult) + val fracDivisionIntro = getString(R.string.show_math_division_fraction) + val divisionIntro = getString(R.string.show_math_division) + val equalityIntro = getString(R.string.show_math_equality) + mathSigns.map::containsKey to { c: Char -> + when (c) { + '+' -> plusIntro + '-' -> minusIntro + DotMul.c -> dotMultIntro + CrossMul.c -> crossMultIntro + '/' -> fracDivisionIntro + Div.c -> divisionIntro + '=' -> equalityIntro + else -> error("Undefined symbol: $c") } } } ) +val Context.captionRules by rules( + { + val specialCaptions = mapOf( + '.' to R.string.show_special_intro_dot, + ',' to R.string.show_special_intro_comma, + Hyphen.c to R.string.show_special_intro_hyphen, + '!' to R.string.show_special_intro_exclamation, + '?' to R.string.show_special_intro_question, + LeftQuotation.c to R.string.show_special_intro_quotation_left, + RightQuotation.c to R.string.show_special_intro_quotation_right, + '(' to R.string.show_special_intro_parenthesis_left, + ')' to R.string.show_special_intro_parenthesis_right, + '*' to R.string.show_special_intro_asterisk, + ':' to R.string.show_special_intro_colon, + ';' to R.string.show_special_intro_semicolon, + '\'' to R.string.show_special_intro_stress, + '+' to R.string.show_math_plus, + '-' to R.string.show_math_minus, + DotMul.c to R.string.show_math_dot_mult, + CrossMul.c to R.string.show_math_cross_mult, + '/' to R.string.show_math_division_fraction, + Div.c to R.string.show_math_division, + '=' to R.string.show_math_equality + ).mapValues { + getString(it.value) + } + return@rules { s: Symbol -> + specialCaptions.containsKey(s.char) + } to { s: Symbol -> + specialCaptions.getValue(s.char) + } + }, -object SymbolType { - const val ru = "ru" - const val special = "special" - const val digit = "digit" -} + { + val specialCaptions = mapOf( + SymbolType.ru to R.string.letter_caption_ru, + SymbolType.greek to R.string.letter_caption_greek, + SymbolType.latin to R.string.letter_caption_latin, + SymbolType.digit to R.string.letter_caption_digit, + SymbolType.special to R.string.letter_caption_special + ).mapValues { + getString(it.value) + } + return@rules { s: Symbol -> + specialCaptions.containsKey(s.type) + } to { s: Symbol -> + specialCaptions.getValue(s.type) + } + }, -private val ruSymbols by symbols(SymbolType.ru) { - symbol(char = 'А', brailleDots = BrailleDots(F, E, E, E, E, E)) - symbol(char = 'Б', brailleDots = BrailleDots(F, F, E, E, E, E)) - symbol(char = 'В', brailleDots = BrailleDots(E, F, E, F, F, F)) - symbol(char = 'Г', brailleDots = BrailleDots(F, F, E, F, F, E)) - symbol(char = 'Д', brailleDots = BrailleDots(F, E, E, F, F, E)) - symbol(char = 'Е', brailleDots = BrailleDots(F, E, E, E, F, E)) - symbol(char = 'Ё', brailleDots = BrailleDots(F, E, E, E, E, F)) - symbol(char = 'Ж', brailleDots = BrailleDots(E, F, E, F, F, E)) - symbol(char = 'З', brailleDots = BrailleDots(F, E, F, E, F, F)) - symbol(char = 'И', brailleDots = BrailleDots(E, F, E, F, E, E)) - symbol(char = 'Й', brailleDots = BrailleDots(F, F, F, F, E, F)) - symbol(char = 'К', brailleDots = BrailleDots(F, E, F, E, E, E)) - symbol(char = 'Л', brailleDots = BrailleDots(F, F, F, E, E, E)) - symbol(char = 'М', brailleDots = BrailleDots(F, E, F, F, E, E)) - symbol(char = 'Н', brailleDots = BrailleDots(F, E, F, F, F, E)) - symbol(char = 'О', brailleDots = BrailleDots(F, E, F, E, F, E)) - symbol(char = 'П', brailleDots = BrailleDots(F, F, F, F, E, E)) - symbol(char = 'Р', brailleDots = BrailleDots(F, F, F, E, F, E)) - symbol(char = 'С', brailleDots = BrailleDots(E, F, F, F, E, E)) - symbol(char = 'Т', brailleDots = BrailleDots(E, F, F, F, F, E)) - symbol(char = 'У', brailleDots = BrailleDots(F, E, F, E, E, F)) - symbol(char = 'Ф', brailleDots = BrailleDots(F, F, E, F, E, E)) - symbol(char = 'Х', brailleDots = BrailleDots(F, F, E, E, F, E)) - symbol(char = 'Ц', brailleDots = BrailleDots(F, E, E, F, E, E)) - symbol(char = 'Ч', brailleDots = BrailleDots(F, F, F, F, F, E)) - symbol(char = 'Ш', brailleDots = BrailleDots(F, E, E, E, F, F)) - symbol(char = 'Щ', brailleDots = BrailleDots(F, E, F, F, E, F)) - symbol(char = 'Ъ', brailleDots = BrailleDots(F, F, F, E, F, F)) - symbol(char = 'Ы', brailleDots = BrailleDots(E, F, F, F, E, F)) - symbol(char = 'Ь', brailleDots = BrailleDots(E, F, F, F, F, F)) - symbol(char = 'Э', brailleDots = BrailleDots(E, F, E, F, E, F)) - symbol(char = 'Ю', brailleDots = BrailleDots(F, F, E, E, F, F)) - symbol(char = 'Я', brailleDots = BrailleDots(F, F, E, F, E, F)) -} + { + { _: Symbol -> true } to { _: Symbol -> "" } + } +) -private val specialSymbols by symbols(SymbolType.special) { - symbol(char = ']', brailleDots = BrailleDots(E, E, F, F, F, F)) // Number sign - symbol(char = ',', brailleDots = BrailleDots(E, F, E, E, E, E)) // Comma - symbol(char = '-', brailleDots = BrailleDots(E, E, F, E, E, F)) // Hyphen - symbol(char = '.', brailleDots = BrailleDots(E, F, E, E, F, F)) // Dot -} +val Context.inputMarkerPrintRules by rules( + { + val s = getString(R.string.input_mod_ru_capital) + MarkerType.RussianCapital::equals to { _: MarkerType -> s } + }, -private val uebDigits by symbols(SymbolType.digit) { - symbol(char = '1', brailleDots = BrailleDots(F, E, E, E, E, E)) - symbol(char = '2', brailleDots = BrailleDots(F, F, E, E, E, E)) - symbol(char = '3', brailleDots = BrailleDots(F, E, E, F, E, E)) - symbol(char = '4', brailleDots = BrailleDots(F, E, E, F, F, E)) - symbol(char = '5', brailleDots = BrailleDots(F, E, E, E, F, E)) - symbol(char = '6', brailleDots = BrailleDots(F, F, E, F, E, E)) - symbol(char = '7', brailleDots = BrailleDots(F, F, E, F, F, E)) - symbol(char = '8', brailleDots = BrailleDots(F, F, E, E, F, E)) - symbol(char = '9', brailleDots = BrailleDots(E, F, E, F, E, E)) - symbol(char = '0', brailleDots = BrailleDots(E, F, E, F, F, E)) -} + { + val s = getString(R.string.input_mod_greek_capital) + MarkerType.GreekCapital::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_mod_latin_capital) + MarkerType.LatinCapital::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_mod_latin_small) + MarkerType.LatinSmall::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_mod_bold) + MarkerType.BoldFont::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_mod_italic) + MarkerType.ItalicFont::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.input_mod_num_sign) + MarkerType.NumberSign::equals to { _: MarkerType -> s } + } +) + +val Context.showMarkerPrintRules by rules( + { + val s = getString(R.string.show_mod_capital) + MarkerType.RussianCapital::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_mod_greek) + MarkerType.GreekCapital::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_mod_latin) + MarkerType.LatinCapital::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_mod_latin) + MarkerType.LatinCapital::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_mod_latin_small) + MarkerType.LatinSmall::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_mod_bold) + MarkerType.BoldFont::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_mod_italic) + MarkerType.ItalicFont::equals to { _: MarkerType -> s } + }, + + { + val s = getString(R.string.show_mod_num_sign) + MarkerType.NumberSign::equals to { _: MarkerType -> s } + } +) diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/res/TestCourse.kt b/app/src/main/java/com/github/braillesystems/learnbraille/res/TestCourse.kt index 89c5acc1..5d38c1a2 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/res/TestCourse.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/res/TestCourse.kt @@ -29,14 +29,14 @@ internal val testLessons by lessons { ) +ShowDots( text = "Перед Вами полное шеститочие", - dots = BrailleDots(F, F, F, F, F, F) + brailleDots = BrailleDots(F, F, F, F, F, F) ) +InputDots( text = "Введите все шесть точек", - dots = BrailleDots(F, F, F, F, F, F) + brailleDots = BrailleDots(F, F, F, F, F, F) ) +Info( - """Откройте букварь на странице 12. + """Откройте пособие на странице 12. В верхней строке 14 раз повторён символ полного шеститочия.""" ).annotate(StepAnnotation.golubinaBookRequired) +Info( @@ -61,26 +61,34 @@ internal val testLessons by lessons { ) +Show(content.symbols.getValue('А')) +Info( - """Откройте букварь на странице 13. Вверху слева рельефно-графическое + """Откройте пособие на странице 13. Вверху слева рельефно-графическое изображение буквы А. Рядом после полного шеститочия пять раз повторена буква А точечным шрифтом.""" ).annotate(StepAnnotation.golubinaBookRequired) +Input(content.symbols.getValue('А')) + + // Testing marker symbols representation + val greek = content.markers.getValue(MarkerType.GreekCapital) + +Show(greek) + +Input(greek) + +Show(content.markers.getValue(MarkerType.LatinCapital)) + +Show(content.markers.getValue(MarkerType.RussianCapital)) + +Show(content.symbols.getValue('Б')) +Info( - """Снова изучим страницу 13 в букваре. Под строкой с буквой А - + """Снова изучим страницу 13 в пособии. Под строкой с буквой А - такая же с буквой Б.""" ).annotate(StepAnnotation.golubinaBookRequired) +Input(content.symbols.getValue('Б')) +Info( - """Ознакомьтесь с буквой Ц на странице 13 букваря. + """Ознакомьтесь с буквой Ц на странице 13 пособия. Строка с буквой Ц находится под строкой с буквой Б.""" ).annotate(StepAnnotation.golubinaBookRequired) +Show(content.symbols.getValue('Ц')) +Input(content.symbols.getValue('Ц')) +Info("""Symbols of other types for testing""") - +Input(content.symbols.getValue(']')) + +Input(content.symbols.getValue(',')) +Input(content.symbols.getValue('1')) +Input(content.symbols.getValue('2')) +Input(content.symbols.getValue('3')) @@ -96,4 +104,4 @@ internal val testLessons by lessons { """ ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/res/_DSL.kt b/app/src/main/java/com/github/braillesystems/learnbraille/res/_DSL.kt index d3668e9e..6b1d111d 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/res/_DSL.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/res/_DSL.kt @@ -1,9 +1,24 @@ package com.github.braillesystems.learnbraille.res import com.github.braillesystems.learnbraille.data.dsl.StepsBuilder +import com.github.braillesystems.learnbraille.data.dsl.annotate +import com.github.braillesystems.learnbraille.data.entities.Info import com.github.braillesystems.learnbraille.data.entities.Input +import com.github.braillesystems.learnbraille.data.entities.Show fun StepsBuilder.inputChars(chars: String): Unit = chars .map(Char::toUpperCase) .forEach { c -> +Input(content.symbols.getValue(c)) } + +fun StepsBuilder.showAndInputChars(chars: String): Unit = + chars.map(Char::toUpperCase).forEach { + +Show(content.symbols.getValue(it)) + +Input(content.symbols.getValue(it)) + } + +fun StepsBuilder.slateStylusLine(char: Char) { + +Info( + "Запишите на брайлевском приборе строку, состоящую из одного символа: $char." + ).annotate(StepAnnotation.slateStylusRequired) +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/Messages.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/Messages.kt index 87aa3c78..6e9626c9 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/Messages.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/Messages.kt @@ -3,29 +3,26 @@ package com.github.braillesystems.learnbraille.ui import android.content.Context import androidx.fragment.app.Fragment 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.entities.* +import com.github.braillesystems.learnbraille.res.inputMarkerPrintRules import com.github.braillesystems.learnbraille.res.inputSymbolPrintRules +import com.github.braillesystems.learnbraille.res.showMarkerPrintRules import com.github.braillesystems.learnbraille.res.showSymbolPrintRules +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode import com.github.braillesystems.learnbraille.utils.* -import timber.log.Timber -enum class PrintMode { - INPUT, SHOW -} +fun Fragment.showCorrectToast() = toast(getString(R.string.input_correct)) -fun Fragment.showCorrectToast(): Unit = toast(getString(R.string.input_correct)) +fun Fragment.showIncorrectToast(hint: String = "") = + toast("${getString(R.string.input_incorrect)} $hint") -fun Fragment.showIncorrectToast(c: Char? = null): Unit = - if (c == null) toast(getString(R.string.input_incorrect)) - else toast( - "${getString(R.string.input_incorrect)} " + - printString(c, PrintMode.INPUT).orEmpty() - ) +fun Fragment.dotsMode(mode: BrailleDotsViewMode): String = + when (mode) { + BrailleDotsViewMode.Writing -> getString(R.string.braille_dots_mode_writing) + BrailleDotsViewMode.Reading -> getString(R.string.braille_dots_mode_reading) + } -val Context.dotsHintRules by lazyWithContext> { +private val Context.dotsHintRules by lazyWithContext> { listOf( getString(R.string.input_dots_hint_1), getString(R.string.input_dots_hint_2), @@ -36,34 +33,33 @@ val Context.dotsHintRules by lazyWithContext> { ) } -fun Fragment.showHintDotsToast(expectedDots: BrailleDots) = - getString(R.string.input_dots_hint_template) - .format( - expectedDots.list - .mapIndexedNotNull { index, brailleDot -> - if (brailleDot == BrailleDot.F) application.dotsHintRules[index] else null - } - .joinToString(separator = ", ") - ) - .side { checkedToast(it) } +fun Fragment.showHintDotsToast(expectedDots: BrailleDots) { + val template = getString(R.string.input_dots_hint_template) + val hint = expectedDots + .filled + .joinToString(separator = ", ") { + contextNotNull.dotsHintRules[it - 1] + } + checkedToast(template.format(hint)) +} fun Fragment.showHintToast(expectedDots: BrailleDots) = checkedToast(getString(R.string.input_hint_template).format(expectedDots.spelling)) -fun Context.printString(c: Char, mode: PrintMode): String? = - when (mode) { - PrintMode.INPUT -> inputSymbolPrintRules[c] - PrintMode.SHOW -> showSymbolPrintRules[c] +fun Context.inputPrint(data: MaterialData): String = + when (data) { + is Symbol -> inputSymbolPrintRules.getValue(data.char) + is MarkerSymbol -> inputMarkerPrintRules.getValue(data.type) } -fun Context.printStringNotNullLogged(c: Char, mode: PrintMode): String = - printString(c, mode) ?: "".also { Timber.e("Intro should be available") } +fun Fragment.inputPrint(data: MaterialData): String = + contextNotNull.inputPrint(data) -fun Fragment.printString(c: Char, mode: PrintMode): String? = - (context ?: null.also { Timber.w("Context is not available") }) - ?.run { printString(c, mode) } +fun Context.showPrint(data: MaterialData): String = + when (data) { + is Symbol -> showSymbolPrintRules.getValue(data.char) + is MarkerSymbol -> showMarkerPrintRules.getValue(data.type) + } -fun Fragment.printStringNotNullLogged(c: Char, mode: PrintMode): String = - context - ?.printStringNotNullLogged(c, mode) - ?: error("Context is not available") +fun Fragment.showPrint(data: MaterialData): String = + contextNotNull.showPrint(data) diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragment.kt new file mode 100644 index 00000000..58c7eb12 --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragment.kt @@ -0,0 +1,72 @@ +package com.github.braillesystems.learnbraille.ui.screens + +import android.util.TypedValue +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.fragment.app.Fragment +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsView +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode +import com.github.braillesystems.learnbraille.utils.contextNotNull +import com.github.braillesystems.learnbraille.utils.extendedTextSize +import com.github.braillesystems.learnbraille.utils.forEach +import com.github.braillesystems.learnbraille.utils.setSize +import org.koin.android.ext.android.inject + +data class BrailleDotsInfo( + val view: BrailleDotsView, + val mode: BrailleDotsViewMode, + val prev: View, + val next: View +) + +interface FragmentBinding { + val leftButton: Button? get() = null + val rightButton: Button? get() = null + val leftMiddleButton: Button? get() = null + val rightMiddleButton: Button? get() = null + val textView: TextView? get() = null + val brailleDotsInfo: BrailleDotsInfo? get() = null +} + +abstract class AbstractFragment : Fragment() { + + protected var binding: FragmentBinding = object : FragmentBinding {} + protected val preferenceRepository: PreferenceRepository by inject() + + protected fun B.ini( + getBinding: B.() -> FragmentBinding = { + object : FragmentBinding {} + } + ) = this.also { + binding = getBinding().apply { + brailleDotsInfo?.let { (view, mode, prev, next) -> + view.setMode(mode, prev, next) + } + if (preferenceRepository.extendedAccessibilityEnabled) { + forEach( + leftButton, + rightButton, + leftMiddleButton, + rightMiddleButton + ) { + it?.setSize( + width = resources + .getDimension(R.dimen.side_buttons_extended_width) + .toInt() + ) + } + textView?.setTextSize( + TypedValue.COMPLEX_UNIT_SP, + contextNotNull.extendedTextSize + ) + } + } + + iniHelper() + } + + protected open fun iniHelper() = Unit +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt index 73bd6254..d7e6fcfc 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt @@ -3,7 +3,6 @@ package com.github.braillesystems.learnbraille.ui.screens import android.view.Menu import android.view.MenuInflater import android.view.MenuItem -import androidx.fragment.app.Fragment import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.ui.screens.help.HelpFragmentDirections import com.github.braillesystems.learnbraille.utils.navigate @@ -11,10 +10,11 @@ import timber.log.Timber typealias HelpMsgId = Int -/** - * Do not forget to add in onCreate `setHasOptionsMenu(true)` - */ -abstract class AbstractFragmentWithHelp(private val helpMsgId: HelpMsgId) : Fragment() { +abstract class AbstractFragmentWithHelp(private val helpMsgId: HelpMsgId) : AbstractFragment() { + + override fun iniHelper() { + setHasOptionsMenu(true) + } protected open val helpMsg: String get() = getString(helpMsgId) diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/DotsChecker.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/DotsChecker.kt index 41732c16..6b57e048 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/DotsChecker.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/DotsChecker.kt @@ -77,7 +77,10 @@ interface MutableDotsChecker : DotsChecker { } /** - * Initialize lateinit callbacks firstly + * Initialize lateinit callbacks firstly. + * + * Callbacks are called before changing state, + * so they can access previous state (the next one is obvious). */ private class DotsCheckerImpl : MutableDotsChecker { @@ -125,7 +128,7 @@ private class DotsCheckerImpl : MutableDotsChecker { private val isCorrect: Boolean get() = (enteredDots == expectedDots).also { Timber.i( - if (it) "Correct: " + if (it) "Correct" else "Incorrect: entered = $enteredDots, expected = $expectedDots" ) } @@ -196,103 +199,104 @@ private class DotsCheckerImpl : MutableDotsChecker { } } -inline fun DotsChecker.observeCheckedOnFly( +@Suppress("LongParameterList") +fun DotsChecker.observeCheckedOnFly( lifecycleOwner: LifecycleOwner, - dotsState: BrailleDotsState, + getDotsState: () -> BrailleDotsState, buzzer: Vibrator? = null, preferenceRepository: PreferenceRepository = get(), - crossinline block: () -> Unit = {}, - crossinline softBlock: () -> Unit = {} + block: () -> Unit = {}, + softBlock: () -> Unit = {} ) { if (preferenceRepository.inputOnFlyCheck) { - observeEventCorrect(lifecycleOwner, dotsState, buzzer = null, block = block) + observeEventCorrect(lifecycleOwner, getDotsState, buzzer = null, block = block) observeEventSoftCorrect(lifecycleOwner, buzzer = buzzer, block = softBlock) } else { - observeEventCorrect(lifecycleOwner, dotsState, buzzer) { + observeEventCorrect(lifecycleOwner, getDotsState, buzzer) { softBlock() block() } } } -inline fun DotsChecker.observeEventCorrect( +fun DotsChecker.observeEventCorrect( lifecycleOwner: LifecycleOwner, - dotsState: BrailleDotsState, + getDotsState: () -> BrailleDotsState, buzzer: Vibrator? = null, preferenceRepository: PreferenceRepository = get(), - crossinline block: () -> Unit = {} + block: () -> Unit = {} ): Unit = eventCorrect.observe( lifecycleOwner, Observer { if (!it) return@Observer Timber.i("Handle correct") - buzzer.checkedBuzz(preferenceRepository.correctBuzzPattern, preferenceRepository) - dotsState.uncheck() + buzzer.checkedBuzz(preferenceRepository.correctBuzzPattern) + getDotsState().uncheck() block() onCorrectComplete() } ) -inline fun DotsChecker.observeEventSoftCorrect( +fun DotsChecker.observeEventSoftCorrect( lifecycleOwner: LifecycleOwner, buzzer: Vibrator? = null, preferenceRepository: PreferenceRepository = get(), - crossinline block: () -> Unit = {} + block: () -> Unit = {} ): Unit = eventSoftCorrect.observe( lifecycleOwner, Observer { if (!it) return@Observer Timber.i("Handle soft correct") - buzzer.checkedBuzz(preferenceRepository.correctBuzzPattern, preferenceRepository) + buzzer.checkedBuzz(preferenceRepository.correctBuzzPattern) block() onSoftCorrectComplete() } ) -inline fun DotsChecker.observeEventIncorrect( +fun DotsChecker.observeEventIncorrect( lifecycleOwner: LifecycleOwner, - dotsState: BrailleDotsState, + getDotsState: () -> BrailleDotsState, buzzer: Vibrator? = null, preferenceRepository: PreferenceRepository = get(), - crossinline block: () -> Unit = {} + block: () -> Unit = {} ): Unit = eventIncorrect.observe( lifecycleOwner, Observer { if (!it) return@Observer - Timber.i("Handle incorrect: entered = ${dotsState.spelling}") - buzzer.checkedBuzz(preferenceRepository.incorrectBuzzPattern, preferenceRepository) - dotsState.uncheck() + Timber.i("Handle incorrect: entered = ${getDotsState().spelling}") + buzzer.checkedBuzz(preferenceRepository.incorrectBuzzPattern) + getDotsState().uncheck() block() onIncorrectComplete() } ) -inline fun DotsChecker.observeEventHint( +fun DotsChecker.observeEventHint( lifecycleOwner: LifecycleOwner, - dotsState: BrailleDotsState, - crossinline block: (BrailleDots) -> Unit = {} + getDotsState: () -> BrailleDotsState, + block: (BrailleDots) -> Unit = {} ): Unit = eventHint.observe( lifecycleOwner, Observer { expectedDots -> if (expectedDots == null) return@Observer Timber.i("Handle hint") - dotsState.display(expectedDots) + getDotsState().display(expectedDots) BrailleTrainer.trySend(expectedDots) block(expectedDots) onHintComplete() } ) -inline fun DotsChecker.observeEventPassHint( +fun DotsChecker.observeEventPassHint( lifecycleOwner: LifecycleOwner, - dotsState: BrailleDotsState, - crossinline block: () -> Unit = {} + getDotsState: () -> BrailleDotsState, + block: () -> Unit = {} ): Unit = eventPassHint.observe( lifecycleOwner, Observer { if (!it) return@Observer - dotsState.uncheck() - dotsState.clickable(true) + getDotsState().uncheck() + getDotsState().clickable(true) block() onPassHintComplete() } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/BrowserFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/BrowserFragment.kt index 922bbb34..eb8eaa15 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/BrowserFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/BrowserFragment.kt @@ -7,15 +7,19 @@ import androidx.databinding.DataBindingUtil import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.MarkerSymbol import com.github.braillesystems.learnbraille.data.entities.Material import com.github.braillesystems.learnbraille.data.entities.Symbol import com.github.braillesystems.learnbraille.data.entities.spelling import com.github.braillesystems.learnbraille.data.repository.BrowserRepository import com.github.braillesystems.learnbraille.databinding.BrowserListItemBinding import com.github.braillesystems.learnbraille.databinding.FragmentBrowserBinding +import com.github.braillesystems.learnbraille.res.showMarkerPrintRules import com.github.braillesystems.learnbraille.res.showSymbolPrintRules import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp -import com.github.braillesystems.learnbraille.utils.* +import com.github.braillesystems.learnbraille.utils.getValue +import com.github.braillesystems.learnbraille.utils.navigate +import com.github.braillesystems.learnbraille.utils.stringify import kotlinx.coroutines.launch import org.koin.android.ext.android.inject @@ -32,21 +36,22 @@ class BrowserFragment : AbstractFragmentWithHelp(R.string.browser_help) { R.layout.fragment_browser, container, false - ).also { binding -> - - title = getString(R.string.browser_title) - setHasOptionsMenu(true) + ).ini().also { binding -> lifecycleScope.launch { val deckId = browserRepository.currentDeckId - val materials = browserRepository.getAllMaterialsFromDeck(deckId) + val materials = browserRepository.allMaterialsFromDeck(deckId) val listener = object : BrowserItemListener { override fun onClick(item: Material) { val arg = stringify(Material.serializer(), item) when (item.data) { is Symbol -> navigate( BrowserFragmentDirections - .actionBrowserFragmentToMaterialViewFragment(arg) + .actionBrowserFragmentToSymbolViewFragment(arg) + ) + is MarkerSymbol -> navigate( + BrowserFragmentDirections + .actionBrowserFragmentToMarkerViewFragment(arg) ) } } @@ -55,7 +60,11 @@ class BrowserFragment : AbstractFragmentWithHelp(R.string.browser_help) { this.item = item materialText.text = when (item.data) { is Symbol -> getString(R.string.browser_represent_template).format( - application.showSymbolPrintRules[item.data.char].toString(), + showSymbolPrintRules.getValue(item.data.char), + item.data.brailleDots.spelling + ) + is MarkerSymbol -> getString(R.string.browser_represent_template).format( + showMarkerPrintRules.getValue(item.data.type), item.data.brailleDots.spelling ) } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/MarkerViewFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/MarkerViewFragment.kt new file mode 100644 index 00000000..555f3cd3 --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/MarkerViewFragment.kt @@ -0,0 +1,59 @@ +package com.github.braillesystems.learnbraille.ui.screens.browser + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView +import androidx.databinding.DataBindingUtil +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.MarkerSymbol +import com.github.braillesystems.learnbraille.data.entities.Material +import com.github.braillesystems.learnbraille.databinding.FragmentMarkerViewBinding +import com.github.braillesystems.learnbraille.res.showMarkerPrintRules +import com.github.braillesystems.learnbraille.ui.dotsMode +import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp +import com.github.braillesystems.learnbraille.ui.screens.BrailleDotsInfo +import com.github.braillesystems.learnbraille.ui.screens.FragmentBinding +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode +import com.github.braillesystems.learnbraille.ui.views.display +import com.github.braillesystems.learnbraille.ui.views.dotsState +import com.github.braillesystems.learnbraille.utils.* + +class MarkerViewFragment : AbstractFragmentWithHelp(R.string.browser_marker_view_help) { + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_marker_view, + container, + false + ).ini { + object : FragmentBinding { + override val textView: TextView? = this@ini.infoTextView + override val rightButton: Button? = this@ini.flipButton + override val brailleDotsInfo: BrailleDotsInfo? = this@ini.run { + BrailleDotsInfo(brailleDots, BrailleDotsViewMode.Reading, infoTextView, flipButton) + } + } + }.apply { + + val m: Material = parse(Material.serializer(), getFragmentStringArg("material")) + require(m.data is MarkerSymbol) + + val text = showMarkerPrintRules.getValue(m.data.type) + infoTextView.text = text + checkedAnnounce(text) + + brailleDots.dotsState.display(m.data.brailleDots) + checkedToast(dotsMode(brailleDots.mode)) + flipButton.setOnClickListener { + brailleDots.reflect().display(m.data.brailleDots) + checkedToast(dotsMode(brailleDots.mode)) + } + + }.root +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/SymbolViewFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/SymbolViewFragment.kt index 86c2ae1f..ef427766 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/SymbolViewFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/browser/SymbolViewFragment.kt @@ -3,17 +3,22 @@ package com.github.braillesystems.learnbraille.ui.screens.browser import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup +import android.widget.Button import androidx.databinding.DataBindingUtil import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.data.entities.Material import com.github.braillesystems.learnbraille.data.entities.Symbol import com.github.braillesystems.learnbraille.databinding.FragmentSymbolViewBinding +import com.github.braillesystems.learnbraille.res.captionRules +import com.github.braillesystems.learnbraille.ui.dotsMode import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp +import com.github.braillesystems.learnbraille.ui.screens.BrailleDotsInfo +import com.github.braillesystems.learnbraille.ui.screens.FragmentBinding +import com.github.braillesystems.learnbraille.ui.showPrint +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode import com.github.braillesystems.learnbraille.ui.views.display import com.github.braillesystems.learnbraille.ui.views.dotsState -import com.github.braillesystems.learnbraille.utils.getFragmentStringArg -import com.github.braillesystems.learnbraille.utils.parse -import com.github.braillesystems.learnbraille.utils.title +import com.github.braillesystems.learnbraille.utils.* class SymbolViewFragment : AbstractFragmentWithHelp(R.string.browser_symbol_view_help) { @@ -26,16 +31,28 @@ class SymbolViewFragment : AbstractFragmentWithHelp(R.string.browser_symbol_view R.layout.fragment_symbol_view, container, false - ).also { binding -> - - title = getString(R.string.browser_symbol_view_title) - setHasOptionsMenu(true) + ).ini { + object : FragmentBinding { + override val rightButton: Button? = this@ini.flipButton + override val brailleDotsInfo: BrailleDotsInfo? = this@ini.run { + BrailleDotsInfo(brailleDots, BrailleDotsViewMode.Reading, letter, flipButton) + } + } + }.apply { val m: Material = parse(Material.serializer(), getFragmentStringArg("material")) require(m.data is Symbol) - binding.letter.text = m.data.char.toString() - binding.brailleDots.dotsState.display(m.data.brailleDots) + letter.letter = m.data.char + letterCaption.text = captionRules.getValue(m.data) + checkedAnnounce(showPrint(m.data)) + + brailleDots.dotsState.display(m.data.brailleDots) + checkedToast(dotsMode(brailleDots.mode)) + flipButton.setOnClickListener { + brailleDots.reflect().display(m.data.brailleDots) + checkedToast(dotsMode(brailleDots.mode)) + } }.root } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt index 95ca0112..9506a7df 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,23 +1,18 @@ package com.github.braillesystems.learnbraille.ui.screens.exit -import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup import androidx.databinding.DataBindingUtil -import androidx.fragment.app.Fragment import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.databinding.FragmentExitBinding -import com.github.braillesystems.learnbraille.utils.SpeechRecognition +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.updateTitle -import org.koin.android.ext.android.get +import com.github.braillesystems.learnbraille.utils.navigateToLauncher +import com.github.braillesystems.learnbraille.utils.title - -class ExitFragment : Fragment() { - - private lateinit var recognizer: SpeechRecognition +class ExitFragment : AbstractFragment() { override fun onCreateView( inflater: LayoutInflater, @@ -28,19 +23,14 @@ class ExitFragment : Fragment() { R.layout.fragment_exit, container, false - ).apply { + ).ini().apply { - val title: String = getString(R.string.exit_question) - updateTitle(title) + title = getString(R.string.exit_question) checkedAnnounce(title) - recognizer = SpeechRecognition(this@ExitFragment, get()) - exitButton.setOnClickListener { - val homeIntent = Intent(Intent.ACTION_MAIN) - homeIntent.addCategory(Intent.CATEGORY_HOME) - homeIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP - startActivity(homeIntent) + navigate(R.id.action_global_menuFragment) + navigateToLauncher() } continueButton.setOnClickListener { @@ -48,9 +38,4 @@ class ExitFragment : Fragment() { } }.root - - override fun onDestroy() { - super.onDestroy() - recognizer.onDestroy() - } } 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 cd5407f0..23eb22a9 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 @@ -4,15 +4,14 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup import androidx.databinding.DataBindingUtil -import androidx.fragment.app.Fragment 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 -import com.github.braillesystems.learnbraille.utils.title -class HelpFragment : Fragment() { +class HelpFragment : AbstractFragment() { private val helpMessageArgName = "help_message" @@ -24,9 +23,7 @@ class HelpFragment : Fragment() { R.layout.fragment_help, container, false - ).apply { - - title = getString(R.string.help_title) + ).ini().apply { val content = getFragmentStringArg(helpMessageArgName) helpView.setSeparatedText(content) 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 ccc18f7b..5a9e2ae9 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,11 +1,8 @@ package com.github.braillesystems.learnbraille.ui.screens.menu -import android.Manifest import android.app.Activity.RESULT_OK import android.content.ActivityNotFoundException import android.content.Intent -import android.content.pm.PackageManager -import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -15,7 +12,6 @@ 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.data.repository.PreferenceRepository import com.github.braillesystems.learnbraille.databinding.FragmentMenuBinding import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp import com.github.braillesystems.learnbraille.ui.screens.theory.toLastCourseStep @@ -27,7 +23,6 @@ import timber.log.Timber class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { private val db: LearnBrailleDatabase by inject() - private val preferenceRepository: PreferenceRepository by inject() override fun onCreateView( inflater: LayoutInflater, @@ -38,11 +33,9 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { R.layout.fragment_menu, container, false - ).also { binding -> + ).ini().also { binding -> title = getString(R.string.menu_actionbar_text_template).format(appName) - setHasOptionsMenu(true) - requestPermissions() val buttons = mutableListOf() @@ -125,28 +118,6 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { } } - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - when (requestCode) { - recordAudioPermissionCode -> if (grantResults.first() != PackageManager.PERMISSION_GRANTED) { - toast(getString(R.string.voice_record_denial)) - } - } - } - - private fun requestPermissions() { - runIf(preferenceRepository.speechRecognitionEnabled) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return@runIf - val permission = requireContext().checkSelfPermission(Manifest.permission.RECORD_AUDIO) - if (permission == PackageManager.PERMISSION_GRANTED) return@runIf - requestPermissions(arrayOf(Manifest.permission.RECORD_AUDIO), recordAudioPermissionCode) - } - } - private fun interruptingOnClickListener(block: (View) -> Unit) = View.OnClickListener { if (db.isInitialized) block(it) @@ -187,6 +158,5 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { companion object { private const val qrRequestCode = 0 - private const val recordAudioPermissionCode = 29 } } 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 cab053f3..8590d13b 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 @@ -3,22 +3,24 @@ 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.repository.PreferenceRepository +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.BrailleDotsState -import com.github.braillesystems.learnbraille.ui.views.brailleDots -import com.github.braillesystems.learnbraille.ui.views.dotsState -import com.github.braillesystems.learnbraille.ui.views.subscribe +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 @@ -26,20 +28,10 @@ import timber.log.Timber class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { - private val preferenceRepository: PreferenceRepository by inject() - - private lateinit var viewModel: CardViewModel + // 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 - private var buzzer: Vibrator? = null - - private val title: String - get() = getString(R.string.practice_actionbar_title_template).let { - if (::viewModel.isInitialized) it.format( - viewModel.nCorrect, - viewModel.nTries - ) - else it.format(0, 0) - } override fun onCreateView( inflater: LayoutInflater, @@ -50,72 +42,101 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { R.layout.fragment_card, container, false - ).apply { + ).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") - updateTitle(title) - setHasOptionsMenu(true) - - if (preferenceRepository.extendedAccessibilityEnabled) { - hintButton.setSize( - width = resources.getDimension(R.dimen.side_buttons_extended_width).toInt() - ) - nextButton.setSize( - width = resources.getDimension(R.dimen.side_buttons_extended_width).toInt() - ) - } + title = title() - dotsState = brailleDots.dotsState.apply { - subscribe(View.OnClickListener { - viewModel.onSoftCheck() - }) - } + dotsState = binding.brailleDots.dotsState val viewModelFactory: CardViewModelFactory by inject { parametersOf({ dotsState.brailleDots }) } - viewModel = ViewModelProvider( - this@CardFragment, viewModelFactory - ).get(CardViewModel::class.java) - buzzer = activity?.getSystemService() + 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 - cardViewModel = viewModel - lifecycleOwner = this@CardFragment + binding.flipButton.setOnClickListener { + dotsState = binding.brailleDots.reflect().apply { + dotsState.subscribe(viewModel) + checkedToast(dotsMode(binding.brailleDots.mode)) + 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 = { updateTitle(title) }, + viewLifecycleOwner, { dotsState }, buzzer, + block = { title = title(viewModel) }, softBlock = ::showCorrectToast ) viewModel.observeEventIncorrect( - viewLifecycleOwner, dotsState, buzzer + viewLifecycleOwner, { dotsState }, buzzer ) { - viewModel.symbol.value?.let { symbol -> - require(symbol.length == 1) - showIncorrectToast(symbol.first()) - } ?: checkedToast(getString(R.string.input_loading)) - updateTitle(title) + viewModel.symbol.value + ?.let { showIncorrectToast(inputPrint(it)) } + ?: checkedToast(getString(R.string.input_loading)) + title = title(viewModel) } viewModel.observeEventHint( - viewLifecycleOwner, dotsState + viewLifecycleOwner, { dotsState } ) { expectedDots -> showHintToast(expectedDots) } viewModel.observeEventPassHint( - viewLifecycleOwner, dotsState + viewLifecycleOwner, { dotsState } ) { viewModel.symbol.value?.let { - announceIntro(it) + checkedAnnounce(inputPrint(it)) } } @@ -123,7 +144,7 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { viewLifecycleOwner, Observer { if (it == null) return@Observer - announceIntro(it) + checkedAnnounce(inputPrint(it)) } ) @@ -136,17 +157,31 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { } else { getString(R.string.practice_deck_name_disabled_template) } - toast(template.format(deckTagToName.getValue(tag))) + toast( + template.format( + deckTagToName.getValue(tag), + dotsMode(binding.brailleDots.mode) + ) + ) } ) + if (viewModel.state == DotsChecker.State.HINT) { + viewModel.expectedDots?.let { dotsState.display(it) } + } + }.root - private fun announceIntro(symbol: String) { - require(symbol.length == 1) - val intro = printStringNotNullLogged(symbol.first(), PrintMode.INPUT) - checkedAnnounce(intro) - } + 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) 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 2ff2091a..acb85db0 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 @@ -2,13 +2,15 @@ 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 com.github.braillesystems.learnbraille.utils.tryN import kotlinx.coroutines.Job import kotlinx.coroutines.launch import timber.log.Timber @@ -18,6 +20,7 @@ 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 { @@ -25,7 +28,13 @@ class CardViewModelFactory( override fun create(modelClass: Class): T = if (modelClass.isAssignableFrom(CardViewModel::class.java)) { @Suppress("UNCHECKED_CAST") - CardViewModel(practiceRepository, actionsRepository, application, getEnteredDots) as T + CardViewModel( + practiceRepository, + actionsRepository, + preferenceRepository, + application, + getEnteredDots + ) as T } else { throw IllegalArgumentException("Unknown ViewModel class") } @@ -34,17 +43,18 @@ class CardViewModelFactory( 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 _symbol = MutableLiveData() + val symbol: LiveData get() = _symbol - private val _deckTag = MutableLiveData() - val deckTag: LiveData get() = _deckTag + private val _deckTag = MutableLiveData() + val deckTag: LiveData get() = _deckTag var nTries: Int = 0 private set @@ -52,7 +62,8 @@ class CardViewModel( var nCorrect: Int = 0 private set - private var expectedDots: BrailleDots? = null + var expectedDots: BrailleDots? = null + private set private val job = Job() private val uiScope = scope(job) @@ -98,29 +109,42 @@ class CardViewModel( } private fun initializeCard(firstTime: Boolean = false) = uiScope.launch { - val material = tryN( - n = 5, - stop = { it.data !in materialsQueue }, - get = { practiceRepository.getNextMaterialNotNull() } - ) ?: practiceRepository.getNextMaterialNotNull() + // 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) } - require(material.data is Symbol) - material.data.run { - _symbol.value = char.toString() - expectedDots = brailleDots + material.data.let { + _symbol.value = it + expectedDots = when (it) { + is OneBrailleSymbol -> it.brailleDots + } } + } - // Should be called after getting material because deck changes automatically - // if `use only known materials` enabled and previous `currentDeck` - // became not available. - if (firstTime) { - val deck = practiceRepository.getCurrDeck() - _deckTag.value = deck.tag + 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/practice/DecksListFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksListFragment.kt index 283e7e53..d48de23f 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksListFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksListFragment.kt @@ -1,32 +1,30 @@ package com.github.braillesystems.learnbraille.ui.screens.practice +import android.graphics.Typeface 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 androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.data.repository.DeckNotEmpty +import com.github.braillesystems.learnbraille.data.repository.DeckWithAvailability import com.github.braillesystems.learnbraille.data.repository.MutablePracticeRepository -import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository import com.github.braillesystems.learnbraille.databinding.DecksListItemBinding import com.github.braillesystems.learnbraille.databinding.FragmentDecksListBinding import com.github.braillesystems.learnbraille.res.deckTagToName +import com.github.braillesystems.learnbraille.ui.screens.AbstractFragment import com.github.braillesystems.learnbraille.utils.application import com.github.braillesystems.learnbraille.utils.checkedToast import com.github.braillesystems.learnbraille.utils.navigate -import com.github.braillesystems.learnbraille.utils.title import kotlinx.coroutines.launch import org.koin.android.ext.android.inject -class DecksListFragment : Fragment() { +class DecksListFragment : AbstractFragment() { private val practiceRepository: MutablePracticeRepository by inject() - private val preferenceRepository: PreferenceRepository by inject() override fun onCreateView( inflater: LayoutInflater, @@ -37,14 +35,13 @@ class DecksListFragment : Fragment() { R.layout.fragment_decks_list, container, false - ).apply { - - title = getString(R.string.decks_list_title) + ).ini().apply { lifecycleScope.launch { - val decks = practiceRepository.getAllDecks() + val decks = practiceRepository.allDecksWithAvailability() + val currDeck = practiceRepository.currentDeck() val listener = object : DecksItemListener { - override fun onClick(item: DeckNotEmpty) = + override fun onClick(item: DeckWithAvailability) = if (item.containsCards) { practiceRepository.currentDeckId = item.deck.id navigate(R.id.action_decksList_to_cardFragment) @@ -58,6 +55,11 @@ class DecksListFragment : Fragment() { decksList.adapter = DecksListAdapter(decks) { item -> this.item = item deckName.text = deckTagToName.getValue(item.deck.tag) + deckName.setTypeface( + deckName.typeface, + if (item.deck == currDeck) Typeface.BOLD + else Typeface.NORMAL + ) clickListener = listener if (item.containsCards) { deckName.setTextColor( @@ -86,8 +88,8 @@ class DecksListFragment : Fragment() { } private class DecksListAdapter( - private val decks: List, - private val bind: DecksListItemBinding.(DeckNotEmpty) -> Unit + private val decks: List, + private val bind: DecksListItemBinding.(DeckWithAvailability) -> Unit ) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = @@ -112,5 +114,5 @@ private class DeckItemViewHolder( ) : RecyclerView.ViewHolder(binding.root) interface DecksItemListener { - fun onClick(item: DeckNotEmpty) + fun onClick(item: DeckWithAvailability) } 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 4e8a427f..c9a45232 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,13 +1,11 @@ -package com.github.braillesystems.learnbraille.ui.screens.settings - -import android.os.Bundle -import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.utils.title - -class SettingsFragment : androidx.preference.PreferenceFragmentCompat() { - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - setPreferencesFromResource(R.xml.settings_hierarchy, rootKey) - title = getString(R.string.preferences_actionbar_title) - } -} \ No newline at end of file +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/screens/stats/StatsFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/stats/StatsFragment.kt index b01f4684..f404903e 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/stats/StatsFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/stats/StatsFragment.kt @@ -13,8 +13,8 @@ import com.github.braillesystems.learnbraille.data.repository.ActionsRepository import com.github.braillesystems.learnbraille.databinding.FragmentStatsBinding import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp import com.github.braillesystems.learnbraille.utils.Days +import com.github.braillesystems.learnbraille.utils.forEach import com.github.braillesystems.learnbraille.utils.scope -import com.github.braillesystems.learnbraille.utils.title import kotlinx.android.synthetic.main.fragment_stats.* import kotlinx.android.synthetic.main.fragment_stats_table.view.* import kotlinx.coroutines.Job @@ -35,35 +35,33 @@ class StatsFragment : AbstractFragmentWithHelp(R.string.stats_help) { R.layout.fragment_stats, container, false - ).also { binding -> - - title = getString(R.string.stats_title) - setHasOptionsMenu(true) + ).ini().also { scope(job).launch { - val statsList = listOf(stats_week to 7, stats_month to 30) - for (statsData in statsList) { - val actions: Actions = actionsRepository.getActionsFrom(Days(statsData.second)) + forEach(stats_week to 7, stats_month to 30) { (view, days) -> + val actions: Actions = actionsRepository.actionsFrom(Days(days)) + val cardsMastered = actions.count { it.type is PracticeSubmission && it.type.isCorrect } val hintsUsed = actions.count { it.type is PracticeHintAction } val totalAttempts = actions.count { it.type is PracticeSubmission } + view.apply { + practice_mastered_cards.text = cardsMastered.toString() + practice_hints.text = hintsUsed.toString() + practice_total_attempts.text = totalAttempts.toString() + } val theoryStepsPassed = actions.count { it.type is TheoryPassStep } val theoryInputStepsPassed = actions.count { it.type is TheoryPassStep && it.type.isInput } - - statsData.first.apply { - practice_mastered_cards.text = """$cardsMastered""" - practice_hints.text = """$hintsUsed""" - practice_total_attempts.text = """$totalAttempts""" - - theory_steps_passed.text = """$theoryStepsPassed""" - theory_input_steps_passed.text = """$theoryInputStepsPassed""" + view.apply { + theory_steps_passed.text = theoryStepsPassed.toString() + theory_input_steps_passed.text = theoryInputStepsPassed.toString() } } } + }.root override fun onDestroy() = super.onDestroy().also { job.cancel() } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/TheoryFreeNavigation.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/TheoryFreeNavigation.kt index 36cfb111..cf26d453 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/TheoryFreeNavigation.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/TheoryFreeNavigation.kt @@ -27,9 +27,11 @@ fun getAction(step: Step): NavDirections = is ShowDots -> MenuFragmentDirections.actionGlobalShowDotsFragment(arg) is Input -> when (step.data.material.data) { is Symbol -> MenuFragmentDirections.actionGlobalInputSymbolFragment(arg) + is MarkerSymbol -> MenuFragmentDirections.actionGlobalInputMarkerFragment(arg) } is Show -> when (step.data.material.data) { is Symbol -> MenuFragmentDirections.actionGlobalShowSymbolFragment(arg) + is MarkerSymbol -> MenuFragmentDirections.actionGlobalShowMarkerFragment(arg) } } } @@ -47,7 +49,7 @@ fun AbstractStepFragment.toNextStep( ): Unit = if (thisStep.data is LastInfo) Timber.w("Tying to access step after last") else scope().launch { - val nextStep = theoryRepository.getNextStepAndUpdate(thisStep, markThisAsPassed) + val nextStep = theoryRepository.nextStepAndMove(thisStep, markThisAsPassed) if (nextStep != null) { toStep(nextStep) } else { @@ -62,7 +64,7 @@ fun AbstractStepFragment.toPrevStep( ): Unit = if (thisStep.data is FirstInfo) Timber.w("Trying to access step before first") else scope().launch { - theoryRepository.getPrevStepAndUpdate(thisStep) + theoryRepository.prevStepAndMove(thisStep) ?.let(::toStep) ?: error("Prev step should always exist") }.devnull @@ -71,7 +73,7 @@ fun AbstractStepFragment.toCurrentStep( courseId: Long, theoryRepository: MutableTheoryRepository = get() ): Unit = scope().launch { - val currStep = theoryRepository.getCurrentStepAndUpdate(courseId) + val currStep = theoryRepository.currentStepAndMove(courseId) toStep(currStep) }.devnull @@ -79,13 +81,13 @@ fun Fragment.toLastCourseStep( courseId: Long, theoryRepository: TheoryRepository = get() ): Unit = scope().launch { - val lastStep = theoryRepository.getLastCourseStep(courseId) + val lastStep = theoryRepository.lastCourseStep(courseId) toStep(lastStep) }.devnull fun Fragment.toLastLessonStep( courseId: Long, lessonId: Long, theoryRepository: MutableTheoryRepository = get() ): Unit = scope().launch { - val lastStep = theoryRepository.getLastLessonOrCurrentStepAndUpdate(courseId, lessonId) + val lastStep = theoryRepository.lastLessonOrCurrentStepAndMove(courseId, lessonId) toStep(lastStep) }.devnull diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt index d9ce8bab..37addef9 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt @@ -1,12 +1,12 @@ package com.github.braillesystems.learnbraille.ui.screens.theory.lessons import android.annotation.SuppressLint +import android.graphics.Typeface import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil -import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import com.github.braillesystems.learnbraille.COURSE @@ -15,14 +15,14 @@ import com.github.braillesystems.learnbraille.data.entities.Lesson import com.github.braillesystems.learnbraille.data.repository.TheoryRepository import com.github.braillesystems.learnbraille.databinding.FragmentLessonsListBinding import com.github.braillesystems.learnbraille.databinding.LessonsListItemBinding +import com.github.braillesystems.learnbraille.ui.screens.AbstractFragment import com.github.braillesystems.learnbraille.ui.screens.theory.toLastLessonStep import com.github.braillesystems.learnbraille.utils.application import com.github.braillesystems.learnbraille.utils.checkedToast -import com.github.braillesystems.learnbraille.utils.title import kotlinx.coroutines.launch import org.koin.android.ext.android.inject -class LessonsListFragment : Fragment() { +class LessonsListFragment : AbstractFragment() { private val theoryRepository: TheoryRepository by inject() @@ -38,11 +38,10 @@ class LessonsListFragment : Fragment() { false ).apply { - title = getString(R.string.lessons_title_lessons_list) - lifecycleScope.launch { - val curr = theoryRepository.getCurrentStep(COURSE.id) - val lessons = theoryRepository.getAllCourseLessons(COURSE.id) + val curr = theoryRepository.currentStep(COURSE.id) + val lessons = theoryRepository.allCourseLessons(COURSE.id) + val last = theoryRepository.lastCourseStep(curr.courseId) val activeListener = object : LessonItemListener { override fun onClick(item: Lesson) = toLastLessonStep(COURSE.id, item.id) } @@ -56,6 +55,11 @@ class LessonsListFragment : Fragment() { val adapter = LessonsListAdapter(lessons) { item -> lesson = item lessonName.text = "${item.id}. ${item.name}" + lessonName.setTypeface( + lessonName.typeface, + if (item.id == last.lessonId) Typeface.BOLD + else Typeface.NORMAL + ) if (item.id <= curr.lessonId) { clickListener = activeListener lessonName.setTextColor( diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractInfoStepFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractInfoStepFragment.kt deleted file mode 100644 index 31cf7165..00000000 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractInfoStepFragment.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.github.braillesystems.learnbraille.ui.screens.theory.steps - -import com.github.braillesystems.learnbraille.ui.screens.HelpMsgId - -abstract class AbstractInfoStepFragment(helpMsgId: HelpMsgId) : AbstractStepFragment(helpMsgId) diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt index 8f790d3b..110f2a5c 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt @@ -1,89 +1,91 @@ package com.github.braillesystems.learnbraille.ui.screens.theory.steps import android.text.method.ScrollingMovementMethod -import android.util.TypedValue import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.widget.Button import android.widget.TextView -import androidx.core.text.parseAsHtml import com.github.braillesystems.learnbraille.COURSE import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.data.entities.Step -import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository +import com.github.braillesystems.learnbraille.ui.dotsMode import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp +import com.github.braillesystems.learnbraille.ui.screens.BrailleDotsInfo +import com.github.braillesystems.learnbraille.ui.screens.FragmentBinding import com.github.braillesystems.learnbraille.ui.screens.HelpMsgId +import com.github.braillesystems.learnbraille.ui.screens.theory.getStepArg import com.github.braillesystems.learnbraille.ui.screens.theory.toCurrentStep import com.github.braillesystems.learnbraille.ui.screens.theory.toNextStep import com.github.braillesystems.learnbraille.ui.screens.theory.toPrevStep -import com.github.braillesystems.learnbraille.utils.* -import org.koin.android.ext.android.inject -import com.github.braillesystems.learnbraille.utils.updateTitle as utilUpdateTitle +import com.github.braillesystems.learnbraille.utils.checkedToast +import com.github.braillesystems.learnbraille.utils.navigate +import com.github.braillesystems.learnbraille.utils.title +interface StepBinding { + val prevButton: Button? get() = null + val nextButton: Button? get() = null + val hintButton: Button? get() = null + val flipButton: Button? get() = null + val textView: TextView? get() = null + val brailleDotsInfo: BrailleDotsInfo? get() = null +} /** * Base class for all steps. */ abstract class AbstractStepFragment(helpMsgId: HelpMsgId) : AbstractFragmentWithHelp(helpMsgId) { - protected val preferenceRepository: PreferenceRepository by inject() protected lateinit var step: Step + private set + + protected lateinit var stepBinding: StepBinding + private set override val helpMsg: String get() = getString(R.string.lessons_help_template).format( super.helpMsg, getString(R.string.lessons_help_common) ) - protected fun initialize( - step: Step, - prevButton: Button? = null, - nextButton: Button? = null, - hintButton: Button? = null - ) { - this.step = step - setHasOptionsMenu(true) - if (preferenceRepository.extendedAccessibilityEnabled) { - prevButton?.setSize( - width = resources.getDimension(R.dimen.side_buttons_extended_width).toInt() - ) - nextButton?.setSize( - width = resources.getDimension(R.dimen.side_buttons_extended_width).toInt() - ) - hintButton?.setSize( - width = resources.getDimension(R.dimen.side_buttons_extended_width).toInt() - ) + protected fun B.iniStep( + titleId: Int, + getBinding: B.() -> StepBinding + ) = ini { + getBinding().run { + object : FragmentBinding { + override val leftButton: Button? get() = this@run.prevButton + override val rightButton: Button? get() = this@run.nextButton + override val leftMiddleButton: Button? get() = this@run.hintButton + override val rightMiddleButton: Button? get() = this@run.flipButton + override val textView: TextView? get() = this@run.textView + override val brailleDotsInfo: BrailleDotsInfo? get() = this@run.brailleDotsInfo + } } - } + }.apply { + step = getStepArg() + stepBinding = getBinding() - protected fun setText(text: String, infoTextView: TextView) { - infoTextView.text = text.parseAsHtml() - infoTextView.movementMethod = ScrollingMovementMethod() - checkedAnnounce(text) - if (preferenceRepository.extendedAccessibilityEnabled) { - infoTextView.setTextSize( - TypedValue.COMPLEX_UNIT_SP, - application.extendedTextSize - ) + val msg = getString(titleId) + title = if (preferenceRepository.extendedAccessibilityEnabled) { + "${step.lessonId} ${step.id} $msg" + } else { + "${step.lessonId}.${step.id} $msg" } - } - protected fun updateTitle(msg: String) { - utilUpdateTitle( - if (preferenceRepository.extendedAccessibilityEnabled) "${step.lessonId} ${step.id} $msg" - else "${step.lessonId}.${step.id} $msg" - ) - } - - protected fun setNextButton(button: Button) { - button.setOnClickListener { - toNextStep(step, markThisAsPassed = true) + stepBinding.run { + prevButton?.setOnClickListener { toPrevStep(step) } + nextButton?.setOnClickListener { toNextStep(step, markThisAsPassed = true) } + textView?.movementMethod = ScrollingMovementMethod() } + + iniStepHelper() } - protected fun setPrevButton(button: Button) { - button.setOnClickListener { - toPrevStep(step) + protected open fun iniStepHelper() = Unit + + protected fun toastDotsMode() { + binding.brailleDotsInfo?.view?.mode?.let { + checkedToast(dotsMode(it)) } } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt deleted file mode 100644 index 2d0cd0ee..00000000 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt +++ /dev/null @@ -1,126 +0,0 @@ -package com.github.braillesystems.learnbraille.ui.screens.theory.steps - -import android.os.Bundle -import android.os.Vibrator -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.content.getSystemService -import androidx.databinding.DataBindingUtil -import androidx.lifecycle.ViewModelProvider -import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.data.entities.BrailleDots -import com.github.braillesystems.learnbraille.data.entities.InputDots -import com.github.braillesystems.learnbraille.data.entities.spelling -import com.github.braillesystems.learnbraille.databinding.FragmentLessonsInputDotsBinding -import com.github.braillesystems.learnbraille.ui.screens.observeCheckedOnFly -import com.github.braillesystems.learnbraille.ui.screens.observeEventHint -import com.github.braillesystems.learnbraille.ui.screens.observeEventIncorrect -import com.github.braillesystems.learnbraille.ui.screens.observeEventPassHint -import com.github.braillesystems.learnbraille.ui.screens.theory.getStepArg -import com.github.braillesystems.learnbraille.ui.screens.theory.toNextStep -import com.github.braillesystems.learnbraille.ui.screens.theory.toPrevStep -import com.github.braillesystems.learnbraille.ui.showCorrectToast -import com.github.braillesystems.learnbraille.ui.showHintDotsToast -import com.github.braillesystems.learnbraille.ui.showIncorrectToast -import com.github.braillesystems.learnbraille.ui.views.* -import com.github.braillesystems.learnbraille.utils.application -import com.github.braillesystems.learnbraille.utils.checkedAnnounce -import com.github.braillesystems.learnbraille.utils.checkedBuzz -import timber.log.Timber - -class InputDotsFragment : AbstractStepFragment(R.string.lessons_help_input_dots) { - - private lateinit var expectedDots: BrailleDots - private lateinit var dotsState: BrailleDotsState - private var userTouchedDots: Boolean = false - private var buzzer: Vibrator? = null - private lateinit var viewModel: InputViewModel - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ) = DataBindingUtil.inflate( - inflater, - R.layout.fragment_lessons_input_dots, - container, - false - ).apply { - - Timber.i("Start initialize input dots fragment") - - val step = getStepArg() - require(step.data is InputDots) - initialize(step, prevButton, nextButton, hintButton) - - val infoText = step.data.text - ?: getString(R.string.lessons_show_dots_info_template).format(step.data.dots.spelling) - setText( - text = infoText, - infoTextView = infoTextView - ) - checkedAnnounce(infoText) - brailleDots.dotsState.display(step.data.dots) - - updateTitle(getString(R.string.lessons_title_input_dots)) - - expectedDots = step.data.dots - userTouchedDots = false - dotsState = brailleDots.dotsState.apply { - uncheck() - clickable(true) - subscribe(View.OnClickListener { - userTouchedDots = true - viewModel.onSoftCheck() - }) - } - - - val viewModelFactory = InputViewModelFactory(application, expectedDots) { - dotsState.brailleDots - } - viewModel = ViewModelProvider( - this@InputDotsFragment, viewModelFactory - ).get(InputViewModel::class.java) - buzzer = activity?.getSystemService() - - - inputViewModel = viewModel - lifecycleOwner = this@InputDotsFragment - - - prevButton.setOnClickListener { - toPrevStep(step) - } - - viewModel.observeCheckedOnFly( - viewLifecycleOwner, dotsState, buzzer, - block = { toNextStep(step, markThisAsPassed = true) }, - softBlock = ::showCorrectToast - ) - - viewModel.observeEventIncorrect( - viewLifecycleOwner, dotsState - ) { - val notify = { - showIncorrectToast() - buzzer.checkedBuzz(preferenceRepository.incorrectBuzzPattern, preferenceRepository) - } - if (userTouchedDots) notify() - else toNextStep(step, markThisAsPassed = false) { notify() } - } - - viewModel.observeEventHint( - viewLifecycleOwner, dotsState - ) { - showHintDotsToast(expectedDots) - userTouchedDots = true - } - - viewModel.observeEventPassHint( - viewLifecycleOwner, dotsState - ) - - }.root -} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt deleted file mode 100644 index ca0ce4cf..00000000 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt +++ /dev/null @@ -1,125 +0,0 @@ -package com.github.braillesystems.learnbraille.ui.screens.theory.steps - -import android.os.Bundle -import android.os.Vibrator -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.content.getSystemService -import androidx.databinding.DataBindingUtil -import androidx.lifecycle.ViewModelProvider -import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.data.entities.BrailleDots -import com.github.braillesystems.learnbraille.data.entities.Input -import com.github.braillesystems.learnbraille.data.entities.Symbol -import com.github.braillesystems.learnbraille.databinding.FragmentLessonsInputSymbolBinding -import com.github.braillesystems.learnbraille.ui.* -import com.github.braillesystems.learnbraille.ui.screens.observeCheckedOnFly -import com.github.braillesystems.learnbraille.ui.screens.observeEventHint -import com.github.braillesystems.learnbraille.ui.screens.observeEventIncorrect -import com.github.braillesystems.learnbraille.ui.screens.observeEventPassHint -import com.github.braillesystems.learnbraille.ui.screens.theory.getStepArg -import com.github.braillesystems.learnbraille.ui.screens.theory.toNextStep -import com.github.braillesystems.learnbraille.ui.screens.theory.toPrevStep -import com.github.braillesystems.learnbraille.ui.views.* -import com.github.braillesystems.learnbraille.utils.announce -import com.github.braillesystems.learnbraille.utils.application -import com.github.braillesystems.learnbraille.utils.checkedAnnounce -import com.github.braillesystems.learnbraille.utils.checkedBuzz -import timber.log.Timber - -class InputSymbolFragment : AbstractStepFragment(R.string.lessons_help_input_symbol) { - - private lateinit var expectedDots: BrailleDots - private lateinit var dotsState: BrailleDotsState - private var userTouchedDots: Boolean = false - private var buzzer: Vibrator? = null - private lateinit var viewModel: InputViewModel - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ) = DataBindingUtil.inflate( - inflater, - R.layout.fragment_lessons_input_symbol, - container, - false - ).apply { - - Timber.i("Initialize input symbol fragment") - - val step = getStepArg() - require(step.data is Input) - initialize(step, prevButton, nextButton, hintButton) - - require(step.data.material.data is Symbol) - val symbol = step.data.material.data - letter.text = symbol.char.toString() - brailleDots.dotsState.display(symbol.brailleDots) - checkedAnnounce(printStringNotNullLogged(symbol.char, PrintMode.INPUT)) - - updateTitle(getString(R.string.lessons_title_input_symbol)) - - expectedDots = symbol.brailleDots - userTouchedDots = false - dotsState = brailleDots.dotsState.apply { - uncheck() - clickable(true) - subscribe(View.OnClickListener { - userTouchedDots = true - viewModel.onSoftCheck() - }) - } - - - val viewModelFactory = InputViewModelFactory(application, expectedDots) { - dotsState.brailleDots - } - viewModel = ViewModelProvider( - this@InputSymbolFragment, viewModelFactory - ).get(InputViewModel::class.java) - buzzer = activity?.getSystemService() - - - inputViewModel = viewModel - lifecycleOwner = this@InputSymbolFragment - - - prevButton.setOnClickListener { - toPrevStep(step) - } - - viewModel.observeCheckedOnFly( - viewLifecycleOwner, dotsState, buzzer, - block = { toNextStep(step, markThisAsPassed = true) }, - softBlock = ::showCorrectToast - ) - - viewModel.observeEventIncorrect( - viewLifecycleOwner, dotsState - ) { - val notify = { - showIncorrectToast(symbol.char) - buzzer.checkedBuzz(preferenceRepository.incorrectBuzzPattern, preferenceRepository) - } - if (userTouchedDots) notify() - else toNextStep(step, markThisAsPassed = false) { notify() } - } - - viewModel.observeEventHint( - viewLifecycleOwner, dotsState - ) { - showHintToast(expectedDots) - userTouchedDots = true - } - - viewModel.observeEventPassHint( - viewLifecycleOwner, dotsState - ) { - val msg = printStringNotNullLogged(symbol.char, PrintMode.INPUT) - announce(msg) - } - - }.root -} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowDotsFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowDotsFragment.kt deleted file mode 100644 index 480f55a7..00000000 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowDotsFragment.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.github.braillesystems.learnbraille.ui.screens.theory.steps - -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.data.entities.ShowDots -import com.github.braillesystems.learnbraille.data.entities.spelling -import com.github.braillesystems.learnbraille.databinding.FragmentLessonsShowDotsBinding -import com.github.braillesystems.learnbraille.ui.screens.theory.getStepArg -import com.github.braillesystems.learnbraille.ui.views.display -import com.github.braillesystems.learnbraille.ui.views.dotsState -import com.github.braillesystems.learnbraille.utils.checkedAnnounce -import timber.log.Timber - -class ShowDotsFragment : AbstractStepFragment(R.string.lessons_help_show_dots) { - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ) = DataBindingUtil.inflate( - inflater, - R.layout.fragment_lessons_show_dots, - container, - false - ).apply { - - Timber.i("Initialize show dots fragment") - - val step = getStepArg() - require(step.data is ShowDots) - initialize(step, prevButton, nextButton) - - val infoText = step.data.text - ?: getString(R.string.lessons_show_dots_info_template).format(step.data.dots.spelling) - setText( - text = infoText, - infoTextView = infoTextView - ) - checkedAnnounce(infoText) - brailleDots.dotsState.display(step.data.dots) - - updateTitle(getString(R.string.lessons_title_show_dots)) - setPrevButton(prevButton) - setNextButton(nextButton) - - }.root -} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt deleted file mode 100644 index e34a4d1b..00000000 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.github.braillesystems.learnbraille.ui.screens.theory.steps - -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.data.entities.Show -import com.github.braillesystems.learnbraille.data.entities.Symbol -import com.github.braillesystems.learnbraille.databinding.FragmentLessonsShowSymbolBinding -import com.github.braillesystems.learnbraille.ui.PrintMode -import com.github.braillesystems.learnbraille.ui.printStringNotNullLogged -import com.github.braillesystems.learnbraille.ui.screens.theory.getStepArg -import com.github.braillesystems.learnbraille.ui.views.display -import com.github.braillesystems.learnbraille.ui.views.dotsState -import com.github.braillesystems.learnbraille.utils.checkedAnnounce -import timber.log.Timber - -class ShowSymbolFragment : AbstractStepFragment(R.string.lessons_help_show_symbol) { - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ) = DataBindingUtil.inflate( - inflater, - R.layout.fragment_lessons_show_symbol, - container, - false - ).apply { - - Timber.i("onCreateView") - - val step = getStepArg() - require(step.data is Show) - initialize(step, prevButton, nextButton) - - require(step.data.material.data is Symbol) - letter.text = step.data.material.data.char.toString() - brailleDots.dotsState.display(step.data.material.data.brailleDots) - checkedAnnounce(printStringNotNullLogged(step.data.material.data.char, PrintMode.SHOW)) - - updateTitle(getString(R.string.lessons_title_show_symbol)) - setPrevButton(prevButton) - setNextButton(nextButton) - - }.root -} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/AbstractInfoStepFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/AbstractInfoStepFragment.kt new file mode 100644 index 00000000..60c05a0e --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/AbstractInfoStepFragment.kt @@ -0,0 +1,17 @@ +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.info + +import androidx.core.text.parseAsHtml +import com.github.braillesystems.learnbraille.data.entities.BaseInfo +import com.github.braillesystems.learnbraille.ui.screens.HelpMsgId +import com.github.braillesystems.learnbraille.ui.screens.theory.steps.AbstractStepFragment +import com.github.braillesystems.learnbraille.utils.checkedAnnounce + +abstract class AbstractInfoStepFragment(helpMsgId: HelpMsgId) : AbstractStepFragment(helpMsgId) { + + override fun iniStepHelper() { + val data = step.data + require(data is BaseInfo) + stepBinding.textView?.text = data.text.parseAsHtml() + checkedAnnounce(data.text) + } +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/FirstInfoFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/FirstInfoFragment.kt similarity index 68% rename from app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/FirstInfoFragment.kt rename to app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/FirstInfoFragment.kt index 4728d261..4c8367cc 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/FirstInfoFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/FirstInfoFragment.kt @@ -1,13 +1,14 @@ -package com.github.braillesystems.learnbraille.ui.screens.theory.steps +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.info import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView import androidx.databinding.DataBindingUtil import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.data.entities.FirstInfo import com.github.braillesystems.learnbraille.databinding.FragmentLessonFirstInfoBinding -import com.github.braillesystems.learnbraille.ui.screens.theory.getStepArg +import com.github.braillesystems.learnbraille.ui.screens.theory.steps.StepBinding class FirstInfoFragment : AbstractInfoStepFragment(R.string.lessons_help_info) { @@ -20,14 +21,12 @@ class FirstInfoFragment : AbstractInfoStepFragment(R.string.lessons_help_info) { R.layout.fragment_lesson_first_info, container, false - ).apply { - - val step = getStepArg() - require(step.data is FirstInfo) - initialize(step, null, nextButton) - updateTitle(getString(R.string.lessons_title_info)) - setText(step.data.text, infoTextView) - setNextButton(nextButton) - + ).iniStep( + titleId = R.string.lessons_title_info + ) { + object : StepBinding { + override val nextButton: Button? = this@iniStep.nextButton + override val textView: TextView? = this@iniStep.infoTextView + } }.root } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InfoFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/InfoFragment.kt similarity index 65% rename from app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InfoFragment.kt rename to app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/InfoFragment.kt index 1f13ca98..711b7a0e 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InfoFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/InfoFragment.kt @@ -1,13 +1,14 @@ -package com.github.braillesystems.learnbraille.ui.screens.theory.steps +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.info import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView import androidx.databinding.DataBindingUtil import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.data.entities.Info import com.github.braillesystems.learnbraille.databinding.FragmentLessonsInfoBinding -import com.github.braillesystems.learnbraille.ui.screens.theory.getStepArg +import com.github.braillesystems.learnbraille.ui.screens.theory.steps.StepBinding class InfoFragment : AbstractInfoStepFragment(R.string.lessons_help_info) { @@ -20,15 +21,13 @@ class InfoFragment : AbstractInfoStepFragment(R.string.lessons_help_info) { R.layout.fragment_lessons_info, container, false - ).apply { - - val step = getStepArg() - require(step.data is Info) - initialize(step, prevButton, nextButton) - updateTitle(getString(R.string.lessons_title_info)) - setText(step.data.text, infoTextView) - setPrevButton(prevButton) - setNextButton(nextButton) - + ).iniStep( + titleId = R.string.lessons_title_info + ) { + object : StepBinding { + override val prevButton: Button? = this@iniStep.prevButton + override val nextButton: Button? = this@iniStep.nextButton + override val textView: TextView? = this@iniStep.infoTextView + } }.root } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/LastInfoFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/LastInfoFragment.kt similarity index 68% rename from app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/LastInfoFragment.kt rename to app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/LastInfoFragment.kt index bd372c30..8a81b866 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/LastInfoFragment.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/info/LastInfoFragment.kt @@ -1,13 +1,14 @@ -package com.github.braillesystems.learnbraille.ui.screens.theory.steps +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.info import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView import androidx.databinding.DataBindingUtil import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.data.entities.LastInfo import com.github.braillesystems.learnbraille.databinding.FragmentLessonLastInfoBinding -import com.github.braillesystems.learnbraille.ui.screens.theory.getStepArg +import com.github.braillesystems.learnbraille.ui.screens.theory.steps.StepBinding class LastInfoFragment : AbstractInfoStepFragment(R.string.lessons_help_last_info) { @@ -20,14 +21,12 @@ class LastInfoFragment : AbstractInfoStepFragment(R.string.lessons_help_last_inf R.layout.fragment_lesson_last_info, container, false - ).apply { - - val step = getStepArg() - require(step.data is LastInfo) - initialize(step, prevButton, null) - updateTitle(getString(R.string.lessons_title_last_info)) - setText(step.data.text, infoTextView) - setPrevButton(prevButton) - + ).iniStep( + titleId = R.string.lessons_title_last_info + ) { + object : StepBinding { + override val prevButton: Button? = this@iniStep.prevButton + override val textView: TextView? = this@iniStep.infoTextView + } }.root } diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/AbstractInputStepFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/AbstractInputStepFragment.kt new file mode 100644 index 00000000..58028265 --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/AbstractInputStepFragment.kt @@ -0,0 +1,111 @@ +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.input + +import android.os.Vibrator +import android.view.View +import androidx.core.content.getSystemService +import androidx.lifecycle.ViewModelProvider +import com.github.braillesystems.learnbraille.data.entities.BaseInput +import com.github.braillesystems.learnbraille.data.entities.StepData +import com.github.braillesystems.learnbraille.ui.screens.* +import com.github.braillesystems.learnbraille.ui.screens.theory.steps.AbstractStepFragment +import com.github.braillesystems.learnbraille.ui.screens.theory.toNextStep +import com.github.braillesystems.learnbraille.ui.showCorrectToast +import com.github.braillesystems.learnbraille.ui.showHintToast +import com.github.braillesystems.learnbraille.ui.showIncorrectToast +import com.github.braillesystems.learnbraille.ui.views.* +import com.github.braillesystems.learnbraille.utils.application +import com.github.braillesystems.learnbraille.utils.checkedBuzz + +abstract class AbstractInputStepFragment(helpMsgId: HelpMsgId) : AbstractStepFragment(helpMsgId) { + + // 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 + + protected lateinit var viewModel: InputViewModel + private set + + override fun iniStepHelper() { + val data = step.data + require(data is BaseInput) + val expectedDots = data.brailleDots + + var userTouchedDots = false + + dotsState = stepBinding.brailleDotsInfo!!.view.dotsState.apply { + uncheck() + clickable(true) + subscribe(View.OnClickListener { + viewModel.onSoftCheck() + userTouchedDots = true + }) + } + + val viewModelFactory = InputViewModelFactory( + application, expectedDots + ) { dotsState.brailleDots } + + viewModel = ViewModelProvider(this, viewModelFactory) + .get(InputViewModel::class.java) + + val buzzer: Vibrator? = activity?.getSystemService() + + toastDotsMode() + stepBinding.flipButton?.setOnClickListener { + dotsState = stepBinding.brailleDotsInfo!!.view.reflect().apply { + subscribe(View.OnClickListener { + viewModel.onSoftCheck() + userTouchedDots = true + }) + toastDotsMode() + if (viewModel.state == DotsChecker.State.HINT) { + display(expectedDots) + } + } + } + + viewModel.observeCheckedOnFly( + viewLifecycleOwner, { dotsState }, buzzer, + block = { toNextStep(step, markThisAsPassed = true) }, + softBlock = { showCorrectToast() } + ) + + viewModel.observeEventHint( + viewLifecycleOwner, { dotsState } + ) { + showHintToast(expectedDots) + userTouchedDots = true + } + + viewModel.observeEventIncorrect( + viewLifecycleOwner, { dotsState } + ) { + fun notify() { + toastIncorrect(data) + buzzer.checkedBuzz( + preferenceRepository.incorrectBuzzPattern, + preferenceRepository + ) + } + if (userTouchedDots) notify() + else toNextStep(step, markThisAsPassed = false) { notify() } + } + + viewModel.observeEventPassHint( + viewLifecycleOwner, { dotsState } + ) { + onPassHint(data) + } + + if (viewModel.state == DotsChecker.State.HINT) { + expectedDots.let { dotsState.display(it) } + } + } + + protected open fun toastIncorrect(data: StepData) { + showIncorrectToast() + } + + protected open fun onPassHint(data: StepData) = Unit +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputDotsFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputDotsFragment.kt new file mode 100644 index 00000000..405941a4 --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputDotsFragment.kt @@ -0,0 +1,62 @@ +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.input + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView +import androidx.core.text.parseAsHtml +import androidx.databinding.DataBindingUtil +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.InputDots +import com.github.braillesystems.learnbraille.data.entities.spelling +import com.github.braillesystems.learnbraille.databinding.FragmentLessonsInputDotsBinding +import com.github.braillesystems.learnbraille.ui.screens.BrailleDotsInfo +import com.github.braillesystems.learnbraille.ui.screens.theory.steps.StepBinding +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode +import com.github.braillesystems.learnbraille.utils.checkedAnnounce +import com.github.braillesystems.learnbraille.utils.removeHtmlMarkup + +class InputDotsFragment : AbstractInputStepFragment(R.string.lessons_help_input_dots) { + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_lessons_input_dots, + container, + false + ).iniStep( + titleId = R.string.lessons_title_input_dots + ) { + object : StepBinding { + override val prevButton: Button? = this@iniStep.prevButton + override val nextButton: Button? = this@iniStep.nextButton + override val flipButton: Button? = this@iniStep.flipButton + override val hintButton: Button? = this@iniStep.hintButton + override val textView: TextView? = this@iniStep.infoTextView + override val brailleDotsInfo: BrailleDotsInfo? = this@iniStep.run { + BrailleDotsInfo( + brailleDots, + if (preferenceRepository.isWriteModeFirst) BrailleDotsViewMode.Writing + else BrailleDotsViewMode.Reading, + prevButton, flipButton + ) + } + } + }.apply { + + val data = step.data + require(data is InputDots) + val text = data.text + ?: getString(R.string.lessons_show_dots_info_template).format(data.brailleDots.spelling) + infoTextView.text = text.parseAsHtml() + checkedAnnounce(text.removeHtmlMarkup()) + + inputViewModel = viewModel + lifecycleOwner = this@InputDotsFragment + + }.root +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputMarkerFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputMarkerFragment.kt new file mode 100644 index 00000000..9d117e42 --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputMarkerFragment.kt @@ -0,0 +1,63 @@ +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.input + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView +import androidx.databinding.DataBindingUtil +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.Input +import com.github.braillesystems.learnbraille.data.entities.MarkerSymbol +import com.github.braillesystems.learnbraille.databinding.FragmentLessonsInputMarkerBinding +import com.github.braillesystems.learnbraille.res.inputMarkerPrintRules +import com.github.braillesystems.learnbraille.ui.screens.BrailleDotsInfo +import com.github.braillesystems.learnbraille.ui.screens.theory.steps.StepBinding +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode +import com.github.braillesystems.learnbraille.utils.checkedAnnounce +import com.github.braillesystems.learnbraille.utils.getValue + +class InputMarkerFragment : AbstractInputStepFragment(R.string.lessons_help_input_marker) { + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_lessons_input_marker, + container, + false + ).iniStep( + titleId = R.string.lessons_title_input_symbol + ) { + object : StepBinding { + override val prevButton: Button? = this@iniStep.prevButton + override val nextButton: Button? = this@iniStep.nextButton + override val flipButton: Button? = this@iniStep.flipButton + override val hintButton: Button? = this@iniStep.hintButton + override val textView: TextView? = this@iniStep.infoTextView + override val brailleDotsInfo: BrailleDotsInfo? = this@iniStep.run { + BrailleDotsInfo( + brailleDots, + if (preferenceRepository.isWriteModeFirst) BrailleDotsViewMode.Writing + else BrailleDotsViewMode.Reading, + prevButton, flipButton + ) + } + } + }.apply { + + val stepData = step.data + require(stepData is Input) + val data = stepData.material.data + require(data is MarkerSymbol) + val text = inputMarkerPrintRules.getValue(data.type) + infoTextView.text = text + checkedAnnounce(text) + + inputViewModel = viewModel + lifecycleOwner = this@InputMarkerFragment + + }.root +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputSymbolFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputSymbolFragment.kt new file mode 100644 index 00000000..93a5e9ca --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputSymbolFragment.kt @@ -0,0 +1,78 @@ +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.input + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.Button +import androidx.databinding.DataBindingUtil +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.Input +import com.github.braillesystems.learnbraille.data.entities.StepData +import com.github.braillesystems.learnbraille.data.entities.Symbol +import com.github.braillesystems.learnbraille.databinding.FragmentLessonsInputSymbolBinding +import com.github.braillesystems.learnbraille.res.captionRules +import com.github.braillesystems.learnbraille.res.inputSymbolPrintRules +import com.github.braillesystems.learnbraille.ui.screens.BrailleDotsInfo +import com.github.braillesystems.learnbraille.ui.screens.theory.steps.StepBinding +import com.github.braillesystems.learnbraille.ui.showIncorrectToast +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode +import com.github.braillesystems.learnbraille.utils.checkedAnnounce +import com.github.braillesystems.learnbraille.utils.getValue + +class InputSymbolFragment : AbstractInputStepFragment(R.string.lessons_help_input_symbol) { + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_lessons_input_symbol, + container, + false + ).iniStep( + titleId = R.string.lessons_title_input_symbol + ) { + object : StepBinding { + override val prevButton: Button? = this@iniStep.prevButton + override val nextButton: Button? = this@iniStep.nextButton + override val flipButton: Button? = this@iniStep.flipButton + override val hintButton: Button? = this@iniStep.hintButton + override val brailleDotsInfo: BrailleDotsInfo? = this@iniStep.run { + BrailleDotsInfo( + brailleDots, + if (preferenceRepository.isWriteModeFirst) BrailleDotsViewMode.Writing + else BrailleDotsViewMode.Reading, + prevButton, flipButton + ) + } + } + }.apply { + + val stepData = step.data + require(stepData is Input) + val data = stepData.material.data + require(data is Symbol) + letter.letter = data.char + letterCaption.text = captionRules.getValue(data) + checkedAnnounce(inputSymbolPrintRules.getValue(data.char)) + + inputViewModel = viewModel + lifecycleOwner = this@InputSymbolFragment + + }.root + + private fun StepData.char(): Char { + require(this is Input) + require(material.data is Symbol) + return material.data.char + } + + override fun toastIncorrect(data: StepData) { + showIncorrectToast(inputSymbolPrintRules.getValue(data.char())) + } + + override fun onPassHint(data: StepData) { + checkedAnnounce(inputSymbolPrintRules.getValue(data.char())) + } +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputViewModel.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputViewModel.kt similarity index 99% rename from app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputViewModel.kt rename to app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputViewModel.kt index 0c4bb192..c33748ad 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputViewModel.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/input/InputViewModel.kt @@ -1,4 +1,4 @@ -package com.github.braillesystems.learnbraille.ui.screens.theory.steps +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.input import android.app.Application import androidx.lifecycle.AndroidViewModel diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/show/AbstractShowStepFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/show/AbstractShowStepFragment.kt new file mode 100644 index 00000000..8f233250 --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/show/AbstractShowStepFragment.kt @@ -0,0 +1,22 @@ +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.show + +import com.github.braillesystems.learnbraille.data.entities.BaseShow +import com.github.braillesystems.learnbraille.ui.screens.HelpMsgId +import com.github.braillesystems.learnbraille.ui.screens.theory.steps.AbstractStepFragment +import com.github.braillesystems.learnbraille.ui.views.display +import com.github.braillesystems.learnbraille.ui.views.dotsState + +abstract class AbstractShowStepFragment(helpMsgId: HelpMsgId) : AbstractStepFragment(helpMsgId) { + + override fun iniStepHelper() { + val data = step.data + require(data is BaseShow) + stepBinding.brailleDotsInfo?.view?.dotsState?.display(data.brailleDots) + ?: error("Show step should have braille dots") + toastDotsMode() + stepBinding.flipButton?.setOnClickListener { + stepBinding.brailleDotsInfo?.view?.reflect()?.display(data.brailleDots) + toastDotsMode() + } + } +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/show/ShowDotsFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/show/ShowDotsFragment.kt new file mode 100644 index 00000000..94ad8fff --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/show/ShowDotsFragment.kt @@ -0,0 +1,53 @@ +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.show + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView +import androidx.core.text.parseAsHtml +import androidx.databinding.DataBindingUtil +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.ShowDots +import com.github.braillesystems.learnbraille.data.entities.spelling +import com.github.braillesystems.learnbraille.databinding.FragmentLessonsShowDotsBinding +import com.github.braillesystems.learnbraille.ui.screens.BrailleDotsInfo +import com.github.braillesystems.learnbraille.ui.screens.theory.steps.StepBinding +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode +import com.github.braillesystems.learnbraille.utils.checkedAnnounce +import com.github.braillesystems.learnbraille.utils.removeHtmlMarkup + +class ShowDotsFragment : AbstractShowStepFragment(R.string.lessons_help_show_dots) { + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_lessons_show_dots, + container, + false + ).iniStep( + titleId = R.string.lessons_title_show_dots + ) { + object : StepBinding { + override val prevButton: Button? = this@iniStep.prevButton + override val nextButton: Button? = this@iniStep.nextButton + override val flipButton: Button? = this@iniStep.flipButton + override val textView: TextView? = this@iniStep.infoTextView + override val brailleDotsInfo: BrailleDotsInfo? = this@iniStep.run { + BrailleDotsInfo(brailleDots, BrailleDotsViewMode.Reading, prevButton, flipButton) + } + } + }.apply { + + val data = step.data + require(data is ShowDots) + val text = data.text + ?: getString(R.string.lessons_show_dots_info_template).format(data.brailleDots.spelling) + infoTextView.text = text.parseAsHtml() + checkedAnnounce(text.removeHtmlMarkup()) + + }.root +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/show/ShowMarkerFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/show/ShowMarkerFragment.kt new file mode 100644 index 00000000..8db9888b --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/show/ShowMarkerFragment.kt @@ -0,0 +1,55 @@ +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.show + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView +import androidx.databinding.DataBindingUtil +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.MarkerSymbol +import com.github.braillesystems.learnbraille.data.entities.Show +import com.github.braillesystems.learnbraille.databinding.FragmentLessonsShowMarkerBinding +import com.github.braillesystems.learnbraille.res.showMarkerPrintRules +import com.github.braillesystems.learnbraille.ui.screens.BrailleDotsInfo +import com.github.braillesystems.learnbraille.ui.screens.theory.steps.StepBinding +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode +import com.github.braillesystems.learnbraille.utils.checkedAnnounce +import com.github.braillesystems.learnbraille.utils.getValue + +class ShowMarkerFragment : AbstractShowStepFragment(R.string.lessons_help_show_marker) { + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_lessons_show_marker, + container, + false + ).iniStep( + titleId = R.string.lessons_title_show_symbol + ) { + object : StepBinding { + override val prevButton: Button? = this@iniStep.prevButton + override val nextButton: Button? = this@iniStep.nextButton + override val flipButton: Button? = this@iniStep.flipButton + override val textView: TextView? = this@iniStep.infoTextView + override val brailleDotsInfo: BrailleDotsInfo? = this@iniStep.run { + BrailleDotsInfo(brailleDots, BrailleDotsViewMode.Reading, prevButton, flipButton) + } + } + }.apply { + + val stepData = step.data + require(stepData is Show) + val data = stepData.material.data + require(data is MarkerSymbol) + + val text = showMarkerPrintRules.getValue(data.type) + infoTextView.text = text + checkedAnnounce(text) + + }.root +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/show/ShowSymbolFragment.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/show/ShowSymbolFragment.kt new file mode 100644 index 00000000..8f4f8a88 --- /dev/null +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/show/ShowSymbolFragment.kt @@ -0,0 +1,54 @@ +package com.github.braillesystems.learnbraille.ui.screens.theory.steps.show + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.Button +import androidx.databinding.DataBindingUtil +import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.entities.Show +import com.github.braillesystems.learnbraille.data.entities.Symbol +import com.github.braillesystems.learnbraille.databinding.FragmentLessonsShowSymbolBinding +import com.github.braillesystems.learnbraille.res.captionRules +import com.github.braillesystems.learnbraille.ui.screens.BrailleDotsInfo +import com.github.braillesystems.learnbraille.ui.screens.theory.steps.StepBinding +import com.github.braillesystems.learnbraille.ui.showPrint +import com.github.braillesystems.learnbraille.ui.views.BrailleDotsViewMode +import com.github.braillesystems.learnbraille.utils.checkedAnnounce +import com.github.braillesystems.learnbraille.utils.getValue + +class ShowSymbolFragment : AbstractShowStepFragment(R.string.lessons_help_show_symbol) { + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_lessons_show_symbol, + container, + false + ).iniStep( + titleId = R.string.lessons_title_show_symbol + ) { + object : StepBinding { + override val prevButton: Button? = this@iniStep.prevButton + override val nextButton: Button? = this@iniStep.nextButton + override val flipButton: Button? = this@iniStep.flipButton + override val brailleDotsInfo: BrailleDotsInfo? = this@iniStep.run { + BrailleDotsInfo(brailleDots, BrailleDotsViewMode.Reading, prevButton, flipButton) + } + } + }.apply { + + val stepData = step.data + require(stepData is Show) + val data = stepData.material.data + require(data is Symbol) + + letter.letter = data.char + letterCaption.text = captionRules.getValue(data) + checkedAnnounce(showPrint(data)) + + }.root +} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt b/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt index e6b908b2..3f8fb765 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt @@ -1,13 +1,15 @@ package com.github.braillesystems.learnbraille.ui.views +import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.widget.TextView import androidx.core.widget.addTextChangedListener -import com.github.braillesystems.learnbraille.ui.PrintMode -import com.github.braillesystems.learnbraille.ui.printString -import timber.log.Timber +import com.github.braillesystems.learnbraille.res.inputSymbolPrintRules +import com.github.braillesystems.learnbraille.res.showSymbolPrintRules +import com.github.braillesystems.learnbraille.utils.getValue +@SuppressLint("AppCompatCustomView") open class BigLetterView : TextView { constructor(context: Context) : super(context) @@ -20,16 +22,16 @@ open class BigLetterView : TextView { context, attrSet, defStyleAttr ) - protected fun addContextListener(mode: PrintMode) { + var letter: Char + get() = super.getText().first() + set(value) = super.setText(value.toString()) + + protected fun addContextListener(hintGetter: Context.(Char) -> String) { addTextChangedListener( afterTextChanged = { text -> if (text == null) return@addTextChangedListener require(text.length == 1) - contentDescription = context - .printString(text.first(), mode) - ?: text.first().toLowerCase().toString().also { - Timber.e("Symbol intro not found: $text") - } + contentDescription = context.hintGetter(text.first()) } ) } @@ -48,7 +50,9 @@ class InputBigLetterView : BigLetterView { ) init { - addContextListener(PrintMode.INPUT) + addContextListener { c -> + inputSymbolPrintRules.getValue(c) + } } } @@ -65,6 +69,8 @@ class ShowBigLetterView : BigLetterView { ) init { - addContextListener(PrintMode.SHOW) + addContextListener { c -> + showSymbolPrintRules.getValue(c) + } } } 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 f249cfa9..c4dc0a99 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,5 +1,6 @@ package com.github.braillesystems.learnbraille.ui.views +import android.annotation.SuppressLint import android.content.Context import android.os.Build import android.util.AttributeSet @@ -7,6 +8,7 @@ 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 @@ -14,11 +16,17 @@ 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.chainify +import com.github.braillesystems.learnbraille.utils.forEach +import com.github.braillesystems.learnbraille.utils.unreachable 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) @@ -37,8 +45,21 @@ class BrailleDotView : CheckBox { } } +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 { @@ -58,36 +79,131 @@ class BrailleDotsView : ConstraintLayout, KoinComponent { LayoutInflater .from(context) .inflate(R.layout.braille_dots_view, this, true) + } - if (Build.VERSION.SDK_INT >= 22) { - if (preferenceRepository.traverseDotsInEnumerationOrder) { - dotButton2.accessibilityTraversalAfter = dotButton1.id - dotButton3.accessibilityTraversalAfter = dotButton2.id - dotButton4.accessibilityTraversalAfter = dotButton3.id - dotButton5.accessibilityTraversalAfter = dotButton4.id - dotButton6.accessibilityTraversalAfter = dotButton5.id - } else { - dotButton4.accessibilityTraversalAfter = dotButton1.id - dotButton2.accessibilityTraversalAfter = dotButton4.id - dotButton5.accessibilityTraversalAfter = dotButton2.id - dotButton3.accessibilityTraversalAfter = dotButton5.id - dotButton6.accessibilityTraversalAfter = dotButton3.id - } + // 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.i("API level < 22, unable co control accessibility traversal order") + Timber.w("API level < 22, unable co control accessibility traversal order") + } + + this.mode = mode + } + + fun reflect(): BrailleDotsState { + setMode(mode.reflected, prevView, nextView) + return dotsState + } + + private fun setDescriptionMode(mode: BrailleDotsViewMode) { + when (mode) { + Writing -> forEach( + dotButton4 to R.string.braille_dot_1, + dotButton5 to R.string.braille_dot_2, + dotButton6 to R.string.braille_dot_3, + dotButton1 to R.string.braille_dot_4, + dotButton2 to R.string.braille_dot_5, + dotButton3 to R.string.braille_dot_6 + ) { (dotButton, id) -> + dotButton.contentDescription = context.getString(id) + } + Reading -> forEach( + dotButton1 to R.string.braille_dot_1, + dotButton2 to R.string.braille_dot_2, + dotButton3 to R.string.braille_dot_3, + dotButton4 to R.string.braille_dot_4, + dotButton5 to R.string.braille_dot_5, + dotButton6 to R.string.braille_dot_6 + ) { (dotButton, id) -> + dotButton.contentDescription = context.getString(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( - arrayOf( - dotButton1, dotButton2, dotButton3, - dotButton4, dotButton5, dotButton6 - ) + when (mode) { + Writing -> listOf( + dotButton4, dotButton5, dotButton6, + dotButton1, dotButton2, dotButton3 + ) + Reading -> listOf( + dotButton1, dotButton2, dotButton3, + dotButton4, dotButton5, dotButton6 + ) + } ) -class BrailleDotsState(val checkBoxes: Array) +class BrailleDotsState(val checkBoxes: List) val BrailleDotsState.spelling: String get() = brailleDots.spelling diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/utils/SpeechRecognition.kt b/app/src/main/java/com/github/braillesystems/learnbraille/utils/SpeechRecognition.kt deleted file mode 100644 index e44fbd47..00000000 --- a/app/src/main/java/com/github/braillesystems/learnbraille/utils/SpeechRecognition.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.github.braillesystems.learnbraille.utils - -import android.content.Intent -import android.os.Bundle -import android.speech.RecognitionListener -import android.speech.RecognizerIntent -import android.speech.SpeechRecognizer -import android.speech.tts.TextToSpeech -import android.speech.tts.UtteranceProgressListener -import androidx.fragment.app.Fragment -import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository -import timber.log.Timber -import java.util.* -import kotlin.collections.HashMap -import kotlin.collections.set -import kotlin.system.exitProcess - -// TODO refactor - -class SpeechRecognition( - private val fragment: Fragment, - private val preferenceRepository: PreferenceRepository -) { - - private var mostRecentUtteranceID: String? = null - private lateinit var mTTs: TextToSpeech - private var mSpeechRecognizer: SpeechRecognizer? = null - private var mSpeechRecognizerIntent: Intent? = null - - init { - runIf(preferenceRepository.speechRecognitionEnabled) { - try { - initialize() - } catch (e: Throwable) { - Timber.e(e) - } - } - } - - private fun initialize() = fragment.apply { - mTTs = TextToSpeech(context, TextToSpeech.OnInitListener { status -> - if (status == TextToSpeech.SUCCESS) { - mTTs.language = Locale.UK - mostRecentUtteranceID = (Random().nextInt() % 9999999).toString() + "" - - val params: MutableMap = HashMap() - params[TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID] = mostRecentUtteranceID - @Suppress("DEPRECATION", "UNCHECKED_CAST") - mTTs.speak( - getString(R.string.exit_question), TextToSpeech.QUEUE_FLUSH, - params as HashMap? - ) - mTTs.setOnUtteranceProgressListener(object : UtteranceProgressListener() { - override fun onDone(utteranceId: String) { - if (utteranceId != mostRecentUtteranceID) { - return - } - activity?.runOnUiThread { - if (mSpeechRecognizer != null) { - mSpeechRecognizer!!.startListening(mSpeechRecognizerIntent) - } - } - } - - override fun onError(utteranceId: String) {} - override fun onStart(utteranceId: String) {} - }) - - } - }) - - mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(context) - mSpeechRecognizerIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH) - mSpeechRecognizerIntent?.putExtra( - RecognizerIntent.EXTRA_LANGUAGE_MODEL, - RecognizerIntent.LANGUAGE_MODEL_FREE_FORM - ) - mSpeechRecognizerIntent?.putExtra( - RecognizerIntent.EXTRA_CALLING_PACKAGE, - context?.packageName - ) - mSpeechRecognizer?.setRecognitionListener(SpeechRecognitionListener(fragment)) - } - - fun onDestroy() = runIf(preferenceRepository.speechRecognitionEnabled) { - try { - mSpeechRecognizer?.destroy() - } catch (e: Throwable) { - Timber.e(e) - } - } -} - -private class SpeechRecognitionListener(fragment: Fragment) : RecognitionListener { - - override fun onBeginningOfSpeech() {} - override fun onBufferReceived(buffer: ByteArray) {} - override fun onEndOfSpeech() {} - override fun onError(error: Int) {} - override fun onEvent(eventType: Int, params: Bundle) {} - override fun onPartialResults(partialResults: Bundle) {} - override fun onReadyForSpeech(params: Bundle) {} - override fun onRmsChanged(rmsdB: Float) {} - - private val hostFragment: Fragment = fragment - - override fun onResults(results: Bundle) { - try { - val matches = - results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION) - ?: return - Timber.i("Speech recognized: $matches") - if (matches[0] == "да") { - exitProcess(0) - } - if (matches[0] == "нет") { - hostFragment.navigate(R.id.action_global_menuFragment) - } - } catch (e: Exception) { - Timber.e(e) - } - } -} diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt b/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt index 9b8ff50c..2bd2ae90 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt @@ -10,6 +10,7 @@ import androidx.fragment.app.Fragment import com.github.braillesystems.learnbraille.LearnBrailleApplication import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository +import com.github.braillesystems.learnbraille.koin import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -28,13 +29,18 @@ val Fragment.application: LearnBrailleApplication fun scope(job: Job = Job()) = CoroutineScope(Dispatchers.Main + job) -fun Vibrator?.checkedBuzz(pattern: BuzzPattern, preferenceRepository: PreferenceRepository) = - runIf(preferenceRepository.buzzEnabled) { buzz(pattern) } +fun Vibrator?.checkedBuzz( + pattern: BuzzPattern, + preferenceRepository: PreferenceRepository = koin.get() +) = runIf(preferenceRepository.buzzEnabled) { buzz(pattern) } -fun checkedToast(msg: String, context: Context?, preferenceRepository: PreferenceRepository) = - runIf(preferenceRepository.toastsEnabled) { - Toast.makeText(context, msg, preferenceRepository.toastDuration).show() - } +fun checkedToast( + msg: String, + context: Context?, + preferenceRepository: PreferenceRepository = koin.get() +) = runIf(preferenceRepository.toastsEnabled) { + Toast.makeText(context, msg, preferenceRepository.toastDuration).show() +} fun Fragment.checkedToast(msg: String, preferenceRepository: PreferenceRepository = get()) = checkedToast(msg, context, preferenceRepository) @@ -53,8 +59,7 @@ fun Context.announce(announcement: String) { manager.sendAccessibilityEvent(event) } -fun Fragment.announce(announcement: String) = - application.announce(announcement) +fun Fragment.announce(announcement: String) = contextNotNull.announce(announcement) fun Fragment.checkedAnnounce( announcement: String, @@ -67,13 +72,16 @@ fun Fragment.checkedAnnounce( val Fragment.actionBar: ActionBar? get() = (activity as AppCompatActivity).supportActionBar +val Fragment.actionBarNotNull: ActionBar + get() = actionBar ?: error("Action bar is not supported") + /** * Throws if action bar is not available */ var Fragment.title: String - get() = requireNotNull(actionBar).title.toString() + get() = actionBarNotNull.title.toString() set(value) { - requireNotNull(actionBar).title = value + actionBarNotNull.title = value } fun Fragment.updateTitle(title: String) { @@ -84,6 +92,7 @@ fun Fragment.updateTitle(title: String) { fun stringify(s: SerializationStrategy, obj: T) = Json.stringify(s, obj) fun parse(d: DeserializationStrategy, s: String) = Json.parse(d, s) + val Context.extendedTextSize: Float by lazyWithContext { // Size applied in runtime is different resources.getDimension(R.dimen.lessons_info_text_size) / 5 * 3 diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt b/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt index 340dc27a..064135fb 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt @@ -26,6 +26,9 @@ import kotlin.reflect.KProperty * that are not specific for particular project. */ +val Fragment.contextNotNull: Context + get() = requireNotNull(context) + val Context.usbManager get() = getSystemService(Context.USB_SERVICE) as UsbManager val Context.accessibilityManager: AccessibilityManager? @@ -36,7 +39,7 @@ val Context.accessibilityManager: AccessibilityManager? fun Fragment.getFragmentStringArg(name: String): String = arguments ?.getString(name) - ?: error("No $name found in args") + ?: error("No $name found in fragment args") typealias BuzzPattern = LongArray @@ -97,9 +100,16 @@ fun Fragment.navigate(action: NavDirections) = try { Timber.e(e, "Multitouch navigation") } +fun Fragment.navigateToLauncher() { + val intent = Intent(Intent.ACTION_MAIN).apply { + addCategory(Intent.CATEGORY_HOME) + flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + } + startActivity(intent) +} + val Context.appName: String get() = getString(R.string.app_name) -val Fragment.appName: String - get() = context?.appName ?: error("Fragment is expected to have a context") +val Fragment.appName: String get() = contextNotNull.appName val Context.preferences: SharedPreferences get() = PreferenceManager.getDefaultSharedPreferences(this) diff --git a/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Kotlin.kt b/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Kotlin.kt index 7c57b06d..257cb37e 100644 --- a/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Kotlin.kt +++ b/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Kotlin.kt @@ -8,23 +8,41 @@ import kotlin.reflect.KProperty * that are not specific for particular project. */ -inline fun T?.side(block: (T) -> R) { +inline fun T?.side(block: (T) -> Unit) { if (this != null) block(this) } +inline fun forEach(vararg xs: T, block: (T) -> Unit) = xs.forEach(block) +inline fun applyForEach(vararg xs: T, block: T.() -> Unit) = xs.forEach { it.block() } + +inline fun chainify(vararg xs: T, block: (T, T) -> Unit) = + xs + .toList() + .windowed(size = 2) { it.first() to it.last() } + .forEach { (a, b) -> block(a, b) } + /** * Try to use sealed classes to avoid using `unreachable`. */ val unreachable: Nothing get() = error("Unreachable code executed") +val Any?.devnull: Unit get() {} + operator fun MatchGroupCollection.component1() = get(0) operator fun MatchGroupCollection.component2() = get(1) operator fun MatchGroupCollection.component3() = get(2) operator fun MatchGroupCollection.component4() = get(3) operator fun MatchGroupCollection.component5() = get(4) -val Any?.devnull: Unit get() {} +operator fun Pair.compareTo(other: Pair): Int + where A : Comparable, B : Comparable = + when { + first < other.first -> -1 + first == other.first && second < other.second -> -1 + equals(other) -> 0 + else -> 1 + } fun String.removeHtmlMarkup() = Regex("""<[^>]*>|&""").replace(this, "") @@ -44,41 +62,25 @@ fun Rules.matchF(key: T): ((T) -> R)? { return null } -fun Iterable>.match(key: T): R? = matchF(key)?.invoke(key) +fun Rules.match(key: T): R? = matchF(key)?.invoke(key) operator fun Rules.get(x: T): R? = match(x) +fun Rules.getValue(x: T): R = match(x) ?: error("No rule match value $x") + /** * It is very useful to choose android text resource depending on some condition. * (In that case prevent lambda of capturing context that will be invalid next time fragment entered, - * so use `Fragment.getString` outside of (...) -> String lambdas.) + * so use `Context::getString` outside of inner paired lambdas) * ``` * val Context.inputSymbolPrintRules by rules( * { * val t = getString(R.string.input_letter_intro_template) * ruSymbols.map::containsKey to { c: Char -> t.format(c) } * }, - * * { * val t = getString(R.string.input_digit_intro_template) * uebDigits.map::containsKey to { c: Char -> t.format(c) } - * }, - * - * { - * val other = getString(R.string.input_special_intro_template) - * val numSign = getString(R.string.input_special_intro_num_sign) - * val dotIntro = getString(R.string.input_special_intro_dot) - * val commaIntro = getString(R.string.input_special_intro_comma) - * val hyphenIntro = getString(R.string.input_special_intro_hyphen) - * specialSymbols.map::containsKey to { c: Char -> - * when (c) { - * ']' -> numSign - * '.' -> dotIntro - * ',' -> commaIntro - * '-' -> hyphenIntro - * else -> other.format(c) - * } - * } * } * ) * ``` @@ -86,17 +88,19 @@ operator fun Rules.get(x: T): R? = match(x) class rules(private vararg val ruleProviders: C.() -> Rule) { private var value: Rules? = null operator fun getValue(thisRef: C, property: KProperty<*>): Rules = - value ?: ruleProviders - .map { thisRef.it() } - .also { value = it } + value + ?: ruleProviders + .map { thisRef.it() } + .also { value = it } } class lazyWithContext(private val getter: C.(KProperty<*>) -> R) { private var value: R? = null operator fun getValue(thisRef: C, property: KProperty<*>): R = - value ?: thisRef - .getter(property) - .also { value = it } + value + ?: thisRef + .getter(property) + .also { value = it } } class Days(val v: Int) { @@ -113,10 +117,9 @@ operator fun Date.plus(days: Days): Date = operator fun Date.minus(days: Days): Date = plus(-days) -inline fun tryN(n: Int, stop: (T) -> Boolean, get: () -> T): T? { +inline fun retryN(n: Int, get: () -> T?): T? { repeat(n) { - val v = get() - if (stop(v)) return v + get()?.let { return it } } return null } diff --git a/app/src/main/res/drawable/ic_flip_white_24dp.xml b/app/src/main/res/drawable/ic_flip_white_24dp.xml new file mode 100644 index 00000000..947234a6 --- /dev/null +++ b/app/src/main/res/drawable/ic_flip_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/left_border.xml b/app/src/main/res/drawable/left_border.xml new file mode 100644 index 00000000..58fc9043 --- /dev/null +++ b/app/src/main/res/drawable/left_border.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/right_border.xml b/app/src/main/res/drawable/right_border.xml new file mode 100644 index 00000000..80e932f5 --- /dev/null +++ b/app/src/main/res/drawable/right_border.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ 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 7a7bc894..1535a5e8 100644 --- a/app/src/main/res/layout/braille_dots_view.xml +++ b/app/src/main/res/layout/braille_dots_view.xml @@ -1,23 +1,20 @@ + android:layout_height="wrap_content" + tools:background="@drawable/left_border"> + type="com.github.braillesystems.learnbraille.data.repository.DeckWithAvailability" /> + + + + + + + type="com.github.braillesystems.learnbraille.ui.screens.theory.steps.input.InputViewModel" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_lessons_input_symbol.xml b/app/src/main/res/layout/fragment_lessons_input_symbol.xml index 82ce9f30..1c120c4a 100644 --- a/app/src/main/res/layout/fragment_lessons_input_symbol.xml +++ b/app/src/main/res/layout/fragment_lessons_input_symbol.xml @@ -7,21 +7,23 @@ + type="com.github.braillesystems.learnbraille.ui.screens.theory.steps.input.InputViewModel" /> + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_lessons_show_symbol.xml b/app/src/main/res/layout/fragment_lessons_show_symbol.xml index 5f4f220a..05e0cf4c 100644 --- a/app/src/main/res/layout/fragment_lessons_show_symbol.xml +++ b/app/src/main/res/layout/fragment_lessons_show_symbol.xml @@ -7,14 +7,16 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_symbol_view.xml b/app/src/main/res/layout/fragment_symbol_view.xml index 73c78ff2..72674d3e 100644 --- a/app/src/main/res/layout/fragment_symbol_view.xml +++ b/app/src/main/res/layout/fragment_symbol_view.xml @@ -7,14 +7,16 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + diff --git a/app/src/main/res/navigation/navigation.xml b/app/src/main/res/navigation/navigation.xml index a6176f16..901cfac5 100644 --- a/app/src/main/res/navigation/navigation.xml +++ b/app/src/main/res/navigation/navigation.xml @@ -5,12 +5,10 @@ android:id="@+id/navigation" app:startDestination="@id/menuFragment"> - - + + + + + android:label="@string/help_title"> + + + android:label="@string/preferences_actionbar_title" /> + + - + android:id="@+id/action_global_lessonsListFragment" + app:destination="@id/lessonsListFragment" /> + + + + + + + + + + + + + + + + + + android:name="com.github.braillesystems.learnbraille.ui.screens.theory.steps.info.InfoFragment" + android:label="@string/lessons_title_info"> + + + android:name="com.github.braillesystems.learnbraille.ui.screens.theory.steps.input.InputDotsFragment" + android:label="@string/lessons_title_input_dots"> + + + android:name="com.github.braillesystems.learnbraille.ui.screens.theory.steps.input.InputSymbolFragment" + android:label="@string/lessons_title_input_symbol"> + + + android:name="com.github.braillesystems.learnbraille.ui.screens.theory.steps.show.ShowDotsFragment" + android:label="@string/lessons_title_show_dots"> + + + android:name="com.github.braillesystems.learnbraille.ui.screens.theory.steps.show.ShowSymbolFragment" + android:label="@string/lessons_title_show_symbol"> + + + android:name="com.github.braillesystems.learnbraille.ui.screens.theory.steps.info.LastInfoFragment" + android:label="@string/lessons_title_last_info"> + + + android:name="com.github.braillesystems.learnbraille.ui.screens.theory.steps.info.FirstInfoFragment" + android:label="@string/lessons_title_info"> - - - - - - - - - + - - - - - + android:id="@+id/inputMarkerFragment" + android:name="com.github.braillesystems.learnbraille.ui.screens.theory.steps.input.InputMarkerFragment" + android:label="@string/lessons_title_input_symbol"> + + - + android:id="@+id/showMarkerFragment" + android:name="com.github.braillesystems.learnbraille.ui.screens.theory.steps.show.ShowMarkerFragment" + android:label="@string/lessons_title_show_symbol"> + + + diff --git a/app/src/main/res/values-sw320dp/dimens.xml b/app/src/main/res/values-sw320dp/dimens.xml index 4c6df5ca..f95fc631 100644 --- a/app/src/main/res/values-sw320dp/dimens.xml +++ b/app/src/main/res/values-sw320dp/dimens.xml @@ -9,7 +9,6 @@ 2.6 1.1 - 0.8 0.85 0.9 @@ -21,8 +20,6 @@ 32dp 30dp - 320dp - 64dp 15sp 0.05 230dp @@ -32,7 +29,7 @@ 394dp 175dp - 170sp + 136sp 30dp diff --git a/app/src/main/res/values-sw360dp/dimens.xml b/app/src/main/res/values-sw360dp/dimens.xml index 16dbc3ba..9fb5067e 100644 --- a/app/src/main/res/values-sw360dp/dimens.xml +++ b/app/src/main/res/values-sw360dp/dimens.xml @@ -21,8 +21,6 @@ 45dp 40dp - 360dp - 64dp 20sp 0.05 240dp diff --git a/app/src/main/res/values-sw400dp/dimens.xml b/app/src/main/res/values-sw400dp/dimens.xml index 51a80913..e0e9ebe7 100644 --- a/app/src/main/res/values-sw400dp/dimens.xml +++ b/app/src/main/res/values-sw400dp/dimens.xml @@ -9,7 +9,6 @@ 2.6 1.5 - 1 0.85 0.9 @@ -21,8 +20,6 @@ 45dp 40dp - 428dp - 64dp 20sp 0.05 300dp diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml index f8fc6350..32c7b84c 100644 --- a/app/src/main/res/values-sw600dp/dimens.xml +++ b/app/src/main/res/values-sw600dp/dimens.xml @@ -8,7 +8,6 @@ 25dp 2.6 1.8 - 1.9 80dp 65dp @@ -18,8 +17,6 @@ 60dp 55dp - 428dp - 80dp 35sp 0.05 450dp @@ -29,7 +26,7 @@ 394dp 175dp - 150sp + 285sp 30dp diff --git a/app/src/main/res/values-sw720dp/dimens.xml b/app/src/main/res/values-sw720dp/dimens.xml index 8685fd91..70f79b1a 100644 --- a/app/src/main/res/values-sw720dp/dimens.xml +++ b/app/src/main/res/values-sw720dp/dimens.xml @@ -8,7 +8,6 @@ 25dp 2.6 2.1 - 2.2 90dp 75dp @@ -18,8 +17,6 @@ 65dp 60dp - 428dp - 100dp 40sp 0.05 590dp @@ -29,7 +26,7 @@ 394dp 175dp - 150sp + 330sp 30dp diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index bb4efba8..cacf9cb5 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -9,7 +9,6 @@ 2.6 1 - 0.8 0.85 0.9 @@ -23,8 +22,6 @@ 0dp 20dp - 428dp - 64dp 20sp 0.0 270dp @@ -38,6 +35,7 @@ 0.2 170sp + 5dp 40dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3b01e9f6..4c2357f1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,11 +16,16 @@ 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 + + Порядок точек: "письмо" + Порядок точек: "чтение" Правильно! Неправильно! @@ -35,20 +40,82 @@ точка 6 справа снизу Введите букву: %s + Введите латинскую букву: %s + Введите греческую букву: %s Введите цифру: %s Введите символ: %s - Введите цифровой знак + Введите математический символ: %s Введите символ: Литературная точка Введите символ: Дефис Введите запятую + Введите восклицательный знак + Введите вопросительный знак + Введите открывающую кавычку + Введите закрывающую кавычку + Введите левую литературную скобку + Введите правую литературную скобку + Введите звёздочку + Введите двоеточие + Введите точку с запятой + Введите знак ударения + + Введите признак большой буквы греческого алфавита + Введите признак большой буквы латинского алфавита + Введите признак малой буквы латинского алфавита + Введите признак большой буквы русского алфавита + Введите цифровой знак + Введите признак жирного шрифта + Введите признак курсивного шрифта + + Введите знак Плюс + Введите знак Минус + Введите знак умножения точкой + Введите знак умножения крестом + Введите знак деления (углом) + Введите знак деления (двумя точками) + Введите знак равенства Буква %s + Латинская буква %s + Греческая буква %s Цифра %s Символ %s - Цифровой знак + Математический символ %s Литературная точка Дефис Запятая + Восклицательный знак + Вопросительный знак + Открывающая кавычка + Закрывающая кавычка + Левая литературная скобка + Правая литературная скобка + Звёздочка + Двоеточие + Точка с запятой + Ударение + + Признак большой греческой буквы + Признак большой латинской буквы + Признак малой латинской буквы + Признак большой буквы русского алфавита + Цифровой знак + Признак жирного шрифта + Признак курсивного шрифта + + Знак Плюс + Знак Минус + Знак умножения точкой + Знак умножения крестом + Знак деления (углом) + Знак деления (двумя точками) + Знак равенства + + Русская буква + Греческая буква + Латинская буква + Цифра + Специальный символ @@ -60,11 +127,12 @@ Подсказка Далее Практика: %d из %d + Практика - Колода: \"%s\"\nПовторять только изученные: включено + Колода: \"%s\"\n\"%s\"\nПовторять только изученные: включено - Колода: \"%s\"\nПовторять только изученные: выключено + Колода: \"%s\"\n\"%s\"\nПовторять только изученные: выключено Список колод @@ -73,9 +141,14 @@ Все символы + Все символы, кроме иностранных букв Русские буквы + Латинские буквы + Греческие буквы Цифры - Специальные символы + Знаки препинания + Специальные символы + Математические символы @@ -90,10 +163,13 @@ По умолчанию выдаются только карточки с символами, изученными в разделе \"теория\". Чтобы повторять любые символы, измените это в настройках приложения. &
+ Шеститочие: письмо/чтение (розовая кнопка справа по центру) - изменяет порядок столбцов: точки + 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). + &
Выход в меню приложения - кнопка вверху слева. &
Список колод - кнопка вверху справа. - &
+
Карточки объединены в колоды по типам символов, например, колода русских букв, колода цифр. Эта кнопка ведёт в меню выбора колоды. &
@@ -102,7 +178,7 @@ Если Вы забыли символ, можно нажать кнопку \"подсказка\" слева внизу экрана. По нажатию этой кнопки будет выведено сообщение с верными номерами точек, шеститочие будет заполнено правильными точками и недоступно для переключения. После этого нужно нажать кнопку - \"далее\" и ввести ту же букву ещё раз. + \"далее\" и ввести тот же символ ещё раз. ]]>
@@ -130,6 +206,7 @@ НАСТРОЙКИ ВЫХОД %s. Меню + Меню Загружаем базу данных. Попробуйте ещё раз! @@ -194,7 +271,7 @@

+ &
%s ]]>
@@ -202,7 +279,7 @@ Переход
к следующему шагу - кнопка \"вперёд\" справа по центру, - к предыдущему - кнопка назад слева по центру. + к предыдущему - кнопка "назад" слева по центру. &
Выход в меню приложения - кнопка \"перейти вверх\" в верхней панели слева. &
@@ -231,6 +308,9 @@ По нажатию кнопки \"подсказка\" выводится сообщение с правильным ответом, и точки переключаются в правильное положение. Чтобы пройти то же упражнение после подсказки, нужно нажать кнопку \"вперёд\". + &
+ Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки + 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). ]]>
@@ -243,6 +323,25 @@ Если нажать кнопку \"подсказка\", расположенную над кнопкой \"назад\", выведется сообщение с правильным ответом и точки переключатся в правильное состояние. Чтобы пройти то же упражнение после подсказки, нажмите кнопку \"вперёд\". + &
+ Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки + 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). + ]]> +
+ + + шаг с вводом специального символа. +

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

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

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

+ В верхней половине экрана словами описан специальный символ, а в шеститочии в нижней части + - его точечный состав. Внимательно изучите этот символ. + &
+ Шеститочие: письмо/чтение (розовая кнопка справа над кнопкой "вперёд") изменяет порядок столбцов: точки + 1, 2, 3 справа (как при письме на брайлевском приборе) или слева (как при чтении). ]]>
@@ -318,7 +435,10 @@ В данном разделе можно просмотреть точечный состав любой буквы, цифры или специального символа. Буквы расположены здесь в порядке \"зрячего\" алфавита, а не в порядке \"ключа Брайля\", как они изучаются в курсе. - Помните, что при написании чисел нужно вначале ставить цифровой знак. + &
+ Помните, что при написании чисел нужно вначале ставить цифровой знак. При написании слов на + латинице или по-гречески внутри русского текста в начале слова надо ставить признак + латинской / греческой буквы. &
Нажатие на символ в списке (или двойное нажатие, если Вы используете программу TalkBack) открывает окно, где этот символ крупным шрифтом выведен на экран. @@ -330,7 +450,17 @@ + Из этого раздела можно выйти обратно к списку символов, нажав кнопку \"Перейти вверх\" + в левом верхнем углу экрана. + ]]> + + + + Из этого раздела можно выйти обратно к списку символов, нажав кнопку \"Перейти вверх\" @@ -344,16 +474,20 @@ Всплывающие уведомления Вибрация Вибрация в ответ на результат выполнения задания. - Шаги с букварём + Шаги с бумажным пособием + Шаги с брайлевским прибором - Показывать шаги с обращением к бумажному букварю Голубиной в теоретическом курсе. + Показывать шаги с обращением к бумажному пособию Голубиной в теоретическом курсе. + + + Показывать шаги для выполнения с брайлевским прибором в теоретическом курсе. Включить/отключить краткие всплывающие подсказки (кроме самых важных). Обход точек в порядке нумерации Работает с Андроид 5.1. Если включено, программа экранного доступа обходит точки шеститочия в порядке 1-2-3-4-5-6, - иначе 1-4-2-5-3-6. + иначе 1-4-2-5-3-6 (при чтении). При письме, если включено - 4-5-6-1-2-3, иначе 4-1-5-2-6-3. Проверка ввода \"на лету\" Выводить сообщение \"правильно\", @@ -384,6 +518,13 @@ Отображать в главном меню кнопку \"QR-код\" для игры с карточками Брайля. + + При вводе столбец с точками 1, 2, 3 справа + + + При заходе в практику или шаг с вводом точек столбец с точками 1, 2, 3 находится справа, + как при письме на брайлевском приборе. + @@ -407,5 +548,5 @@ Пройдено шагов с вводом За последние 7 дней За последние 30 дней - + Шеститочие: письмо/чтение diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index bf9a2db7..d2cfacdc 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,3 +1,4 @@ + @@ -41,6 +42,25 @@ + + + +