From a60a277c03ab2d5f75d5bba7f4552261024f0098 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Mon, 18 May 2020 13:21:04 +0400 Subject: [PATCH 01/63] Make some important toasts not checked --- .../learnbraille/ui/screens/menu/MenuFragment.kt | 11 ++++------- android/app/src/main/res/values/strings.xml | 3 +-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt index f1f046fc..f24f0806 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt @@ -20,10 +20,7 @@ import com.github.braillesystems.learnbraille.data.repository.PreferenceReposito import com.github.braillesystems.learnbraille.databinding.FragmentMenuBinding import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp import com.github.braillesystems.learnbraille.ui.screens.theory.toLastCourseStep -import com.github.braillesystems.learnbraille.utils.checkedToast -import com.github.braillesystems.learnbraille.utils.executeIf -import com.github.braillesystems.learnbraille.utils.sendMarketIntent -import com.github.braillesystems.learnbraille.utils.updateTitle +import com.github.braillesystems.learnbraille.utils.* import org.koin.android.ext.android.inject import timber.log.Timber @@ -85,7 +82,7 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { private fun processQrResult(resultCode: Int, data: Intent?) { when (resultCode) { - RESULT_OK -> checkedToast( + RESULT_OK -> toast( data?.getStringExtra("SCAN_RESULT") ?: getString(R.string.menu_qr_empty_result).also { Timber.e("QR: empty result with OK code") @@ -102,7 +99,7 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { recordAudioPermissionCode -> if (grantResults.first() != PackageManager.PERMISSION_GRANTED) { - checkedToast(getString(R.string.voice_record_denial)) + toast(getString(R.string.voice_record_denial)) } } } @@ -119,7 +116,7 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { private fun interruptingOnClickListener(block: (View) -> Unit) = View.OnClickListener { if (db.isInitialized) block(it) - else checkedToast(getString(R.string.menu_db_not_initialized_warning)) + else toast(getString(R.string.menu_db_not_initialized_warning)) } companion object { diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 4106d9e5..42b6e5ff 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -119,8 +119,7 @@ Выведены точки: %s - Урок %d еще недоступен, пройдите сначала предыдущие уроки. \n - Вы остановились на уроке %d + Урок %d еще недоступен, пройдите сначала предыдущие уроки. \nВы остановились на уроке %d Текст From 62fe666905f42bd447fe5ccd4531c4c8f8ca0835 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 00:58:29 +0400 Subject: [PATCH 02/63] 7.5 fix multiple touches navigation --- .../ui/screens/AbstractFragmentWithHelp.kt | 4 +- .../learnbraille/ui/screens/MainActivity.kt | 7 +++- .../ui/screens/exit/ExitFragment.kt | 8 ++-- .../ui/screens/menu/MenuFragment.kt | 16 ++++---- .../ui/screens/theory/TheoryFreeNavigation.kt | 4 +- .../theory/steps/AbstractStepFragment.kt | 4 +- .../learnbraille/utils/SpeechRecognition.kt | 3 +- .../learnbraille/utils/Utils.kt | 30 +++++++++----- .../learnbraille/utils/_Android.kt | 40 ++++++++++--------- 9 files changed, 66 insertions(+), 50 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt index 36bc96c0..5da0959c 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt @@ -4,10 +4,10 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import androidx.fragment.app.Fragment -import androidx.navigation.fragment.findNavController import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.ui.screens.help.HelpFragmentDirections import com.github.braillesystems.learnbraille.utils.announceByAccessibility +import com.github.braillesystems.learnbraille.utils.navigate import timber.log.Timber typealias HelpMsgId = Int @@ -38,6 +38,6 @@ abstract class AbstractFragmentWithHelp(private val helpMsgId: HelpMsgId) : Frag val action = HelpFragmentDirections.actionGlobalHelpFragment() action.helpMessage = helpMsg announceByAccessibility(helpMsg) - findNavController().navigate(action) + navigate(action) } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/MainActivity.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/MainActivity.kt index 09cf1d62..f3d0e3ad 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/MainActivity.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/MainActivity.kt @@ -34,5 +34,10 @@ class MainActivity : AppCompatActivity() { requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT } - override fun onSupportNavigateUp(): Boolean = navController.navigateUp() + override fun onSupportNavigateUp(): Boolean = try { + navController.navigateUp() + } catch (e: IllegalArgumentException) { + Timber.e("Multitouch navigation", e) + false + } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt index 57dbd1d6..2c403aed 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt @@ -5,11 +5,11 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment -import androidx.navigation.Navigation 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.utils.announceByAccessibility +import com.github.braillesystems.learnbraille.utils.navigate import com.github.braillesystems.learnbraille.utils.updateTitle import org.koin.android.ext.android.get import kotlin.system.exitProcess @@ -39,9 +39,9 @@ class ExitFragment : Fragment() { exitProcess(0) } - continueButton.setOnClickListener( - Navigation.createNavigateOnClickListener(R.id.action_global_menuFragment) - ) + continueButton.setOnClickListener { + navigate(R.id.action_global_menuFragment) + } }.root diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt index f24f0806..2e71efe3 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt @@ -11,8 +11,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.databinding.DataBindingUtil -import androidx.navigation.Navigation -import androidx.navigation.fragment.findNavController import com.github.braillesystems.learnbraille.COURSE_ID import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.data.db.LearnBrailleDatabase @@ -49,7 +47,7 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { }) practiceButton.setOnClickListener(interruptingOnClickListener { - findNavController().navigate(R.id.action_menuFragment_to_practiceFragment) + navigate(R.id.action_menuFragment_to_practiceFragment) }) qrPracticeButton.setOnClickListener { @@ -63,13 +61,13 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { } } - settingsButton.setOnClickListener( - Navigation.createNavigateOnClickListener(R.id.action_menuFragment_to_settingsFragment) - ) + settingsButton.setOnClickListener { + navigate(R.id.action_menuFragment_to_settingsFragment) + } - exitButton.setOnClickListener( - Navigation.createNavigateOnClickListener(R.id.action_menuFragment_to_exitFragment) - ) + exitButton.setOnClickListener { + navigate(R.id.action_menuFragment_to_exitFragment) + } }.root diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/TheoryFreeNavigation.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/TheoryFreeNavigation.kt index d9c925e5..26e4b6fd 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/TheoryFreeNavigation.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/TheoryFreeNavigation.kt @@ -6,7 +6,6 @@ package com.github.braillesystems.learnbraille.ui.screens.theory import androidx.fragment.app.Fragment import androidx.navigation.NavDirections -import androidx.navigation.fragment.findNavController import com.github.braillesystems.learnbraille.data.entities.* import com.github.braillesystems.learnbraille.data.repository.MutableTheoryRepository import com.github.braillesystems.learnbraille.data.repository.TheoryRepository @@ -37,8 +36,7 @@ fun getAction(step: Step): NavDirections = fun AbstractStepFragment.getStepArg(): Step = parse(Step.serializer(), getStringArg(stepArgName)) -fun Fragment.toStep(step: Step) = - findNavController().navigate(getAction(step)) +fun Fragment.toStep(step: Step) = navigate(getAction(step)) fun AbstractStepFragment.toNextStep( thisStep: Step, diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt index df6feb60..ff3b9b3a 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt @@ -3,12 +3,12 @@ package com.github.braillesystems.learnbraille.ui.screens.theory.steps import android.view.Menu import android.view.MenuInflater import android.view.MenuItem -import androidx.navigation.fragment.findNavController import com.github.braillesystems.learnbraille.COURSE_ID import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp import com.github.braillesystems.learnbraille.ui.screens.HelpMsgId import com.github.braillesystems.learnbraille.ui.screens.theory.toCurrentStep +import com.github.braillesystems.learnbraille.utils.navigate import com.github.braillesystems.learnbraille.utils.updateTitle @@ -31,7 +31,7 @@ abstract class AbstractStepFragment(helpMsgId: HelpMsgId) : AbstractFragmentWith override fun onOptionsItemSelected(item: MenuItem) = false.also { when (item.itemId) { R.id.help -> navigateToHelp() - R.id.lessons_list -> findNavController().navigate(R.id.action_global_lessonsListFragment) + R.id.lessons_list -> navigate(R.id.action_global_lessonsListFragment) R.id.current_course_pos -> toCurrentStep(COURSE_ID) } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/SpeechRecognition.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/SpeechRecognition.kt index 6efa5ae3..da1887d5 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/SpeechRecognition.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/SpeechRecognition.kt @@ -8,7 +8,6 @@ import android.speech.SpeechRecognizer import android.speech.tts.TextToSpeech import android.speech.tts.UtteranceProgressListener import androidx.fragment.app.Fragment -import androidx.navigation.fragment.findNavController import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository import timber.log.Timber @@ -116,7 +115,7 @@ private class SpeechRecognitionListener(fragment: Fragment) : RecognitionListene exitProcess(0) } if (matches[0] == "нет") { - hostFragment.findNavController().navigate(R.id.action_global_menuFragment) + hostFragment.navigate(R.id.action_global_menuFragment) } } catch (e: Exception) { Timber.e(e) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt index 6f2dd533..c2ef9c57 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt @@ -8,6 +8,8 @@ import android.content.Context import android.os.Vibrator import android.view.accessibility.AccessibilityEvent import android.widget.Toast +import androidx.appcompat.app.ActionBar +import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import com.github.braillesystems.learnbraille.LearnBrailleApplication import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository @@ -27,24 +29,18 @@ val Fragment.application: LearnBrailleApplication fun scope(job: Job = Job()) = CoroutineScope(Dispatchers.Main + job) fun Vibrator?.checkedBuzz(pattern: BuzzPattern, preferenceRepository: PreferenceRepository) = - executeIf(preferenceRepository.buzzEnabled) { - buzz(pattern) - } + executeIf(preferenceRepository.buzzEnabled) { buzz(pattern) } fun checkedToast(msg: String, context: Context?, preferenceRepository: PreferenceRepository) = executeIf(preferenceRepository.toastsEnabled) { - Toast.makeText( - context, msg, preferenceRepository.toastDuration - ).show() + Toast.makeText(context, msg, preferenceRepository.toastDuration).show() } fun Fragment.checkedToast(msg: String, preferenceRepository: PreferenceRepository = get()) = checkedToast(msg, context, preferenceRepository) fun Fragment.toast(msg: String, preferenceRepository: PreferenceRepository = get()) = - Toast.makeText( - context, msg, preferenceRepository.toastDuration - ).show() + Toast.makeText(context, msg, preferenceRepository.toastDuration).show() fun Context.announceByAccessibility( announcement: String @@ -62,6 +58,22 @@ fun Context.announceByAccessibility( fun Fragment.announceByAccessibility(announcement: String) = application.announceByAccessibility(announcement) +val Fragment.actionBar: ActionBar? + get() = (activity as AppCompatActivity).supportActionBar + +/** + * Throws if action bar is not available + */ +var Fragment.title: String + get() = requireNotNull(actionBar).title.toString() + set(value) { + requireNotNull(actionBar).title = value + } + +fun Fragment.updateTitle(title: String) { + this.title = title +} + fun stringify(s: SerializationStrategy, obj: T) = Json.stringify(s, obj) fun parse(d: DeserializationStrategy, s: String) = Json.parse(d, s) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt index 41e298a8..d0173d2b 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt @@ -13,9 +13,9 @@ import android.net.Uri import android.os.Vibrator import android.provider.Settings import android.view.accessibility.AccessibilityManager -import androidx.appcompat.app.ActionBar -import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment +import androidx.navigation.NavDirections +import androidx.navigation.fragment.findNavController import timber.log.Timber val Context.usbManager get() = getSystemService(Context.USB_SERVICE) as UsbManager @@ -24,22 +24,6 @@ val Context.accessibilityManager: AccessibilityManager? if (!isAccessibilityEnabled) null else getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager -val Fragment.actionBar: ActionBar? - get() = (activity as AppCompatActivity).supportActionBar - -/** - * Throws if action bar is not available - */ -var Fragment.title: String - get() = requireNotNull(actionBar).title.toString() - set(value) { - requireNotNull(actionBar).title = value - } - -fun Fragment.updateTitle(title: String) { - this.title = title -} - fun Fragment.getStringArg(name: String): String = arguments?.getString(name) ?: error("No $name found in args") @@ -76,3 +60,23 @@ val Context.isAccessibilityEnabled: Boolean by logged { Settings.Secure.ACCESSIBILITY_ENABLED ) == 1 } + +/** + * Fixes multiple navigation issue: `IllegalArgumentException x is unknown to this NavController` + * https://blog.jakelee.co.uk/resolving-crash-illegalargumentexception-x-is-unknown-to-this-navcontroller/ + */ +fun Fragment.navigate(id: Int): Unit = try { + findNavController().navigate(id) +} catch (e: IllegalArgumentException) { + Timber.e("Multitouch navigation", e) +} + +/** + * Fixes multiple navigation issue: `IllegalArgumentException x is unknown to this NavController` + * https://blog.jakelee.co.uk/resolving-crash-illegalargumentexception-x-is-unknown-to-this-navcontroller/ + */ +fun Fragment.navigate(action: NavDirections) = try { + findNavController().navigate(action) +} catch (e: IllegalArgumentException) { + Timber.e("Multitouch navigation", e) +} From 87740845b2a3e260cc4f2c0c1da7e53d02dfe23b Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 12:21:34 +0400 Subject: [PATCH 03/63] 7.12 add deck list UI --- .../ui/screens/practice/CardFragment.kt | 36 +++++-- .../ui/screens/practice/CardViewModel.kt | 2 +- .../ui/screens/practice/DecksList.kt | 97 +++++++++++++++++++ .../src/main/res/layout/decks_list_item.xml | 41 ++++++++ ...ragment_practice.xml => fragment_card.xml} | 0 .../main/res/layout/fragment_decks_list.xml | 11 +++ .../layout/fragment_lessons_input_dots.xml | 2 +- .../layout/fragment_lessons_input_symbol.xml | 2 +- android/app/src/main/res/menu/card_menu.xml | 19 ++++ .../src/main/res/navigation/navigation.xml | 21 +++- android/app/src/main/res/values/strings.xml | 10 ++ 11 files changed, 225 insertions(+), 16 deletions(-) create mode 100644 android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt create mode 100644 android/app/src/main/res/layout/decks_list_item.xml rename android/app/src/main/res/layout/{fragment_practice.xml => fragment_card.xml} (100%) create mode 100644 android/app/src/main/res/layout/fragment_decks_list.xml create mode 100644 android/app/src/main/res/menu/card_menu.xml diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt index dc34a0fa..0b9e54b9 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt @@ -2,17 +2,18 @@ package com.github.braillesystems.learnbraille.ui.screens.practice import android.os.Bundle import android.os.Vibrator -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* import androidx.core.content.getSystemService import androidx.databinding.DataBindingUtil import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.data.entities.dummyMaterialOf +import com.github.braillesystems.learnbraille.data.repository.PracticeRepository import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository -import com.github.braillesystems.learnbraille.databinding.FragmentPracticeBinding +import com.github.braillesystems.learnbraille.databinding.FragmentCardBinding +import com.github.braillesystems.learnbraille.res.deckTagToName import com.github.braillesystems.learnbraille.ui.brailletrainer.BrailleTrainer import com.github.braillesystems.learnbraille.ui.brailletrainer.BrailleTrainerSignalHandler import com.github.braillesystems.learnbraille.ui.screens.* @@ -20,9 +21,8 @@ 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.utils.announceByAccessibility -import com.github.braillesystems.learnbraille.utils.checkedToast -import com.github.braillesystems.learnbraille.utils.updateTitle +import com.github.braillesystems.learnbraille.utils.* +import kotlinx.coroutines.launch import org.koin.android.ext.android.inject import org.koin.core.parameter.parametersOf import timber.log.Timber @@ -48,9 +48,9 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ) = DataBindingUtil.inflate( + ) = DataBindingUtil.inflate( inflater, - R.layout.fragment_practice, + R.layout.fragment_card, container, false ).apply { @@ -60,6 +60,12 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { updateTitle(title) setHasOptionsMenu(true) + lifecycleScope.launch { + val practiceRepository: PracticeRepository by inject() + val tag = practiceRepository.getCurrDeck().tag + toast(deckTagToName.getValue(tag)) + } + dotsState = brailleDots.dotsState.apply { subscribe(View.OnClickListener { viewModel.onSoftCheck() @@ -83,6 +89,7 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { lifecycleOwner = this@CardFragment + // TODO extract to method if (preferences.inputOnFlyCheck) { viewModel.observeEventCorrect( viewLifecycleOwner, preferences, dotsState, buzzer = null @@ -138,4 +145,15 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { val intro = introStringNotNullLogged(material) announceByAccessibility(intro) } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.card_menu, menu) + } + + override fun onOptionsItemSelected(item: MenuItem) = false.also { + when (item.itemId) { + R.id.help -> navigateToHelp() + R.id.decks_list -> navigate(R.id.action_cardFragment_to_decksList) + } + } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt index 1e81ae10..ae697e68 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt @@ -78,7 +78,7 @@ class CardViewModel( val material = practiceRepository.getNextMaterial() require(material.data is Symbol) material.data.apply { - _symbol.value = symbol.toString() + _symbol.value = char.toString() expectedDots = brailleDots } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt new file mode 100644 index 00000000..c66e5e2d --- /dev/null +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt @@ -0,0 +1,97 @@ +package com.github.braillesystems.learnbraille.ui.screens.practice + +import android.graphics.Color +import android.os.Bundle +import android.view.LayoutInflater +import android.view.ViewGroup +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.MutablePracticeRepository +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.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 DecksList : Fragment() { + + private val practiceRepository: MutablePracticeRepository by inject() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DataBindingUtil.inflate( + inflater, + R.layout.fragment_decks_list, + container, + false + ).apply { + + title = getString(R.string.decks_list_title) + + lifecycleScope.launch { + val decks = practiceRepository.getAllDecks() + val listener = object : DecksItemListener { + override fun onClick(item: DeckNotEmpty) = + if (item.containsCards) { + practiceRepository.currentDeckId = item.deck.id + navigate(R.id.action_decksList_to_cardFragment) + } else { + checkedToast( + getString(R.string.decks_list_no_material_in_deck) + .format(deckTagToName.getValue(item.deck.tag)) + ) + } + } + decksList.adapter = DecksListAdapter(decks) { item -> + this.item = item + deckName.text = deckTagToName.getValue(item.deck.tag) + clickListener = listener + if (item.containsCards) { + deckName.setTextColor(Color.BLACK) + } else { + deckName.setTextColor(Color.GRAY) // TODO change for accessibility + } + } + } + + }.root +} + +private class DecksListAdapter( + private val decks: List, + private val bind: DecksListItemBinding.(DeckNotEmpty) -> Unit +) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = + DeckItemViewHolder( + DataBindingUtil.inflate( + LayoutInflater.from(parent.context), + R.layout.decks_list_item, + parent, false + ) + ) + + override fun getItemCount(): Int = decks.size + + override fun onBindViewHolder(holder: DeckItemViewHolder, position: Int) { + val item = decks[position] + holder.binding.bind(item) + } +} + +private class DeckItemViewHolder( + val binding: DecksListItemBinding +) : RecyclerView.ViewHolder(binding.root) + +interface DecksItemListener { + fun onClick(item: DeckNotEmpty) +} diff --git a/android/app/src/main/res/layout/decks_list_item.xml b/android/app/src/main/res/layout/decks_list_item.xml new file mode 100644 index 00000000..0cf8d79a --- /dev/null +++ b/android/app/src/main/res/layout/decks_list_item.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/layout/fragment_practice.xml b/android/app/src/main/res/layout/fragment_card.xml similarity index 100% rename from android/app/src/main/res/layout/fragment_practice.xml rename to android/app/src/main/res/layout/fragment_card.xml diff --git a/android/app/src/main/res/layout/fragment_decks_list.xml b/android/app/src/main/res/layout/fragment_decks_list.xml new file mode 100644 index 00000000..050e49a5 --- /dev/null +++ b/android/app/src/main/res/layout/fragment_decks_list.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/fragment_lessons_input_dots.xml b/android/app/src/main/res/layout/fragment_lessons_input_dots.xml index 606eb2ad..cf19c838 100644 --- a/android/app/src/main/res/layout/fragment_lessons_input_dots.xml +++ b/android/app/src/main/res/layout/fragment_lessons_input_dots.xml @@ -45,11 +45,11 @@ android:id="@+id/hint_button" android:layout_width="@dimen/practice_buttons_width" android:layout_height="@dimen/practice_buttons_small_height" - app:backgroundTint="@color/colorSecondary" android:contentDescription="@string/lessons_hint" android:onClick="@{() -> inputViewModel.onHint()}" android:text="@string/hint_icon" android:textSize="@dimen/practice_buttons_icon_size" + app:backgroundTint="@color/colorSecondary" app:layout_constraintBottom_toTopOf="@+id/prev_button" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/android/app/src/main/res/layout/fragment_lessons_input_symbol.xml b/android/app/src/main/res/layout/fragment_lessons_input_symbol.xml index a73f654f..f8b327db 100644 --- a/android/app/src/main/res/layout/fragment_lessons_input_symbol.xml +++ b/android/app/src/main/res/layout/fragment_lessons_input_symbol.xml @@ -46,11 +46,11 @@ android:id="@+id/hint_button" android:layout_width="@dimen/practice_buttons_width" android:layout_height="@dimen/practice_buttons_small_height" - app:backgroundTint="@color/colorSecondary" android:contentDescription="@string/lessons_hint" android:onClick="@{() -> inputViewModel.onHint()}" android:text="@string/hint_icon" android:textSize="@dimen/practice_buttons_icon_size" + app:backgroundTint="@color/colorSecondary" app:layout_constraintBottom_toTopOf="@+id/prev_button" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/android/app/src/main/res/menu/card_menu.xml b/android/app/src/main/res/menu/card_menu.xml new file mode 100644 index 00000000..cf1f24aa --- /dev/null +++ b/android/app/src/main/res/menu/card_menu.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/navigation/navigation.xml b/android/app/src/main/res/navigation/navigation.xml index e6d123fe..7634ab9b 100644 --- a/android/app/src/main/res/navigation/navigation.xml +++ b/android/app/src/main/res/navigation/navigation.xml @@ -14,7 +14,7 @@ tools:layout="@layout/fragment_menu"> + app:destination="@id/cardFragment" /> @@ -28,10 +28,14 @@ android:label="fragment_exit" tools:layout="@layout/fragment_exit" /> + android:label="fragment_card" + tools:layout="@layout/fragment_card"> + + + + + Подсказка Далее Практика: %d из %d + Список колод + + Колода %s еще не доступна, пройдите эти карточки в уроках! + + + Все символы + Русские буквы + Цифры + Специальные символы К текущему шагу Навигация по курсу Скрыть это меню + Список колод From 93231df70e6f8e93b643754cf0d922706e025d1b Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 12:59:43 +0400 Subject: [PATCH 04/63] 7.12 refactor data --- .../braillesystems/learnbraille/DslTest.kt | 24 +++-- .../learnbraille/LearnBrailleDatabaseTest.kt | 6 +- .../learnbraille/LearnBrailleApplication.kt | 17 ++- .../learnbraille/data/db/Database.kt | 11 +- .../learnbraille/data/dsl/Data.kt | 7 +- .../learnbraille/data/dsl/Materials.kt | 11 +- .../learnbraille/data/entities/Cards.kt | 11 ++ .../learnbraille/data/entities/Decks.kt | 6 +- .../data/entities/KnownMaterials.kt | 3 + .../data/entities/MaterialData.kt | 2 +- .../data/repository/PracticeRepository.kt | 47 +++++++- .../data/repository/PreferenceRepository.kt | 6 +- .../braillesystems/learnbraille/res/Data.kt | 51 ++++++--- .../learnbraille/res/Materials.kt | 102 +++++++++--------- .../learnbraille/ui/screens/Messages.kt | 4 +- .../theory/lessons/LessonsListFragment.kt | 4 +- .../theory/steps/InputSymbolFragment.kt | 2 +- .../theory/steps/ShowSymbolFragment.kt | 2 +- .../learnbraille/utils/_Android.kt | 10 ++ database.md | 4 +- 20 files changed, 215 insertions(+), 115 deletions(-) diff --git a/android/app/src/androidTest/java/com/github/braillesystems/learnbraille/DslTest.kt b/android/app/src/androidTest/java/com/github/braillesystems/learnbraille/DslTest.kt index 9343d2a2..6bf8a4ba 100644 --- a/android/app/src/androidTest/java/com/github/braillesystems/learnbraille/DslTest.kt +++ b/android/app/src/androidTest/java/com/github/braillesystems/learnbraille/DslTest.kt @@ -5,6 +5,7 @@ import com.github.braillesystems.learnbraille.data.dsl.* 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.DeckTags import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test @@ -18,13 +19,13 @@ private val content by materials { } private val ruSymbols by symbols(symbolType = "ru") { - symbol(symbol = 'А', brailleDots = BrailleDots(F, E, E, E, E, E)) - symbol(symbol = 'Б', brailleDots = BrailleDots(F, F, E, E, E, E)) + symbol(char = 'А', brailleDots = BrailleDots(F, E, E, E, E, E)) + symbol(char = 'Б', brailleDots = BrailleDots(F, F, E, E, E, E)) // ... } private val enSymbols by symbols(symbolType = "en") { - symbol(symbol = 'Z', brailleDots = BrailleDots(F, F, F, E, E, E)) + symbol(char = 'Z', brailleDots = BrailleDots(F, F, F, E, E, E)) // ... } @@ -71,6 +72,7 @@ private val prepopulationData by data( deck("En letters") { it is Symbol && it.type == "en" } + deck(DeckTags.all) { true } // ... } } @@ -127,18 +129,22 @@ private val users = listOf( User(0, "default3", "") ) private val mats = listOf( - Material(1, Symbol(symbol = 'А', brailleDots = BrailleDots(F, E, E, E, E, E), type = "ru")), - Material(2, Symbol(symbol = 'Б', brailleDots = BrailleDots(F, F, E, E, E, E), type = "ru")), - Material(3, Symbol(symbol = 'Z', brailleDots = BrailleDots(F, F, F, E, E, E), type = "en")) + Material(1, Symbol(char = 'А', brailleDots = BrailleDots(F, E, E, E, E, E), type = "ru")), + Material(2, Symbol(char = 'Б', brailleDots = BrailleDots(F, F, E, E, E, E), type = "ru")), + Material(3, Symbol(char = 'Z', brailleDots = BrailleDots(F, F, F, E, E, E), type = "en")) ) private val decks = listOf( - Deck(1, "Ru letters"), - Deck(2, "En letters") + Deck(2, "Ru letters"), + Deck(3, "En letters"), + Deck(1, DeckTags.all) ) private val cards = listOf( + Card(2, 1), + Card(2, 2), + Card(3, 3), Card(1, 1), Card(1, 2), - Card(2, 3) + Card(1, 3) ) private val courses = listOf( Course(1, "Best course", "The best"), diff --git a/android/app/src/androidTest/java/com/github/braillesystems/learnbraille/LearnBrailleDatabaseTest.kt b/android/app/src/androidTest/java/com/github/braillesystems/learnbraille/LearnBrailleDatabaseTest.kt index d29ada80..41c550d3 100644 --- a/android/app/src/androidTest/java/com/github/braillesystems/learnbraille/LearnBrailleDatabaseTest.kt +++ b/android/app/src/androidTest/java/com/github/braillesystems/learnbraille/LearnBrailleDatabaseTest.kt @@ -34,7 +34,7 @@ class LearnBrailleDatabaseTest { private val materials = listOf( Material( 1, Symbol( - symbol = 'А', + char = 'А', brailleDots = BrailleDots(F, E, E, E, E, E), type = SymbolType.ru ) @@ -43,7 +43,7 @@ class LearnBrailleDatabaseTest { private val decks = listOf( Deck( id = 1, - name = "Ru letters" + tag = "Ru letters" ) ) private val cards = listOf( @@ -170,7 +170,7 @@ class LearnBrailleDatabaseTest { @Test fun testDecks() = runBlocking { - assertEquals("Ru letters", db.deckDao.getDeck(1)!!.name) + assertEquals("Ru letters", db.deckDao.getDeck(1)!!.tag) } @Test diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt index 22ec3448..752c76a7 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt @@ -20,9 +20,6 @@ class LearnBrailleApplication : Application() { val koinModule = module { single { LearnBrailleDatabase.buildDatabase(this@LearnBrailleApplication) } - factory { - PracticeRepositoryImpl(get().materialDao) - } factory { PreferenceRepositoryImpl( this@LearnBrailleApplication, @@ -35,6 +32,20 @@ class LearnBrailleApplication : Application() { get().userDao ) } + factory { + val db = get() + PracticeRepositoryImpl( + this@LearnBrailleApplication, + db.deckDao, db.cardDao, get() + ) + } + factory { + val db = get() + PracticeRepositoryImpl( + this@LearnBrailleApplication, + db.deckDao, db.cardDao, get() + ) + } factory { get().run { TheoryRepositoryImpl( diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt index 96e4084f..1d60235a 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt @@ -24,8 +24,8 @@ import timber.log.Timber Course::class, Lesson::class, Step::class, StepAnnotation::class, StepHasAnnotation::class, CurrentStep::class, LastCourseStep::class, LastLessonStep::class ], - version = 14, - exportSchema = false + version = 15, + exportSchema = false // TODO export ) @TypeConverters( BrailleDotsConverters::class, @@ -77,7 +77,7 @@ abstract class LearnBrailleDatabase : RoomDatabase(), KoinComponent { ) } - companion object : KoinComponent { + companion object { const val name = "learn_braille_database" @@ -87,7 +87,7 @@ abstract class LearnBrailleDatabase : RoomDatabase(), KoinComponent { LearnBrailleDatabase::class.java, name ) - .addCallback(object : Callback() { + .addCallback(object : Callback(), KoinComponent { @SuppressLint("SyntheticAccessor") override fun onCreate(db: SupportSQLiteDatabase) { @@ -118,7 +118,6 @@ abstract class LearnBrailleDatabase : RoomDatabase(), KoinComponent { stepDao.insert(steps) stepAnnotationDao.insert(stepAnnotations) stepHasAnnotationDao.insert(stepHasAnnotations) - // TODO insert minimum amount of letters to known Timber.i("Finnish database prepopulation") prepopulationFinished = true @@ -127,7 +126,7 @@ abstract class LearnBrailleDatabase : RoomDatabase(), KoinComponent { } } }) - .fallbackToDestructiveMigration() + .fallbackToDestructiveMigration() // TODO change migration strategy .build() } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Data.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Data.kt index 923ca47b..97876e28 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Data.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Data.kt @@ -1,6 +1,7 @@ package com.github.braillesystems.learnbraille.data.dsl import com.github.braillesystems.learnbraille.data.entities.* +import com.github.braillesystems.learnbraille.res.DeckTags import com.github.braillesystems.learnbraille.utils.side import kotlin.reflect.KProperty @@ -137,7 +138,7 @@ class DataBuilder( fun decks(block: DecksBuilder.() -> Unit) = DecksBuilder(block).side { it.deckToPredicate.forEach { (deck, p) -> - val deckId = decks.size + 1L + val deckId = if (deck.tag == DeckTags.all) 1 else decks.size + 2L _decks += deck.copy(id = deckId) materials @@ -161,8 +162,8 @@ class DecksBuilder(block: DecksBuilder.() -> Unit) { block() } - fun deck(name: String, description: String = "", entryCriterion: (MaterialData) -> Boolean) { - val deck = Deck(DEFAULT_ID, name, description) + fun deck(tag: String, entryCriterion: (MaterialData) -> Boolean) { + val deck = Deck(DEFAULT_ID, tag) _deckToPredicate[deck] = entryCriterion } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Materials.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Materials.kt index da8c4231..45f92641 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Materials.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Materials.kt @@ -30,10 +30,10 @@ class MaterialsBuilder(block: MaterialsBuilder.() -> Unit) { _materials = materials.mapIndexed { index, material -> material.copy(id = index + 1L).apply { if (data is Symbol) { - require(!_symbols.contains(data.symbol)) { - "Symbol (symbol = ${data.symbol}, type = ${data.type}) already exists" + require(!_symbols.contains(data.char)) { + "Symbol (symbol = ${data.char}, type = ${data.type}) already exists" } - _symbols[data.symbol] = this + _symbols[data.char] = this } } }.toMutableList() @@ -69,7 +69,8 @@ class SymbolsBuilder(private val symbolType: String, block: SymbolsBuilder.() -> operator fun get(symbol: Char): Symbol? = _map[symbol] - fun symbol(symbol: Char, brailleDots: BrailleDots) { - _map[symbol] = Symbol(symbol, brailleDots, symbolType) + fun symbol(char: Char, brailleDots: BrailleDots) { + @Suppress("NAME_SHADOWING") val char = char.toUpperCase() + _map[char] = Symbol(char, brailleDots, symbolType) } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Cards.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Cards.kt index f08a2f8f..7c00e445 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Cards.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Cards.kt @@ -16,4 +16,15 @@ interface CardDao { @Insert(onConflict = OnConflictStrategy.ABORT) suspend fun insert(cards: List) + + @Query( + """ + select materials.* from decks + inner join cards on decks.id = cards.deck_id + inner join materials on materials.id = cards.material_id + where decks.id = :deckId + order by RANDOM() limit 1 + """ + ) + suspend fun getRandomMaterialFromDeck(deckId: Long): Material? } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Decks.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Decks.kt index 9e1acb48..84ab07f3 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Decks.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Decks.kt @@ -6,8 +6,7 @@ import androidx.room.* @Entity(tableName = "decks") data class Deck( @PrimaryKey val id: Long, - val name: String, - val description: String = "" + val tag: String ) @Dao @@ -18,4 +17,7 @@ interface DeckDao { @Query("select * from decks where id = :id") suspend fun getDeck(id: Long): Deck? + + @Query("select * from decks") + suspend fun getAllDecks(): List } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/KnownMaterials.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/KnownMaterials.kt index 475fa755..79af163e 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/KnownMaterials.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/KnownMaterials.kt @@ -16,4 +16,7 @@ interface KnownMaterialDao { @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insert(knowledge: KnownMaterial) + + @Insert(onConflict = OnConflictStrategy.IGNORE) + suspend fun insert(knowledge: List) } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/MaterialData.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/MaterialData.kt index 8c0b33c0..d5d800b3 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/MaterialData.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/MaterialData.kt @@ -24,7 +24,7 @@ class MaterialDataTypeConverters { @Serializable data class Symbol( - val symbol: Char, + val char: Char, val brailleDots: BrailleDots, @SerialName("symbol_type") val type: String diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt index 8d56cc9a..314ef24e 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt @@ -1,20 +1,59 @@ package com.github.braillesystems.learnbraille.data.repository +import android.content.Context +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.MaterialDao +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 MutablePracticeRepository : PracticeRepository +interface MutablePracticeRepository : PracticeRepository { + + override var currentDeckId: Long +} class PracticeRepositoryImpl( - private val materialDao: MaterialDao + private val context: Context, + private val deckDao: DeckDao, + private val cardDao: CardDao, + private val preferenceRepository: PreferenceRepository ) : MutablePracticeRepository { + private val currDeckPreference get() = "practice_curr_deck ${preferenceRepository.currentUserId}" + + override var currentDeckId: Long + get() = context.preferences.getLong( + currDeckPreference, 1 + ) + set(value) { + with(context.preferences.edit()) { + putLong(currDeckPreference, value) + apply() + } + } + + // TODO deck filling constraints override suspend fun getNextMaterial(): Material = - materialDao.getRandomMaterial() + cardDao.getRandomMaterialFromDeck(currentDeckId) ?: error("Material is expected to be prepopulated") + + override suspend fun getCurrDeck(): Deck = + deckDao.getDeck(currentDeckId) ?: error("Current deck should always exist") + + override suspend fun getAllDecks(): List = + deckDao.getAllDecks().map { DeckNotEmpty(it, true) } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt index 8704ba68..db7d6465 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt @@ -1,14 +1,13 @@ package com.github.braillesystems.learnbraille.data.repository import android.content.Context -import android.content.SharedPreferences import android.widget.Toast -import androidx.preference.PreferenceManager import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.data.entities.User import com.github.braillesystems.learnbraille.data.entities.UserDao import com.github.braillesystems.learnbraille.utils.BuzzPattern import com.github.braillesystems.learnbraille.utils.logged +import com.github.braillesystems.learnbraille.utils.preferences import timber.log.Timber @@ -99,6 +98,3 @@ class PreferenceRepositoryImpl( Timber.i("Current user = $it") } ?: error("Current user should always exist") } - -private val Context.preferences: SharedPreferences - get() = PreferenceManager.getDefaultSharedPreferences(this) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt index 6538b904..60c44ba8 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt @@ -1,18 +1,11 @@ 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.data import com.github.braillesystems.learnbraille.data.entities.Symbol - - -object SymbolType { - const val ru = "ru" - const val special = "special" - const val digit = "digit" -} - -object StepAnnotation { - const val golubinaBookRequired = "golubina_book_required" -} +import com.github.braillesystems.learnbraille.utils.lazyWithContext /** @@ -29,7 +22,7 @@ object StepAnnotation { * If you need some additional info, do not hardcode it. Just make request to the new DSL feature. */ val prepopulationData by data( - materials = practiceContent, // TODO replace with `content` + materials = content, stepAnnotations = listOf( StepAnnotation.golubinaBookRequired ) @@ -63,16 +56,40 @@ val prepopulationData by data( } decks { - // TODO replace names with tags and provide mapping from tags to names - // to be able to make localization - deck("Русские буквы") { data -> + deck(DeckTags.all) { true } + deck(DeckTags.ruLetters) { data -> data is Symbol && data.type == SymbolType.ru } - deck("Специальные символы") { data -> + deck(DeckTags.special) { data -> data is Symbol && data.type == SymbolType.special } - deck("Цифры") { data -> + deck(DeckTags.digits) { data -> data is Symbol && data.type == SymbolType.digit } } } + +object StepAnnotation { + const val golubinaBookRequired = "golubina_book_required" +} + +object DeckTags { + const val all = "all" + const val ruLetters = "ru_letters" + const val digits = "digits" + const val special = "special" +} + +val Context.deckTagToName: Map by lazyWithContext { + DeckTags.run { + mapOf( + all to getString(R.string.deck_name_all), + ruLetters to getString(R.string.deck_name_ru_letters), + digits to getString(R.string.deck_name_digits), + special to getString(R.string.deck_name_special_symbols) + ) + } +} + +val Fragment.deckTagToName + get() = context?.deckTagToName ?: error("Fragment should have context") diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt index 804124e2..dabd48e1 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt @@ -21,70 +21,72 @@ val content by materials { +uebDigits } -val practiceContent by materials { - +ruSymbols +object SymbolType { + const val ru = "ru" + const val special = "special" + const val digit = "digit" } val ruSymbols by symbols(SymbolType.ru) { - symbol(symbol = 'А', brailleDots = BrailleDots(F, E, E, E, E, E)) - symbol(symbol = 'Б', brailleDots = BrailleDots(F, F, E, E, E, E)) - symbol(symbol = 'В', brailleDots = BrailleDots(E, F, E, F, F, F)) - symbol(symbol = 'Г', brailleDots = BrailleDots(F, F, E, F, F, E)) - symbol(symbol = 'Д', brailleDots = BrailleDots(F, E, E, F, F, E)) - symbol(symbol = 'Е', brailleDots = BrailleDots(F, E, E, E, F, E)) - symbol(symbol = 'Ё', brailleDots = BrailleDots(F, E, E, E, E, F)) - symbol(symbol = 'Ж', brailleDots = BrailleDots(E, F, E, F, F, E)) - symbol(symbol = 'З', brailleDots = BrailleDots(F, E, F, E, F, F)) - symbol(symbol = 'И', brailleDots = BrailleDots(E, F, E, F, E, E)) - symbol(symbol = 'Й', brailleDots = BrailleDots(F, F, F, F, E, F)) - symbol(symbol = 'К', brailleDots = BrailleDots(F, E, F, E, E, E)) - symbol(symbol = 'Л', brailleDots = BrailleDots(F, F, F, E, E, E)) - symbol(symbol = 'М', brailleDots = BrailleDots(F, E, F, F, E, E)) - symbol(symbol = 'Н', brailleDots = BrailleDots(F, E, F, F, F, E)) - symbol(symbol = 'О', brailleDots = BrailleDots(F, E, F, E, F, E)) - symbol(symbol = 'П', brailleDots = BrailleDots(F, F, F, F, E, E)) - symbol(symbol = 'Р', brailleDots = BrailleDots(F, F, F, E, F, E)) - symbol(symbol = 'С', brailleDots = BrailleDots(E, F, F, F, E, E)) - symbol(symbol = 'Т', brailleDots = BrailleDots(E, F, F, F, F, E)) - symbol(symbol = 'У', brailleDots = BrailleDots(F, E, F, E, E, F)) - symbol(symbol = 'Ф', brailleDots = BrailleDots(F, F, E, F, E, E)) - symbol(symbol = 'Х', brailleDots = BrailleDots(F, F, E, E, F, E)) - symbol(symbol = 'Ц', brailleDots = BrailleDots(F, E, E, F, E, E)) - symbol(symbol = 'Ч', brailleDots = BrailleDots(F, F, F, F, F, E)) - symbol(symbol = 'Ш', brailleDots = BrailleDots(F, E, E, E, F, F)) - symbol(symbol = 'Щ', brailleDots = BrailleDots(F, E, F, F, E, F)) - symbol(symbol = 'Ъ', brailleDots = BrailleDots(F, F, F, E, F, F)) - symbol(symbol = 'Ы', brailleDots = BrailleDots(E, F, F, F, E, F)) - symbol(symbol = 'Ь', brailleDots = BrailleDots(E, F, F, F, F, F)) - symbol(symbol = 'Э', brailleDots = BrailleDots(E, F, E, F, E, F)) - symbol(symbol = 'Ю', brailleDots = BrailleDots(F, F, E, E, F, F)) - symbol(symbol = 'Я', brailleDots = BrailleDots(F, F, E, F, E, F)) + 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)) } val specialSymbols by symbols(SymbolType.special) { - symbol(symbol = ']', brailleDots = BrailleDots(E, E, F, F, F, F)) // цифровой знак // number - symbol(symbol = ',', brailleDots = BrailleDots(E, F, E, E, E, E)) // Знак препинания 'Запятая' - symbol(symbol = '-', brailleDots = BrailleDots(E, E, F, E, E, F)) // Дефис - symbol(symbol = '.', brailleDots = BrailleDots(E, F, F, E, F, E)) // Точка + 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, F, E, F, E)) // Dot } val uebDigits by symbols(SymbolType.digit) { - symbol(symbol = '1', brailleDots = BrailleDots(F, E, E, E, E, E)) - symbol(symbol = '2', brailleDots = BrailleDots(F, F, E, E, E, E)) - symbol(symbol = '3', brailleDots = BrailleDots(F, E, E, F, E, E)) - symbol(symbol = '4', brailleDots = BrailleDots(F, E, E, F, F, E)) - symbol(symbol = '5', brailleDots = BrailleDots(F, E, E, E, F, E)) - symbol(symbol = '6', brailleDots = BrailleDots(F, F, E, F, E, E)) - symbol(symbol = '7', brailleDots = BrailleDots(F, F, E, F, F, E)) - symbol(symbol = '8', brailleDots = BrailleDots(F, F, E, E, F, E)) - symbol(symbol = '9', brailleDots = BrailleDots(E, F, E, F, E, E)) - symbol(symbol = '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)) + symbol(char = '0', brailleDots = BrailleDots(E, F, E, F, F, E)) } /** * Add here rules, how to display hints for symbols. */ -val Context.symbolTypeDisplayList: List> by lazyWithContext { +val Context.symbolTypeIntroList: List> by lazyWithContext { // Prevent lambda of capturing context that will be invalid next time fragment entered, // so use `Fragment.getString` outside of lambdas. listOfP2F( diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt index 866407ee..c1cfbea0 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt @@ -7,7 +7,7 @@ import com.github.braillesystems.learnbraille.data.entities.BrailleDots 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.res.symbolTypeDisplayList +import com.github.braillesystems.learnbraille.res.symbolTypeIntroList import com.github.braillesystems.learnbraille.utils.checkedToast import com.github.braillesystems.learnbraille.utils.peek import com.github.braillesystems.learnbraille.utils.toast @@ -24,7 +24,7 @@ fun Fragment.showHintDotsToast(expectedDots: BrailleDots) = fun Context.introString(material: Material): String? = when (material.data) { - is Symbol -> symbolTypeDisplayList.peek(material.data.symbol) + is Symbol -> symbolTypeIntroList.peek(material.data.char) } fun Context.introStringNotNullLogged(material: Material): String = introString(material) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt index 593c6f98..aba9cc1f 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt @@ -7,6 +7,7 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import com.github.braillesystems.learnbraille.COURSE_ID import com.github.braillesystems.learnbraille.R @@ -16,7 +17,6 @@ import com.github.braillesystems.learnbraille.databinding.FragmentLessonsListBin import com.github.braillesystems.learnbraille.databinding.LessonsListItemBinding import com.github.braillesystems.learnbraille.ui.screens.theory.toLastLessonStep import com.github.braillesystems.learnbraille.utils.checkedToast -import com.github.braillesystems.learnbraille.utils.scope import com.github.braillesystems.learnbraille.utils.title import kotlinx.coroutines.launch import org.koin.android.ext.android.inject @@ -39,7 +39,7 @@ class LessonsListFragment : Fragment() { title = getString(R.string.lessons_title_lessons_list) - scope().launch { + lifecycleScope.launch { val curr = theoryRepository.getCurrentStep(COURSE_ID) val lessons = theoryRepository.getAllCourseLessons(COURSE_ID) val activeListener = object : LessonItemListener { diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt index 30e2efec..4b9077ad 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt @@ -51,7 +51,7 @@ class InputSymbolFragment : AbstractStepFragment(R.string.lessons_help_input_sym require(step.data is Input) require(step.data.material.data is Symbol) val symbol = step.data.material.data - letter.text = symbol.symbol.toString() + letter.text = symbol.char.toString() brailleDots.dotsState.display(symbol.brailleDots) announceByAccessibility(introStringNotNullLogged(step.data.material)) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt index da846f71..26a4c602 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt @@ -33,7 +33,7 @@ class ShowSymbolFragment : AbstractStepFragment(R.string.lessons_help_show_symbo val step = getStepArg() require(step.data is Show) require(step.data.material.data is Symbol) - letter.text = step.data.material.data.symbol.toString() + letter.text = step.data.material.data.char.toString() brailleDots.dotsState.display(step.data.material.data.brailleDots) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_show_symbol) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt index d0173d2b..c056a1bd 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt @@ -8,6 +8,7 @@ package com.github.braillesystems.learnbraille.utils import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent +import android.content.SharedPreferences import android.hardware.usb.UsbManager import android.net.Uri import android.os.Vibrator @@ -16,6 +17,8 @@ import android.view.accessibility.AccessibilityManager import androidx.fragment.app.Fragment import androidx.navigation.NavDirections import androidx.navigation.fragment.findNavController +import androidx.preference.PreferenceManager +import com.github.braillesystems.learnbraille.R import timber.log.Timber val Context.usbManager get() = getSystemService(Context.USB_SERVICE) as UsbManager @@ -80,3 +83,10 @@ fun Fragment.navigate(action: NavDirections) = try { } catch (e: IllegalArgumentException) { Timber.e("Multitouch navigation", e) } + +val Context.appName: String by lazyWithContext { getString(R.string.app_name) } +val Fragment.appName: String + get() = context?.appName ?: error("Fragment is expected to have a context") + +val Context.preferences: SharedPreferences + get() = PreferenceManager.getDefaultSharedPreferences(this) diff --git a/database.md b/database.md index 1d567157..df290e3d 100644 --- a/database.md +++ b/database.md @@ -3,6 +3,9 @@ ![LBDB](https://user-images.githubusercontent.com/25281147/81498004-5a5cb000-92d3-11ea-9c53-7246ef9d0176.png) +Update: `Deck` does not have description. + + By `dbdiagram.io`: ``` @@ -85,7 +88,6 @@ Table step_annotations { Table decks { id long [pk] name varchar [not null] - description varchar [not null, default: ''] } Table cards { From 673c6d4d44b8e0e9785959f10947b78e93e4c244 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 13:57:36 +0400 Subject: [PATCH 05/63] Refactor on fly check --- .../learnbraille/ui/screens/DotsChecker.kt | 29 ++++++++++++++++--- .../ui/screens/practice/CardFragment.kt | 23 ++++----------- .../screens/theory/steps/InputDotsFragment.kt | 28 ++++-------------- .../theory/steps/InputSymbolFragment.kt | 28 ++++-------------- 4 files changed, 43 insertions(+), 65 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/DotsChecker.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/DotsChecker.kt index d5469595..6b023cc0 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/DotsChecker.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/DotsChecker.kt @@ -10,12 +10,14 @@ import com.github.braillesystems.learnbraille.data.repository.PreferenceReposito import com.github.braillesystems.learnbraille.ui.brailletrainer.BrailleTrainer import com.github.braillesystems.learnbraille.ui.views.* import com.github.braillesystems.learnbraille.utils.checkedBuzz +import org.koin.core.KoinComponent +import org.koin.core.get import timber.log.Timber /** * Represents state machine that serves input tasks. */ -interface DotsChecker { +interface DotsChecker : KoinComponent { /** * User pressed `next` button. @@ -194,11 +196,30 @@ private class DotsCheckerImpl : MutableDotsChecker { } } +inline fun DotsChecker.observeCheckedOnFly( + lifecycleOwner: LifecycleOwner, + dotsState: BrailleDotsState, + buzzer: Vibrator? = null, + preferenceRepository: PreferenceRepository = get(), + crossinline block: () -> Unit = {}, + crossinline softBlock: () -> Unit = {} +) { + if (preferenceRepository.inputOnFlyCheck) { + observeEventCorrect(lifecycleOwner, dotsState, buzzer = null, block = block) + observeEventSoftCorrect(lifecycleOwner, buzzer = buzzer, block = softBlock) + } else { + observeEventCorrect(lifecycleOwner, dotsState, buzzer) { + softBlock() + block() + } + } +} + inline fun DotsChecker.observeEventCorrect( lifecycleOwner: LifecycleOwner, - preferenceRepository: PreferenceRepository, dotsState: BrailleDotsState, buzzer: Vibrator? = null, + preferenceRepository: PreferenceRepository = get(), crossinline block: () -> Unit = {} ): Unit = eventCorrect.observe( lifecycleOwner, @@ -214,8 +235,8 @@ inline fun DotsChecker.observeEventCorrect( inline fun DotsChecker.observeEventSoftCorrect( lifecycleOwner: LifecycleOwner, - preferenceRepository: PreferenceRepository, buzzer: Vibrator? = null, + preferenceRepository: PreferenceRepository = get(), crossinline block: () -> Unit = {} ): Unit = eventSoftCorrect.observe( lifecycleOwner, @@ -230,9 +251,9 @@ inline fun DotsChecker.observeEventSoftCorrect( inline fun DotsChecker.observeEventIncorrect( lifecycleOwner: LifecycleOwner, - preferenceRepository: PreferenceRepository, dotsState: BrailleDotsState, buzzer: Vibrator? = null, + preferenceRepository: PreferenceRepository = get(), crossinline block: () -> Unit = {} ): Unit = eventIncorrect.observe( lifecycleOwner, diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt index 0b9e54b9..9a77e5d7 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt @@ -89,25 +89,14 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { lifecycleOwner = this@CardFragment - // TODO extract to method - if (preferences.inputOnFlyCheck) { - viewModel.observeEventCorrect( - viewLifecycleOwner, preferences, dotsState, buzzer = null - ) { updateTitle(title) } - viewModel.observeEventSoftCorrect( - viewLifecycleOwner, preferences, buzzer - ) { showCorrectToast() } - } else { - viewModel.observeEventCorrect( - viewLifecycleOwner, preferences, dotsState, buzzer - ) { - updateTitle(title) - showCorrectToast() - } - } + viewModel.observeCheckedOnFly( + viewLifecycleOwner, dotsState, buzzer, + block = { updateTitle(title) }, + softBlock = ::showCorrectToast + ) viewModel.observeEventIncorrect( - viewLifecycleOwner, preferences, dotsState, buzzer + viewLifecycleOwner, dotsState, buzzer ) { viewModel.symbol.value?.let { symbol -> require(symbol.length == 1) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt index 56a33ad4..082bd798 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt @@ -89,30 +89,14 @@ class InputDotsFragment : AbstractStepFragment(R.string.lessons_help_input_dots) toPrevStep(step) } - if (preferenceRepository.inputOnFlyCheck) { - viewModel.observeEventCorrect( - viewLifecycleOwner, - preferenceRepository, - dotsState, buzzer = null - ) { toNextStep(step, markThisAsPassed = true) } - viewModel.observeEventSoftCorrect( - viewLifecycleOwner, preferenceRepository, buzzer - ) { showCorrectToast() } - } else { - viewModel.observeEventCorrect( - viewLifecycleOwner, - preferenceRepository, - dotsState, buzzer - ) { - showCorrectToast() - toNextStep(step, markThisAsPassed = true) - } - } + viewModel.observeCheckedOnFly( + viewLifecycleOwner, dotsState, buzzer, + block = { toNextStep(step, markThisAsPassed = true) }, + softBlock = ::showCorrectToast + ) viewModel.observeEventIncorrect( - viewLifecycleOwner, - preferenceRepository, - dotsState + viewLifecycleOwner, dotsState ) { val notify = { showIncorrectToast() diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt index 4b9077ad..5737cc9b 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt @@ -87,30 +87,14 @@ class InputSymbolFragment : AbstractStepFragment(R.string.lessons_help_input_sym toPrevStep(step) } - if (preferenceRepository.inputOnFlyCheck) { - viewModel.observeEventCorrect( - viewLifecycleOwner, - preferenceRepository, - dotsState, buzzer = null - ) { toNextStep(step, markThisAsPassed = true) } - viewModel.observeEventSoftCorrect( - viewLifecycleOwner, preferenceRepository, buzzer - ) { showCorrectToast() } - } else { - viewModel.observeEventCorrect( - viewLifecycleOwner, - preferenceRepository, - dotsState, buzzer - ) { - showCorrectToast() - toNextStep(step, markThisAsPassed = true) - } - } + viewModel.observeCheckedOnFly( + viewLifecycleOwner, dotsState, buzzer, + block = { toNextStep(step, markThisAsPassed = true) }, + softBlock = ::showCorrectToast + ) viewModel.observeEventIncorrect( - viewLifecycleOwner, - preferenceRepository, - dotsState + viewLifecycleOwner, dotsState ) { val notify = { showIncorrectToast(step.data.material) From e624944f271774880045de4621e4d058c8de123d Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 14:44:37 +0400 Subject: [PATCH 06/63] 7.12 add decks menu icon --- android/app/src/main/res/menu/card_menu.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/src/main/res/menu/card_menu.xml b/android/app/src/main/res/menu/card_menu.xml index cf1f24aa..d6f710f1 100644 --- a/android/app/src/main/res/menu/card_menu.xml +++ b/android/app/src/main/res/menu/card_menu.xml @@ -13,6 +13,7 @@ From b3f81a30738f1f93b6870286b0283721fa09df9a Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 14:45:29 +0400 Subject: [PATCH 07/63] Reformat --- .../ui/screens/AbstractFragmentWithHelp.kt | 4 ++-- .../learnbraille/ui/screens/Messages.kt | 2 +- .../learnbraille/ui/screens/exit/ExitFragment.kt | 8 ++++---- .../learnbraille/ui/screens/help/HelpFragment.kt | 4 ++-- .../learnbraille/ui/screens/menu/MenuFragment.kt | 2 +- .../ui/screens/practice/CardFragment.kt | 5 +---- .../ui/screens/settings/SettingsFragment.kt | 4 ++-- .../ui/screens/theory/steps/FirstInfoFragment.kt | 4 ++-- .../ui/screens/theory/steps/InfoFragment.kt | 4 ++-- .../ui/screens/theory/steps/InputDotsFragment.kt | 4 ++-- .../ui/screens/theory/steps/InputSymbolFragment.kt | 7 ++++--- .../ui/screens/theory/steps/LastInfoFragment.kt | 4 ++-- .../ui/screens/theory/steps/ShowDotsFragment.kt | 4 ++-- .../learnbraille/ui/views/BrailleDotsView.kt | 14 +++++--------- .../braillesystems/learnbraille/utils/Utils.kt | 11 ++++++++--- .../main/res/drawable/decks_list_button_icon.xml | 9 +++++++++ android/app/src/main/res/layout/fragment_card.xml | 2 +- .../res/layout/fragment_lessons_input_dots.xml | 2 +- .../res/layout/fragment_lessons_input_symbol.xml | 2 +- android/app/src/main/res/values/strings.xml | 12 +++++------- 20 files changed, 57 insertions(+), 51 deletions(-) create mode 100644 android/app/src/main/res/drawable/decks_list_button_icon.xml diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt index 5da0959c..ac9ea4c6 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt @@ -6,7 +6,7 @@ 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.announceByAccessibility +import com.github.braillesystems.learnbraille.utils.announce import com.github.braillesystems.learnbraille.utils.navigate import timber.log.Timber @@ -37,7 +37,7 @@ abstract class AbstractFragmentWithHelp(private val helpMsgId: HelpMsgId) : Frag Timber.i("Navigate to help") val action = HelpFragmentDirections.actionGlobalHelpFragment() action.helpMessage = helpMsg - announceByAccessibility(helpMsg) + announce(helpMsg) navigate(action) } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt index c1cfbea0..c9dd61d7 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt @@ -28,7 +28,7 @@ fun Context.introString(material: Material): String? = } fun Context.introStringNotNullLogged(material: Material): String = introString(material) - ?: Timber.e("Intro should be available").let { "" } + ?: "".also { Timber.e("Intro should be available") } fun Fragment.introString(material: Material): String? = (context ?: null.also { Timber.w("Context is not available") }) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt index 2c403aed..49260e30 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt @@ -8,7 +8,7 @@ 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.utils.announceByAccessibility +import com.github.braillesystems.learnbraille.utils.announce import com.github.braillesystems.learnbraille.utils.navigate import com.github.braillesystems.learnbraille.utils.updateTitle import org.koin.android.ext.android.get @@ -29,9 +29,9 @@ class ExitFragment : Fragment() { false ).apply { - val announcement: String = getString(R.string.exit_question) - updateTitle(announcement) - announceByAccessibility(announcement) + val title: String = getString(R.string.exit_question) + updateTitle(title) + announce(title) recognizer = SpeechRecognition(this@ExitFragment, get()) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/help/HelpFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/help/HelpFragment.kt index 64e152d2..b0c9d166 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/help/HelpFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/help/HelpFragment.kt @@ -10,7 +10,7 @@ import androidx.fragment.app.Fragment import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.databinding.FragmentHelpBinding import com.github.braillesystems.learnbraille.utils.getStringArg -import com.github.braillesystems.learnbraille.utils.updateTitle +import com.github.braillesystems.learnbraille.utils.title class HelpFragment : Fragment() { @@ -26,7 +26,7 @@ class HelpFragment : Fragment() { false ).apply { - updateTitle(getString(R.string.help_title)) + title = getString(R.string.help_title) helpMessage.movementMethod = ScrollingMovementMethod() helpMessage.text = getStringArg(helpMessageArgName).parseAsHtml() diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt index 2e71efe3..52268cad 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt @@ -38,7 +38,7 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { false ).apply { - updateTitle(getString(R.string.menu_actionbar_text)) + title = getString(R.string.menu_actionbar_text).format(appName) setHasOptionsMenu(true) requestPermissions() diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt index 9a77e5d7..19d6062a 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt @@ -11,7 +11,6 @@ import androidx.lifecycle.lifecycleScope import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.data.entities.dummyMaterialOf import com.github.braillesystems.learnbraille.data.repository.PracticeRepository -import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository import com.github.braillesystems.learnbraille.databinding.FragmentCardBinding import com.github.braillesystems.learnbraille.res.deckTagToName import com.github.braillesystems.learnbraille.ui.brailletrainer.BrailleTrainer @@ -29,8 +28,6 @@ import timber.log.Timber class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { - private val preferences: PreferenceRepository by inject() - private lateinit var viewModel: CardViewModel private lateinit var dotsState: BrailleDotsState private var buzzer: Vibrator? = null @@ -132,7 +129,7 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { require(symbol.length == 1) val material = dummyMaterialOf(symbol.first()) val intro = introStringNotNullLogged(material) - announceByAccessibility(intro) + announce(intro) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/settings/SettingsFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/settings/SettingsFragment.kt index d5cd40a7..4e8a427f 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/settings/SettingsFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/settings/SettingsFragment.kt @@ -2,12 +2,12 @@ package com.github.braillesystems.learnbraille.ui.screens.settings import android.os.Bundle import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.utils.updateTitle +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) - updateTitle(getString(R.string.preferences_actionbar_title)) + title = getString(R.string.preferences_actionbar_title) } } \ No newline at end of file diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/FirstInfoFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/FirstInfoFragment.kt index 79bb73d8..036489b7 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/FirstInfoFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/FirstInfoFragment.kt @@ -11,7 +11,7 @@ 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.toNextStep -import com.github.braillesystems.learnbraille.utils.announceByAccessibility +import com.github.braillesystems.learnbraille.utils.announce class FirstInfoFragment : AbstractStepFragment(R.string.lessons_help_info) { @@ -30,7 +30,7 @@ class FirstInfoFragment : AbstractStepFragment(R.string.lessons_help_info) { require(step.data is FirstInfo) infoTextView.text = step.data.text.parseAsHtml() infoTextView.movementMethod = ScrollingMovementMethod() - announceByAccessibility(step.data.text) + announce(step.data.text) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_info) setHasOptionsMenu(true) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InfoFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InfoFragment.kt index 21118773..b2cae8c2 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InfoFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InfoFragment.kt @@ -12,7 +12,7 @@ import com.github.braillesystems.learnbraille.databinding.FragmentLessonsInfoBin 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.utils.announceByAccessibility +import com.github.braillesystems.learnbraille.utils.announce class InfoFragment : AbstractStepFragment(R.string.lessons_help_info) { @@ -31,7 +31,7 @@ class InfoFragment : AbstractStepFragment(R.string.lessons_help_info) { require(step.data is Info) infoTextView.text = step.data.text.parseAsHtml() infoTextView.movementMethod = ScrollingMovementMethod() - announceByAccessibility(step.data.text) + announce(step.data.text) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_info) setHasOptionsMenu(true) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt index 082bd798..6feb0f6a 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt @@ -20,7 +20,7 @@ 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.announceByAccessibility +import com.github.braillesystems.learnbraille.utils.announce import com.github.braillesystems.learnbraille.utils.application import com.github.braillesystems.learnbraille.utils.checkedBuzz import org.koin.android.ext.android.inject @@ -54,7 +54,7 @@ class InputDotsFragment : AbstractStepFragment(R.string.lessons_help_input_dots) ?: getString(R.string.lessons_show_dots_info_template) .format(step.data.dots.spelling) infoTextView.text = infoText - announceByAccessibility(infoText.toString()) + announce(infoText.toString()) brailleDots.dotsState.display(step.data.dots) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_input_dots) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt index 5737cc9b..d793717f 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt @@ -19,7 +19,7 @@ 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.announceByAccessibility +import com.github.braillesystems.learnbraille.utils.announce import com.github.braillesystems.learnbraille.utils.application import com.github.braillesystems.learnbraille.utils.checkedBuzz import org.koin.android.ext.android.inject @@ -53,7 +53,7 @@ class InputSymbolFragment : AbstractStepFragment(R.string.lessons_help_input_sym val symbol = step.data.material.data letter.text = symbol.char.toString() brailleDots.dotsState.display(symbol.brailleDots) - announceByAccessibility(introStringNotNullLogged(step.data.material)) + announce(introStringNotNullLogged(step.data.material)) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_input_symbol) setHasOptionsMenu(true) @@ -114,7 +114,8 @@ class InputSymbolFragment : AbstractStepFragment(R.string.lessons_help_input_sym viewModel.observeEventPassHint( viewLifecycleOwner, dotsState ) { - announceByAccessibility(introStringNotNullLogged(step.data.material)) + val msg = introStringNotNullLogged(step.data.material) + announce(msg) } }.root diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/LastInfoFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/LastInfoFragment.kt index c4b61ba0..b97eeb17 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/LastInfoFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/LastInfoFragment.kt @@ -11,7 +11,7 @@ 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.toPrevStep -import com.github.braillesystems.learnbraille.utils.announceByAccessibility +import com.github.braillesystems.learnbraille.utils.announce class LastInfoFragment : AbstractStepFragment(R.string.lessons_help_last_info) { @@ -30,7 +30,7 @@ class LastInfoFragment : AbstractStepFragment(R.string.lessons_help_last_info) { require(step.data is LastInfo) infoTextView.text = step.data.text.parseAsHtml() infoTextView.movementMethod = ScrollingMovementMethod() - announceByAccessibility(step.data.text) + announce(step.data.text) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_info) setHasOptionsMenu(true) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowDotsFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowDotsFragment.kt index 36fd498f..93f28f2d 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowDotsFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowDotsFragment.kt @@ -14,7 +14,7 @@ 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.display import com.github.braillesystems.learnbraille.ui.views.dotsState -import com.github.braillesystems.learnbraille.utils.announceByAccessibility +import com.github.braillesystems.learnbraille.utils.announce import timber.log.Timber class ShowDotsFragment : AbstractStepFragment(R.string.lessons_help_show_dots) { @@ -38,7 +38,7 @@ class ShowDotsFragment : AbstractStepFragment(R.string.lessons_help_show_dots) { ?: getString(R.string.lessons_show_dots_info_template) .format(step.data.dots.spelling) infoTextView.text = infoText - announceByAccessibility(infoText.toString()) + announce(infoText.toString()) brailleDots.dotsState.display(step.data.dots) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_show_dots) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BrailleDotsView.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BrailleDotsView.kt index 6be02e4e..0155d8d9 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BrailleDotsView.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BrailleDotsView.kt @@ -97,17 +97,13 @@ val BrailleDotsState.brailleDots: BrailleDots checkBoxes.map(CheckBox::isChecked).toBooleanArray() ) -fun BrailleDotsState.uncheck() = checkBoxes.forEach { - it.isChecked = false -} +fun BrailleDotsState.uncheck() = checkBoxes.forEach { it.isChecked = false } -fun BrailleDotsState.clickable(isClickable: Boolean) = checkBoxes.forEach { - it.isClickable = isClickable -} +fun BrailleDotsState.clickable(isClickable: Boolean) = + checkBoxes.forEach { it.isClickable = isClickable } -fun BrailleDotsState.subscribe(listener: View.OnClickListener) = checkBoxes.forEach { - it.setOnClickListener(listener) -} +fun BrailleDotsState.subscribe(listener: View.OnClickListener) = + checkBoxes.forEach { it.setOnClickListener(listener) } fun BrailleDotsState.display(brailleDots: BrailleDots): Unit = (checkBoxes zip brailleDots.list).forEach { (checkBox, dot) -> diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt index c2ef9c57..a6010070 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt @@ -23,11 +23,13 @@ import org.koin.android.ext.android.get import timber.log.Timber import kotlin.reflect.KProperty + val Fragment.application: LearnBrailleApplication get() = requireNotNull(activity).application as LearnBrailleApplication fun scope(job: Job = Job()) = CoroutineScope(Dispatchers.Main + job) + fun Vibrator?.checkedBuzz(pattern: BuzzPattern, preferenceRepository: PreferenceRepository) = executeIf(preferenceRepository.buzzEnabled) { buzz(pattern) } @@ -42,7 +44,7 @@ fun Fragment.checkedToast(msg: String, preferenceRepository: PreferenceRepositor fun Fragment.toast(msg: String, preferenceRepository: PreferenceRepository = get()) = Toast.makeText(context, msg, preferenceRepository.toastDuration).show() -fun Context.announceByAccessibility( +fun Context.announce( announcement: String ) { val manager = accessibilityManager ?: return @@ -55,8 +57,9 @@ fun Context.announceByAccessibility( manager.sendAccessibilityEvent(event) } -fun Fragment.announceByAccessibility(announcement: String) = - application.announceByAccessibility(announcement) +fun Fragment.announce(announcement: String) = + application.announce(announcement) + val Fragment.actionBar: ActionBar? get() = (activity as AppCompatActivity).supportActionBar @@ -74,9 +77,11 @@ fun Fragment.updateTitle(title: String) { this.title = title } + fun stringify(s: SerializationStrategy, obj: T) = Json.stringify(s, obj) fun parse(d: DeserializationStrategy, s: String) = Json.parse(d, s) + class logged(private val getter: C.(String) -> R) { operator fun getValue(thisRef: C, property: KProperty<*>): R = diff --git a/android/app/src/main/res/drawable/decks_list_button_icon.xml b/android/app/src/main/res/drawable/decks_list_button_icon.xml new file mode 100644 index 00000000..7dcf8d07 --- /dev/null +++ b/android/app/src/main/res/drawable/decks_list_button_icon.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/fragment_card.xml b/android/app/src/main/res/layout/fragment_card.xml index 752e95b2..4d58b518 100644 --- a/android/app/src/main/res/layout/fragment_card.xml +++ b/android/app/src/main/res/layout/fragment_card.xml @@ -50,7 +50,7 @@ android:contentDescription="@string/practice_hint" android:onClick="@{() -> cardViewModel.onHint()}" android:scaleType="fitCenter" - android:text="@string/hint_icon" + android:text="@string/practice_hint_icon_symbol" android:textColor="@color/colorOnPrimary" android:textSize="@dimen/practice_buttons_icon_hint_size" app:layout_constraintBottom_toBottomOf="parent" diff --git a/android/app/src/main/res/layout/fragment_lessons_input_dots.xml b/android/app/src/main/res/layout/fragment_lessons_input_dots.xml index cf19c838..d479cae4 100644 --- a/android/app/src/main/res/layout/fragment_lessons_input_dots.xml +++ b/android/app/src/main/res/layout/fragment_lessons_input_dots.xml @@ -47,7 +47,7 @@ android:layout_height="@dimen/practice_buttons_small_height" android:contentDescription="@string/lessons_hint" android:onClick="@{() -> inputViewModel.onHint()}" - android:text="@string/hint_icon" + android:text="@string/practice_hint_icon_symbol" android:textSize="@dimen/practice_buttons_icon_size" app:backgroundTint="@color/colorSecondary" app:layout_constraintBottom_toTopOf="@+id/prev_button" diff --git a/android/app/src/main/res/layout/fragment_lessons_input_symbol.xml b/android/app/src/main/res/layout/fragment_lessons_input_symbol.xml index f8b327db..ebf80987 100644 --- a/android/app/src/main/res/layout/fragment_lessons_input_symbol.xml +++ b/android/app/src/main/res/layout/fragment_lessons_input_symbol.xml @@ -48,7 +48,7 @@ android:layout_height="@dimen/practice_buttons_small_height" android:contentDescription="@string/lessons_hint" android:onClick="@{() -> inputViewModel.onHint()}" - android:text="@string/hint_icon" + android:text="@string/practice_hint_icon_symbol" android:textSize="@dimen/practice_buttons_icon_size" app:backgroundTint="@color/colorSecondary" app:layout_constraintBottom_toTopOf="@+id/prev_button" diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 13ff99cb..5b78f42a 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -35,7 +35,7 @@ - \? + \? Подсказка Далее Практика: %d из %d @@ -69,10 +69,8 @@ - Выйти из приложения Learn Braille - Остаться в меню приложения Learn Braille - Да - Нет + Выйти + Вернуться в меню Хотите ли Вы выйти? @@ -90,7 +88,7 @@ СПРАВКА НАСТРОЙКИ ВЫХОД - Learn Braille. Меню + %s. Меню Загружаем базу данных. Попробуйте еще раз! @@ -238,7 +236,7 @@ - Learn Braille. Настройки + Настройки Всплывающие уведомления Вибрация Вибрация в ответ на результат выполнения задания From 3f2b0198bdee2192e3acb2a8035bdbfed65162ac Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 15:12:09 +0400 Subject: [PATCH 08/63] WIP --- .../learnbraille/data/repository/PreferenceRepository.kt | 8 ++++++++ android/app/src/main/res/values/strings.xml | 1 + 2 files changed, 9 insertions(+) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt index db7d6465..eddff0cd 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt @@ -21,6 +21,7 @@ interface PreferenceRepository { val practiceUseMaterialsPassedInCourse: Boolean val traverseDotsInEnumerationOrder: Boolean val inputOnFlyCheck: Boolean + val additionalAnnouncementsEnabled: Boolean val currentUserId: Long suspend fun getCurrentUser(): User @@ -86,6 +87,13 @@ class PreferenceRepositoryImpl( false ) } + + override val additionalAnnouncementsEnabled: Boolean by logged { + context.preferences.getBoolean( + context.getString(R.string.preference_enable_additional_announcements), + false + ) + } override val currentUserId: Long by logged { context.preferences.getLong( diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 5b78f42a..4e4201bb 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -16,6 +16,7 @@ golubina_book_steps_enabled practice_use_passed_material traverse_dots_in_enumeration_order + enable_additional_announcements Правильно! Неправильно! From 86f6c8f8a5f2064fd326279a42c0a7554d1fe7e2 Mon Sep 17 00:00:00 2001 From: kaustika Date: Wed, 20 May 2020 14:18:30 +0300 Subject: [PATCH 09/63] 7.7 improve contrast in lessons list --- .../ui/screens/theory/lessons/LessonsListFragment.kt | 6 ++++-- android/app/src/main/res/values/colors.xml | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt index aba9cc1f..69ed8b38 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt @@ -5,6 +5,7 @@ import android.graphics.Color 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 @@ -16,6 +17,7 @@ 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.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 @@ -57,10 +59,10 @@ class LessonsListFragment : Fragment() { lessonName.text = "${item.id}. ${item.name}" if (item.id <= curr.lessonId) { clickListener = activeListener - lessonName.setTextColor(Color.BLACK) + lessonName.setTextColor(ContextCompat.getColor(application, R.color.colorOnBackgroundDark)) } else { clickListener = disabledListener - lessonName.setTextColor(Color.GRAY) + lessonName.setTextColor(ContextCompat.getColor(application, R.color.colorOnBackgroundLight)) } } lessonsList.adapter = adapter diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index 114bd50b..74e896d7 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -5,4 +5,6 @@ #ffffff #ffffff #ffffff + #000000 + #504c51 From d11bcd877611dc11adc670193ecd8d326abac3c1 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 15:28:25 +0400 Subject: [PATCH 10/63] 7.8 add switch to settings --- .../learnbraille/data/repository/PreferenceRepository.kt | 8 ++++++++ android/app/src/main/res/values/strings.xml | 5 +++++ android/app/src/main/res/xml/settings_hierarchy.xml | 8 ++++++++ 3 files changed, 21 insertions(+) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt index db7d6465..7a48ec98 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt @@ -21,6 +21,7 @@ interface PreferenceRepository { val practiceUseMaterialsPassedInCourse: Boolean val traverseDotsInEnumerationOrder: Boolean val inputOnFlyCheck: Boolean + val practiceUseOnlySeenMaterials: Boolean val currentUserId: Long suspend fun getCurrentUser(): User @@ -87,6 +88,13 @@ class PreferenceRepositoryImpl( ) } + override val practiceUseOnlySeenMaterials: Boolean by logged { + context.preferences.getBoolean( + context.getString(R.string.preference_practice_use_only_seen_materials), + false + ) + } + override val currentUserId: Long by logged { context.preferences.getLong( context.getString(R.string.preference_current_user), 1 diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 5b78f42a..057de4d2 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -16,6 +16,7 @@ golubina_book_steps_enabled practice_use_passed_material traverse_dots_in_enumeration_order + practice_use_only_seen_materials Правильно! Неправильно! @@ -256,5 +257,9 @@ как только в шеститочии введена корректная комбинация, не дожидаясь нажатия кнопки \"вперёд\" + Только пройденные символы + + Использовать в практике только символы, пройденные в уроках + diff --git a/android/app/src/main/res/xml/settings_hierarchy.xml b/android/app/src/main/res/xml/settings_hierarchy.xml index 0b0df056..a48e03e0 100644 --- a/android/app/src/main/res/xml/settings_hierarchy.xml +++ b/android/app/src/main/res/xml/settings_hierarchy.xml @@ -41,4 +41,12 @@ android:title="@string/preference_title_on_fly_check" app:iconSpaceReserved="false" /> + + \ No newline at end of file From 7c11a4b9795b66abb4e24f8c6aee84398597bf36 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 15:31:49 +0400 Subject: [PATCH 11/63] 7.12 add card navigation pop behaviour --- android/app/src/main/res/navigation/navigation.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/res/navigation/navigation.xml b/android/app/src/main/res/navigation/navigation.xml index 7634ab9b..37737c16 100644 --- a/android/app/src/main/res/navigation/navigation.xml +++ b/android/app/src/main/res/navigation/navigation.xml @@ -57,7 +57,8 @@ tools:layout="@layout/fragment_decks_list"> + app:destination="@id/cardFragment" + app:popUpTo="@+id/menuFragment" /> Date: Wed, 20 May 2020 14:43:29 +0300 Subject: [PATCH 12/63] 7.7 improve contrast in decks list --- .../learnbraille/ui/screens/practice/DecksList.kt | 13 +++++++++++-- .../screens/theory/lessons/LessonsListFragment.kt | 15 ++++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt index c66e5e2d..ac7c2d05 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt @@ -4,6 +4,7 @@ import android.graphics.Color 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 @@ -14,6 +15,7 @@ import com.github.braillesystems.learnbraille.data.repository.MutablePracticeRep 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.utils.application import com.github.braillesystems.learnbraille.utils.checkedToast import com.github.braillesystems.learnbraille.utils.navigate import com.github.braillesystems.learnbraille.utils.title @@ -56,9 +58,16 @@ class DecksList : Fragment() { deckName.text = deckTagToName.getValue(item.deck.tag) clickListener = listener if (item.containsCards) { - deckName.setTextColor(Color.BLACK) + deckName.setTextColor( + ContextCompat.getColor( + application, + R.color.colorOnBackgroundDark) + ) } else { - deckName.setTextColor(Color.GRAY) // TODO change for accessibility + deckName.setTextColor( + ContextCompat.getColor( + application, + R.color.colorOnBackgroundLight)) } } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt index 69ed8b38..e1ccc9e0 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt @@ -1,7 +1,6 @@ package com.github.braillesystems.learnbraille.ui.screens.theory.lessons import android.annotation.SuppressLint -import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup @@ -59,10 +58,20 @@ class LessonsListFragment : Fragment() { lessonName.text = "${item.id}. ${item.name}" if (item.id <= curr.lessonId) { clickListener = activeListener - lessonName.setTextColor(ContextCompat.getColor(application, R.color.colorOnBackgroundDark)) + lessonName.setTextColor( + ContextCompat.getColor( + application, + R.color.colorOnBackgroundDark + ) + ) } else { clickListener = disabledListener - lessonName.setTextColor(ContextCompat.getColor(application, R.color.colorOnBackgroundLight)) + lessonName.setTextColor( + ContextCompat.getColor( + application, + R.color.colorOnBackgroundLight + ) + ) } } lessonsList.adapter = adapter From 60dc5cfbcaa1af0f0784694a7ef3841a22fae223 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 18:16:15 +0400 Subject: [PATCH 13/63] 7.12 change deck list icon size --- .../app/src/main/res/drawable/action_menu_help_button.xml | 4 ++-- .../app/src/main/res/drawable/decks_list_button_icon.xml | 8 ++++---- android/app/src/main/res/values/dimens.xml | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/android/app/src/main/res/drawable/action_menu_help_button.xml b/android/app/src/main/res/drawable/action_menu_help_button.xml index 2a326d16..7ab092bb 100644 --- a/android/app/src/main/res/drawable/action_menu_help_button.xml +++ b/android/app/src/main/res/drawable/action_menu_help_button.xml @@ -1,6 +1,6 @@ + android:width="@dimen/action_bar_button_size" + android:height="@dimen/action_bar_button_size" + android:viewportWidth="25.0" + android:viewportHeight="25.0"> diff --git a/android/app/src/main/res/values/dimens.xml b/android/app/src/main/res/values/dimens.xml index 99af7a2f..9ea2c8c1 100644 --- a/android/app/src/main/res/values/dimens.xml +++ b/android/app/src/main/res/values/dimens.xml @@ -38,5 +38,6 @@ 40dp 36dp + 50dp \ No newline at end of file From f2857dfe5e28245eb1f7c413d57f53bb11064434 Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Wed, 20 May 2020 17:34:06 +0300 Subject: [PATCH 14/63] 7.5 update version tag and version code --- android/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index c0b8d56a..a2bf71bd 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -24,8 +24,8 @@ android { applicationId "com.github.braillesystems.learnbraille" minSdkVersion 19 targetSdkVersion 29 - versionCode 2 - versionName "0.6.1" + versionCode 3 + versionName "0.6.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true multiDexEnabled = true From 6e47ad067e883d099f6da9c3844a247c1c7f6a6a Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 18:17:12 +0400 Subject: [PATCH 15/63] 7.8 implement known cards filter --- .../learnbraille/LearnBrailleApplication.kt | 4 +-- .../learnbraille/data/db/Database.kt | 9 +++--- .../learnbraille/data/dsl/Data.kt | 17 ++++++---- .../learnbraille/data/dsl/Materials.kt | 18 +++++++++++ .../learnbraille/data/entities/Cards.kt | 14 ++++++++ .../learnbraille/data/entities/Decks.kt | 13 ++++++++ .../data/repository/PracticeRepository.kt | 32 +++++++++++++++---- .../data/repository/PreferenceRepository.kt | 4 +-- .../data/repository/TheoryRepository.kt | 32 +++++++++++++++---- .../braillesystems/learnbraille/res/Data.kt | 2 +- .../learnbraille/res/Materials.kt | 13 ++++++-- .../ui/screens/practice/CardFragment.kt | 10 +++--- 12 files changed, 132 insertions(+), 36 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt index 752c76a7..deeaa5f1 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/LearnBrailleApplication.kt @@ -50,7 +50,7 @@ class LearnBrailleApplication : Application() { get().run { TheoryRepositoryImpl( lessonDao, stepDao, - currentStepDao, lastCourseStepDao, lastLessonStepDao, + currentStepDao, lastCourseStepDao, lastLessonStepDao, knownMaterialDao, get() ) } @@ -59,7 +59,7 @@ class LearnBrailleApplication : Application() { get().run { TheoryRepositoryImpl( lessonDao, stepDao, - currentStepDao, lastCourseStepDao, lastLessonStepDao, + currentStepDao, lastCourseStepDao, lastLessonStepDao, knownMaterialDao, get() ) } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt index 1d60235a..e3904f08 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt @@ -8,6 +8,7 @@ import androidx.room.RoomDatabase import androidx.room.TypeConverters import androidx.sqlite.db.SupportSQLiteDatabase import com.github.braillesystems.learnbraille.data.entities.* +import com.github.braillesystems.learnbraille.res.knownMaterials import com.github.braillesystems.learnbraille.res.prepopulationData import com.github.braillesystems.learnbraille.utils.scope import kotlinx.coroutines.Job @@ -50,18 +51,16 @@ abstract class LearnBrailleDatabase : RoomDatabase(), KoinComponent { abstract val lastCourseStepDao: LastCourseStepDao abstract val lastLessonStepDao: LastLessonStepDao - private lateinit var forcePrepopulationJob: Job @Volatile private var prepopulationFinished = true + private lateinit var forcePrepopulationJob: Job fun init(): LearnBrailleDatabase = this.also { forcePrepopulationJob = scope().launch { - // Expect to have at list one user in prepopulation list if (userDao.getUser(1) != null) { Timber.i("DB has been already initialized") } else { - Timber.i("DB is not been initialized yet") prepopulationFinished = false } } @@ -103,7 +102,7 @@ abstract class LearnBrailleDatabase : RoomDatabase(), KoinComponent { } private fun prepopulate() { - Timber.i("Prepopulate") + Timber.i("Prepopulate DB") get().apply { prepopulationData.use { scope().launch { @@ -118,6 +117,8 @@ abstract class LearnBrailleDatabase : RoomDatabase(), KoinComponent { stepDao.insert(steps) stepAnnotationDao.insert(stepAnnotations) stepHasAnnotationDao.insert(stepHasAnnotations) + // TODO apply for multiple users + knownMaterialDao.insert(knownMaterials.map { it.copy(userId = 1) }) Timber.i("Finnish database prepopulation") prepopulationFinished = true diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Data.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Data.kt index 97876e28..ae9f1a90 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Data.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Data.kt @@ -7,6 +7,7 @@ import kotlin.reflect.KProperty const val DEFAULT_ID = -1L +const val ALL_CARDS_DECK_ID = 1L typealias StepAnnotationName = String typealias StepWithAnnotations = Pair> @@ -138,14 +139,16 @@ class DataBuilder( fun decks(block: DecksBuilder.() -> Unit) = DecksBuilder(block).side { it.deckToPredicate.forEach { (deck, p) -> - val deckId = if (deck.tag == DeckTags.all) 1 else decks.size + 2L - _decks += deck.copy(id = deckId) - - materials - .filter { material -> p(material.data) } - .forEach { material -> - _cards += Card(deckId, material.id) + val deckId = + if (deck.tag == DeckTags.all) ALL_CARDS_DECK_ID + else decks.size + 2L + val deckMaterials = materials.filter { material -> p(material.data) } + if (deckMaterials.isNotEmpty()) { + _decks += deck.copy(id = deckId) + _cards += deckMaterials.map { material -> + Card(deckId, material.id) } + } } } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Materials.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Materials.kt index 45f92641..633100c2 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Materials.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/dsl/Materials.kt @@ -1,8 +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.res.content import kotlin.reflect.KProperty @@ -74,3 +76,19 @@ class SymbolsBuilder(private val symbolType: String, block: SymbolsBuilder.() -> _map[char] = Symbol(char, brailleDots, symbolType) } } + + +class known(vararg chars: Char) { + + private val cs = chars.map(Char::toUpperCase) + private var knownMaterials: List? = null + + operator fun getValue(thisRef: Any?, property: KProperty<*>): List { + require(property.name == "knownMaterials") { + "Property should have name knownMaterials to be used by database" + } + return knownMaterials ?: cs.map { + KnownMaterial(DEFAULT_ID, content.symbols.getValue(it).id) + }.also { knownMaterials = it } + } +} diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Cards.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Cards.kt index 7c00e445..42c780d4 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Cards.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Cards.kt @@ -27,4 +27,18 @@ interface CardDao { """ ) suspend fun getRandomMaterialFromDeck(deckId: Long): Material? + + @Query( + """ + select m.* from cards as c + inner join materials as m on m.id = c.material_id + where c.deck_id = :deckId + and exists ( + select * from known_materials as km + where km.user_id = :userId and km.material_id = m.id + ) + order by RANDOM() limit 1 + """ + ) + suspend fun getRandomKnownMaterialFromDeck(userId: Long, deckId: Long): Material? } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Decks.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Decks.kt index 84ab07f3..b6013718 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Decks.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Decks.kt @@ -20,4 +20,17 @@ interface DeckDao { @Query("select * from decks") suspend fun getAllDecks(): List + + @Query( + """ + select * from decks as d + where exists ( + select * from cards as c + inner join known_materials as km on km.material_id = c.material_id + where km.user_id = :userId and c.deck_id = d.id + ) + order by d.id + """ + ) + suspend fun getAvailableDecks(userId: Long): List } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt index 314ef24e..a3016a75 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt @@ -1,6 +1,7 @@ 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.Deck import com.github.braillesystems.learnbraille.data.entities.DeckDao @@ -33,11 +34,12 @@ class PracticeRepositoryImpl( private val preferenceRepository: PreferenceRepository ) : MutablePracticeRepository { - private val currDeckPreference get() = "practice_curr_deck ${preferenceRepository.currentUserId}" + private val currDeckPreference: String + get() = "practice_curr_deck_${preferenceRepository.currentUserId}" override var currentDeckId: Long get() = context.preferences.getLong( - currDeckPreference, 1 + currDeckPreference, ALL_CARDS_DECK_ID ) set(value) { with(context.preferences.edit()) { @@ -46,14 +48,32 @@ class PracticeRepositoryImpl( } } - // TODO deck filling constraints override suspend fun getNextMaterial(): Material = - cardDao.getRandomMaterialFromDeck(currentDeckId) - ?: error("Material is expected to be prepopulated") + if (preferenceRepository.practiceUseOnlyKnownMaterials) { + cardDao.getRandomKnownMaterialFromDeck( + preferenceRepository.currentUserId, currentDeckId + ) ?: run { + currentDeckId = ALL_CARDS_DECK_ID + getNextMaterial() + } + } else { + cardDao + .getRandomMaterialFromDeck(currentDeckId) + ?: error( + "Material is expected to be prepopulated and " + + "user should not have possibilities to access empty deck" + ) + } override suspend fun getCurrDeck(): Deck = deckDao.getDeck(currentDeckId) ?: error("Current deck should always exist") override suspend fun getAllDecks(): List = - deckDao.getAllDecks().map { DeckNotEmpty(it, true) } + 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) } + } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt index 7a48ec98..a3816d8a 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt @@ -21,7 +21,7 @@ interface PreferenceRepository { val practiceUseMaterialsPassedInCourse: Boolean val traverseDotsInEnumerationOrder: Boolean val inputOnFlyCheck: Boolean - val practiceUseOnlySeenMaterials: Boolean + val practiceUseOnlyKnownMaterials: Boolean val currentUserId: Long suspend fun getCurrentUser(): User @@ -88,7 +88,7 @@ class PreferenceRepositoryImpl( ) } - override val practiceUseOnlySeenMaterials: Boolean by logged { + override val practiceUseOnlyKnownMaterials: Boolean by logged { context.preferences.getBoolean( context.getString(R.string.preference_practice_use_only_seen_materials), false diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepository.kt index 9cf8e21a..abff9f8e 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/TheoryRepository.kt @@ -2,6 +2,9 @@ package com.github.braillesystems.learnbraille.data.repository import com.github.braillesystems.learnbraille.data.entities.* import com.github.braillesystems.learnbraille.res.StepAnnotation +import com.github.braillesystems.learnbraille.utils.devnull +import com.github.braillesystems.learnbraille.utils.scope +import kotlinx.coroutines.launch interface TheoryRepository { @@ -24,6 +27,7 @@ class TheoryRepositoryImpl( private val currentStepDao: CurrentStepDao, private val lastCourseStepDao: LastCourseStepDao, private val lastLessonStepDao: LastLessonStepDao, + private val knownMaterialDao: KnownMaterialDao, private val preferenceRepository: PreferenceRepository ) : MutableTheoryRepository { @@ -42,9 +46,14 @@ class TheoryRepositoryImpl( stepDao.getCurrentStep(preferenceRepository.currentUserId, thisStep.courseId) ) - if (next <= curr) { + val updateAndReturn = { updateLast(next) - return next + updateKnown(thisStep) + next + } + + if (next <= curr) { + return updateAndReturn() } if (!markThisAsPassed) return null @@ -54,8 +63,8 @@ class TheoryRepositoryImpl( next.courseId, next.lessonId, next.id ) ) - updateLast(next) - return next + + return updateAndReturn() } override suspend fun getPrevStepAndUpdate(thisStep: Step): Step? = @@ -102,7 +111,7 @@ class TheoryRepositoryImpl( ) } ?: error("First course step should always exist") - private suspend fun updateLast(step: Step) { + private fun updateLast(step: Step): Unit = scope().launch { lastCourseStepDao.update( LastCourseStep( preferenceRepository.currentUserId, @@ -115,5 +124,16 @@ class TheoryRepositoryImpl( step.courseId, step.lessonId, step.id ) ) - } + }.devnull + + private fun updateKnown(step: Step): Unit = scope().launch { + when (step.data) { + is Input -> knownMaterialDao.insert( + KnownMaterial( + preferenceRepository.currentUserId, + step.data.material.id + ) + ) + } + }.devnull } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt index 60c44ba8..8ce80794 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Data.kt @@ -56,7 +56,7 @@ val prepopulationData by data( } decks { - deck(DeckTags.all) { true } + deck(DeckTags.all) { true } // This deck should always exist deck(DeckTags.ruLetters) { data -> data is Symbol && data.type == SymbolType.ru } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt index dabd48e1..73a27f3a 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt @@ -2,6 +2,7 @@ package com.github.braillesystems.learnbraille.res import android.content.Context import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.dsl.known 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 @@ -14,6 +15,8 @@ import com.github.braillesystems.learnbraille.utils.listOfP2F /** * Do not forget to register new types of symbols in `symbolTypeDisplayList` below. + * + * Always use this variable to get materials. */ val content by materials { +ruSymbols @@ -21,13 +24,17 @@ val content by materials { +uebDigits } +val knownMaterials by known( + 'А', 'Б', 'Ц', 'Д', 'Е', 'Ф', 'Г' +) + object SymbolType { const val ru = "ru" const val special = "special" const val digit = "digit" } -val ruSymbols by symbols(SymbolType.ru) { +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)) @@ -63,14 +70,14 @@ val ruSymbols by symbols(SymbolType.ru) { symbol(char = 'Я', brailleDots = BrailleDots(F, F, E, F, E, F)) } -val specialSymbols by symbols(SymbolType.special) { +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, F, E, F, E)) // Dot } -val uebDigits by symbols(SymbolType.digit) { +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)) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt index 19d6062a..c86b5dce 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt @@ -57,11 +57,6 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { updateTitle(title) setHasOptionsMenu(true) - lifecycleScope.launch { - val practiceRepository: PracticeRepository by inject() - val tag = practiceRepository.getCurrDeck().tag - toast(deckTagToName.getValue(tag)) - } dotsState = brailleDots.dotsState.apply { subscribe(View.OnClickListener { @@ -120,6 +115,11 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { Observer { if (it == null) return@Observer announceIntro(it) + lifecycleScope.launch { + val practiceRepository: PracticeRepository by inject() + val tag = practiceRepository.getCurrDeck().tag + toast(deckTagToName.getValue(tag)) + } } ) From 8181957884f553a29fd64bc5ba6f591384a287fd Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 18:47:08 +0400 Subject: [PATCH 16/63] Apply reformatting --- .../learnbraille/ui/screens/practice/DecksList.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt index ac7c2d05..a8b8593a 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt @@ -1,6 +1,5 @@ package com.github.braillesystems.learnbraille.ui.screens.practice -import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup @@ -61,13 +60,16 @@ class DecksList : Fragment() { deckName.setTextColor( ContextCompat.getColor( application, - R.color.colorOnBackgroundDark) + R.color.colorOnBackgroundDark + ) ) } else { deckName.setTextColor( ContextCompat.getColor( application, - R.color.colorOnBackgroundLight)) + R.color.colorOnBackgroundLight + ) + ) } } } From 4679f741a08267301712ed685e0b8783fb773d8e Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 19:13:18 +0400 Subject: [PATCH 17/63] Implement checked announcements --- .../learnbraille/data/repository/PreferenceRepository.kt | 2 +- .../learnbraille/ui/screens/exit/ExitFragment.kt | 4 ++-- .../learnbraille/ui/screens/help/HelpFragment.kt | 7 ++++++- .../learnbraille/ui/screens/practice/CardFragment.kt | 2 +- .../ui/screens/theory/steps/FirstInfoFragment.kt | 4 ++-- .../learnbraille/ui/screens/theory/steps/InfoFragment.kt | 4 ++-- .../ui/screens/theory/steps/InputDotsFragment.kt | 4 ++-- .../ui/screens/theory/steps/InputSymbolFragment.kt | 3 ++- .../ui/screens/theory/steps/LastInfoFragment.kt | 4 ++-- .../ui/screens/theory/steps/ShowDotsFragment.kt | 4 ++-- .../ui/screens/theory/steps/ShowSymbolFragment.kt | 3 +++ .../com/github/braillesystems/learnbraille/utils/Utils.kt | 7 +++++++ android/app/src/main/res/values/strings.xml | 3 +++ android/app/src/main/res/xml/settings_hierarchy.xml | 7 +++++++ 14 files changed, 42 insertions(+), 16 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt index eddff0cd..c818d0ee 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt @@ -87,7 +87,7 @@ class PreferenceRepositoryImpl( false ) } - + override val additionalAnnouncementsEnabled: Boolean by logged { context.preferences.getBoolean( context.getString(R.string.preference_enable_additional_announcements), diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt index 49260e30..0c04fc15 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt @@ -8,7 +8,7 @@ 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.utils.announce +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 @@ -31,7 +31,7 @@ class ExitFragment : Fragment() { val title: String = getString(R.string.exit_question) updateTitle(title) - announce(title) + checkedAnnounce(title) recognizer = SpeechRecognition(this@ExitFragment, get()) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/help/HelpFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/help/HelpFragment.kt index b0c9d166..5967f1c7 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/help/HelpFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/help/HelpFragment.kt @@ -9,7 +9,9 @@ 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.utils.checkedAnnounce import com.github.braillesystems.learnbraille.utils.getStringArg +import com.github.braillesystems.learnbraille.utils.removeHtmlMarkup import com.github.braillesystems.learnbraille.utils.title class HelpFragment : Fragment() { @@ -27,8 +29,11 @@ class HelpFragment : Fragment() { ).apply { title = getString(R.string.help_title) + + val content = getStringArg(helpMessageArgName) + helpMessage.text = content.parseAsHtml() helpMessage.movementMethod = ScrollingMovementMethod() - helpMessage.text = getStringArg(helpMessageArgName).parseAsHtml() + checkedAnnounce(content.removeHtmlMarkup()) }.root } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt index 19d6062a..60e56966 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt @@ -129,7 +129,7 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { require(symbol.length == 1) val material = dummyMaterialOf(symbol.first()) val intro = introStringNotNullLogged(material) - announce(intro) + checkedAnnounce(intro) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/FirstInfoFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/FirstInfoFragment.kt index 036489b7..4a5f1a8d 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/FirstInfoFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/FirstInfoFragment.kt @@ -11,7 +11,7 @@ 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.toNextStep -import com.github.braillesystems.learnbraille.utils.announce +import com.github.braillesystems.learnbraille.utils.checkedAnnounce class FirstInfoFragment : AbstractStepFragment(R.string.lessons_help_info) { @@ -30,7 +30,7 @@ class FirstInfoFragment : AbstractStepFragment(R.string.lessons_help_info) { require(step.data is FirstInfo) infoTextView.text = step.data.text.parseAsHtml() infoTextView.movementMethod = ScrollingMovementMethod() - announce(step.data.text) + checkedAnnounce(step.data.text) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_info) setHasOptionsMenu(true) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InfoFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InfoFragment.kt index b2cae8c2..ebea518c 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InfoFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InfoFragment.kt @@ -12,7 +12,7 @@ import com.github.braillesystems.learnbraille.databinding.FragmentLessonsInfoBin 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.utils.announce +import com.github.braillesystems.learnbraille.utils.checkedAnnounce class InfoFragment : AbstractStepFragment(R.string.lessons_help_info) { @@ -31,7 +31,7 @@ class InfoFragment : AbstractStepFragment(R.string.lessons_help_info) { require(step.data is Info) infoTextView.text = step.data.text.parseAsHtml() infoTextView.movementMethod = ScrollingMovementMethod() - announce(step.data.text) + checkedAnnounce(step.data.text) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_info) setHasOptionsMenu(true) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt index 6feb0f6a..6cbc5d2b 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt @@ -20,8 +20,8 @@ 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 org.koin.android.ext.android.inject import timber.log.Timber @@ -54,7 +54,7 @@ class InputDotsFragment : AbstractStepFragment(R.string.lessons_help_input_dots) ?: getString(R.string.lessons_show_dots_info_template) .format(step.data.dots.spelling) infoTextView.text = infoText - announce(infoText.toString()) + checkedAnnounce(infoText.toString()) brailleDots.dotsState.display(step.data.dots) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_input_dots) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt index d793717f..2fe9b909 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt @@ -21,6 +21,7 @@ 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 org.koin.android.ext.android.inject import timber.log.Timber @@ -53,7 +54,7 @@ class InputSymbolFragment : AbstractStepFragment(R.string.lessons_help_input_sym val symbol = step.data.material.data letter.text = symbol.char.toString() brailleDots.dotsState.display(symbol.brailleDots) - announce(introStringNotNullLogged(step.data.material)) + checkedAnnounce(introStringNotNullLogged(step.data.material)) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_input_symbol) setHasOptionsMenu(true) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/LastInfoFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/LastInfoFragment.kt index b97eeb17..3a029454 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/LastInfoFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/LastInfoFragment.kt @@ -11,7 +11,7 @@ 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.toPrevStep -import com.github.braillesystems.learnbraille.utils.announce +import com.github.braillesystems.learnbraille.utils.checkedAnnounce class LastInfoFragment : AbstractStepFragment(R.string.lessons_help_last_info) { @@ -30,7 +30,7 @@ class LastInfoFragment : AbstractStepFragment(R.string.lessons_help_last_info) { require(step.data is LastInfo) infoTextView.text = step.data.text.parseAsHtml() infoTextView.movementMethod = ScrollingMovementMethod() - announce(step.data.text) + checkedAnnounce(step.data.text) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_info) setHasOptionsMenu(true) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowDotsFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowDotsFragment.kt index 93f28f2d..a3c70f42 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowDotsFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowDotsFragment.kt @@ -14,7 +14,7 @@ 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.display import com.github.braillesystems.learnbraille.ui.views.dotsState -import com.github.braillesystems.learnbraille.utils.announce +import com.github.braillesystems.learnbraille.utils.checkedAnnounce import timber.log.Timber class ShowDotsFragment : AbstractStepFragment(R.string.lessons_help_show_dots) { @@ -38,7 +38,7 @@ class ShowDotsFragment : AbstractStepFragment(R.string.lessons_help_show_dots) { ?: getString(R.string.lessons_show_dots_info_template) .format(step.data.dots.spelling) infoTextView.text = infoText - announce(infoText.toString()) + checkedAnnounce(infoText.toString()) brailleDots.dotsState.display(step.data.dots) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_show_dots) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt index 26a4c602..2683d008 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt @@ -8,11 +8,13 @@ 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.screens.introStringNotNullLogged 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.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) { @@ -35,6 +37,7 @@ class ShowSymbolFragment : AbstractStepFragment(R.string.lessons_help_show_symbo require(step.data.material.data is Symbol) letter.text = step.data.material.data.char.toString() brailleDots.dotsState.display(step.data.material.data.brailleDots) + checkedAnnounce(introStringNotNullLogged(step.data.material)) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_show_symbol) setHasOptionsMenu(true) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt index a6010070..7e54fb01 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt @@ -60,6 +60,13 @@ fun Context.announce( fun Fragment.announce(announcement: String) = application.announce(announcement) +fun Fragment.checkedAnnounce( + announcement: String, + preferenceRepository: PreferenceRepository = get() +) = executeIf(preferenceRepository.additionalAnnouncementsEnabled) { + announce(announcement) +} + val Fragment.actionBar: ActionBar? get() = (activity as AppCompatActivity).supportActionBar diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 4e4201bb..bfd5681e 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -257,5 +257,8 @@ как только в шеститочии введена корректная комбинация, не дожидаясь нажатия кнопки \"вперёд\" + + Дополнительные голосовые сообщения + diff --git a/android/app/src/main/res/xml/settings_hierarchy.xml b/android/app/src/main/res/xml/settings_hierarchy.xml index 0b0df056..96e881fe 100644 --- a/android/app/src/main/res/xml/settings_hierarchy.xml +++ b/android/app/src/main/res/xml/settings_hierarchy.xml @@ -41,4 +41,11 @@ android:title="@string/preference_title_on_fly_check" app:iconSpaceReserved="false" /> + + \ No newline at end of file From b454a3c65a054f1044587395d0c2939d83ae6853 Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Wed, 20 May 2020 18:31:46 +0300 Subject: [PATCH 18/63] 7.8 tweak setting description for practice-only-passed --- android/app/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 057de4d2..65efab7c 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -257,9 +257,9 @@ как только в шеститочии введена корректная комбинация, не дожидаясь нажатия кнопки \"вперёд\" - Только пройденные символы + Повторять только изученное - Использовать в практике только символы, пройденные в уроках + В разделе \"Практика\" повторять только символы, пройденные в разделе \"Теория\" From f903efbcfcee98190b6058d991b7e4a8b88d3388 Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Wed, 20 May 2020 18:32:53 +0300 Subject: [PATCH 19/63] 7.8 change settings order: most important to top --- .../src/main/res/xml/settings_hierarchy.xml | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/android/app/src/main/res/xml/settings_hierarchy.xml b/android/app/src/main/res/xml/settings_hierarchy.xml index a48e03e0..c9427d3f 100644 --- a/android/app/src/main/res/xml/settings_hierarchy.xml +++ b/android/app/src/main/res/xml/settings_hierarchy.xml @@ -1,6 +1,22 @@ + + + + - - - - \ No newline at end of file From c19d6f923b9b4ee84e0187b9bd2d843787f91aed Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 19:44:03 +0400 Subject: [PATCH 20/63] Improve helps --- .../theory/steps/AbstractStepFragment.kt | 4 +- android/app/src/main/res/values/strings.xml | 42 ++++++++++++------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt index ff3b9b3a..8e7282a2 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt @@ -18,7 +18,9 @@ import com.github.braillesystems.learnbraille.utils.updateTitle abstract class AbstractStepFragment(helpMsgId: HelpMsgId) : AbstractFragmentWithHelp(helpMsgId) { override val helpMsg: String - get() = super.helpMsg + getString(R.string.lessons_help_common) + get() = getString(R.string.lessons_help_template).format( + super.helpMsg, getString(R.string.lessons_help_common) + ) protected fun updateStepTitle(lessonId: Long, stepId: Long, msgId: Int) { updateTitle("$lessonId.$stepId ${getString(msgId)}") diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 5b78f42a..1c54723a 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -53,12 +53,16 @@
- В верхней половине экрана крупным шрифтом выведен символ, - который нужно ввести в шеститочии в нижней половине экрана и нажать кнопку “далее” справа. + В верхней половине экрана крупным шрифтом выведен символ, + который нужно ввести в шеститочии, в нижней половине экрана и нажать кнопку “далее” справа.
Выход в меню приложения - кнопка вверху слева.
- Вызов справки - кнопка вверху справа. + Список колод - кнопка вверху справа. +
+ Вызов справки - кнопка вверху справа, левее списка колод. +
+ Подсказка - кнопка посередине слева.

Если Вы забыли символ, можно нажать кнопку с вопросительным знаком слева внизу экрана. По нажатию этой кнопки будет выведено сообщение с верными номерами точек, шеститочие будет @@ -98,12 +102,15 @@ Обучение системе Луи Брайля: главное меню.

- “Теория“: пошаговые уроки с демонстрацией плоскопечатных и + Теория: пошаговые уроки с демонстрацией плоскопечатных и рельефно-точечных символов крупным шрифтом, вводом символов и поясняющими комментариями.
- ”Практика“: Повторяйте буквы, вводя их в шеститочии на экране. + Практика: набор карточек с символами для повторения. + Карточки объединены в колоды по типам символов. На каждой карточке находится буква и + предлагается ввести её шеститочие. Чтобы в практике появлялись только карточки с пройденными буквами, + включите эту опцию в настройках
- ”Сканировать QR-код“: если у Вас есть набор карточек с текстом Брайля и + Сканировать QR-код: если у Вас есть набор карточек с текстом Брайля и QR-кодами на обороте, по нажатию кнопки вы можете отсканировать QR-код и проверить, что написано на карточке. Для этого необходимо дополнительное приложение. Если его ещё нет, по нажатию кнопки Вы будете перенаправлены на страницу для скачивания приложения. @@ -137,11 +144,16 @@ Точки Уроки - %s\n\n%s + +


+ %s + ]]> +

Переход к следующему шагу - кнопка вперёд справа по центру, к предыдущему - кнопка назад слева по центру.
@@ -162,12 +174,14 @@

В верхней половине экрана крупным шрифтом выведен символ, который нужно ввести в шеститочии в нижней половине экрана. +
+ Над кнопкой "назад" находится кнопка подсказки, она работает так же, как и в практике. ]]>
шаг с вводом символа. + Пошаговый курс - шаг с вводом точек.

Введите в шеститочии на экране точки с указанными номерами. ]]> @@ -184,7 +198,7 @@ шаг с демонстрацией символа. + Пошаговый курс - шаг с демонстрацией точек.

На экран выведен точечный символ. Внимательно изучите его. ]]> @@ -239,22 +253,22 @@ Настройки Всплывающие уведомления Вибрация - Вибрация в ответ на результат выполнения задания + Вибрация в ответ на результат выполнения задания. Шаги с букварём - Показывать шаги с обращением к бумажному букварю Голубиной в теоретическом курсе + Показывать шаги с обращением к бумажному букварю Голубиной в теоретическом курсе. Включить/отключить краткие всплывающие подсказки (кроме самых важных). Обход точек в порядке нумерации Работает с Андроид 5.1. Если включено, программа экранного доступа обходит точки шеститочия в порядке 1-2-3-4-5-6, - иначе 1-4-2-5-3-6 + иначе 1-4-2-5-3-6. Проверка ввода \"на лету\" Выводить сообщение \"правильно\", как только в шеститочии введена корректная комбинация, - не дожидаясь нажатия кнопки \"вперёд\" + не дожидаясь нажатия кнопки \"вперёд\". From 82674a897dbad0db47298ae5f7c3bd068bfbadf7 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 19:59:49 +0400 Subject: [PATCH 21/63] Some fixes --- android/app/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index e94c6821..8850d0b9 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -42,7 +42,7 @@ Практика: %d из %d Список колод - Колода %s еще не доступна, пройдите эти карточки в уроках! + Колода \"%s\" еще не доступна, пройдите эти карточки в уроках! Все символы @@ -109,7 +109,7 @@ Практика: набор карточек с символами для повторения. Карточки объединены в колоды по типам символов. На каждой карточке находится буква и предлагается ввести её шеститочие. Чтобы в практике появлялись только карточки с пройденными буквами, - включите эту опцию в настройках + включите эту опцию в настройках.
Сканировать QR-код: если у Вас есть набор карточек с текстом Брайля и QR-кодами на обороте, по нажатию кнопки вы можете отсканировать QR-код и проверить, From fbdc0a97e81d9933e11c35dd0f6fced98504ed37 Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Wed, 20 May 2020 19:42:32 +0300 Subject: [PATCH 22/63] 7.12 modify instant help --- android/app/src/main/res/values/strings.xml | 30 ++++++++++++--------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index e94c6821..4e3db7aa 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -42,7 +42,7 @@ Практика: %d из %d Список колод - Колода %s еще не доступна, пройдите эти карточки в уроках! + Колода %s еще недоступна, пройдите эти карточки в уроках! Все символы @@ -50,25 +50,32 @@ Цифры Специальные символы +
- В верхней половине экрана крупным шрифтом выведен символ, - который нужно ввести в шеститочии в нижней половине экрана и нажать кнопку “далее” справа. + В случайном порядке выдаются задания - \"карточки\" с символами.
+ В верхней половине экрана выведен символ, + который нужно ввести в шеститочии в нижней половине экрана и нажать кнопку \“далее\” + справа. +
+ По умолчанию выдаются только карточки с символами, изученными в разделе \"теория\". Чтобы + повторять любые символы, измените это в настройках приложения.
Выход в меню приложения - кнопка вверху слева.
Список колод - кнопка вверху справа.
+ Карточки объединены в колоды по типам символов, например, колода русских букв, колода цифр. + Эта кнопка ведёт в меню выбора колоды. +
Вызов справки - кнопка вверху справа, левее списка колод.
- Подсказка - кнопка посередине слева. -

- Если Вы забыли символ, можно нажать кнопку с вопросительным знаком слева внизу экрана. + Если Вы забыли символ, можно нажать кнопку \"подсказка\" слева внизу экрана. По нажатию этой кнопки будет выведено сообщение с верными номерами точек, шеститочие будет заполнено правильными точками и недоступно для переключения. После этого нужно нажать кнопку - "далее" и ввести ту же букву ещё раз. + \"далее\" и ввести ту же букву ещё раз. ]]>
@@ -106,17 +113,16 @@ Теория: пошаговые уроки с демонстрацией плоскопечатных и рельефно-точечных символов крупным шрифтом, вводом символов и поясняющими комментариями.
- Практика: набор карточек с символами для повторения. - Карточки объединены в колоды по типам символов. На каждой карточке находится буква и - предлагается ввести её шеститочие. Чтобы в практике появлялись только карточки с пройденными буквами, - включите эту опцию в настройках + Практика: Повторение символов. +
Сканировать QR-код: если у Вас есть набор карточек с текстом Брайля и QR-кодами на обороте, по нажатию кнопки вы можете отсканировать QR-код и проверить, что написано на карточке. Для этого необходимо дополнительное приложение. Если его ещё нет, по нажатию кнопки Вы будете перенаправлены на страницу для скачивания приложения. +
Находясь в любом разделе приложения, Вы всегда можете вызвать справку по данному разделу, - нажав экранную кнопку "справка" в правом верхнем углу экрана. + нажав экранную кнопку \"справка\" в правом верхнем углу экрана. ]]>
From 1202fffea80f8927b288c851908b68403959c64c Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 21:30:12 +0400 Subject: [PATCH 23/63] 7.8 fix practice deck toast --- .../learnbraille/ui/screens/menu/MenuFragment.kt | 2 +- .../learnbraille/ui/screens/practice/CardFragment.kt | 10 +++++++++- android/app/src/main/res/values/strings.xml | 8 +++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt index 52268cad..3b0b17c0 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt @@ -38,7 +38,7 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { false ).apply { - title = getString(R.string.menu_actionbar_text).format(appName) + title = getString(R.string.menu_actionbar_text_template).format(appName) setHasOptionsMenu(true) requestPermissions() diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt index c86b5dce..d3d83345 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt @@ -11,6 +11,7 @@ import androidx.lifecycle.lifecycleScope import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.data.entities.dummyMaterialOf import com.github.braillesystems.learnbraille.data.repository.PracticeRepository +import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository import com.github.braillesystems.learnbraille.databinding.FragmentCardBinding import com.github.braillesystems.learnbraille.res.deckTagToName import com.github.braillesystems.learnbraille.ui.brailletrainer.BrailleTrainer @@ -28,6 +29,8 @@ import timber.log.Timber class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { + private val preferenceRepository: PreferenceRepository by inject() + private lateinit var viewModel: CardViewModel private lateinit var dotsState: BrailleDotsState private var buzzer: Vibrator? = null @@ -118,7 +121,12 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { lifecycleScope.launch { val practiceRepository: PracticeRepository by inject() val tag = practiceRepository.getCurrDeck().tag - toast(deckTagToName.getValue(tag)) + val template = if (preferenceRepository.practiceUseOnlyKnownMaterials) { + getString(R.string.practice_deck_name_enabled_template) + } else { + getString(R.string.practice_deck_name_disabled_template) + } + toast(template.format(deckTagToName.getValue(tag))) } } ) diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 65efab7c..1cc42c66 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -40,6 +40,12 @@ Подсказка Далее Практика: %d из %d + + Колода: \"%s\"\nПовторять только пройденные: включено + + + Колода: \"%s\"\nПовторять только пройденные: выключено + Список колод Колода %s еще не доступна, пройдите эти карточки в уроках! @@ -89,7 +95,7 @@ СПРАВКА НАСТРОЙКИ ВЫХОД - %s. Меню + %s. Меню Загружаем базу данных. Попробуйте еще раз! From f89879eeab8ede12dc4c84b333a021bd1f354ed3 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 21:51:27 +0400 Subject: [PATCH 24/63] Fix gray list API warning --- .../learnbraille/ui/screens/MainActivity.kt | 8 +------- .../github/braillesystems/learnbraille/utils/_Android.kt | 1 + 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/MainActivity.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/MainActivity.kt index f3d0e3ad..5ab7390d 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/MainActivity.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/MainActivity.kt @@ -3,12 +3,10 @@ package com.github.braillesystems.learnbraille.ui.screens import android.content.pm.ActivityInfo import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import androidx.databinding.DataBindingUtil import androidx.navigation.NavController import androidx.navigation.findNavController import androidx.navigation.ui.NavigationUI import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.databinding.ActivityMainBinding import com.github.braillesystems.learnbraille.ui.brailletrainer.BrailleTrainer import timber.log.Timber @@ -20,11 +18,7 @@ class MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) Timber.i("onCreate") - - DataBindingUtil.setContentView( - this, - R.layout.activity_main - ) + setContentView(R.layout.activity_main) navController = findNavController(R.id.navHostFragment) NavigationUI.setupActionBarWithNavController(this, navController) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt index c056a1bd..ca2063ea 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt @@ -57,6 +57,7 @@ fun Fragment.sendMarketIntent(appPackageName: String) { } } +// TODO do not work val Context.isAccessibilityEnabled: Boolean by logged { Settings.Secure.getInt( contentResolver, From d8d78c96c6e6180ff484f2d149d2ad7c8fe919e2 Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Wed, 20 May 2020 21:00:22 +0300 Subject: [PATCH 25/63] 7.8 enable only-passed-symbols in practice by default --- android/app/src/main/res/xml/settings_hierarchy.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/res/xml/settings_hierarchy.xml b/android/app/src/main/res/xml/settings_hierarchy.xml index 0c1856ef..c6d30119 100644 --- a/android/app/src/main/res/xml/settings_hierarchy.xml +++ b/android/app/src/main/res/xml/settings_hierarchy.xml @@ -2,7 +2,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> Date: Wed, 20 May 2020 22:24:39 +0400 Subject: [PATCH 26/63] Fix exit --- .../learnbraille/ui/screens/exit/ExitFragment.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt index 49260e30..5f1e648f 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/exit/ExitFragment.kt @@ -1,5 +1,6 @@ package com.github.braillesystems.learnbraille.ui.screens.exit +import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup @@ -12,7 +13,7 @@ import com.github.braillesystems.learnbraille.utils.announce import com.github.braillesystems.learnbraille.utils.navigate import com.github.braillesystems.learnbraille.utils.updateTitle import org.koin.android.ext.android.get -import kotlin.system.exitProcess + class ExitFragment : Fragment() { @@ -36,7 +37,10 @@ class ExitFragment : Fragment() { recognizer = SpeechRecognition(this@ExitFragment, get()) exitButton.setOnClickListener { - exitProcess(0) + val homeIntent = Intent(Intent.ACTION_MAIN) + homeIntent.addCategory(Intent.CATEGORY_HOME) + homeIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + startActivity(homeIntent) } continueButton.setOnClickListener { From bdf76f1a8337654e439d0a0a0afedcb70300cf2c Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 22:33:04 +0400 Subject: [PATCH 27/63] Fix help announcement --- .../learnbraille/ui/screens/AbstractFragmentWithHelp.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt index ac9ea4c6..d60c007d 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/AbstractFragmentWithHelp.kt @@ -6,7 +6,6 @@ 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.announce import com.github.braillesystems.learnbraille.utils.navigate import timber.log.Timber @@ -37,7 +36,6 @@ abstract class AbstractFragmentWithHelp(private val helpMsgId: HelpMsgId) : Frag Timber.i("Navigate to help") val action = HelpFragmentDirections.actionGlobalHelpFragment() action.helpMessage = helpMsg - announce(helpMsg) navigate(action) } } From e84e2c24eca5a1b60fcbf32e89c49949a519774b Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 22:54:45 +0400 Subject: [PATCH 28/63] Fix accessibility enabled check --- .../braillesystems/learnbraille/utils/Utils.kt | 4 +--- .../learnbraille/utils/_Android.kt | 16 +++++++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt index a6010070..6cfe6427 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/Utils.kt @@ -44,9 +44,7 @@ fun Fragment.checkedToast(msg: String, preferenceRepository: PreferenceRepositor fun Fragment.toast(msg: String, preferenceRepository: PreferenceRepository = get()) = Toast.makeText(context, msg, preferenceRepository.toastDuration).show() -fun Context.announce( - announcement: String -) { +fun Context.announce(announcement: String) { val manager = accessibilityManager ?: return val event = AccessibilityEvent.obtain().apply { eventType = AccessibilityEvent.TYPE_ANNOUNCEMENT diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt index ca2063ea..df48d58f 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Android.kt @@ -13,6 +13,7 @@ import android.hardware.usb.UsbManager import android.net.Uri import android.os.Vibrator import android.provider.Settings +import android.provider.Settings.SettingNotFoundException import android.view.accessibility.AccessibilityManager import androidx.fragment.app.Fragment import androidx.navigation.NavDirections @@ -21,6 +22,7 @@ import androidx.preference.PreferenceManager import com.github.braillesystems.learnbraille.R import timber.log.Timber + val Context.usbManager get() = getSystemService(Context.USB_SERVICE) as UsbManager val Context.accessibilityManager: AccessibilityManager? get() = @@ -57,12 +59,16 @@ fun Fragment.sendMarketIntent(appPackageName: String) { } } -// TODO do not work val Context.isAccessibilityEnabled: Boolean by logged { - Settings.Secure.getInt( - contentResolver, - Settings.Secure.ACCESSIBILITY_ENABLED - ) == 1 + try { + Settings.Secure.getInt( + contentResolver, + Settings.Secure.ACCESSIBILITY_ENABLED + ) == 1 + } catch (e: SettingNotFoundException) { + Timber.e(e) + false + } } /** From 42220cff439a898ccd623b4fbbc62e361e565ddf Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Wed, 20 May 2020 22:02:21 +0300 Subject: [PATCH 29/63] 7.8 reformat settings descriptions --- android/app/src/main/res/values/strings.xml | 6 +++++- android/app/src/main/res/xml/settings_hierarchy.xml | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 096d9d32..09f04af1 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -288,7 +288,11 @@ В разделе \"Практика\" повторять только символы, пройденные в разделе \"Теория\". - Дополнительные голосовые сообщения + Автоозвучка текстов + + + Автоматически озвучивать длинные тексты (например, в справке и разделе \"теория\") + средством экранного чтения, не дожидаясь фокусировки на текстовом поле. diff --git a/android/app/src/main/res/xml/settings_hierarchy.xml b/android/app/src/main/res/xml/settings_hierarchy.xml index c6d30119..0c9b20ef 100644 --- a/android/app/src/main/res/xml/settings_hierarchy.xml +++ b/android/app/src/main/res/xml/settings_hierarchy.xml @@ -52,6 +52,7 @@ From 52edc8d42a47f75027d8fbbfecfa4c895646bbfb Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 23:10:46 +0400 Subject: [PATCH 30/63] Add special symbols to devs course --- .../github/braillesystems/learnbraille/res/TestCourse.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/TestCourse.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/TestCourse.kt index 08218ca4..880e135f 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/TestCourse.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/TestCourse.kt @@ -78,6 +78,12 @@ internal val testLessons by lessons { ).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('1')) + +Input(content.symbols.getValue('2')) + +Info( """Поздравляем! Второй урок пройден. В следующем занятии мы узнаем, как с помощью букв А, Б и Ц составить цифры 1, 2 и 3.""" From 44f673bd312f10d8f9180fc2fad296643d08fd33 Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Wed, 20 May 2020 22:25:29 +0300 Subject: [PATCH 31/63] 7.13 update version tag & version code --- android/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index a2bf71bd..dd18a67b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -24,8 +24,8 @@ android { applicationId "com.github.braillesystems.learnbraille" minSdkVersion 19 targetSdkVersion 29 - versionCode 3 - versionName "0.6.2" + versionCode 4 + versionName "0.6.3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true multiDexEnabled = true From 8a7964da033daaaf88a5de2a4898734e0b22b4eb Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Wed, 20 May 2020 23:32:21 +0400 Subject: [PATCH 32/63] Fix deck name toast each time new symbol appeares --- .../ui/screens/practice/CardFragment.kt | 21 ++++++++++--------- .../ui/screens/practice/CardViewModel.kt | 2 +- android/app/src/main/res/values/strings.xml | 4 ++-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt index d3d83345..b13ad125 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt @@ -118,19 +118,20 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { Observer { if (it == null) return@Observer announceIntro(it) - lifecycleScope.launch { - val practiceRepository: PracticeRepository by inject() - val tag = practiceRepository.getCurrDeck().tag - val template = if (preferenceRepository.practiceUseOnlyKnownMaterials) { - getString(R.string.practice_deck_name_enabled_template) - } else { - getString(R.string.practice_deck_name_disabled_template) - } - toast(template.format(deckTagToName.getValue(tag))) - } } ) + lifecycleScope.launch { + val practiceRepository: PracticeRepository by inject() + val tag = practiceRepository.getCurrDeck().tag + val template = if (preferenceRepository.practiceUseOnlyKnownMaterials) { + getString(R.string.practice_deck_name_enabled_template) + } else { + getString(R.string.practice_deck_name_disabled_template) + } + toast(template.format(deckTagToName.getValue(tag))) + } + }.root private fun announceIntro(symbol: String) { diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt index ae697e68..73f921d3 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt @@ -47,7 +47,7 @@ class CardViewModel( private var expectedDots: BrailleDots? = null - private val job = Job() + val job = Job() private val uiScope = scope(job) init { diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index a2c97cbb..d7b69297 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -41,10 +41,10 @@ Далее Практика: %d из %d - Колода: \"%s\"\nПовторять только пройденные: включено + Колода: \"%s\"\nПовторять только изученные: включено - Колода: \"%s\"\nПовторять только пройденные: выключено + Колода: \"%s\"\nПовторять только изученные: выключено Список колод From b1f8dcb9b92cb109151cb0db64b6248cadaba0d5 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Thu, 21 May 2020 00:10:38 +0400 Subject: [PATCH 33/63] Add additional exit item in settings --- .../data/repository/PreferenceRepository.kt | 8 ++++++++ android/app/src/main/res/values/strings.xml | 7 +++++++ android/app/src/main/res/xml/settings_hierarchy.xml | 10 +++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt index 701feefb..de75b084 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt @@ -23,6 +23,7 @@ interface PreferenceRepository { val inputOnFlyCheck: Boolean val additionalAnnouncementsEnabled: Boolean val practiceUseOnlyKnownMaterials: Boolean + val additionalExitButtonsEnabled: Boolean val currentUserId: Long suspend fun getCurrentUser(): User @@ -103,6 +104,13 @@ class PreferenceRepositoryImpl( ) } + override val additionalExitButtonsEnabled: Boolean by logged { + context.preferences.getBoolean( + context.getString(R.string.preference_additional_exit_buttons_enabled), + false + ) + } + override val currentUserId: Long by logged { context.preferences.getLong( context.getString(R.string.preference_current_user), 1 diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 48b59ed9..c2ab46dc 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -18,6 +18,7 @@ traverse_dots_in_enumeration_order enable_additional_announcements practice_use_only_seen_materials + additional_exit_buttons_enabled Правильно! Неправильно! @@ -294,5 +295,11 @@ Автоматически озвучивать длинные тексты (например, в справке и разделе \"теория\") средством экранного чтения, не дожидаясь фокусировки на текстовом поле. + + Дополнительные кнопки \"Выход\" + + + В главном меню кнопка \"Выход из приложения\", в контекстных меню кнопка \"Скрыть\". + diff --git a/android/app/src/main/res/xml/settings_hierarchy.xml b/android/app/src/main/res/xml/settings_hierarchy.xml index 0c9b20ef..c0de950b 100644 --- a/android/app/src/main/res/xml/settings_hierarchy.xml +++ b/android/app/src/main/res/xml/settings_hierarchy.xml @@ -56,5 +56,13 @@ android:theme="@style/SwitchPreferenceButton" android:title="@string/preference_title_additional_announcements" app:iconSpaceReserved="false" /> - + + + \ No newline at end of file From 93f2d2f288f2d74a97afe7e16c949b8e36520f1d Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Thu, 21 May 2020 00:47:03 +0400 Subject: [PATCH 34/63] Make main menu exit button hide --- .../ui/screens/menu/MenuFragment.kt | 70 +++++++++++++++++-- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt index 3b0b17c0..239e1f9c 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt @@ -10,6 +10,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil import com.github.braillesystems.learnbraille.COURSE_ID import com.github.braillesystems.learnbraille.R @@ -19,6 +20,7 @@ import com.github.braillesystems.learnbraille.databinding.FragmentMenuBinding import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp import com.github.braillesystems.learnbraille.ui.screens.theory.toLastCourseStep import com.github.braillesystems.learnbraille.utils.* +import com.google.android.material.button.MaterialButton import org.koin.android.ext.android.inject import timber.log.Timber @@ -42,15 +44,23 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { setHasOptionsMenu(true) requestPermissions() - lessonsButton.setOnClickListener(interruptingOnClickListener { + val buttons = mutableListOf() + + lessonsButton.also { + buttons += it + }.setOnClickListener(interruptingOnClickListener { toLastCourseStep(COURSE_ID) }) - practiceButton.setOnClickListener(interruptingOnClickListener { + practiceButton.also { + buttons += it + }.setOnClickListener(interruptingOnClickListener { navigate(R.id.action_menuFragment_to_practiceFragment) }) - qrPracticeButton.setOnClickListener { + qrPracticeButton.also { + buttons += it + }.setOnClickListener { try { val intent = Intent("com.google.zxing.client.android.SCAN") intent.putExtra("SCAN_MODE", "QR_CODE_MODE") @@ -61,14 +71,24 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { } } - settingsButton.setOnClickListener { + settingsButton.also { + buttons += it + }.setOnClickListener { navigate(R.id.action_menuFragment_to_settingsFragment) } - exitButton.setOnClickListener { - navigate(R.id.action_menuFragment_to_exitFragment) + if (preferenceRepository.additionalExitButtonsEnabled) { + exitButton.also { + buttons += it + }.setOnClickListener { + navigate(R.id.action_menuFragment_to_exitFragment) + } + } else { + exitButton.visibility = View.GONE } + colorButtons(buttons) + }.root override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -117,6 +137,44 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { else toast(getString(R.string.menu_db_not_initialized_warning)) } + private fun colorButtons(buttons: List) { + val (ps, bs) = when (buttons.size) { + 3 -> { + val (b1, b2, b3) = buttons + listOf(b1, b3) to listOf(b2) + } + 4 -> { + val (b1, b2, b3, b4) = buttons + listOf(b1, b3) to listOf(b2, b4) + } + else -> listOf() to listOf() + } + ps.forEach { + it.setTextColor( + ContextCompat.getColor( + application, R.color.colorOnSecondary + ) + ) + it.setBackgroundColor( + ContextCompat.getColor( + application, R.color.colorSecondary + ) + ) + } + bs.forEach { + it.setTextColor( + ContextCompat.getColor( + application, R.color.colorOnPrimary + ) + ) + it.setBackgroundColor( + ContextCompat.getColor( + application, R.color.colorPrimary + ) + ) + } + } + companion object { private const val qrRequestCode = 0 private const val recordAudioPermissionCode = 29 From fed979c695226e4c074bda74ea2b868876946d8d Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Thu, 21 May 2020 00:51:52 +0400 Subject: [PATCH 35/63] Make hide button in context menu removable --- .../theory/steps/AbstractStepFragment.kt | 12 +++++-- android/app/src/main/res/menu/steps_menu.xml | 7 ---- .../app/src/main/res/menu/steps_menu_hide.xml | 33 +++++++++++++++++++ 3 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 android/app/src/main/res/menu/steps_menu_hide.xml diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt index 8e7282a2..389c930f 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/AbstractStepFragment.kt @@ -5,18 +5,22 @@ import android.view.MenuInflater import android.view.MenuItem import com.github.braillesystems.learnbraille.COURSE_ID import com.github.braillesystems.learnbraille.R +import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository import com.github.braillesystems.learnbraille.ui.screens.AbstractFragmentWithHelp import com.github.braillesystems.learnbraille.ui.screens.HelpMsgId import com.github.braillesystems.learnbraille.ui.screens.theory.toCurrentStep import com.github.braillesystems.learnbraille.utils.navigate import com.github.braillesystems.learnbraille.utils.updateTitle +import org.koin.android.ext.android.inject /** - * Base class for all lessons. + * Base class for all steps. */ abstract class AbstractStepFragment(helpMsgId: HelpMsgId) : AbstractFragmentWithHelp(helpMsgId) { + private val preferenceRepository: PreferenceRepository by inject() + override val helpMsg: String get() = getString(R.string.lessons_help_template).format( super.helpMsg, getString(R.string.lessons_help_common) @@ -27,7 +31,11 @@ abstract class AbstractStepFragment(helpMsgId: HelpMsgId) : AbstractFragmentWith } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.steps_menu, menu) + inflater.inflate( + if (preferenceRepository.additionalExitButtonsEnabled) R.menu.steps_menu_hide + else R.menu.steps_menu, + menu + ) } override fun onOptionsItemSelected(item: MenuItem) = false.also { diff --git a/android/app/src/main/res/menu/steps_menu.xml b/android/app/src/main/res/menu/steps_menu.xml index f59b72fa..239f8b68 100644 --- a/android/app/src/main/res/menu/steps_menu.xml +++ b/android/app/src/main/res/menu/steps_menu.xml @@ -23,11 +23,4 @@ android:title="@string/menu_title_current_step" android:visible="true" app:showAsAction="never" /> - - \ No newline at end of file diff --git a/android/app/src/main/res/menu/steps_menu_hide.xml b/android/app/src/main/res/menu/steps_menu_hide.xml new file mode 100644 index 00000000..f59b72fa --- /dev/null +++ b/android/app/src/main/res/menu/steps_menu_hide.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + \ No newline at end of file From 6cecde3387617f827e0aa2832e60f5996f9731c5 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Thu, 21 May 2020 01:26:46 +0400 Subject: [PATCH 36/63] Fix comments --- android/app/src/main/AndroidManifest.xml | 10 ++++++---- .../braillesystems/learnbraille/data/db/Database.kt | 4 ++-- .../data/repository/PreferenceRepository.kt | 2 +- android/app/src/main/res/xml/settings_hierarchy.xml | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f599ff80..05a132ca 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,7 +2,8 @@ - + + - + + + + \ No newline at end of file diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt index e3904f08..caf8770d 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt @@ -26,7 +26,7 @@ import timber.log.Timber CurrentStep::class, LastCourseStep::class, LastLessonStep::class ], version = 15, - exportSchema = false // TODO export + exportSchema = false ) @TypeConverters( BrailleDotsConverters::class, @@ -127,7 +127,7 @@ abstract class LearnBrailleDatabase : RoomDatabase(), KoinComponent { } } }) - .fallbackToDestructiveMigration() // TODO change migration strategy + .fallbackToDestructiveMigration() .build() } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt index 701feefb..5866ce29 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt @@ -15,7 +15,7 @@ interface PreferenceRepository { val buzzEnabled: Boolean val toastsEnabled: Boolean - val brailleTrainerEnabled: Boolean get() = false + val brailleTrainerEnabled: Boolean get() = false // Uncomment in android manifest when set true val speechRecognitionEnabled: Boolean val golubinaBookStepsEnabled: Boolean val practiceUseMaterialsPassedInCourse: Boolean diff --git a/android/app/src/main/res/xml/settings_hierarchy.xml b/android/app/src/main/res/xml/settings_hierarchy.xml index 0c9b20ef..12753579 100644 --- a/android/app/src/main/res/xml/settings_hierarchy.xml +++ b/android/app/src/main/res/xml/settings_hierarchy.xml @@ -56,5 +56,5 @@ android:theme="@style/SwitchPreferenceButton" android:title="@string/preference_title_additional_announcements" app:iconSpaceReserved="false" /> - + \ No newline at end of file From 1bdc53a878f1bf80feb4663dbcb7251000afdbdd Mon Sep 17 00:00:00 2001 From: kaustika Date: Thu, 21 May 2020 00:36:52 +0300 Subject: [PATCH 37/63] 7.7 add icons for (un)locked to be distinguishable --- .../learnbraille/ui/screens/practice/DecksList.kt | 2 ++ .../screens/theory/lessons/LessonsListFragment.kt | 2 ++ android/app/src/main/res/drawable/locked.xml | 10 ++++++++++ android/app/src/main/res/drawable/unlocked.xml | 10 ++++++++++ .../app/src/main/res/layout/decks_list_item.xml | 13 ++++++++++++- .../app/src/main/res/layout/lessons_list_item.xml | 14 +++++++++++++- 6 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 android/app/src/main/res/drawable/locked.xml create mode 100644 android/app/src/main/res/drawable/unlocked.xml diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt index a8b8593a..9959bf15 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt @@ -63,6 +63,7 @@ class DecksList : Fragment() { R.color.colorOnBackgroundDark ) ) + deckState.setImageResource(R.drawable.unlocked) } else { deckName.setTextColor( ContextCompat.getColor( @@ -70,6 +71,7 @@ class DecksList : Fragment() { R.color.colorOnBackgroundLight ) ) + deckState.setImageResource(R.drawable.locked) } } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt index e1ccc9e0..f621b056 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/lessons/LessonsListFragment.kt @@ -64,6 +64,7 @@ class LessonsListFragment : Fragment() { R.color.colorOnBackgroundDark ) ) + lessonState.setImageResource(R.drawable.unlocked) } else { clickListener = disabledListener lessonName.setTextColor( @@ -72,6 +73,7 @@ class LessonsListFragment : Fragment() { R.color.colorOnBackgroundLight ) ) + lessonState.setImageResource(R.drawable.locked) } } lessonsList.adapter = adapter diff --git a/android/app/src/main/res/drawable/locked.xml b/android/app/src/main/res/drawable/locked.xml new file mode 100644 index 00000000..79c3b73d --- /dev/null +++ b/android/app/src/main/res/drawable/locked.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable/unlocked.xml b/android/app/src/main/res/drawable/unlocked.xml new file mode 100644 index 00000000..563c5bcd --- /dev/null +++ b/android/app/src/main/res/drawable/unlocked.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/decks_list_item.xml b/android/app/src/main/res/layout/decks_list_item.xml index 0cf8d79a..1e396e0a 100644 --- a/android/app/src/main/res/layout/decks_list_item.xml +++ b/android/app/src/main/res/layout/decks_list_item.xml @@ -23,12 +23,23 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="@{() -> clickListener.onClick(item)}" - android:orientation="vertical" + android:orientation="horizontal" android:paddingStart="18dp" android:paddingTop="18dp" android:paddingEnd="18dp" android:paddingBottom="18dp"> + + + + From 825e53cdcdee788d1fca28bda80f54bad095a22b Mon Sep 17 00:00:00 2001 From: kaustika Date: Thu, 21 May 2020 00:54:59 +0300 Subject: [PATCH 38/63] 7.7 extract params in dimens file --- .../src/main/res/layout/decks_list_item.xml | 22 +++++++++---------- .../src/main/res/layout/lessons_list_item.xml | 22 +++++++++---------- android/app/src/main/res/values/dimens.xml | 6 +++++ 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/android/app/src/main/res/layout/decks_list_item.xml b/android/app/src/main/res/layout/decks_list_item.xml index 1e396e0a..f377111b 100644 --- a/android/app/src/main/res/layout/decks_list_item.xml +++ b/android/app/src/main/res/layout/decks_list_item.xml @@ -17,34 +17,34 @@ android:id="@+id/lesson_card" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="8dp"> + android:layout_margin="@dimen/list_item_margin"> + android:paddingStart="@dimen/list_item_padding" + android:paddingTop="@dimen/list_item_padding" + android:paddingEnd="@dimen/list_item_padding" + android:paddingBottom="@dimen/list_item_padding"> + android:layout_marginEnd="@dimen/list_icon_marginEnd" + android:importantForAccessibility="no" + android:scaleX="@dimen/list_icon_scale" + android:scaleY="@dimen/list_icon_scale" + tools:srcCompat="@drawable/locked" /> diff --git a/android/app/src/main/res/layout/lessons_list_item.xml b/android/app/src/main/res/layout/lessons_list_item.xml index 301e4224..e5ea91a9 100644 --- a/android/app/src/main/res/layout/lessons_list_item.xml +++ b/android/app/src/main/res/layout/lessons_list_item.xml @@ -17,35 +17,35 @@ android:id="@+id/lesson_card" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="8dp"> + android:layout_margin="@dimen/list_item_margin"> + android:paddingStart="@dimen/list_item_padding" + android:paddingTop="@dimen/list_item_padding" + android:paddingEnd="@dimen/list_item_padding" + android:paddingBottom="@dimen/list_item_padding"> + android:layout_marginEnd="@dimen/list_icon_marginEnd" + android:importantForAccessibility="no" + android:scaleX="@dimen/list_icon_scale" + android:scaleY="@dimen/list_icon_scale" + tools:srcCompat="@drawable/locked" /> diff --git a/android/app/src/main/res/values/dimens.xml b/android/app/src/main/res/values/dimens.xml index 9ea2c8c1..496a1df5 100644 --- a/android/app/src/main/res/values/dimens.xml +++ b/android/app/src/main/res/values/dimens.xml @@ -40,4 +40,10 @@ 36dp 50dp + 20sp + 1.2 + 16dp + 18dp + 8dp + \ No newline at end of file From e3ed927680eb8ca973aa814517f92a9a81144e8c Mon Sep 17 00:00:00 2001 From: kaustika Date: Thu, 21 May 2020 13:29:14 +0300 Subject: [PATCH 39/63] 7.7 fix pixeled icons --- android/app/src/main/res/drawable/locked.xml | 4 ++-- android/app/src/main/res/drawable/unlocked.xml | 4 ++-- android/app/src/main/res/layout/decks_list_item.xml | 3 +-- android/app/src/main/res/layout/lessons_list_item.xml | 3 +-- android/app/src/main/res/values/dimens.xml | 1 - 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/android/app/src/main/res/drawable/locked.xml b/android/app/src/main/res/drawable/locked.xml index 79c3b73d..9a9c7d2a 100644 --- a/android/app/src/main/res/drawable/locked.xml +++ b/android/app/src/main/res/drawable/locked.xml @@ -1,7 +1,7 @@ 50dp 20sp - 1.2 16dp 18dp 8dp From 28f45f56fde271d1c35535f4e8a6ad0ccbc387b3 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Thu, 21 May 2020 20:54:16 +0400 Subject: [PATCH 40/63] Fix wrong deck toast after enabling known only materials --- .../data/repository/PracticeRepository.kt | 4 ++++ .../ui/screens/practice/CardFragment.kt | 23 +++++++++---------- .../ui/screens/practice/CardViewModel.kt | 20 ++++++++++++---- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt index a3016a75..40c0b407 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt @@ -48,6 +48,10 @@ class PracticeRepositoryImpl( } } + /** + * Deck changes automatically if `use only known materials` enabled + * and previous `currentDeck` became not available. + */ override suspend fun getNextMaterial(): Material = if (preferenceRepository.practiceUseOnlyKnownMaterials) { cardDao.getRandomKnownMaterialFromDeck( diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt index 0b31ab29..f459e31b 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt @@ -7,10 +7,8 @@ import androidx.core.content.getSystemService import androidx.databinding.DataBindingUtil import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.lifecycleScope import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.data.entities.dummyMaterialOf -import com.github.braillesystems.learnbraille.data.repository.PracticeRepository import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository import com.github.braillesystems.learnbraille.databinding.FragmentCardBinding import com.github.braillesystems.learnbraille.res.deckTagToName @@ -22,7 +20,6 @@ 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.utils.* -import kotlinx.coroutines.launch import org.koin.android.ext.android.inject import org.koin.core.parameter.parametersOf import timber.log.Timber @@ -121,16 +118,18 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { } ) - lifecycleScope.launch { - val practiceRepository: PracticeRepository by inject() - val tag = practiceRepository.getCurrDeck().tag - val template = if (preferenceRepository.practiceUseOnlyKnownMaterials) { - getString(R.string.practice_deck_name_enabled_template) - } else { - getString(R.string.practice_deck_name_disabled_template) + viewModel.deckTag.observe( + viewLifecycleOwner, + Observer { tag -> + if (tag == null) return@Observer + val template = if (preferenceRepository.practiceUseOnlyKnownMaterials) { + getString(R.string.practice_deck_name_enabled_template) + } else { + getString(R.string.practice_deck_name_disabled_template) + } + toast(template.format(deckTagToName.getValue(tag))) } - toast(template.format(deckTagToName.getValue(tag))) - } + ) }.root diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt index 73f921d3..1565c9cc 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt @@ -35,9 +35,11 @@ class CardViewModel( ) : AndroidViewModel(application), DotsChecker by dotsChecker { - private var _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 var nTries: Int = 0 private set @@ -52,7 +54,7 @@ class CardViewModel( init { Timber.i("Initialize practice view model") - initializeCard() + initializeCard(firstTime = true) dotsChecker.apply { getEnteredDots = this@CardViewModel.getEnteredDots @@ -74,12 +76,20 @@ class CardViewModel( job.cancel() } - private fun initializeCard() = uiScope.launch { + private fun initializeCard(firstTime: Boolean = false) = uiScope.launch { val material = practiceRepository.getNextMaterial() require(material.data is Symbol) material.data.apply { _symbol.value = char.toString() expectedDots = 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 + } } } From 7b1972227a058f223a3feef22bdb3430a63603e3 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Thu, 21 May 2020 21:11:04 +0400 Subject: [PATCH 41/63] Hide availibility icons if use only known disabled --- .../learnbraille/ui/screens/practice/DecksList.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt index 9959bf15..583372d3 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/DecksList.kt @@ -2,6 +2,7 @@ package com.github.braillesystems.learnbraille.ui.screens.practice 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 @@ -11,6 +12,7 @@ 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.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 @@ -24,6 +26,7 @@ import org.koin.android.ext.android.inject class DecksList : Fragment() { private val practiceRepository: MutablePracticeRepository by inject() + private val preferenceRepository: PreferenceRepository by inject() override fun onCreateView( inflater: LayoutInflater, @@ -73,6 +76,9 @@ class DecksList : Fragment() { ) deckState.setImageResource(R.drawable.locked) } + if (!preferenceRepository.practiceUseOnlyKnownMaterials) { + deckState.visibility = View.GONE + } } } From 712267eb0b2221642e1c83293214b75f659a98b5 Mon Sep 17 00:00:00 2001 From: kaustika Date: Thu, 21 May 2020 21:28:32 +0300 Subject: [PATCH 42/63] 7.4 narrow buttons + info to full screen height --- .../app/src/main/res/layout/fragment_card.xml | 2 ++ .../app/src/main/res/layout/fragment_help.xml | 2 +- .../res/layout/fragment_lesson_first_info.xml | 3 ++- .../res/layout/fragment_lesson_last_info.xml | 3 ++- .../main/res/layout/fragment_lessons_info.xml | 2 ++ .../res/layout/fragment_lessons_input_dots.xml | 4 +++- .../layout/fragment_lessons_input_symbol.xml | 4 +++- .../app/src/main/res/values-sw320dp/dimens.xml | 8 ++++---- .../app/src/main/res/values-sw360dp/dimens.xml | 8 ++++---- .../app/src/main/res/values-sw400dp/dimens.xml | 8 ++++---- .../app/src/main/res/values-sw600dp/dimens.xml | 11 ++++++----- .../app/src/main/res/values-sw720dp/dimens.xml | 9 +++++---- android/app/src/main/res/values/dimens.xml | 17 ++++++++++------- android/app/src/main/res/values/styles.xml | 3 ++- 14 files changed, 50 insertions(+), 34 deletions(-) diff --git a/android/app/src/main/res/layout/fragment_card.xml b/android/app/src/main/res/layout/fragment_card.xml index 4d58b518..776a7a3f 100644 --- a/android/app/src/main/res/layout/fragment_card.xml +++ b/android/app/src/main/res/layout/fragment_card.xml @@ -49,6 +49,8 @@ android:layout_height="@dimen/practice_buttons_height" android:contentDescription="@string/practice_hint" android:onClick="@{() -> cardViewModel.onHint()}" + android:paddingStart="@dimen/practice_buttons_icon_hint_padding" + android:paddingEnd="@dimen/practice_buttons_icon_hint_padding" android:scaleType="fitCenter" android:text="@string/practice_hint_icon_symbol" android:textColor="@color/colorOnPrimary" diff --git a/android/app/src/main/res/layout/fragment_help.xml b/android/app/src/main/res/layout/fragment_help.xml index 3b624b46..ff2142ae 100644 --- a/android/app/src/main/res/layout/fragment_help.xml +++ b/android/app/src/main/res/layout/fragment_help.xml @@ -13,7 +13,7 @@ android:paddingBottom="2dp" android:scrollbars="vertical" android:text="@string/default_help_message" - android:textSize="18sp" + android:textSize="@dimen/help_message_text_size" app:autoSizeTextType="none" /> \ No newline at end of file diff --git a/android/app/src/main/res/layout/fragment_lesson_first_info.xml b/android/app/src/main/res/layout/fragment_lesson_first_info.xml index 1525d9a9..4b9ef5fd 100644 --- a/android/app/src/main/res/layout/fragment_lesson_first_info.xml +++ b/android/app/src/main/res/layout/fragment_lesson_first_info.xml @@ -13,14 +13,15 @@ android:id="@+id/info_text_view" android:layout_width="@dimen/lessons_info_width" android:layout_height="@dimen/lessons_info_height" + android:layout_marginTop="@dimen/lessons_info_top_margin" android:scrollbars="vertical" android:text="" android:textSize="@dimen/lessons_info_text_size" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="@dimen/lessons_info_horizontal_bias" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="@dimen/lessons_info_vertical_bias" tools:text="ИНФОРМАЦИЯ!" /> 0.85 0.9 - 50dp + 40dp 190dp 85dp 35dp - 30dp + 32dp 30dp 320dp 64dp 15sp 0.05 - 190dp - 360dp + 230dp + 0dp 18sp 0.0 394dp diff --git a/android/app/src/main/res/values-sw360dp/dimens.xml b/android/app/src/main/res/values-sw360dp/dimens.xml index b2363e30..7cd062fa 100644 --- a/android/app/src/main/res/values-sw360dp/dimens.xml +++ b/android/app/src/main/res/values-sw360dp/dimens.xml @@ -13,19 +13,19 @@ 0.85 0.9 - 70dp + 50dp 230dp 115dp 50dp - 50dp + 45dp 40dp 360dp 64dp 20sp 0.05 - 200dp - 460dp + 240dp + 0dp 18sp 0.0 394dp diff --git a/android/app/src/main/res/values-sw400dp/dimens.xml b/android/app/src/main/res/values-sw400dp/dimens.xml index 919c82c8..6c53dbdf 100644 --- a/android/app/src/main/res/values-sw400dp/dimens.xml +++ b/android/app/src/main/res/values-sw400dp/dimens.xml @@ -13,19 +13,19 @@ 0.85 0.9 - 70dp + 50dp 240dp 125dp 50dp - 50dp + 45dp 40dp 428dp 64dp 20sp 0.05 - 250dp - 550dp + 300dp + 0dp 18sp 0.0 394dp diff --git a/android/app/src/main/res/values-sw600dp/dimens.xml b/android/app/src/main/res/values-sw600dp/dimens.xml index fe1d6a58..83f75acc 100644 --- a/android/app/src/main/res/values-sw600dp/dimens.xml +++ b/android/app/src/main/res/values-sw600dp/dimens.xml @@ -10,25 +10,26 @@ 1.8 1.9 - 100dp + 65dp 300dp 150dp 70dp - 65dp - 60dp + 60dp + 55dp 428dp 80dp 35sp 0.05 - 350dp - 650dp + 450dp + 0dp 30sp 0.0 394dp 175dp 150sp + 25sp 30dp diff --git a/android/app/src/main/res/values-sw720dp/dimens.xml b/android/app/src/main/res/values-sw720dp/dimens.xml index 53a82680..01abc79b 100644 --- a/android/app/src/main/res/values-sw720dp/dimens.xml +++ b/android/app/src/main/res/values-sw720dp/dimens.xml @@ -10,25 +10,26 @@ 2.1 2.2 - 120dp + 75dp 320dp 170dp 90dp - 85dp + 65dp 60dp 428dp 100dp 40sp 0.05 - 480dp - 870dp + 590dp + 0dp 35sp 0.0 394dp 175dp 150sp + 30sp 30dp diff --git a/android/app/src/main/res/values/dimens.xml b/android/app/src/main/res/values/dimens.xml index 496a1df5..dd8458ba 100644 --- a/android/app/src/main/res/values/dimens.xml +++ b/android/app/src/main/res/values/dimens.xml @@ -13,25 +13,27 @@ 0.85 0.9 - 45dp + 35dp 200dp 75dp 30dp - 50dp + 45dp + 0dp 20dp 428dp 64dp 20sp - 0.05 - 250dp - 500dp + 0.0 + 270dp + 0dp 18sp 0.0 394dp 175dp - 0.496 - 1.0 + 0.5 + 10dp + 0.2 170sp @@ -39,6 +41,7 @@ 36dp 50dp + 18sp 20sp 1.2 diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index dc191240..107b4ee1 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -31,9 +31,10 @@ @dimen/practice_buttons_border_radius - From 058a6a48e101be42fa96380d1c70eabe56c00744 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Thu, 21 May 2020 23:48:19 +0400 Subject: [PATCH 43/63] Refactor getNextMaterial --- .../data/repository/PracticeRepository.kt | 36 +++++++++++-------- .../ui/screens/practice/CardViewModel.kt | 10 +++--- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt index 40c0b407..1a8b4167 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PracticeRepository.kt @@ -16,8 +16,7 @@ data class DeckNotEmpty( interface PracticeRepository { val currentDeckId: Long - - suspend fun getNextMaterial(): Material + suspend fun getNextMaterial(): Material? suspend fun getCurrDeck(): Deck suspend fun getAllDecks(): List } @@ -25,6 +24,7 @@ interface PracticeRepository { interface MutablePracticeRepository : PracticeRepository { override var currentDeckId: Long + suspend fun getNextMaterialNotNull(): Material } class PracticeRepositoryImpl( @@ -48,26 +48,32 @@ class PracticeRepositoryImpl( } } + override suspend fun getNextMaterial(): Material? = + if (preferenceRepository.practiceUseOnlyKnownMaterials) { + cardDao.getRandomKnownMaterialFromDeck( + preferenceRepository.currentUserId, + currentDeckId + ) + } else { + cardDao.getRandomMaterialFromDeck(currentDeckId) + } + /** * Deck changes automatically if `use only known materials` enabled * and previous `currentDeck` became not available. */ - override suspend fun getNextMaterial(): Material = + override suspend fun getNextMaterialNotNull(): Material = getNextMaterial() ?: run { if (preferenceRepository.practiceUseOnlyKnownMaterials) { - cardDao.getRandomKnownMaterialFromDeck( - preferenceRepository.currentUserId, currentDeckId - ) ?: run { - currentDeckId = ALL_CARDS_DECK_ID - getNextMaterial() - } + currentDeckId = ALL_CARDS_DECK_ID + getNextMaterial() + ?: error("Some materials are expected to be added as known by default") } else { - cardDao - .getRandomMaterialFromDeck(currentDeckId) - ?: error( - "Material is expected to be prepopulated and " + - "user should not have possibilities to access empty deck" - ) + error( + "Materials are expected to be prepopulated and " + + "user should not have possibilities to access empty deck" + ) } + } override suspend fun getCurrDeck(): Deck = deckDao.getDeck(currentDeckId) ?: error("Current deck should always exist") diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt index 1565c9cc..c6ecbe41 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardViewModel.kt @@ -4,7 +4,7 @@ import android.app.Application import androidx.lifecycle.* import com.github.braillesystems.learnbraille.data.entities.BrailleDots import com.github.braillesystems.learnbraille.data.entities.Symbol -import com.github.braillesystems.learnbraille.data.repository.PracticeRepository +import com.github.braillesystems.learnbraille.data.repository.MutablePracticeRepository import com.github.braillesystems.learnbraille.ui.screens.DotsChecker import com.github.braillesystems.learnbraille.ui.screens.MutableDotsChecker import com.github.braillesystems.learnbraille.utils.scope @@ -13,7 +13,7 @@ import kotlinx.coroutines.launch import timber.log.Timber class CardViewModelFactory( - private val practiceRepository: PracticeRepository, + private val practiceRepository: MutablePracticeRepository, private val application: Application, private val getEnteredDots: () -> BrailleDots ) : ViewModelProvider.Factory { @@ -28,7 +28,7 @@ class CardViewModelFactory( } class CardViewModel( - private val practiceRepository: PracticeRepository, + private val practiceRepository: MutablePracticeRepository, application: Application, private val getEnteredDots: () -> BrailleDots, private val dotsChecker: MutableDotsChecker = MutableDotsChecker.create() @@ -49,7 +49,7 @@ class CardViewModel( private var expectedDots: BrailleDots? = null - val job = Job() + private val job = Job() private val uiScope = scope(job) init { @@ -77,7 +77,7 @@ class CardViewModel( } private fun initializeCard(firstTime: Boolean = false) = uiScope.launch { - val material = practiceRepository.getNextMaterial() + val material = practiceRepository.getNextMaterialNotNull() require(material.data is Symbol) material.data.apply { _symbol.value = char.toString() From 0a1da22e766439a4e2464c1823e0b9c363bf2f49 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Fri, 22 May 2020 00:00:58 +0400 Subject: [PATCH 44/63] Remove greeting fragment --- .../ui/screens/greeting/GreetingFragment.kt | 29 -------------- .../src/main/res/layout/fragment_greeting.xml | 38 ------------------- 2 files changed, 67 deletions(-) delete mode 100644 android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/greeting/GreetingFragment.kt delete mode 100644 android/app/src/main/res/layout/fragment_greeting.xml diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/greeting/GreetingFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/greeting/GreetingFragment.kt deleted file mode 100644 index e5f3cfaf..00000000 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/greeting/GreetingFragment.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.braillesystems.learnbraille.ui.screens.greeting - -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.FragmentGreetingBinding -import com.github.braillesystems.learnbraille.utils.updateTitle - -// TODO decide what to do with this functionality -class GreetingFragment : Fragment() { - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ) = DataBindingUtil.inflate( - inflater, - R.layout.fragment_greeting, - container, - false - ).apply { - - updateTitle(getString(R.string.greeting_title)) - - }.root -} diff --git a/android/app/src/main/res/layout/fragment_greeting.xml b/android/app/src/main/res/layout/fragment_greeting.xml deleted file mode 100644 index 76f23ec6..00000000 --- a/android/app/src/main/res/layout/fragment_greeting.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - From 4270ce37a76b03c2e8b4ee44b0fb2994b38f6cbf Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Fri, 22 May 2020 00:09:44 +0400 Subject: [PATCH 45/63] Make back buttons enabled by default --- android/app/src/main/res/xml/settings_hierarchy.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/res/xml/settings_hierarchy.xml b/android/app/src/main/res/xml/settings_hierarchy.xml index c0de950b..80014510 100644 --- a/android/app/src/main/res/xml/settings_hierarchy.xml +++ b/android/app/src/main/res/xml/settings_hierarchy.xml @@ -58,7 +58,7 @@ app:iconSpaceReserved="false" /> Date: Thu, 21 May 2020 23:53:30 +0300 Subject: [PATCH 46/63] 7.10 add universal icon --- android/app/src/main/AndroidManifest.xml | 4 ++-- android/app/src/main/icon_univ-playstore.png | Bin 0 -> 73711 bytes .../main/res/mipmap-anydpi-v26/icon_univ.xml | 5 +++++ .../res/mipmap-anydpi-v26/icon_univ_round.xml | 5 +++++ .../app/src/main/res/mipmap-hdpi/icon_univ.png | Bin 0 -> 3769 bytes .../res/mipmap-hdpi/icon_univ_foreground.png | Bin 0 -> 6388 bytes .../main/res/mipmap-hdpi/icon_univ_round.png | Bin 0 -> 5957 bytes .../app/src/main/res/mipmap-mdpi/icon_univ.png | Bin 0 -> 2411 bytes .../res/mipmap-mdpi/icon_univ_foreground.png | Bin 0 -> 3805 bytes .../main/res/mipmap-mdpi/icon_univ_round.png | Bin 0 -> 3645 bytes .../app/src/main/res/mipmap-xhdpi/icon_univ.png | Bin 0 -> 5285 bytes .../res/mipmap-xhdpi/icon_univ_foreground.png | Bin 0 -> 9657 bytes .../main/res/mipmap-xhdpi/icon_univ_round.png | Bin 0 -> 8438 bytes .../src/main/res/mipmap-xxhdpi/icon_univ.png | Bin 0 -> 8412 bytes .../res/mipmap-xxhdpi/icon_univ_foreground.png | Bin 0 -> 17655 bytes .../main/res/mipmap-xxhdpi/icon_univ_round.png | Bin 0 -> 13789 bytes .../src/main/res/mipmap-xxxhdpi/icon_univ.png | Bin 0 -> 12404 bytes .../res/mipmap-xxxhdpi/icon_univ_foreground.png | Bin 0 -> 27489 bytes .../main/res/mipmap-xxxhdpi/icon_univ_round.png | Bin 0 -> 20325 bytes .../main/res/values/icon_univ_background.xml | 4 ++++ 20 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 android/app/src/main/icon_univ-playstore.png create mode 100644 android/app/src/main/res/mipmap-anydpi-v26/icon_univ.xml create mode 100644 android/app/src/main/res/mipmap-anydpi-v26/icon_univ_round.xml create mode 100644 android/app/src/main/res/mipmap-hdpi/icon_univ.png create mode 100644 android/app/src/main/res/mipmap-hdpi/icon_univ_foreground.png create mode 100644 android/app/src/main/res/mipmap-hdpi/icon_univ_round.png create mode 100644 android/app/src/main/res/mipmap-mdpi/icon_univ.png create mode 100644 android/app/src/main/res/mipmap-mdpi/icon_univ_foreground.png create mode 100644 android/app/src/main/res/mipmap-mdpi/icon_univ_round.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/icon_univ.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/icon_univ_foreground.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/icon_univ_round.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_univ.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_univ_foreground.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/icon_univ_round.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/icon_univ.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/icon_univ_foreground.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/icon_univ_round.png create mode 100644 android/app/src/main/res/values/icon_univ_background.xml diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 05a132ca..18285f0c 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,9 +8,9 @@ diff --git a/android/app/src/main/icon_univ-playstore.png b/android/app/src/main/icon_univ-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..c479e78b2ff3ca2bd06b99028ac079819cdf9f38 GIT binary patch literal 73711 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-anydpi-v26/icon_univ.xml b/android/app/src/main/res/mipmap-anydpi-v26/icon_univ.xml new file mode 100644 index 00000000..64686c50 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/icon_univ.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/icon_univ_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/icon_univ_round.xml new file mode 100644 index 00000000..64686c50 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/icon_univ_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/icon_univ.png b/android/app/src/main/res/mipmap-hdpi/icon_univ.png new file mode 100644 index 0000000000000000000000000000000000000000..895942ee9be1677b1c08885acb96ae47611b4670 GIT binary patch literal 3769 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-hdpi/icon_univ_foreground.png b/android/app/src/main/res/mipmap-hdpi/icon_univ_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..0aa91ee08e54e0fa066dc458a6501236ab33b164 GIT binary patch literal 6388 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

ZGSwi@dOl6GkpwBT~Mjfy18-AF;zj9@hE` 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-mdpi/icon_univ.png b/android/app/src/main/res/mipmap-mdpi/icon_univ.png new file mode 100644 index 0000000000000000000000000000000000000000..190ec74c8742f1726939736d449b2d173214e7ee GIT binary patch literal 2411 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-mdpi/icon_univ_foreground.png b/android/app/src/main/res/mipmap-mdpi/icon_univ_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..51f85c15f067696e46c24884d5c1ef17cf9b760a GIT binary patch literal 3805 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 MzKPyD9hbQO1OFCTO8@`> literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-mdpi/icon_univ_round.png b/android/app/src/main/res/mipmap-mdpi/icon_univ_round.png new file mode 100644 index 0000000000000000000000000000000000000000..30c9cfcbbf295d6fdbeedb66d1868d8260bf79bd GIT binary patch literal 3645 zcmV-D4#M$?P)Wes{i^Z~im$pYOhG*>3hD64FUlhb8Y! zi{S6|re~R}&u06-eIzxs@#fU2#0cD0Gp(s;Ip5(n~UM;6O=COqAHzSjn9`w~a!D z3d!Af-!1RG_nut5cu{`&pHaNW+E=rCqyr zvUu@gIehr=B{eL=ffjOy-?S0l2UXx=ch{XAHwc_MbxQj8?=LxX<`f<&TC}M2>C;D6 zu3Txmy?_6H*|TSl?A*Cix^?R&;o;#n&wo^`SW#Yh;e|{`wymSyS@MUC@MZ%Qw!%@~ z#jh>!{rBHXmo8mQshF4;89H>R95`@5PM$m|AAkI@y!z^^vSGsp`>sKQ1`-+?DiNU( zQaZY{G%40pT9;@oH4E01=*Soadx%6wN6W^I8^!U2t#jeRg_Wv8$nP@38$7D;V<_(^ z?o(caQbvv(DPdt@29ZUJ7Ri}2XJq~Q^=4Gbk|oV6bLPyE0RsluyztO)=}@|(%&GIR zq}5+$V{yt78CqqS6v|P^JaOlpcgm3?N3v|zxpU{zJVu1vhymfw&JS~U-m5(0`RAXv zU5|)}kY}EGMl{>VgAYC^k&%(62+Fr?*;2M`+a^&_Q4$&wD$R?xkjGNee9C)Aze@eh zK%U17Zk*}*83esTb%a;9>*jgIUu`#Fy|8hxDfQ~rGf4F8*;5W3IwYMtceY*5moK06 z>eWlq)6?yH#kCm}9T_eEs6IBJ@{B1pr~14=PSF6)K0h^@& zee}^sraaKux^=6}ojccdE>E63^7PYB<8lk)>C>mnpxny{i;Xw`PQ?ajvg@j4=?Ac}0rcM4;I}<43dG*5Jz)*6? zl-|gxyT8w|{F1-&2mvBYC5XX3xu2r7W5*8j2>BmHCQO(h6u12O^IL65Dwrg5>;5x~ z;!El;mGL#kOMLEllSyqrrvJL=!lvp5?SJjH*P^{TUtxs$(W2DTv-?*GqJolTcpOiB z_0?CBD_1VVKG1sVsi*8-N=k|xJ$lrNTxx2nFSx4}sICD!K&IB3F5^`3E@it&Tn?ZU zVhKT&L)l>5DMMpHzWnmb-d>%vc{#bJ$MwOm&j2!AyLL5YCQh6vUw{3z&wyRKcF7Y@ zJYn~mHf?H9s8OSa88&R#Fki7F+eC#$No45d?8JA)ix)TSlhG)Sf!20)Qm<}wW5INH zn~>wjk0J`GZf+^iF4bxZ%=faqil3X{G<5l z?Z7897zI`%EyD~NG{{nCj~+cNcnMY(b`5COteNp6uB%q9YR2&#Rr%9TKb1G%eAD8E z|B*|!B2MnQ>#m?4aA1@PjFg@XLTA(6^?+YSpS4QSLdx5d^JF zOc@^x$mMUp{kCN&0{5MF-jVX<%lit|Bab{{9-|nt264o*9XoauMZ$FrP?#O7Fr#Zp zp8?5h`jt_roD5_@ty;BAc-^?$WnsO=0V^p533`kdiWMs+PU*Lxe&dZdEDPZY*8pHf z-iKy;t2>E-T5~-E6lSNYEa+CjXF&B8y%GW$0RBsqC}E1Vify^@l0A;niV%XHSSZjF@UCrRDB}`RGQf#@k$J&-5;+H9t-3FAaL0O zb=3ol0va&y_W4pZe^r}DYe@$-ZrnJ74%L;cgmEaWmDDKnv}w~UIZ(~11HF6qwpu`% zyq*E--Q#7)wJYN@Abw!|yt-=vgV-f!c_Q-2$f_d)?0%ZuU+#XZu;7}M_~esM8eCAC z!Sh9m6fwo|%)9TtYmE*DksvBms9;7C8z)YjxUKTF5>3++)?7IQa3$A7%Xb z@xJDtXMkt6Y?rUu0F3(JgAW37B3HM=hYzRg{G47Q7p@EcD_8?VfudNkC^;W~4++Z? zna30$C9*JzyJ?X4A*Z`R$^XELREmt-au$}B@vWp^#s1PmJxG5X85(J8)TvX)s(!FS zrknb2)j7g+cc?^&2HIekfBVx3jDXv8=+MCe(OZcrP^8*#zx~!~0CVW7sJ^>*?=}y9 z_St6!8+YfE3M9$Q+Ow~MVwW;qBuDt=NS>AYH_aSo%^|M;|>Rv(56*`?;-L@$hodyah3oU5Xs+BE3;Q=<#Z03+*Fb+zP zkdR=?yQdbMI9RW6TT0M$%;;FUqs@aueeuN?SJCNx`}S?s`!QKs0XGgbM-Op=Cku># z0nnH_b*fca(qnRRvMmHGFl5Dw6$tkk<1A`}&Wsk*m}>E6*EFDw7DVRJOK>pY$jQ{r z&qTID8Tf=}J=%BLGp)MC)kXXLTS|Bh=bIdn(DnfrlpQf*gcUln2Skm=1jgE?O&g=y zoLX>vNP48ovQk29f-Fc`7*KgA2?t+pAK}spx24qVbD(LssgDLsvHb}jW|$r#p2 zJ6<4&!Fy6tk_l(`p$P0Q5Q2==u3bCm(IJ%K(MKP(`(&E#3Ei(cBYLd<5+#L=te)`9 zi!Z+D3trFePejLn=R=;Oio)7y?FGMMmJ!DM}Gy;eb(9i@5*yo>rZj>4?Qrv)|HA7B+ z?DocFu73UcnXyjoxJ2gBH7`t>G^rE1sr-l=Ia-WW4l|8izLl`dV{;sV9kd9oKq&@aH5$Wjvy0aE*{%+V@E3j1>;3|bdI-Erb+z~B6Cm39R^UXI0 z6n^QA@C|j6o^TVy}e|kLd_~SXK>AT+;ir{ zInPA=W8MN||8!9v~de23iO+?ga~Ld48u?pkL}GIQtNnf;%$_rL$W?;Srs-{#wV zn{V@NzRkD!Hs9vkJZO`wfWERe&^OiwVv93ocgIP6+nKjZ=7y|F*5An=BNABf7_P=GYHUi2T*yQlvfB!Ap zw{Oob8vqLERs4$aLF(X}Kp43H?Js-y@L^FaQhxsVXZhuqU*z}Sf7j3b+n>v_&F>CvNyj2}N3|2IpS@;22&)ecrvJa3yw{`yNF^PgNP z761zTgV!vZ)&bj<1poo9?z*bgnF0URYStXVU;;DQTe z^XAQZmOt{yBa)e!DU~Z%E)uZ7z(8r;y0xrXvqlacJXqEM92~S@dHF7ad zTwI(iS+Yb2Vf=V;$9V6(_w-7@6}h-4Kb})Vy>{YnS0G|yVzkSuZ~(mW$}7^aVMCiS zs#dKk*Ijp=eE{Kai!bEy#~;_eebJ&tdNnY6n>TOnV$`6(AZb;rm0aGq zpNvTyE8|*TD+8MjkPdY_NcAe!Pk>_V-?C*(ea;F40B(mb?K~47dgvh`p|NNv`z>6! zP`iq{b?a(?@ZmFN%+Txx3Tn>$K>t8VsF@&B+Dws^X{#hR<2K3DBVY15=gaE!9GQ_k zL)t{PkpQ)?dHx9#COFRR)c}x4IU5c)^uh}-IA;BqUw$bQCr*^J&N|D*uSZ5k%6H#= zC+pU&lc1oWlRjM;G%YGk7NsmcVQ`1>T}QUsht*)Ngqk&L%IBYd?)?BPUAj~=8taQQ zv!8nEDTk|J6*Otmq)?yE7&vgC2I`!1&apioAJ$YBbXr)f;mt9-!yJjI7GXQa{Q2{} z8vtf*{rdG!`f4?U7cX9{1MMGw{2}w^&2ve-0D@V6?z!j6oH=vkv(G-${@SFPl>;iv z=)^H4Ha=rbMy{M&znd*MlP^GLy%vD1tSp;Lx#*&cWZ%Ahdgc>Yo%uV4Cn$gL!3TOD z>o}|!Tl@%(QL{hWEdUsRX0Ei39Xpmv%J2vP2%536v5qfGw)^tSFYDKyd+s?~_KQ&n zyy!p{4P}Mkqe-4tuU>5fATc6Q2UvFjn3y!#wgO1de*E!A?*!o4XP>prHUcrmL_Tm% z#1P%AnTWp!D13gsdi7-2u3b8dHTO@d)mn0tOK}f?JQ?0%xGfMP^zGfd*ZTlq7GHbq zwKk)X-SXNq&pe~4rS*K{#*O(Rj-X6nwBiDp@z$+dwd7zPt3izhvN(0oDcO%Xax&H^ z)|6^H&VT^}Y_8600Kk2q-I%@|X@S@9@uob$?6%&QpPw%wAt8>}`TM1pUXriA`pOnd zssvV%>yjq8HT!2K&y>*0AvOT=^76cs1;PEHo7NY_zq8cIW^p&Br2Ne{-{|+?YM`Sg zIMe{p{8>?L+qShCFCn72EG|I~a3T*_q~nHjBDizsPVWTZ*I$2?YSpSazHHB)J#|oK zWpwG%#U@4Y9Pe4be!Y#70-6O5gV|cCQY9k`4v-Y(QWk19R2T^PDl^WLtoE~1Cfrsf zZuSHZid)*6LAeJ2pKkhj3jjFx*=L_^%L^=t_<;u=uo>7)k}wqINWe1#J4TJG5hp{M z57mj*^0bw*O0lCQDND4=>D{QeL4ZJ@?#m zj}5eC%a%D9(a4b_HF&rq(@mK`)DDi6#$oZ&EWEi2(ha3Xl^TUAqA00oPAH;$9E1Bf_;Y90N`N?z``n z%P+rNmmIAez!?DQ0KAuyq?x%|fC1)PZ@r~EB;w=aHKStH0WOaObfgCA!cbs7!-Ee# zD0S-8(Z{;@;)@*w!0Q0Ocf(+49ho#~l2$bP_wO%b#*ERfgjt9|cI?>U5X0`f?>?Ig zvWo6z9RVX`*Raps2~evmicd{a|f z3#4zq{Z@Y_>sWM8cvTQ)R^pdS+Sj9HWd#I583Q6Yv}x1E@fyup0D#-Eo}(NIeFOk2 zV`kN`%B;^of(1Cd&P1@dK1r8Zm%s&j4FFI&7)DVu-rp*c7ZEU-*+|f+p;pnJ7(y^N zGvKC8o4NqNKzHJEY~Q}!W@G@c+z>9&baA*uuP8~j+Sf^zu%K|4{X2B%pbbbc#<;n;xf+0W?b^8rcg_He9Xr-xWD@`wmjwWy z)0`m0rP2s-00eg{&=v&%OvnWN$}6w*d?|t;XEF(7L)WCGB&YgdP#XJwh^L0>9u!*00%05@Us>e|&JWp0Q6 zx>+X7mkF&WNMOLpSb)o61(;d&qmMqSt72B>`o!VZ_<)arYSYSK%^y$+bl7tBi z7jRgLC!m=CSjn5m0)SgE8Ant|R6%vhy%o?kwyVuBh-CQfhaY}e_YPV00tV_O@&#wmh2vQ|iEwFfkJ(*bZz6%?MGBq>Xq*ai2_^*Xc8b z=6W;$%u#K2%qrSlVHsSQ}4iP?^;A)zCf zM@29uB9IGAH$);v7xF>{LT-^DsF|;Fh4gyqwn90xdCHV2 z+9Xys6e4XrQn;Y}n~fgYm%ch;XQ=7k*Yv2rxhKyf|( z#mzvv6iAXR9`6n}$5ruu=VL~P#Hf`vNam{*m|LJptx>LTK}xpD)(1;a_K?W)LiYu-t_L5O^5ERKqt^OPe<`nO7I~evBSdc^p3U^5-M&nK%WlD5c)ld(9D@L^-3u0eZ&JYR&LzU zty?$U5kV1#gbMD@6*tU$W+}Zu<~3uJk|w(iz^aVZ5*-|E>(nbr94L1H;BL@k)~;Qv zgR$jDNtDQF%~*hl2Cqj@BMuamjlxKF@7}HHAuf%=KSdfqB1i*_Ben!qmH)K+kJ|vO zNM9jQ!BI8<9((LD&jJ8rbB!-P97B^3p_E!OFohe~O<`9<(n13LqbG=-O9DZ`@x!1tRy|5m>bdW+S2$s?ek^_yMO=D`25 zGAN{@Wa6r_v$Hk(A+XP$JzFMEo~&C9DcG1g6kzsg+~<^aXlF^}GW8xaF{9*JlD747 z=Yr@`z}1+#5QCX-R=1epT(}Jo&ij^hkoCf@oUcJc4qukO)Qt86`Bt8xCa{7(U$ zoB%(J&QFF+0!RM9dkDTTF3OD7-Bp~gn8BEcZqynEG#Mx(Ta1)p&4){mh8OG0nJNXi zCVT)-7VlLTYBNJA`#Im13?ORPuB|VSAbZBhmO)DcHm=B|q^256y@5=Zu8X2p6mmB) z#VhX>L0rH<7~5=NF>?aIASlx=PH`Q!M0}w84T+k}nw}nHZzv+SIUok0C*6GW&3d)4 zLYyz)JJn!faLqC_+3RoEM28!9zoMXm!AsJ%PUcKtBm*XcrVtNL2T04%?1zp-Y=LcY zT^XgwqFUs6A6YfUc`|OVy-2UPE9jZkx7>1z)^dmmC6%v>8U*aiF1t);;G6>>AOc+G z3a#WdZb^VUM57|3E^Sw|#{z(lM@ArsTG9hj1A&$H6Ot}EqLHAfjxpnz{mgh)0)xMK zj@Q_SeYrO%?g(wjj3FGGOP0)TiGSk8tsVgY(lcFXxCO*CG=6j=W~#FbU|)haS`Sq+ zcsVhI!QaFIv?2U{VXftW0GlC6gU&)z-C;Lg2>=FV#uny_nf1sG1Xl|DtPXd}lGg2t z0S&iGCv?E1u*2YHe{|*1A&2W? zmeaywwkn!8s(BNnFdz>?H$bmb!b}fuaV1n%g2!g@gM3(|1qj=G|orK>k&2&eYx0nVGO7>buAd zxD@ml5;A5l7ibeZ;Qz27f;fW&irfI{n0_9TJfv^#wn7{z?foB0y8?z}PfRU409N#E zp$@+H-}nIs(_Imep^;qK2z|9GVhl>1bVWctVN#{N-9T9Ypg!ZTeP@nIK5YOBetsh` zv~7H(Vicd1m!B{xuLIj)BQ+7W# z4DLdEk7FB>y!GqX-;K=*Z0A=N0EG7G)2IEnY9nBBB6ZRuECzMr3O%p^KVyYWu+64T zn|5{U){OvMJu$0q6~8jr{3A0H`9+|ZsZ*!+S1Vzcbu%U3c4EWr&{(!{ z(tD*>$98^Yv{h0(y|#Lc&&k=YDLl zZr!>EyL9PtJ~mPtKjc=XzbOflLG_!}kLCnwB-MnyRiu&+g0SA);^U0GZI7*6gnyBaWzYF~(>K;YQx&Pz|(`O|) z8ExO0{Y;Di?Fr&PED51h1~$Y7VU=i44G5~gKeN=}D5veC{|=5eB$k;Y71Br)VqYCjQNfR}c{qRqB`O znVM$Rx-6Q0mAKGP74^tB_L|T+IqJae#|=7qF8hJ$8+?#pZ@+#qIq>$P7yJS%S3O%Y z9Kgkw=h3z5M*h~b(Ad3~>15g}w)gkry$kB|b3$n0$zIKHACT09(d3u6pqxFs<_iX+ z3e%Rgo85{OJyXBDK_|K-pi`td;2`*Jm4?|+Eloh_7?ZNg`zyd<-$`=}Y3slZ|+-_Rg8xsPW?jdrgcsYKdRb zgHSuzUusY@W;#B%n4^H}w<)&BcDdkRAy9zP-@mKRM)^ zh)eCa2Fu`j&I&Q4L$JQoidD8l#48!s+QZmrAI~d-B}x|`XBQuIhr~|@N?jBr;AN&Q zGa17uC#)>ZuBQ$>)7T!%Q(1d~iI0?z?{&nyJBpA>bX)ynlc|qzTxj+?K{=%XMm}1< zsmU(NE$fL$NaPLUtPc-zL)#%OWm6iLP|c=nUtLpgt0v5 zF)X+SBe z^kb`s2+z67zPBMu0&=1@IbS_sg>bCg9+#~qDW1Be?ZCk z<=^@ofU+VyrBY?pFXYC*W8*~`M|EH9ji@Y$-l-x#RBK#lPb#bVwT$0XM5lG?aGtLV zRyTUqWJsL6*`BY`s~(60TaOQC1)TI|(4|%vh7m&T$I6WA6W7vst-_U3f00n;ti7?|2TN^Ez zBZ;7kh0E5d@kUMJoNFX%TPd?3@CuG1%buicmXKesVMh52^jn9j`iYe_}1hMO4W(>K*VXXOpk3Z0?{QvxrByO~)tYVu+&PZG>g=`Q?eN@iel&jr^ zCG6+iSM{R(>7mY9Ydvv_Qq&ISVV|&;u3WQn?B7{WQ_@qpede?LsqORAla$|gz<=(8 zZH=u3!+HTEYn;Z^e=_{z})(%J~)YDB{7Gth!W4I zGkXRXOtAJ_%nMaw5;=~svn8{~n-pwn@oB%;X%hDD6JW*1Iw>NtCffnrg~!*@8mI$H zzq+1c<}FVX+G72zBf71d%E#C+6mF}lsY&lM=N1vL!JiF7jWUIU|7FS|#x&~C2e8F> z`F=4?T-fG|xix7Cdr6*NLDoiwoj+Ccze56WAB1#`I8$34Ob09<>lg>CWlzt1Z>o~S1MaxCnL_l~~=u4gk zCiuhO2;X#%>uF599aBmQpw`3dhktx#SsZ;IzvHU1>Y6L z265Iq&Dsileb~H0CD_HIPw(GuGx}ffR!9rkiGitz(A24Ux`=9>Xy|Dkjv|s!$ywMQ zOLV6&rxda^Y2+fohzZMW;~SKJQX7(6(NQ|SC7&hcF<{&SW-378D-E16r-td1a*2+((}0z%DU{^Q{m5hJ_50I+N@~H!AaHoD%Ns z6J_3ED5`k)cWk9kh(6}T^lyaZ8D0H!-KjK^-^z#Nqs7nghKZ=%25JVT;p0zk&6-7t z1OO()kmT*2Vi`kLT*_M5e$bGu1>~!fZLz1MTJi&9w2DT%M;V}1Zs+wyfZ#e3OYk9{ z7n0Xzt+)@%R4hkTCwYw1ol%5kkRI;Vb9(&D?z&sI9vtwfY+ZL}z@Kcge&4(&txfdQ zsV%LUw|#Xdx4Gq|+w^3EDL$$#6KA1~lGvMHmJjz z7Jytsd^ESy-4qkZqo;2518co&K?!-q%^T*p`X+xcaJUVse|RRG9g4RV!8Zk+=9_dV zxsuF_5O8lBwEHeIX%mi(E&LpFDl{XhS*xr0#k&pRGUOngYa|f=(}d=#-qBIO%slpBtDLfbg1>iQDb|^88gs_Fm5qul0T`wN<-`k0ddV))BoxS)|`AiNfthdZ% z^OkO#5$L13YA%W9ur`(|$0kNAll$*x{Xm`_V;S0%>iOYFeNtBCf;8)7C8C*|dqO>zHu~aVUJOoSX*|<4tKybzDSFs&x|X(D zktCMCz?80B*OU48AbywGlm#Ns8NdBJU!~rAet_gJ*=DDvaFpdt$16>)K_V@ZbrA$> z;+UL+kv8IVlsouZWiypk1lH;G2a93)mE_S=4=LNX55T)^_28Ln89XeFxy^1YLZc=y zS`;RJl zy}Tu5{|yXG2m?4Yk7Y1##bpN)Bz$O$fplqHCrQ|%9b^ADHG-vj zaGLO0^Vyh6%#cF)i|xR%+vOep+ji2{5!b~oQnR_OP3(EB{0C7|ON!_0B)tXaS?m8Hw}N(rt(TY#TG(|s-b|OmysSq7?XpV_ z<^9~h=dKNDpP5X7_}v;##E(L9PlItibk1UHd#^ZGn>VeWbYZY$j6~MnWnyrkx)^== z+~p6$lSf+FH;JN-0c#92ybkWw0dhhI7+mrszD`a~A#Q~?E_)l`D1ld)571 zj*M^X4DgR`MpWa&F&P^aicI!-U5dY!U==3pQ$@*!Di55pRS zuax4~&GX6bV`AA%S6bLEoU{hss_Kp&z9gRP+G8gOUgt1#Xz-aR)|tGWj=w=PLXnoH z%fb`>18a+6g&=>4VaeOle-T*Hze=%i+x4O6Z0}uNOjZ>Sl24z}w^d*TPY>HEwnist-)dO4?Q-n4h&i z-0a3s2$dOq7%j42KU=>So;TxKKHICo8S^D2RvQRdD-M2hWB@>0(%(90T36?}I*>5pu> zk;#=OC%E|1zKdBS;_ATKXc}+~M5WHXN8j@aJ)6DhszG=VZAg8<^~uY0r{PZKX>S5X zGU}+hO>4@X?obRudO~c`t;^m&Oo{xq zGl$=c?bn90#R2pQ7!dwX_?(rWqw|+wPQgc*AdZOT*WmlS_eS@4_4&uV~;I`;A-; zOJFEQ_`>MLgVIr`K}92lhF>|uencUfSUXyXF~Brej;+^5^F)au^ZJy( zwP-iJo*4{BoBdp`>Q8qcQ>9J_ z-{;{1TgdH13>HQ}zU&6kc30n1#w}y_OIEjdsiny%=b<@vpyf!0rK%v%7d&rhs-C&h+bf)7D~Ya1w3v@jk`b30^CYG(QV4i?;O8YY6t00D zE;)jm@@2aNS1O>Vg{a1<0=7k+oNwnynf&b-oupRt1NbK$xGFLQV6b9*)f<__((?8q zzW+~|Vq8HVHbI5hR;Bh-&oDjG_E40^f+s|DGYm+oxY_=m`KF7Cky6y%y2B4zM(@rX z^yt%inyF~VWBcy*OK7h*H_$_>=cw|4GP2;A{)7 z0x^4^jl6>>OKYKfSR1g?r+JksqFH^)zcY$mAMTpB-BBI6#cSo@8_SBA zK}X=NH$|*;*??k?s|y0`2o2w}&>I+(8H+oo62&_}k#-cpCkff0=|84^>q!xw(!bD! z?1nOA7wjAy9Qx=8s6V(&!|^vemh_F|`Y7lIB>w#<`kxH$DIpw|yqLW7YQBS4Qtj{s z;7}QSaX0IR2n*S9bH2;qg5TfQS9&=)KK{m?8#9;^p=j-ECo47mtO=cjxk3~C(;a-A zeQ@?UcD?rwhLz^64?ZmBE=h4@Hn9PeIaOGUe3!vddQoQ^MEt^ux31f1x^g-0($K)b zp(RCTH>e?_GV+2^04%NLFxbSbNPa=aq|d=Z_O)g*Eb%I6_;ri5!u40c>#~xRd46w| zimxr1UR9M@ys@sTb=a-1LR)0I&8N^*F1-KsZ!hk#e9 zTy&D~q33?swxv!oN1-Qm2wz*H1U*(C_1VOA#PUzg1ls8pIM8;WWN@#hsp+P2k%MYb zW7u4dGTnj=?G`y4G`V#Rb46w?>WZO!(;Yl2Zht=>;}f?}ELN>j8M3Ok8Jp(PiKv?b z`*bg_tR<+|Nj{>m4ctajzJ_=y3tf-eoauwjM!xMVUGPs=aHFc>%%)GG3BR{Zs<{_) zbSB>!kz;XIR;DpXO6nH+?=iNHZc9z9tb znP%y_WS2oc#7GQhnaS5;=%SU) z$0?hGLOU}^ll~)%;bNr}XM-@^YutdK`|-6|nL%aM#Mv;x5f~ z$1`A?zsN>`crsmJQ1C5}J8#@GJ@$M%>T)D|EK9H3<-E@(+9=9#RCyl$-d-MNyThPT zH@<$~SDqHBgGe3td-vvaI8ul!5{JS}w766=LhmuKANG)D-F%(|rp>-?IYq2NBjZsx zPcIzRwk_YBiegAzOj{`=TKBYv|r`(xH+fI1V@*qHxH35j*Ak|2u3+})c#r@|KSmD z)Q-N%GNcbR^~#L>)&Z4HiIn>jhYmFaza>gAjtsh*%z5%7e1qVb?Ars`p+q(Ju6L@s zv2wBBs<+Vbu;Z8@D;w!PBc%Aj9YH2Hy-*pJzIF}B+Q~})dK$)5&&~>mjmBsgpT`k^ zd?Bz?M&)yJh~7?@&nS@8Mqn##4|j)dWj4^`Y1j{~VQ-2p9mZ(^loBSC2r{(zBZ?Q(%^ zb^Z*W1<_1K_J;i{iSDIuf3f(SCB>WS51DIT4JT7FFVOz#^Py2aW2~U?nCO4LKD*wx z!ePDhnP6Lg+5nS&I{b7Y9ZscyG5HLA0|s>~us=>)Regu{150$8JE{1~iR)*5ktB#4 zUln1>!3&ACjMbs{advhq%?4IIiK-{#Z)Q@}P751%^2$qfg_1yV@lW$`H0FqVMWw=j zwxN0*I)wNVFR#@fXen$!#fX1O6a)n*L1$fl&rfFxGL8Cj5zrPHL#tk8yPh#3jhhf6 zWOkY#3XJ^M8;Hk>Oj6eBL~ZMVw11IrJ^^hsUgHfIjmQz#aSu>Cv%`RVAkTg8+i=2@<-Gx@E|EPsB+_}s>; z+IB1rt+$E!m6-^A<*P5EjDYhMx^RwW$t#y2!be2(V722;q@t1u1MwmxrWm9B$W zEEH1ZtEO7U(*Y=AJrR&>^(=8Dy1CS4Qx9oxab zTzU+}o7xg^EGtVVB#41hqOf#~6nyCc)* zu;B->m9eVt33D3mc3?a~gJWu>{-=~$?!6^($njHDhJJ3x11EMO1r?FCOZ#`{rI>zv z8Om_#uv3O@n~lxovh!s(GF9TcZq?-3$qJu;PX>0_z-U>!`j&7nqXr0Vw%M@+>%@KtdPYA;7i3Hn1@p3*UE7T3 z?a!zwx%L^{5LGou3_-){gDCP`@k6ZF?ce6sW4kZOM3RczW}~o1!x^mb+o(j)p(b%i zW_&)HlG5apsd43UgaVFAMnZA5Y9&RZ$Bqes&v>Uu1Uq7e2O*9(y0xO_@ zL`#9(HtBG~$z@i7>Ed_{#;EoSqTdjm65Uh}dQFbH3aHgP8+Db);CCC7Wk&P8arm2* zdGfj#$|Bv_e_qitToIm_a7}suO-ire~&Biefg34@p8LJbh-_8O1Z4=Z_?~3dTmZahwck z9jwE|5c@Ck2A>rbFrG-P#51Wt`HyWa9B&3UH}`Ec+o34Ro5O%|$vOf<)P=`+Xr5mPZ7Qa;$+<>5$Tt`I#r-QGCb@(p-uWCmoh@kEhF>^INW$^hNi!8Y=w*9`r@XvEN7XP)@tWA zsd$bRsc-lldq8YZQo{nDo5w->eTUbq5O;0%F42whUkA}dP7QVl@C#da>!$ttxYQyR zT#2(dr4JdOc8Y8jso{&}9(MX^QX9M9H+Vx{;U;hYEud6+iy|wG>HuNOY6An%gZZjh z0QIi{sB5tj@gVz0==jG==)8Mx12JD^f18|uYFRqtiN}T$7iw00NY6^=i(Zv`{AH+)i2=tfhj(9-l5k&Y$B@A3P~>Ng zQsOH-X^XLpj%(C*sPeds{sr^7t#tN09Od3?L`sK)1RQPC*kVizAe0O`AN4aa#gu+? z-P`EL{@^RUHQ%0UtngIKs5&{tVgLf98Y4#rC9X9M5?9eqNm%3a1sx~lhF*Tk-m1;0 ztc(LvDODf)M}7A7%?-NuYvpkM!zDQVCH<1v@rokJm~2j??>S za|G(prLx+~k}Y86%tq|nwc?40DBt)aDm;#Kf~rU?wh# zVq>Kk32gS>pJ5)QrG^u#f@G@%DEV!nyydB4KIKJ&oF+}LRVjDJkIC|mEWb^HR0h`9eDeT;AmfbuPd$FDTU4c#VEMyt`=pNXc= zUagY<3by=2gRM_BZ5Z&6KkA^TDE`#UQ%T%YXUOo^iSn6IT_oayG3sV7+;1uIG_ z`ethtyiejZsv}caHl|DdS9<{pC)*fb4NK)}m}=Q_$q5gC`F4@M6iONky86sB8D&9K z+e=&Dejjy8KdbREFb>8SYA2di@`2>%JMfF%@TROk@9OLsNvD+sS(e4~UMZ2sQ!`56 zDaCqXE;W+;Kw(o$*z)bP(d0!_eyaRy{KMi_fTpOIis>T1uNh48sm6kE{RyZ+7)6T` z=i^#97iimf#d)&-NYy_Vktp?vTs)eu@80~csh(sw*ZbgpDaJg>aBR%Q+x^-4bSH{d z;>g%u3O;a>vu){2q`ZJ{x!LapkizcH&OqHhDqZtMk;1i`|_WE=)y({%K5`+f&y0yaeR>bZ)hb4HUc|tSiAEQhFbzsM1 zn@Gk9yUh^wP}QaloS%D$)R30IRmV>+B=;kx`6n#VuX$SC5Y92J7-5X5X9|^Z%Uu!o zW~?&@De%hK^r!a8hEA!qpadsqjzY94a9hs?a>*fQ28FKzxvK!68CkpeN96^cwvIu| z_|P90VVrOJ2zNZowi`SYB!D0I67ingJU8rh`&%2zV?oG=5>Him%L@ zZE(wBJ90-MAPBPk?{h5tO7#MQA{0DD{x&xhy&z4?5q=a@8Tn)zKUJ(53(2>kEYrxw zbrz>CejI9xpJd*sfh#)~O-$Ywk*q9KVL@bZj3NPlc%jzi93Qjxg6r3nMWx#5ouGTF zK1c~B?`G=QtARp!!gDp_V!+&irbIv3NN6w`6JxaHW>Wd>`m!YaRcQ7O%75JUgMl!6GECFoUqS{s=~4Fk}teaqdJ@UNeg2QcYVGJT72HC4_8a~aj5 zR2`Iuasz65<;D&aTyE&{U%RGtO6R9Ptz5h9RduH=F4GZE;NVXg)oQw_>T;n{5eNq!;d} zQc-nOX*6T7S{@T>*!}b|zkVVWcxiOs)upivEz}4<9Ba>`mGbK+xP>FEF0Ylxg%Ws| zU8RY=KbVr)d+XGe#anO8FW!7Zp&s%(|6?2bgi=V=HGZ8T$X&(Ah*d6O$lviY+R8XZ z#v~bc%UC7jT^T!Nd@17_8Q+@mjrre)W}j8&m`UawoGadSjVN>Nm|vpc{T^KYZX#od zj4~N-%J^D_I(YD4)u^bb2zxk>Gv|84oSSPj31kSr6pN2>{GB4>R2dJ-*zFX!Lzo9- z?01X=v^>L2B z&ycajA$UJ*4mS4h->!$wjhd-z}bLOZQUU)%$_~D0Mlwl3RDuR0+>UfU0!+ti%4Mtn+l%H8L z9&!lY!c4Wo`1im6t=6nrqb|AR64j|wCzX-H&rN z5MK-k^sI*+-pfs2=d${P;Xuv$_3PD;AwyJy1`R?AI4UYi)va4s?+5$fLwWRsa3(fAvp<3}msNTcMn%UcGv%Ns}h(*kg}XciwrY+P!TF# z=VUoH>ag+)_C~M@x!HTI@Z~|mvU-H|;DZmU?CfkUlt4m{j*iwCf99EIs#|WkMLqoR z!|Lg$pH|O5|GXZ5`O9C_V~;(itsVHn2efY8I_UWVjo8>&t%#RjemRiiubK@4U-zwE zy*l==P=a-~1T%7T<$52p8+W_~Hw7;e{8f`1tsMLeI_3)o}6mzyDq1c6H}_@4fd_ z|Ni|Wf-XClO*s4j*Q?~ol}dPa%a$$moig|_Ek8|eYON7|*tGJKPd-uSo_lUU;kRtr zQeAuPwOXM6```ZpQc-3OK7-#G&`}(3OiWB*3y+RfDe);PEg?;%)lE~0afvEADmvi4 zNl8hnyu3V+Z_uoe7aj#sM5T_&sRI!{s; zw;!uc&N)dnPiY=d5%`T~o_Qu%ETWb0?YG~q6*x-!L6zX3nK5z`PkR+U77Y_|TRv0& z`s=S(d-v}3an3l?dGqF}K7IPA)YMdc9PorA=Ksuu2@@vh|G6!`O$60a_qp8+`MqR`{I6Wi?LAkWns=(q44l8O0=swbu9X{13H#v`c*%J4Iup=i2 zmW$ux6?PCP7VPHr{5^N>Tn#DghgD?rz>TfYc$ zlEdMKok}2e08e-0?Ti^SsvZ(0Em^We<>%*zbe30NeYM`-4P{Yc#cdiCsF^)yRx5bt zSk$LX4agp#Vr1qxuk+jA{#G-qV5toGRA4aT(@GENEU?~Q1ayLf=U0R)e4yf@i!SmN zmi!JJ$u!pc?Ao^F-_L@G9IKW4taY^d(juXPNeD|2%W4g*r z%=9ymk3ar6)U#C>f55CAbV!(ybua=ES#q%tz3_ZHNG&vL*39SOx^(HHVZ>6>;lqdf z!Ho@=jymcn%}fwH`!Z92AGbma(+jmVdlfzdpU|UG4?T;V=OnLGO@izx4*5#Q~%oeD)W?otArA zM8db@nC;Kv?}s4{Euzq@_YC0?Z-ZDz4ca~KnMqZ z)>&t%BaS#ioqqc18vk+LZUvmz?t;h_P*SFjZr<0Aj*{vKvsR&@NFZ{H7)iq#J7sfl zlvw`_26{XTA1g<2ZhmcZ^<*U7(1Yc3oTa2tS91P#`-vx>sN)3^Xl}?lsnsAY$Ve1$ zTkqRcv*c!ej(^*>ZC;uI4nR0Q_~3(EkX^(Ium|qjx386)^h;y?65xotM^H&F?cm0tZ~CMz$TP>c;v)A=y-_ zZ5}Fix{x5VVDF1BzStHS+T>j%*dHZ=niHw@Gv0aU9o4vTV;?KM=bn42IDcVbq0a-8 zQntqeGcYAL9c|UBl@8RHCC-_l;Vm{n{J?0BG<1o&wdc)Z{Y`z&MPBK;>#hqc5$LY2 znORU~Wky<~4JU_X5=YzfR#v6JYFhV}wx$U;wZmT->kAM85PadCi z!_h|{t&?;%S46hdoq)?o%uo|MT^*(p%GHhCr>c$(i~O7~+1P*l;~$=%8CfASAs=6e zFDO8kLZ9P2W4}1T>l_hpL|q8v*WDP8L$ygc=j>pR-?7KeoH^4+xCCK;``h1q&QDx` zpnU15?3>b3UDST8nqRyifFdgMK=WkVgrf1PwZK+PRIDHNPn|l|FDV%AKy1uVF_!6` ze1Xkn2yP|IiQbEp`Pe}a<4%-Wt6~PY72uo!IAbTqJCkDw29^XxYmkIFh6kAeab1PXbK$Zb6c5-ui98zTAChq zp2%XqIG&Ru<^9#o05Y7G*SjHT)~s1o$6j;IHGT>}CUaw@kbwhq|@RyQjb6ZIxfJ0uUoh7% z$Z!kh?YG~qa#ytM*hYyGg^-@0z~c_+ybSL1cfb3c`sY9YsWxob zpiVydh0BnN*~6X$EkE3dp#mES>NJfu|s7)g(h zQwWp;gUx^*J$h&bP!CQ^ORGw0EL^xyGZmuH+i$;Jvm`I|VD=?X#q6W5ym|9x)wy$L z?Q^WZif|TaXCSb-Yw_a6*}6r3d5HB6^v*L?#F|d44ftUo?2}G9Nk1GBCDv=xy9kX0 z+Zkt^q4WM&@3d*ts*WY)97qArKKrasY6C$L6WDWZ+_+I63x}uB6G$2OC6q{Afw}|t z$$ilT#&v9IRH$T46i@|2f&{~n?eh1Q$JYt9fC2cC8(CNz)^D99RWO_-c+K>Nni3x% zOtv$yI`~7>_neOc_?`b*1=tcM!5WD?VD0?#&)1!Gg9i`Rzt5gM zTN?zaW}9n5vCiTS3LxI7p$sZ`enFKxk?r!J3s4OFNZ=DE53#;B6o9~~t5Py-+qSJP zCprac+fA4O4?XmdF4Q}#Pwp9j0zUifGi{~m>FGWSu=QK6h2o&6Hk}d5IGoobyXy2; zMBtG+VJ8gnbNlw~Z;IclT)ne=u)h-YIx3kWGJW&qfW6Lxy$+@_{W_$QNuGs+#}qzb zANSsSZ&m0o^v#FD_UhG3XI59OTIHhvpaj3bF(XEd&`bu!Qq3Y?WEDYL1gIhn4#y`2 z?o9YCSg^n+?#Ri>(QK%u3IK*==I(pofd`6^ahLKjaX}Rz)Asic{I7__>aV==%4$2@ zLNO64XUqm*scYA+RohSy3P8CiHjn~FjvVQ8z7tM3K?i(nyW)x~e2f9#LCV11J9TNN z!g1!nSWO`ac_J4voU@U!k#%7Fmz%xXQEv3)fXQ$U8Z;;TdbZ@r}(bgcrQn*?{xQY4P{&wu`NSPF1%!@`|;9*BZdL3w$3H44TYbpjm~i|?C? zjQtlP>UFr9pT^4czax_9mm4LK=8k%Ga1$M)(i75Er;N^OVD>;YwDpRK?ueDTE>eH1{u3Pl?3W7McoK@~7@;zYk(k2BSP zvvw+g_C{u0O^FC8Y8z)C`0w~FrKAq>l4>42OIzWx?~> z`S*+>M4kaB4{L$rH)_<#Ck`Np;(lOI&SP2ZVHE(zJGVhqEe%D%6knX%iCwCTCA?5k zVRm~)mY&}(m;$2P-8-tS?CdE9fVaCl(|Tm}Pz!V(-n#-QC)Q7>??W=<~wy|!&d9UQ5tc9Zhw~wG&7pOZsQUwT6**E5vD?0lz zp(+L3KCFWr@Rb(@kOp=;PIB#$b|&DM+cj+Gb57uf?4B+0^t4b#@NYSOKa2`(z9_d zA6kl)zxwK{DiVWmDu7I>lO|IPvMdKdj~_qYuOJ_3MJ$*CM%{d6Ph=fX0Y@$vTIdym z$e;Ug{-{_rwa3&ND4<*&*|5-W22jMYiM?R=!mMzwkRcNbb7DJb31_MSKVsnt1-O+* zLqG&;k3&Mx*;6j-8dL!V(|_Ma9@3K&CRp-?plCI*^HmWmfL8I;_*9>Bg4I|tna~Fw zcp#t>P!5aooDBfL4biWK5xOyKln!mCz;EG;ecbB|C;-)_0(R}%wP(oq5gmgnpy{Z- z&4gua^Fl*et>O;82IZU-QFk)OjFzCx9TmgnO(3IjnM&0YPduS#1%tFQ3;5hCA#u{{ z)~&0$S8_-&9U^H2Z$%w(aOUcu*d)_RR-X24c~vh0W51 zVR#C}$iXK`_*V+FI;Q&>eV{$W77|$5{PEqK_G+F z1%a0n!?AK`CutL632Fkw4YWEkSTKn2DWlrDMJ>IbLd$aV=FJ;2vNIYXYu}W#sRPSM z6*~8vmsA8PUZPcBKMA>#;<*b~^KpZgVpdeEVvp`RRciwr2R7AR#b*WD(LKINimno4f)^V2?1z=U= zP~6Q?bR7)HK2go;H9Mpd$PZ2JHdS?(*daDL&QB2Z2H95&uzmo`4kkFZQ8S>b=3Lc6 zN*U$R_84pxWw6vZ=?a{;b>tDiPgOE*?b@{u$$3+>tlhnwSEa<&R-P&`e8q#`*PCvMDA4@MODXC&9 zcD5~BwoC_J)NG)-DjL6=97=7=CSq!|zt`L4U}8jXw# zO9#gXtBA$VojbRSIIb_emL!>CSfK6K32sqQ)wYOGcwzZbO|X|z;=q9e1D-!wXl~cc zu4-i4QL41-4QhI~8G1~S0CIT2IjSh5li!L->swer=p{I_6jbhK?`4-=R)UNJ&H#f+ zY{(GjwLX%XFj!4`z`YXE%WGx`?3}G2P)90bUKK!QGthdoR;jJjxc1{D-dJpW(-Mb2 zqGwcMk>UkvSivxrUeCXt4b*yR*0M4by8N~r-&D)EG{`$l^Tzd0kRJ_p?ATErNmm34 z0JHH4q<$%AgyZyZo)k_fs@Nb421E7|ZVv{tj)0{?@OtXSwlCDawtygwg%sQaWlUDO z`^~IIy>)MBIIN6ub?0kThHKR%=YdIisD@Q=ud7zAq8ip<+`MrKLHmO8DmNwzo)35U zfrDPUA#S+g20cv?23#5jL9~qJ(B^@kpcoE+XZ1UkH2epOvycOPz|Y-n?LYv>6O7TK zLZ*`e^TB6ab=6h+9{A}Sn?&hu>05iw^1OCeQWj$EuwP>qL0@@!`B-FI zdRJ-P;H`<3!9hK-wj;m&_S+95fg!S%)P{i)ESTsO%*1&CB}`x1{jk_zSV}ZX9^XrQ zA%mAPp-4Eq&kT!8Qe)bkA3=1!L@nquUo}f<>NjJPLzyD1WzKgZco|GNygrd6&9jseX;l#xpwkBSJwMElU`jBoL0ft#?(c*K0xg)6 z8~fX&m`;}2S4^RX3Q8`Co%=+W|&zJR+AP&uvUt7BMckKavs)p(Wm2PM!*M5 z2L}K4!aXSA`OFIf7A456-htA-42LGqB z#SCF4xt;gXO^F!X?cFn=YY z_rfe>*;Od($FP#E%Pd7_9j z)K#6YQB%83)8o1>lhrw7LK_z8C9Q#kPjnyRC4RQ>&NE+*%tZL95nI3G6D!k>GFqZmx$vVnH`cpWJbRkB>-5 z(CgChUDd4-b}YmuGMU@z+H<0Up^wCFGAQNO` zmZBcAZ`z2?%gakOrM33LOr9krfCCCw304SLjk7ZiD+OmsQ?p!@>?d6j_za?4=q%AN z7K&iOW(L3WKbsXL#lfn3aGUx7l-AwK$lo~3AYa@NR-uJwCI#~J(@#Gyvgm+JjBF11 zH*Ca+qtX6A1W>eo{rXqDD1m^B^Z~-8B1R_~^fB05((TK+$@QZgqG_y`@+1Sg$!d7o z4XH0U*AWCG1N4jHTWD|bx)9q6pV==IdY#Ci3$idW`9Z!}L$9WkKqo_fW{YKZS-W=a z8Vg6Ed;%6ng3SprSTSwl^kVWFN-!G+1M{gZN@}5w=3Jb#jgq0LTr=G37|gFQYkpsq zS=SvI7+J&~O6Uos1nZP@WOR~1=sD|{xzRvYh?PSzL-B1Og_wb~HOeB`BF$}=CwYN6 zN}IfVo3+D4#_pJa`+ypUf%ndr@zD|50XDFMsI>A_G!DI!?+1Ok%`bsD*gB~CPI<}G63-rcycj^L0R2>9ao^RH8 zk>AJR^2&i$=Go(a4B`7IL7Y#9nF;OhzWeT(GTpxeyG_LGHya3$U?LPxXdC2}08qn2 zMMvHIo`f2)M5ulvYxTyrK}o+WD=UL)w&z*RGykZ<_kkm^x+8>vpoBu1)}v(lZv`tu z%?2C2A_$x*g?p@+ns03@1B>T9K(x(3HABIU)9QH!Ar9Ndj~`EvRmd~)tUng)gj7O< z1`XUo`SXg}SOyeH-@eIZv3#iVTReq1z5DLFyJf~K;-0y8o`GlKnSO{Mt5zY2 z#R%eHTbS9^Jaq@K7ql2>qaenf-<`Yih|oEnBwi?&9L& z{>IU^Hus)#xI#|2kW{x(qedAmTeieO=QE|n8TJz^n=8}&J$!_Tq^&Y440EzS4_3Dh zAlSMA19Mg9vJhk6^AD}$9`3l~j(Od>bvwbpO+NR^JsYc!`6&wDFLDMyWjbexgKT9K z(O$TE_eql`U9xiJ%E#V*`|TYFgdB~X8TNzV45dBb`dEMR{Xb+M_H_!|S~`j*PLXSE z=bBtw?xBaV_NQ6(zXeQj+&Wd&jMn<=O{rU}*c;b?!OP9`i{PD*h7Phl`{rdH93RZux zapT61w`|$+sYJ1#Gx%K;_A%SoM;OuSC!ToXA%T`z*Is+=#hiPAq0FFv99U)|IRyaFNlx@lmad+8)>8=xzgh3@AB? z!SDP}ww17t*|*4GdOLHjJkHHEk}L5&*<3s3=LR*AC?eWmZi0q^`t=(mWoI<1*Cf4p z-9~BIb?Vn;hU6N^$~OXKv~`REv#qt>SB~LW&cV4jr-7IRbFJuKy5Pf62)UMeT5u9& z*J+lTUZ+V)!=#+dEQIPa=1Nu07*qoM6N<$g86b-=l}o! literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_univ.png b/android/app/src/main/res/mipmap-xxhdpi/icon_univ.png new file mode 100644 index 0000000000000000000000000000000000000000..cf2d07c7f9edb7268ca375f1b2fe95973097a51b GIT binary patch literal 8412 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/icon_univ_foreground.png b/android/app/src/main/res/mipmap-xxhdpi/icon_univ_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..998d64cd5416b40b996e542d03e5231bf6604af3 GIT binary patch literal 17655 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≧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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/icon_univ_round.png b/android/app/src/main/res/mipmap-xxxhdpi/icon_univ_round.png new file mode 100644 index 0000000000000000000000000000000000000000..e83781958419d081f49e86e85a8dffe8a130d7cf GIT binary patch literal 20325 zcmZr%Q+QGwv$OFn#lwc+qNdQZQC{`wmmU#|L^yq`+WUWr}y5qYt>q- z!c~-{k>K&+0RRA!tc-*j=-K<<2NoLit+w$41^`fIkd+YCa9g{~f^oyrTpL-yKuEZ#EPr zpzo+QG{EB7e#!Dwa=|^?O7nCgaJ&AON%Js0ndQFy^uGP*o0x!JGf00c*ZF@R>G$a5 zyt0$>X{5`=^-{n7q+(C9UGL+b>4aoqrGmHTl>y6W|&o61zjN~~$w_2Kp6_QBUp zPiN?pBVx^Xyh_9%{UjT++JRg$J!9|J!KfzrN}gjD)rvuz593OK9#I&$qP#-=~7l9Oiq^=Cf}C<*~flIr}Q!)0u#k;8M`)@I+yFMpTTtWsb z=GcnYd{P;;i$VyL#~zr46JkU@3!$=Bh`S_$Qh8_Ws&Iu*Z5`SteNSgtw0(7Fx3nTZN2mN$Sf)vCY+BsAv`z#P%e|T*uhH0k>Ujc)6 z`q48Gp9x%PKo!GrwymQ(~=x)WuceC*YO$VuKR@7w-FLwhVIW-+lOu* zl_0}rB7aZ)#8sp{@#m@lr+{Iq5xS-#akzF$widlfa^=)xcO>Wj&u*RV$qf!!;TN;BlQ5%o){0j?qMFWwnD*WoF%zF!|t zW)mrld(G>17$sRQ>9{=hxmL@SX);PmTb2$leN0;;w;m5?R7!lOly&yIVz=-AW+Jor zU8OuzSHGzTJ+(cBZ<3DdY~Oj;8T7PBI=e`VpY*)n$=~)PFo&I2cPJs^bI2SV9Yr)Y zHpT@D;9BemFzPj;(q!5PxZMune8UoJl8SbN9#Wd2y^TOUh!=CKe|Puf*qufZe$Tak z_kP->i4l6Wn#&W1iBC_*cgSmj7mk1yu(7g2{nhi)yar`g zOm;FEJo~^Gza^$#9VgZTu=BD~k~t5tp=BDXZKi@>ll@^`gvzT>hEZukiHeI(Fv6+n+ zn0nqF9YANb;vfx81d=3D*(lHR>55!XP;hUW3pJdQXa%kRW5Xgzq(3jTaA1os4^kAi zXG5q%R}QZ=4Grnn9*2>~X%Rd-x1`%DX9yAjb#>bj{%xr6yQnEBU*5JoMR9BLw9cPk z_xfN+DqW(i__ih5k7jbVPdH!@!lUM1LHqrAH%60luR!Y`dXR#d0{`-{T@d`#w;D?1 zq9db4LPnP1c@Qrhv9_jn`}uYq%Bdb17|FIfINXoI8uPqb_YI2!Q55d+#FDm9)$@zw z>v?2WVxB?dk2?Ge+1;D3&F&ZLyj#xf&du&XU~{Di>dut=aTd+z`>}B3jL;iHrLJzw z1L)nt{>=yre^+XCHYL14GV*yjRQU)XX+nQ@8i^qarRgv9MDUGJvWrgK_9Z~tg-KBH zlWJwbl}4BB+fy(2cUDnDM+NW73qC?|EmDkA!f|3K%Xn%+BMY-$Y2MaB^+tG=R5&Zo z7KZ)taqasn0bPdYzDgrtpHVdCAgAN7#OsAe5f>dC5NBWw_6l+ueRWx@o&nd1nuAi$*j`xmH;I)EvpE3@X<+1E z#)O2;nNYKA@5jG%_OnTaf2Xtfd%jKzVwGm);vO3?eSoAex)=uWm_CqEY=;R6=rjGv ziHQ_Uoi}2^BVE}8Ns{UeuIE)P5ev$~h8h|grQlrZ{Db7|o-2l~mqh^XZLbEt63YsO z3n41;q()jSntL8m1K~aBy82?JMJ?{I_Ht-djWCAt<0A*l%k}0|G?}5gjNp7XjrxCK<|-|gE#>+gy=f*l?M09|4aT{M zOGxRZtSN3>q4{2Kv^CI;+RCwJP4`@^f-c>goM80Tz4vR$fnCW~R1SRg&K(dclEPO$Z(B(R~j_pr#zwc2ue!pk8)%o{& z-~f!Q71mbWCb;yFY```qGH3Vdxmdw*lV56nJo=x*=ZqY^FJYe$`^uDb~x^-jIU}#qY>*F1G*1IEm>V8L1Py4fIWbjGJ zROt(gl?>qG=u;liw50?={qrtU=hP|73=7LbZ16klP+t7w@1{sSlqG5ps>sf?0iW#8At@)1{z*@758IWJH}dE4>9+;i=D%c6@#)_PXu zBgJYGkZ|==HVuIY)oL=P4=Md#&7h&u!PdSSE%F_pOD@OU!Y3innbcE{_Uecaz!*Xnc#7R6*_?jt3Z_RQ1e+) zp8}g?8Md-(C62TW8ea)q)45mBg+Kn$BTFCIEGI|eW%2^`kMG%# zSsD37MaAJx9yu6uwI%E6i#Sj+PE&{r&3Jh)Q03{5OWMYQ{M7|w(61M(bp=&PFD(T~ z-l!b73mdh<;RFm_A6;>OJx<}Itim@3jtF6|Znz1>^{A}VAX7xOEIvPlzy1j`!IKj0 zBq^|d>kVeb@Nvmb^vottP}_h2qeP<^5|IXo;>SG^d)@RYIUodcd`Lz#R<=k44)t}H z771Uq?t|0$h3l^G!jlOQs&OCoxr#iO1t3zsOQgU$^bYQ&L0=(U0Yq#FC zYqKK^H*Tad{?5V&apI4CpBIFUr0u%CNfW>yHrN(@A}pmaA5@!uibr2xuPZ&}48M1X z=rsv}jP^et=q%z3&Jj4m=wXcW^ni&5H^Loz$2q>l4s1qVv~39Qvf1oY2QYh3ZzVEd zJY?80HFbN5G|DbhuS|yRi>!pF%2rj^!Hwg_m;61)tN#5puzaoCoiIF*aH$#K_ly_x zc4Y?n9c}rgfIBY?&rI5RgB_F=-##cDp6?43yQl-RUDgZ54RLLMEW@0~Z0ZNirwUQ+ zqLLqIP9e9D?{ok3i=E7U8r8OEJkazVGS^8~;+yHUL?}&*GS_mNL6NBDI?<;zPYO6Z z#8prd*F=jk#-Y5eR7A`c*CVmdnryBdXS8qX?X>(OM z=tL}jj@8wFGO*Q1N=kx}U_S8hyIib>{>xFS6A^=T8F_sxprm;}4mJ&NieqOP5p^gP zznk}QbGyY=t-#m9C(r&Ex~nNS0#eJI=cJ|V;(t=6!Dz~1mMqe){h7+(Hm)Svi~6JY zg+#!`k&5%UVx{G=hQmQ~qYI6m4^%I{P``zQrm7Gpp5Rl&}qAQBn);_sk8acn$i`hV?KFl<{ydxF%kq#W2(kzo58Z3_7*B zA{23pe{_A_Sy`JtE0-#?VGgd3MbTni$KM|>{<80gQwy9O?Bt@FVH75-+;Q>842hD zVl=j8->pt}T{Tp$A^0}}(I6j66=)oKgy6i7E(wd;8-LJ8hb{s z-WE|+aHRwDw%5O@o)`jdChoHizv;mu&`)lf!L?Tk3B(En$}k=qB}`(>?p;C{kJ;l) zr#5BzWWNFdpJF?&$aK#=lw$v zkZ|`~vWe8Z**G2|EfUxTVQw=1)j5?M2m0CYH);uzE>cbo3a4{>%V+BvD_ZvaeZEzK ze-D@DPpQ^D9bORUx^g=@Lw=Jmt0Z_*%;8yPqMcp%)n0;&jIJ1YUIN_}@l+=ea4&kk zjl{^Y|KY>mmxWOL71Xd09o}aR14wC=jCGq;I))O03SgoZx+;=OV&KZM-10!NN*Kco zTt`G>pJ0xUvue}PRIh-mIolA=)I^HlwX=ATbGV$fEncTxZj0n%__jskFdgJ6@gx=C0QC*~=jNa5kM7J+26pfwD=7=^X(d>r zHU)$T*bFxu%m6CgZm+guQLk>HAdUE(zhY4rI*i|wjj5qLf zr6Xh7vLw^*8506cqKY$vT74O)m-#QiJ7DQLpZ6kN$r90?AP^r@u`3cg{ZJYoV#~k* z{(oXliUBGVd_X^f&yd0K)6OVP_ygBm0LK^)9DJ72Y=!;LkepPikB_*+?1qa51^UMP3$nqD;Hpj?UNCljHMRf4fj zV5ndha}flGK{z9iD69ju<2g(n&+o^wVx%9G!}fjh52_T|i%Zbr0b+c7I#NhrVpumjKnuoHlg^-{|=>9o+k zcm4cfs1L*Pbd4t1&XP8N4bC?_?`=4Pi) zl-~arO9);>{11KX*MF$EOf014b=C^3DLI6rZO7RJPPFEkrfmgwaVu*IE3icpldrxv zeK1&R=iL!a|72I9q?4j0@U#YCJ3IxoS@sF1+ z+tcBaUB0AHxy8eZV5lx{o_)J^&H{tCS;TB&s29k7Y`jbJ;%!zR!qcG(J|F-%VlLJ- zW-8(;2D+u0j@kHZJ%J>IP~%}a%#@%}mj z21>;KPt|!bKxr{Fhy>3C=cH>kh5zTjB(4r1V0rV^2(~bjoDcCbS9F+3;m0MYJQr9k z6f5e4nsrG2v3E&Bgp=TisaBPPg+e?{ZE9s+o@SAwbA6c_2tk?<*J_#!!TEf@{N+o4 zu6#2>3-}qc3}eNBOEtcDLUEv^!~*Y^j6i7sLTbNPF#&mZmOo81sOf*iR_;*5)l%R? z5r!v`NT(?_)|w zVn*~)M+f+QC;|mPjk_nMA+rVGJOH1Nn;#GF@;?ECvQdb+<(`A8xWv3FKJr^Kce1)+c;R(cN+f2>= z1SR+#D#Qp8-I~y4`IcsR8q3XWj)D+4DjGdZx^JAz*-=bI#JKRD1F^(xUF;VU2p5iP?BR*a}u249Xb2*KpH zx9jotDNT;tytV@Uy5d1c2zHE*_go3VN`iuKyjMwlI30{+k_KWqerKSMS`PwJV%48J z$T|FMc66W?Wq)T4hr!k0SFYnPuNt>_F!KbUttPE8q(bbsKa}b$bq|^K(jeJfLwzgb}BV0%1rey)y573q@#-Lko zUbCOCU^m6BWofw5{*0woqHBd}AOA0R8t!oC^{=KMS1+wlWU3*0ge-$^?)jn6@LYqo zS3d0(s9WTbln!J|Pyfh2${`{Z7YfSQ1HM~(-`w1&r29$IF*9rH%2znMtaVS3FDqe& zFdV0I+9u`dKyBsJ<_g&H`!z~ zQ;8Z@_GGc+6tChX7@I~J>en0L=bA9oIBCA6G$tqy%Rev7BAK1SV$7qWC|jx%if>K0 zpacQd(XWxs-NQFQIirB4Db9+g9D<5FXqn*CE9LGz6~8$r1VOxJdk_6^#8IZYxd-)iZl8Jv24JH-yJ882 z<|WP)6rmZd%zTG_jf2Qa%?iFP(*mPNIxPlhU6C%Fn@oR`qiy9Hr}cAvCfAx)t=Jx5 z>qgpMh>Z$hZSkYT4l?<~1dOaIg4xIITf>NOaq|9kasLSsw`u*bAoc`7=k-g;GQEZP z2TsEtH!y1_44@M$ttxPG6x@d&;3FH|EdlqowA%p>id*W_)6szmTimo;lsO+ccA zQv{)^>w%VvbX@N?a_}94CjfRL|52rFut)=gcz@mSA6|l>)y`a`gzm=f} zs51NV1Ae&rdt`5tYA|5=S=NIfzcz^LUr@UZ3n>PSzm~|KugenioFTK+xicUhB9SRt zYfywFNZQCn;ByY<<5BWbY0i0fuoA;~+)yU3!c8jDbR$dDzf$d7m|y{N0;tpMX#vhM zcBrsqPbC15@Z`D!$90!Z%N^b#5a$qN?ECR>`5*3AHs`U{lk_M>wh#GTD>WYIgE0k( zi$c6RHZz057?GGn&ZgENG16%^*Hhbr``5IPr*MI6uXZ{#LX}85vJV806H=(iX#}MU zFBZ^Gq7N1IaF#mFAj96lhRmdNQ~_?%fvSMqq~xB(NbnnO{zHBQm?cH9+gdG6@oMZVIiMh3{tlaU~c!^?i)p zSm*`ilNwK$Vy^hK4D=krb$BkmJDiUenMoeX4HXUO=|Ww9RGICD1@EPkX-B9}D4{{< zqhPx0zO-AgOsTJ6E@uy>kcEh$*%MAB;gOtaeeraV0lFk(mDgWhwoQ9m0u?iO`HD(;cQ`j>35H32Z15F; z`KL%y2$BobzMmmrMKH^zPQ@#IieQ!T6b<$&o0z1ikl@`=W~Dow&!`UNfO5*cx)I_x zEVfDuULGi!a+MhIX%JuiufvL`SKvnib2jvnECQ0jsphP-ftpX5PEm;T$r5I}$d+-N z+$3#Io(tw5>`oCfw&CSjTB@bN*-KPVEVo!gFj^ibbwCN2^M!();E+G$AJpm$a1|lq z68PH4{$3D8=OXxDNLZ*pxi)@o>iENHsV!-2hgLtXP^>HVwNyy^{tuoQda5RB&+)8?h`Cpm^(O?nw1UdLS-{;-X)Fipf zwI==qy$P2KDfs04AYsk>A9L~yxDMWsB{bk!_=D~cGurQbEz`-@$vl8BZigTrv&X4oAO;*}b$SwqL9p+sd5TGb}TMjTkD>8NKatCoNNenPp z@DD(d?O z*3`4O!Oh~3%1Q7)5bpjaADGbF|i*@Il)tz^O0_6u$c49q-Oz5 z)OiR4OaWkw&n{ip@niMsPJ~244=l{PqmH9u)k7|wr|m*xDuVDu)PZOCU&suOWSeAl zo@%e5gI0f5$VfoQTU$e}ci=liA`dp15Db73wp1Nl>c>-ZmX(REq(T4hc?Y{e3vV@mt?u_hl`0XAo_>>D z6r%_lnK>cZwwx3)yVFW{K7)gg2Tjlt~7R5TwG>L=UI@1h(ZPpZcQq zvb3pACMTN)b2YwCvW&corM1&Hxo_Gx+?vosfR^=+Iocr?bpZX3`V@AUvlFww{L2^o zu^Hfh9OHPRAj^K-fO^L6^xqHe1@VV*n7LoSrirOh?ipbslfYSSV?9W?&$rv$>ss9p z$fMs1N&wDkU|KPHWNVf!PQm1yoSZ(Ea2b*_Oi{nQzC%U@;Sv4hDSSNgghY#Q=7!3B z+y;Sv8LSg;mo@Bj_pWE+dxDV$a1dq5&rf6QJC-L(@Ar9p(cO}xNHqHtuK^(BX_2K@ z@zZXqnJ1fBQZ+P>KDV(M+Ht|^Hfsm0$hL97Uk;s?gphO-Ri1>B_?)b6S=F{?P`TWJ z^&Tr(tStvSzN{y2_SZ0!KpJ$?0!j}-qU*;D?~1V_Wc`LiKkUrk9rG-{=_}u-LL1<_ zu#_5@Oi4%Zxmx2tedC9UKePY~j8JD@g+euYOj1HUEv=)})o~IedD79r?TvCiG_?}2 z9mJd5c8;WMBg~x$`fAuRNQ!Jskt=|(Vu$tP+^FMR|ML65A{3Rkzmfjl)uR zgQLV2OPDE);c5c$Y001qG8Y)?i#JGGmZc!$Kg~;K>(?O_Dm#1X)I*}lXWe(TqMe|g zede2Rh=LhxVGk6+?uCz&ma^Eyh;PS0Gs^1Qa#no8AAUgKO}Y)6g^%pe{>D1 z5Pag0iwVXI1gQK4S}o>(Gr0!0B>)!T4#}fTshQzKa2cN0LtsNOAz>ppF6`oddmY3< z-zVCb0KlTdj#K0m#9#IMFA&+jQS?AmVk1;whhHRpuZFk3s2jN&B9mwg4Z&6?M~k|_ zF!g_5R)kFxQzpwxy;asabGzc@*&c(V2_Fg5ZqZVxLH4agH~rOlJKPhK;qqN`7Ili3|Q6o2HJ-9gblt}HL81_=Jn?j>Rx>9E}o7jbLBLX4t3cK%3B|NQ`lIKJWlf! zQVztS0)5;BGtMx(EV0t>?}>SS2gFa8>)HyLJgTO?^~)FNN-Kl6R^bwDxwegJvU&)B zD&2E8hTu4**AbX3kKdBB@NvJ*xzSB|FJtMLQmt@d_x&^W34AlAWk*vVDi({u9zq0N3Gzybt$p69SyC>{jj_=VWFYYz}Mt6 z%`_YkyXk`#QclJ_1V>KN1eG7*CL$C3U4lX?O63qML_N8Az)cfSZA|yv5oiVFu+>?zz)IIju;=_o~5*h>1a)%~LGvKJN2QFr2WXQ{L*SI|)BO zNU7Qvc-sK1tvMic4^6)W74;!J2$9#^1&$&w7#=0xB1bBfHDWmPBT(~4A+Ss$**PGZ zpim8ZadO;gQ5{;!gaPXV_mhZWePUJ|t=R4yX*S8PHDRbGykE^BD*!x5RYmGg5{0(@ z8{|Y~=ss~l}5Vt2sgEWFQA4Yv)pOfzmP zk=#1)XGRj+0w?s3g1~4kjql_GMk6!k0oIX)g#oinmL@$z z7vM^~5Dl|ps1Qg1i*C0I;}M0=DYt~_ehYji$wg@cFSi2Z0hR9Yxi?X&Os>$NcWz8(DzdGXUYxpo{ z`b}2Ya;Dy5f=MFe0U0$Dypd=jhgKzW{a25766S0T`6bNFjeU2gglGaM#)e-%nAzj( z-|hqp9)J9|AP9QMeq`ggGy3X#VDFN64eG#ptjhSIr2PwteWSWI<}?eLvJMj>jxWo2 z@0TM)8W{o~1sMZ6at=C*PL29|Jq<_8(_wPS0m$IViC_~!8_^cGfBer2T* zbv3?YFi-tNe7=QLMJ_p460J(Bmyy-8$Q&A@qr zs+Dod%7X5gJlE&q%xHm}$UN{onmR!(?@pnBJ^hFFK+npw^yW{oPUV4P>;2v6}=1EI)u~C&Kr?Z)) zbJhC?<6e10#n6FJ;$UtwRyhqs=^uPZj{cHxHs=VNpm^xBk(YnRIl&h$1j~%fe+YYx z;B&i_;fwiUnfm;?AeKP0w;q+Gpu@eCIQd{ z+;;CLAXg4y_{Ae48(gzCEk{{F1Y}cg?g>b{{rbj1X!;gP{)i-vv`mS;D>yA5T%4#r zGPAg(GK3dhm7Nu8#=)HEq(Q5x_i@ok$Xf4R^oQL%1|NB^=Vr!u@S>+GkG?!LJQ8{u zRS?00Hbn>uXy^io!RQqEJs9EZ939riFCu<3pBxKJ4oMQMS0T+~R_D!i%l)|6>ILjk zk_>w;F|L4!OwZfKeP^Q%J3#jr4_XH6x8c%Z46~d2Q7JZLC2O{o1E4Zl*6RDDmxEX= zoLQRL0)p}{j*g*d@#9Fad1V>J2cZ9YlZq$i>9}g(SDQ4*RBJ8rz3JjRGy(V~J-UXb zrlueF$ISpr4xPnU2`I2ug=KUFvr(r`(GCSvtyf3@i;bF6Vn+CHpQ1HxZ%gYXHB}~W zh<9-2j`(Cj{sg#Mkk$CI{dSn3grWfiEUfV4AgyX-W@}B$sY~tW3uPe=xkxmH0sfX! zELqY&@?gR8IK|>7d-E;gCN0%npcXbnG2qkm`SNNnK8dl9Q5p%-uISS>cU~zw6!9Py zsV-3O5mib8Bn!X8cqt?zDhY>+;A`^;y}xTMLy_XL!*sG(9Ty*mTYnt7S7=FAMj~7N zzPxG6Z?p(6@l;?(%3+;PI++3WoYK<7?&ef%^f{_BXt`a4rYstEc6RhH-ZU?@#9>E` zGD4`xB-kX>-x$i1RU)ilo&OU}HNa)jhu6A~n@fLC21YoO=iP&H`>kntn8aT>@q3&} z(ljmJY8IEIjc2P-`pI8OXk|a_x42DHDMdb@P8K69*sHA^+rl6NyXUiqs}mjVHK2r{-;fR zdYvtn?Z++oe7s`m-1y4C&z``HGWN9DF`ko~&hF14ho(Ps|A2!XPP5Dg9>8d1$?3>3 z@FfMsGmR`^jB37-TJ*-R!Ktv&Z`0(=D4wdRB@B(`)74cvx-X`&rz|mG6unSY7g33H z+Oy04xuFqCxXqavQ99h~{%*!fi6WHj|W8Qze%z{AbG>7&VCs zhvjtU+&0L09%CgHxKYVAmk45YIr;i@Ayfs`N|h2WiDx$K&&KxfQv3xiR@`!70N7qL$4Z9`bQ-s9%m4ayE~DXsR4*QNkcd zQPQe#{am~M6^=MeUSMHiQT6*>xTy?&_76%N${%}LjHmYTpBJX|5ZVHbeWr7!tTzc% zoIDvURJ&uv!zH}J*svsEAQc5tEHCc@vGW*oBmKJgBsZ692-nZk-`4s_riy{`rnJDq zNu9yeEt@iMVmOfLC;A}!Lxwa5jpVzAiHUi@P4#(=0&M~6rCJy{m(M}-1pc!U?38B~ zyAS|hRNNXv!@=`B=Ae_NG9gHmAS=u{*k%u@Xg0hAqLCoJTm9aC`=ml=XNjzl?TOfDG`^ON)z1@`N9t6|#inSLk1(1mDH%$5d#dRN`!aPX~{DH)M~1 zDg-OvZQ0qW|JU0Jok7^(f2(VfKw1yg$d-eJp8#6aR=VEj`ENsKL1H8c>#x%hvd%$Aq!Dt{rd$;!3~dOk9MLyy6<8C-*RP*~69 zDUjU-Iz<+JpU)?tK?YYaLm(ktH8QAVog##_${o~c#FpC~{x>rl6D~+&v)p;2|Fx8i zWOmkDMlQ&^*~Ah<#Fu$bW4`>K->f5EmNBDkH%R!cH+Ozs?cGml9#81MF^yL?H5S-i zns@0DjOii=yD{1XNhtY%k9S>LALT-58lKfw!8DyprWkfSt7aW?;kpxuUI1Xhz}4?+ zf6@NBw80VsDnY3CnRp*UX@(UMT?yhtjM01cJ;zu%jqOmvTonET+PMB z500vJ5TaVKhPf-T*OQA0hC1%!*R>ze<8rysfuaFVW=4e{D&vsDl+&b@rDYpy+488! zhvblSoLeRGQ`AszzceV|TamGBWP&!GT{2rL)nhHK-QYy=+ZN|G{IfHoyr=?lJwC#j zqoql34d8t^C`61c83>^gMl;RxWs{lqSKD8M(_(ETU5({nLg|*GK1fdwPUTBwdgPeA zgZtv95S6-KZ_(kwWP5`IT21trjR4?k1D!PK;r%hY&i0H$8Ij`zMAAWWL^3D+w|u|~ zQVz2ow=GeeWbFBIcW|IG=K0Kr`(d)Si?Q>PCJt%j(9qCj{FFUqk3piDySqEj0MX$Y z8RH^5$aSj!eU)X%9ZMkyeZ!8XwBG4KMB8VSEVR^1wAGDIRRn&;Dv5?(5i;~(19Tab zkc;wkiO`aZ|4!QNxk;`)1u=jcNuftB5wE=?5;4`CcjLoMbXQPNo_6kjN2gOmox3ZN zt|4s8#{D^Pg47XFjZuD6tOmk^9lPW239yEsu^#!mRg2Mlrqv(VaL*0OKT%_(Vu=NF zvzX#hE(J1&?61JRWcn@oYBO$oIOg>!Xg^O@^NjM~8_EpQ>}m%U%*enXKZ{brYiZix zla?ouYp;KI;smp-2zWmJczS_EFV9-Ngj7Y~PS{iY8Ro`HShNhX1fgf$bv)xzC4kSc zeZzHYtrYH#-&kT6hwcEW#B)dQQW9W~jv_zpV%+M+vAgEirXS5FR$vSX06H` zm*T3-Gf}AcA;@{KKN73Gyb#$mjIODzUGdzeAF@)bTuTv$#kHWkCkiK%?f=!LG;?n+YwI!f4;ufK*3Vy(W&0T7B!pC-Q9yTytbKCSoj@-yN`5fKr5*2@W%R=Qu_V)<6Ni#JTq z;QuwFflTaML}j?VN(>x8I}D3TA24U0Bx#;H{?wcuaN{27H!qr=;X$PVAxG zZ>%T%mm49x9nurod+B*j1MtE42kD$AYN~7bi`*O=1IM}ENtGVzhhR{-sU}nDoW2sv zalAi2s`6$^fqo7lRUIN_K`7EP3SAJ2(HhaH+~}gz7z$=XwsQo_iOVvp_yLR_`J7}5$;uL zZyE`WE|6kJ>(IuWJ7kV3O=ty*cg7uqQ{Z9FCn2`~JX*1&$@)EiJDNWAS|X|bd`YIt zDPQSyJWX|Hs|+NSH&q;_2&te-kOKsYvpfi0K#Snhc8Xt;vR9?hLX4224I_g|(d;Wg z2p`UB*qeQwHYzuRSl%0#DUZw7i&`XT9+%2& zjJ&Npqm;#$T>jTs=$Y+{15R)K?1mDRCFq^s znZ?HAtkKnWX>E7(bad$2+rreHeoEBgrZ|9LIxPt0BXo~f8N7c!^=LZ*O5umbN+tu& z$?`&(_XdFy=u-zQ8_A?a@tCddQrV?e{4$w;Jri2(o{`Fbt*R97EWVD`Xwm!3>R0^2 z&_+zB7)~anZ*TI0VggM|@Rp1a;RHhqh4j6C6($<}lFNz7TPb4HZ@ws{5LO-eRO;|c z18$68fX|a<|2IVUSxPjs8PAj~MT<*d!`B}$b^s2}yaN4WK%^4}=T;3zT$=B25rR^F)6%;P1;Sl*m${=H&bD)ejZ zLeEVI9H_gWTcx_#4_qAtO90#j4Nw?0Y?#PoS*D|ZWo zhMpybF+;H!r^p~eVdDU^Nfa#54Q#0GPwI+q0b;EuGXZnX+jXYJ`X2 zj~tWbBl1j|M>wSWJKYu|K9fC*n2+z1xm%i`DERE2R1O3e^2&euc0t*|kBr1a%jT@Ob81`DrFALydq`Kl$DNrm zS!F=NGbVrxGK3f!Mo0l#RFotDmR`I1os+H5qO2>WDoo2OWQuHg9Sdj6xIaPI*4IS4 zF%MEqvh7xuCJS@%Mz96un&ULyp2k$`EP{63k0}_8q~ZG%Iw+Yn1zuRz=Jv+e0`J0- z)ujZT&oq|Y2BJ)Dj#arTR5A?VvPsV;O&ZSUxZn}PweqW#bA)V4hV(-B+wf1sRm8=B za#=8>;&*M%;WQ&Fk>ZHKa{4bUn3#O8Cj}_p|EZdT<&qhy)9Iqr^Wu8hZk`;KMOjfe zRJ)zaMdM*a=+>V>tCSBXzPS|RWyfGP*tj(0qPsGV$fH9_keG-oI|%1~9S`R#zK*M= zp+vzKR(LiQ7If@exv`1jKZ3>7mLojsqZtiaZ#qFkoSr4k*sOVKtd)+ia1vEIIuw4l z8pxNN4O4ca(|tnH0Lk1Se0XW}pNbz*A)D!i`vFi0K^))3EZ+vkZ+aWzg_fT{!@+Hs zX>;}J=7B;$S%>#E82Gs=3aIGntZop!p*#9mTqQO69EA8-T%Bo8UO7YU)h-N#Sdka< zTUMYh&|z)8WxSEM-#Q)cckLrFJFXkxd9w=+aQLx<+XY8?hPVjl`qcvbO|eIvx65WO zIiWD}0rmi|57XM!S0{3+ zU%RmSgmxu35{N>Pcu3G1xIvz$cYmZr5mNX|_Kb3uoMgoHv$ zF&dGSR-IC+ydj<=GUP}nTAYa6W&eZN_x-k{acGUG%p(;IN`>0W)jzlKfw{PFeg8O4y=@}`1K($b|YuAo; zw!ZQKAG>+;=8p({NgenpQo;|z1cGP{3lh$wcbGVQC_Msh<0I47l&usv}qHhP;8Hc5^%E-GJgznNfvG&W(eRI677JK zCzjSRT#K+GJ0F}yPUBT^_Ms|_CLBWe*?k^)jbQ#s}^yp@~w0DO0AL z#Ee(0gLG1Ng(F1DgQe-kZMbwGN0hiZ78HyRbK9m(n;MS8WCQPnvF+}=?^a<*V&+^ojy0jwNK}fNshLEk!1WZ$0bw8spg51j zl@WZepNVX7q!hq1OKvyRkoVtz|KG-s9}nj5FQ0jTE&S~dgosa*!lQDP=o`{sTH8bA zL;VZkF==J@G&N&PL3;4kevv2p?4J%pfCFO7;}z`+)}@P<{mw7C*9S`3FjLe(h3N7xg^A->Pzd%C=c)}T~CH-E=!>h-ac0hc1-+nB;O6qzX3 zop;{(ob*L1dA4oZwCQgV{+3z>em27I)I?)df<~V}KKwyz)~xw0+q}+l?rN%I$gV4{vh~N zh*Dy~fNi?az~SMT%o51zbNp9cc?CnVv8JZxD4tniVc|es1zc>OQ%xGlz5>%Tz#KAa?wMD>v=;madST;9=yC4J?9!i0c?TsI znx{3AA*xF3;ZtFT!8pD3`Sj$ht~t@4>n<=OCz^t0-wpIxCP z!kZK&0eE5B!UIbeeZ|7l9p~K51w$I#-TB3szLC$4g`Xh zOXBv>ATC_EP-T%}pCf)xo`B3U$^zXA4A9i|L*c>@3?ek-6DY?x>ZqesMJQz%4DqIW zjcQJs{LMgZYiV~RGrH#3++zlzU<7btP!FX8cQljBrQ!}vm3}Y z__o__TlVCWPku^w5e^xSS*&)117#VU&%Go-z`cF+(MLa(yV^>5&xcy9eqRgZX{(== zHPVw1{w9V=jbD&wfkecVds=q`2`|RodFP!ENQm$){jWSU{Qwh+5aiBf?i)K2Vd7_= zdFI>t`uc~)j2UyRvHIM5o`HTA2J+-t@O~oF69K=VxsD~b(-DSl85tRU&a*N)udGqEUDNxgjkk}Et3;r$yADj;}^Idn{^{n(^rg3k%$J}e~xwiV9^|L6@ z&nAKQqX3CasMNX}^e8AO=%165Gtel)tXZ>W-gn=9QV%x&M8_4Wf2$(II~95?1Rp;H zXMWSBO+Vdp&pj{BoH_FsM)0|}++&UPtyVwZQqY!wJW&djT6Y8E8IM z%RRPWeXG?^gg9fk8-P1o>F7hThDE)JOc?(FD>iM~v?+JoamT~bS^f+%TiP&9_|&BG z4^&04M3DAUd?II*YCrMB6aQYldi5hyo4&D@588FZ3Z{6nYiU#MC zCZXYM?tu|}?u~{zM(_>JZ{q-W5>`J>BSjfH43}&14?3D1K`!MZv~Jyckam`pGK?NQ zdhGSrUw?zdzfT!uz;UMZ0hq+gSW&w|#40)EWZ|{YH_{;z$wBBP#M%Cv-~8sO`Sa)h zvZkhHoW`SvYDbRv zH8m?eaPp)eD0pO7hAv&Yj3(v3c=6&Da(8$Vp?dDQ=f0*ao2UuZqWBNA|7qBybVEar zMhInrQUPD~|2N0c$5byA5MejOTNDLu+_>@U_3PKa%QfD7^UW)#Oqp_iadGiM=5rXK z*PqeAoF4ifbk_HzFd_H{z9KZ&aqCt(fn(s2+7%EC1O!-MNv;`%sOZ?SR2L)?pwinf;hF9Od8<^T46bZZzd;xwz z!NSgivat)j^2#eapMCb(&!n)w&AGV-uI2R8Pd}S$qb$SE!N(^oOb$N z@VAHf|)22)0bp4VgOIFp_ z*Z%=){J;YbyeMJR8(3$F!#|M1`?1_XK7HYZ7yh+r)27cPy!wpqpMU=OPv!4Frt|Hw z#~yoED%I<}cg>nLPc$?%+{ZEHv2WlUz!`bY8jG+GvRv0dqr4j4^s)(kC!4i5;Y{P) zCItUypa`jcc7>K2|F*a645ivJERzGsO$KUuL{#O^94YuI+L3Uy1-0B+S zwQx;b8`sFS>T52wSo-$*v$gbN?Nbwi|MQC)S$Bm@2W2SK5TS#{$yS9>J46y#IwPZB zQjXjmq!6aJ&L5oJzV+a2DVXW`*@M#eR*>Kp@0G{kSR9jMa}Lg>&gsMP#==_#zO%mO zcKS2eg+9|}?GsU*#IDe|D_E5wM?-`b8YdftDAG`(SjWmsHN@zlg|SQvX73~{vQI$< zu%rO%C53yh<@>$)KP}*W)iHd>D$~dAq0d#S&t0sqrN|=it@JhL>Ca#n`h>MlygVyI zhQ((v3SoAL)*4o{(-5PB7RJt6FuQ0WEwLzxS>0{R`nOX3p04^B#rl|?^f}t=bGFgf zU@W{5c!TLP5<;JNHB}j$6vFHf*&0^lY5{Da9m`f)FblPiw$%b_6veC}y{x}8|JPQ( zw@@Fel|FV0eXd-6?re*NcM|x7&?jDDQ3kt0I4MPz7RF|l9izELN%AZ^h|@BE&$YZS z$0Fp-^f{b_-AUjRLZ5g=O)2b(VU#1?q9hp>WpP^8|FOO|&5xibggx+5MB0MS+SO5S307*qoM6N<$f^_a6ZU6uP literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/values/icon_univ_background.xml b/android/app/src/main/res/values/icon_univ_background.xml new file mode 100644 index 00000000..ab14f093 --- /dev/null +++ b/android/app/src/main/res/values/icon_univ_background.xml @@ -0,0 +1,4 @@ + + + #3DDC84 + \ No newline at end of file From 623f6c5519df2f6620774211ee54a5cdad6d913d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D1=80=D0=B8=D1=8F?= Date: Fri, 22 May 2020 00:05:25 +0300 Subject: [PATCH 47/63] 7.3 add hide qr-code button --- .../data/repository/PreferenceRepository.kt | 8 ++++++ .../ui/screens/menu/MenuFragment.kt | 25 +++++++++++-------- android/app/src/main/res/values/strings.xml | 7 ++++++ .../src/main/res/xml/settings_hierarchy.xml | 8 ++++++ 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt index c5eb08a3..0d617471 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt @@ -24,6 +24,7 @@ interface PreferenceRepository { val additionalAnnouncementsEnabled: Boolean val practiceUseOnlyKnownMaterials: Boolean val additionalExitButtonsEnabled: Boolean + val additionalQrCodeButtonEnabled: Boolean val currentUserId: Long suspend fun getCurrentUser(): User @@ -111,6 +112,13 @@ class PreferenceRepositoryImpl( ) } + override val additionalQrCodeButtonEnabled: Boolean by logged { + context.preferences.getBoolean( + context.getString(R.string.preference_additional_qrcode_button_enabled), + false + ) + } + override val currentUserId: Long by logged { context.preferences.getLong( context.getString(R.string.preference_current_user), 1 diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt index 239e1f9c..d4625af0 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt @@ -58,19 +58,24 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { navigate(R.id.action_menuFragment_to_practiceFragment) }) - qrPracticeButton.also { - buttons += it - }.setOnClickListener { - try { - val intent = Intent("com.google.zxing.client.android.SCAN") - intent.putExtra("SCAN_MODE", "QR_CODE_MODE") - startActivityForResult(intent, qrRequestCode) - } catch (e: ActivityNotFoundException) { - checkedToast(getString(R.string.qr_intent_cancelled)) - sendMarketIntent("com.google.zxing.client.android") + if (preferenceRepository.additionalQrCodeButtonEnabled) { + qrPracticeButton.also { + buttons += it + }.setOnClickListener { + try { + val intent = Intent("com.google.zxing.client.android.SCAN") + intent.putExtra("SCAN_MODE", "QR_CODE_MODE") + startActivityForResult(intent, qrRequestCode) + } catch (e: ActivityNotFoundException) { + checkedToast(getString(R.string.qr_intent_cancelled)) + sendMarketIntent("com.google.zxing.client.android") + } } + } else { + qrPracticeButton.visibility = View.GONE } + settingsButton.also { buttons += it }.setOnClickListener { diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index c2ab46dc..0b58f279 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -19,6 +19,7 @@ enable_additional_announcements practice_use_only_seen_materials additional_exit_buttons_enabled + additional_qrcode_button_enabled Правильно! Неправильно! @@ -301,5 +302,11 @@ В главном меню кнопка \"Выход из приложения\", в контекстных меню кнопка \"Скрыть\". + + Дополнительная кнопка \"Сканирование qr-кода\" + + + Отображать в главном меню кнопку \"Сканирование qr-кода\". + diff --git a/android/app/src/main/res/xml/settings_hierarchy.xml b/android/app/src/main/res/xml/settings_hierarchy.xml index 80014510..3e10f1bd 100644 --- a/android/app/src/main/res/xml/settings_hierarchy.xml +++ b/android/app/src/main/res/xml/settings_hierarchy.xml @@ -65,4 +65,12 @@ android:title="@string/preference_title_additional_exit" app:iconSpaceReserved="false" /> + + \ No newline at end of file From 4bcb9e315ae2af4b55f1247ed8d998a4592c274d Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Fri, 22 May 2020 03:03:22 +0400 Subject: [PATCH 48/63] Autoformatting --- android/app/src/main/res/drawable/locked.xml | 2 +- android/app/src/main/res/drawable/unlocked.xml | 2 +- android/app/src/main/res/mipmap-anydpi-v26/icon_univ.xml | 4 ++-- .../app/src/main/res/mipmap-anydpi-v26/icon_univ_round.xml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/android/app/src/main/res/drawable/locked.xml b/android/app/src/main/res/drawable/locked.xml index 9a9c7d2a..7115d90c 100644 --- a/android/app/src/main/res/drawable/locked.xml +++ b/android/app/src/main/res/drawable/locked.xml @@ -6,5 +6,5 @@ android:viewportHeight="24.0"> + android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1s3.1,1.39 3.1,3.1v2L8.9,8L8.9,6zM18,20L6,20L6,10h12v10z" /> \ No newline at end of file diff --git a/android/app/src/main/res/drawable/unlocked.xml b/android/app/src/main/res/drawable/unlocked.xml index 7c202e73..68d85ba3 100644 --- a/android/app/src/main/res/drawable/unlocked.xml +++ b/android/app/src/main/res/drawable/unlocked.xml @@ -6,5 +6,5 @@ android:viewportHeight="24.0"> + android:pathData="M12.45,16h2.09L9.43,3L7.57,3L2.46,16h2.09l1.12,-3h5.64l1.14,3zM6.43,11L8.5,5.48 10.57,11L6.43,11zM21.59,11.59l-8.09,8.09L9.83,16l-1.41,1.41 5.09,5.09L23,13l-1.41,-1.41z" /> \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/icon_univ.xml b/android/app/src/main/res/mipmap-anydpi-v26/icon_univ.xml index 64686c50..a9094723 100644 --- a/android/app/src/main/res/mipmap-anydpi-v26/icon_univ.xml +++ b/android/app/src/main/res/mipmap-anydpi-v26/icon_univ.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/icon_univ_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/icon_univ_round.xml index 64686c50..a9094723 100644 --- a/android/app/src/main/res/mipmap-anydpi-v26/icon_univ_round.xml +++ b/android/app/src/main/res/mipmap-anydpi-v26/icon_univ_round.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file From 01e6c6fb2deb119703bb3029cfe6b1ab2bc21bc7 Mon Sep 17 00:00:00 2001 From: kaustika Date: Fri, 22 May 2020 02:12:08 +0300 Subject: [PATCH 49/63] 7.10 localize app name --- android/app/src/main/res/values-ab/strings.xml | 4 ++++ android/app/src/main/res/values-ba/strings.xml | 4 ++++ android/app/src/main/res/values-be/strings.xml | 4 ++++ android/app/src/main/res/values-bg/strings.xml | 4 ++++ android/app/src/main/res/values-bs/strings.xml | 4 ++++ android/app/src/main/res/values-bua/strings.xml | 4 ++++ android/app/src/main/res/values-cv/strings.xml | 4 ++++ android/app/src/main/res/values-kk/strings.xml | 4 ++++ android/app/src/main/res/values-kv/strings.xml | 4 ++++ android/app/src/main/res/values-ky/strings.xml | 4 ++++ android/app/src/main/res/values-mn/strings.xml | 4 ++++ android/app/src/main/res/values-os/strings.xml | 4 ++++ android/app/src/main/res/values-ro/strings.xml | 4 ++++ android/app/src/main/res/values-ru/strings.xml | 5 +++++ android/app/src/main/res/values-sr/strings.xml | 4 ++++ android/app/src/main/res/values-tg/strings.xml | 4 ++++ android/app/src/main/res/values-tk/strings.xml | 4 ++++ android/app/src/main/res/values-tt/strings.xml | 4 ++++ android/app/src/main/res/values-uk/strings.xml | 4 ++++ android/app/src/main/res/values-uz/strings.xml | 4 ++++ 20 files changed, 81 insertions(+) create mode 100644 android/app/src/main/res/values-ab/strings.xml create mode 100644 android/app/src/main/res/values-ba/strings.xml create mode 100644 android/app/src/main/res/values-be/strings.xml create mode 100644 android/app/src/main/res/values-bg/strings.xml create mode 100644 android/app/src/main/res/values-bs/strings.xml create mode 100644 android/app/src/main/res/values-bua/strings.xml create mode 100644 android/app/src/main/res/values-cv/strings.xml create mode 100644 android/app/src/main/res/values-kk/strings.xml create mode 100644 android/app/src/main/res/values-kv/strings.xml create mode 100644 android/app/src/main/res/values-ky/strings.xml create mode 100644 android/app/src/main/res/values-mn/strings.xml create mode 100644 android/app/src/main/res/values-os/strings.xml create mode 100644 android/app/src/main/res/values-ro/strings.xml create mode 100644 android/app/src/main/res/values-ru/strings.xml create mode 100644 android/app/src/main/res/values-sr/strings.xml create mode 100644 android/app/src/main/res/values-tg/strings.xml create mode 100644 android/app/src/main/res/values-tk/strings.xml create mode 100644 android/app/src/main/res/values-tt/strings.xml create mode 100644 android/app/src/main/res/values-uk/strings.xml create mode 100644 android/app/src/main/res/values-uz/strings.xml diff --git a/android/app/src/main/res/values-ab/strings.xml b/android/app/src/main/res/values-ab/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-ab/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-ba/strings.xml b/android/app/src/main/res/values-ba/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-ba/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-be/strings.xml b/android/app/src/main/res/values-be/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-be/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-bg/strings.xml b/android/app/src/main/res/values-bg/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-bg/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-bs/strings.xml b/android/app/src/main/res/values-bs/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-bs/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-bua/strings.xml b/android/app/src/main/res/values-bua/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-bua/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-cv/strings.xml b/android/app/src/main/res/values-cv/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-cv/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-kk/strings.xml b/android/app/src/main/res/values-kk/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-kk/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-kv/strings.xml b/android/app/src/main/res/values-kv/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-kv/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-ky/strings.xml b/android/app/src/main/res/values-ky/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-ky/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-mn/strings.xml b/android/app/src/main/res/values-mn/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-mn/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-os/strings.xml b/android/app/src/main/res/values-os/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-os/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-ro/strings.xml b/android/app/src/main/res/values-ro/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-ro/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-ru/strings.xml b/android/app/src/main/res/values-ru/strings.xml new file mode 100644 index 00000000..becaea6d --- /dev/null +++ b/android/app/src/main/res/values-ru/strings.xml @@ -0,0 +1,5 @@ + + + Азбука Брайля + + diff --git a/android/app/src/main/res/values-sr/strings.xml b/android/app/src/main/res/values-sr/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-sr/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-tg/strings.xml b/android/app/src/main/res/values-tg/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-tg/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-tk/strings.xml b/android/app/src/main/res/values-tk/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-tk/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-tt/strings.xml b/android/app/src/main/res/values-tt/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-tt/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-uk/strings.xml b/android/app/src/main/res/values-uk/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-uk/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file diff --git a/android/app/src/main/res/values-uz/strings.xml b/android/app/src/main/res/values-uz/strings.xml new file mode 100644 index 00000000..2e320d3e --- /dev/null +++ b/android/app/src/main/res/values-uz/strings.xml @@ -0,0 +1,4 @@ + + + Азбука Брайля + \ No newline at end of file From b5c22a90a82cdd5881212c42ac490281cb8573c4 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Fri, 22 May 2020 03:12:52 +0400 Subject: [PATCH 50/63] Fix texts and formatting --- .../braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt | 1 - android/app/src/main/res/drawable/locked.xml | 2 +- android/app/src/main/res/drawable/unlocked.xml | 2 +- android/app/src/main/res/values/strings.xml | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt index d4625af0..74a24187 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/menu/MenuFragment.kt @@ -75,7 +75,6 @@ class MenuFragment : AbstractFragmentWithHelp(R.string.menu_help) { qrPracticeButton.visibility = View.GONE } - settingsButton.also { buttons += it }.setOnClickListener { diff --git a/android/app/src/main/res/drawable/locked.xml b/android/app/src/main/res/drawable/locked.xml index 9a9c7d2a..7115d90c 100644 --- a/android/app/src/main/res/drawable/locked.xml +++ b/android/app/src/main/res/drawable/locked.xml @@ -6,5 +6,5 @@ android:viewportHeight="24.0"> + android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1s3.1,1.39 3.1,3.1v2L8.9,8L8.9,6zM18,20L6,20L6,10h12v10z" /> \ No newline at end of file diff --git a/android/app/src/main/res/drawable/unlocked.xml b/android/app/src/main/res/drawable/unlocked.xml index 7c202e73..68d85ba3 100644 --- a/android/app/src/main/res/drawable/unlocked.xml +++ b/android/app/src/main/res/drawable/unlocked.xml @@ -6,5 +6,5 @@ android:viewportHeight="24.0"> + android:pathData="M12.45,16h2.09L9.43,3L7.57,3L2.46,16h2.09l1.12,-3h5.64l1.14,3zM6.43,11L8.5,5.48 10.57,11L6.43,11zM21.59,11.59l-8.09,8.09L9.83,16l-1.41,1.41 5.09,5.09L23,13l-1.41,-1.41z" /> \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 0b58f279..19e57528 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -303,7 +303,7 @@ В главном меню кнопка \"Выход из приложения\", в контекстных меню кнопка \"Скрыть\". - Дополнительная кнопка \"Сканирование qr-кода\" + Опция \"Сканирование qr-кода\" Отображать в главном меню кнопку \"Сканирование qr-кода\". From 87724a481b1dad6f21ce3a5d7e4e323a941e1217 Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Fri, 22 May 2020 10:18:37 +0300 Subject: [PATCH 51/63] 7.12 prompt user to disable issue-only-passed --- android/app/src/main/res/values/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 7b9a3d46..def18929 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -49,7 +49,8 @@ Список колод - Колода \"%s\" ещё недоступна, пройдите эти карточки в уроках! + Колода \"%s\" ещё недоступна, пройдите эти карточки в уроках или отключите + \"повторять только изученное\" в разделе \"настройки\". Все символы From 85a9dd1ebf73be4f8a307c88af3165744d0c0e2d Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Fri, 22 May 2020 11:32:21 +0400 Subject: [PATCH 52/63] Fix prefference default values consistency --- .../data/repository/PreferenceRepository.kt | 25 ++++++++----------- android/app/src/main/res/values/strings.xml | 3 +-- .../src/main/res/xml/settings_hierarchy.xml | 2 +- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt index 0d617471..1009a7b5 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/repository/PreferenceRepository.kt @@ -18,7 +18,6 @@ interface PreferenceRepository { val brailleTrainerEnabled: Boolean get() = false // Uncomment in android manifest when set true val speechRecognitionEnabled: Boolean val golubinaBookStepsEnabled: Boolean - val practiceUseMaterialsPassedInCourse: Boolean val traverseDotsInEnumerationOrder: Boolean val inputOnFlyCheck: Boolean val additionalAnnouncementsEnabled: Boolean @@ -36,6 +35,10 @@ interface PreferenceRepository { interface MutablePreferenceRepository : PreferenceRepository +/** + * Keep default values same as in `settings_hierarchy`. + * Values here will be used before the user first time visit to the preferences. + */ class PreferenceRepositoryImpl( private val context: Context, private val userDao: UserDao @@ -58,7 +61,7 @@ class PreferenceRepositoryImpl( override val speechRecognitionEnabled: Boolean by logged { context.preferences.getBoolean( context.getString(R.string.preference_speech_recognition_enabled), - false + false // To enable, set to `true` and uncomment permission in AndroidManifest ) } @@ -69,18 +72,10 @@ class PreferenceRepositoryImpl( ) } - override val practiceUseMaterialsPassedInCourse: Boolean by logged { - context.preferences.getBoolean( - context.getString(R.string.preference_practice_use_passed_material), - // to enable recognition, set to `true` and uncomment permission in AndroidManifest - false // TODO - ) - } - override val traverseDotsInEnumerationOrder: Boolean by logged { context.preferences.getBoolean( context.getString(R.string.preference_traverse_dots_in_enumeration_order), - false + true ) } @@ -94,21 +89,21 @@ class PreferenceRepositoryImpl( override val additionalAnnouncementsEnabled: Boolean by logged { context.preferences.getBoolean( context.getString(R.string.preference_enable_additional_announcements), - false + true ) } override val practiceUseOnlyKnownMaterials: Boolean by logged { context.preferences.getBoolean( - context.getString(R.string.preference_practice_use_only_seen_materials), - false + context.getString(R.string.preference_practice_use_only_known_materials), + true ) } override val additionalExitButtonsEnabled: Boolean by logged { context.preferences.getBoolean( context.getString(R.string.preference_additional_exit_buttons_enabled), - false + true ) } diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 19e57528..11daddde 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -14,10 +14,9 @@ current_user speech_recognition_enabled golubina_book_steps_enabled - practice_use_passed_material traverse_dots_in_enumeration_order enable_additional_announcements - practice_use_only_seen_materials + practice_use_only_known_materials additional_exit_buttons_enabled additional_qrcode_button_enabled diff --git a/android/app/src/main/res/xml/settings_hierarchy.xml b/android/app/src/main/res/xml/settings_hierarchy.xml index 3e10f1bd..19501773 100644 --- a/android/app/src/main/res/xml/settings_hierarchy.xml +++ b/android/app/src/main/res/xml/settings_hierarchy.xml @@ -3,7 +3,7 @@ Date: Fri, 22 May 2020 11:01:03 +0300 Subject: [PATCH 53/63] 7.3 update help and settings labels --- android/app/src/main/res/values/strings.xml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 4ca7697a..c11d919a 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -124,11 +124,16 @@
Практика: Повторение символов.
+ Настройки: Параметры приложения. +
+ В настройках можно включить ещё одну кнопку - \"сканировать QR-код\". +
Сканировать QR-код: если у Вас есть набор карточек с текстом Брайля и QR-кодами на обороте, по нажатию кнопки вы можете отсканировать QR-код и проверить, что написано на карточке. Для этого необходимо дополнительное приложение. Если его ещё нет, по нажатию кнопки Вы будете перенаправлены на страницу для скачивания приложения.
+
Находясь в любом разделе приложения, Вы всегда можете вызвать справку по данному разделу, нажав экранную кнопку \"справка\" в правом верхнем углу экрана. ]]> @@ -297,16 +302,17 @@ средством экранного чтения, не дожидаясь фокусировки на текстовом поле. - Дополнительные кнопки \"Выход\" + Кнопки навигации для незрячих - В главном меню кнопка \"Выход из приложения\", в контекстных меню кнопка \"Скрыть\". + В главном меню кнопка \"Выход из приложения\", в разделе \"Теория\" - + кнопка \"Скрыть это меню\" в списке \"Другие функции\". - Опция \"Сканирование qr-кода\" + Опция \"Сканирование QR-кода\" - Отображать в главном меню кнопку \"Сканирование qr-кода\". + Отображать в главном меню кнопку \"Сканирование QR-кода\" для игры с карточками. From cc436dfe04977f5532fe43433720201bcf8e6ad5 Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Fri, 22 May 2020 11:11:23 +0300 Subject: [PATCH 54/63] 7.3 update settings labels --- android/app/src/main/res/values/strings.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index c11d919a..073d904d 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -302,11 +302,12 @@ средством экранного чтения, не дожидаясь фокусировки на текстовом поле. - Кнопки навигации для незрячих + Дополнительные кнопки навигации В главном меню кнопка \"Выход из приложения\", в разделе \"Теория\" - - кнопка \"Скрыть это меню\" в списке \"Другие функции\". + кнопка \"Скрыть это меню\" в списке \"Другие функции\" (удобно при использовании программы + экранного доступа). Опция \"Сканирование QR-кода\" From 25743e4ebd778fc7fd4b63bbb7c7fd7b6f07b072 Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Fri, 22 May 2020 11:25:00 +0300 Subject: [PATCH 55/63] 7.6 fix wrong lesson 3 number --- .../github/braillesystems/learnbraille/res/GolubinaCourse.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt index 6a79d93d..4b8adba0 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt @@ -184,7 +184,7 @@ internal val golubinaIntroLessons by lessons { ) inputChars("БЕГ") +Info( - """Поздравляем! Второй урок пройден. В следующем занятии мы узнаем, + """Поздравляем! Третий урок пройден. В следующем занятии мы узнаем, как с помощью букв А, Б, Ц, Д, Е, Ф, Г составить цифры 1, 2, 3, 4, 5 и 6.""" ) } From 49bbd6a7c622c8b11c021b9d1742ba9c6fcff95a Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Fri, 22 May 2020 12:38:50 +0400 Subject: [PATCH 56/63] Disable MissingTranslation --- android/app/build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/android/app/build.gradle b/android/app/build.gradle index dd18a67b..b05f86e2 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -98,3 +98,9 @@ dependencies { //Settings implementation 'androidx.preference:preference:1.1.1' } + +android { + lintOptions { + disable 'MissingTranslation' + } +} From 68807b1875abae610f213b948bb3ce675dd5cace Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Fri, 22 May 2020 11:58:07 +0300 Subject: [PATCH 57/63] 7.6 extend help in lessons --- android/app/src/main/res/values/strings.xml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 073d904d..a5114c21 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -174,17 +174,20 @@ Переход к следующему шагу - кнопка вперёд справа по центру, + Переход к следующему шагу - кнопка \"вперёд\" справа по центру, к предыдущему - кнопка назад слева по центру.
- Выход в меню приложения - кнопка перейти вверх в верхней панели слева. + Выход в меню приложения - кнопка \"перейти вверх\" в верхней панели слева.
Вызов справки - кнопка в верхней панели справа.
- Другие функции - кнопка в верхней панели рядом с кнопкой справка. - Нажатие этой кнопки вызывает выпадающее меню с функциями навигация по курсу, то есть - переход к другому уроку, и к текущему шагу, то есть переход к последнему + \"Другие функции\" - кнопка в верхней панели рядом с кнопкой \"справка\". + Нажатие этой кнопки вызывает выпадающее меню с функциями \"навигация по курсу\", то есть + переход к другому уроку, и \"к текущему шагу\", то есть переход к последнему непройденному шагу. +
+ Чтобы урок стал доступен в меню \"навигация по курсу\", надо пройти все шаги в + предыдущих уроках. В уроке обычно от 20 до 30 шагов. ]]>
From c85eb584cc7b5085e334fe9717681d52d1b95c2e Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Fri, 22 May 2020 12:22:43 +0300 Subject: [PATCH 58/63] 7.6 slightly change lessons content --- .../learnbraille/data/db/Database.kt | 2 +- .../learnbraille/res/GolubinaCourse.kt | 91 +++++++------------ 2 files changed, 36 insertions(+), 57 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt index caf8770d..198c7b1b 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/db/Database.kt @@ -25,7 +25,7 @@ import timber.log.Timber Course::class, Lesson::class, Step::class, StepAnnotation::class, StepHasAnnotation::class, CurrentStep::class, LastCourseStep::class, LastLessonStep::class ], - version = 15, + version = 16, exportSchema = false ) @TypeConverters( diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt index 4b8adba0..3697d5e3 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/GolubinaCourse.kt @@ -24,17 +24,15 @@ internal val golubinaIntroLessons by lessons { ) +Info( """ - Урок 1. Тренировка чтения и ввода отдельных комбинаций. + Урок 1. Тренировка чтения и ввода отдельных комбинаций.

- В рельефной азбуке Брайля любой символ - это шеститочие. - Каждая точка из шести может быть выдавлена или пропущена. - В следующих шагах Вы ознакомитесь со всеми точками.""" - ) - +Info( - """Точки расположены в два столбца по три. + В азбуке Брайля любой символ - это шеститочие. +
+ Точки расположены в два столбца по три. +
Точки в первом столбце имеют номера 1, 2, 3 сверху вниз. - Точки во втором столбце - 4, 5, 6 сверху вниз. - Важно выучить, где какая точка.""" + Во втором столбце - 4, 5, 6 сверху вниз. + """ ) +ShowDots( text = "Перед Вами символ Брайля -
" + @@ -99,7 +97,7 @@ internal val golubinaIntroLessons by lessons { В верхней строке 14 раз повторён символ полного шеститочия.""" ).annotate(StepAnnotation.golubinaBookRequired) +Info( - """Первый урок закончен. В следующем уроке мы изучим буквы А, Б, Ц, Д, Е, Ф, Г.""" + """Первый урок закончен. В следующем уроке мы изучим буквы А, Б, Ц, Д, Е.""" ) } @@ -108,7 +106,7 @@ internal val golubinaIntroLessons by lessons { ) { +Info( """ - Урок 2. Буквы А, Б, Ц, Д, Е. + Урок 2. Буквы А, Б, Ц, Д, Е.

Некоторые символы Брайля обозначают как букву, так и цифру. Это буквы А, Б, Ц и так далее. С добавлением цифрового знака, который мы изучим в следующем @@ -150,7 +148,8 @@ internal val golubinaIntroLessons by lessons { +Input(content.symbols.getValue('Е')/*, repeat = 5*/) +Info( """Урок 2 закончен. В следующем занятии займёмся повторением букв А, Б, Ц, Д, Е - и выучим буквы Ф, Г.""" + и выучим буквы Ф, Г. Рекомендуем в дополнение к уроку потренироваться + вводить изученные буквы в разделе "Практика".""" ) } lesson( @@ -158,7 +157,7 @@ internal val golubinaIntroLessons by lessons { ) { +Info( """ - Урок 3. Буквы Ф, Г. + Урок 3. Буквы Ф, Г.

Перед прохождением нового материала повторим пройденное. В следующих трёх шагах нужно ввести буквы Б, А, Ц (вместе это слово БАЦ).""" @@ -194,7 +193,7 @@ internal val golubinaIntroLessons by lessons { ) { +Info( """ - Урок 4. Цифры от 1 до 6. + Урок 4. Цифры от 1 до 6.

В этом уроке мы начнём изучать цифры. Но для начала повторим пройденное. В следующих шагах введите буквы, изученные в прошлых двух уроках.""" @@ -257,7 +256,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 5. Буква Х и цифра 8. + Урок 5. Буква Х и цифра 8.

По окончании этого урока Вы узнаете букву Х и цифру 8. Но перед изучением нового повторим пройденное. Введите по буквам слово ФЕБ. """ @@ -304,7 +303,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 6. Буквы Ж, И и цифры 9, 0 + Урок 6. Буквы Ж, И и цифры 9, 0

Буква И - это точки 2 и 4. Этот же символ с добавлением цифрового знака обозначает цифру 9. @@ -336,15 +335,6 @@ internal val someMoreGolubinaLessons by lessons { """Введите по символам число 850""" ) inputChars("]850") - +Info( - """Введите разные цифры""" - ) - +Input(content.symbols.getValue('1')) - +Input(content.symbols.getValue('3')) - +Input(content.symbols.getValue('2')) - +Input(content.symbols.getValue('6')) - +Input(content.symbols.getValue('9')) - +Input(content.symbols.getValue('7')) +Info( """ Урок 6 закончен. Теперь Вы, помимо букв, знаете все цифры. @@ -359,7 +349,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 7. Буквы К, Л, М + Урок 7. Буквы К, Л, М

До сих пор мы изучали только буквы, образованные верхними четырьмя точками, то есть точками 1, 2, 4, 5. В нескольких следующих уроках будут рассмотрены @@ -390,10 +380,6 @@ internal val someMoreGolubinaLessons by lessons { """Введите по буквам слово БЕЛКА""" ) inputChars("БЕЛКА") - +Info( - """Теперь введите отдельными символами слово ФЛАГ""" - ) - inputChars("ФЛАГ") +Info( """ Сегодня осталось изучить букву М. @@ -431,7 +417,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 8. Буква Н. + Урок 8. Буква Н.

Рекомендуем в дополнение к этому уроку изучить букву Н на страницах 21 и 22 букваря. @@ -477,7 +463,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 9. Буква О и знак препинания "Запятая". + Урок 9. Буква О и знак препинания "Запятая".

Материалы к этому уроку есть на страницах 23-24 в букваре.
@@ -499,10 +485,6 @@ internal val someMoreGolubinaLessons by lessons { """Наберите по буквам слово ОБЛАКО""" ) inputChars("ОБЛАКО") - +Info( - """Теперь введите по буквам: ОКНО""" - ) - inputChars("ОКНО") +Info( """ Кратко ознакомимся со знаком 'Запятая'. @@ -533,7 +515,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 10. Буква П. + Урок 10. Буква П.

Этот урок полезно закрепить, изучив страницу 25 в книге Голубиной.
@@ -577,7 +559,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 11. Буква Ч. + Урок 11. Буква Ч.

Буква Ч и слова с ней находятся на странице 26 в букваре Голубиной.
@@ -628,7 +610,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 12. Буква Р. + Урок 12. Буква Р.

Этому уроку соответствует страницы 27 и 28 букваря.
@@ -678,7 +660,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 13. Буква С. + Урок 13. Буква С.

Буква С изучается в букваре на странице 29.
@@ -731,7 +713,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 14. Буква Т и знак 'Дефис'. + Урок 14. Буква Т и знак 'Дефис'.

В букваре В. В. Голубиной Вы найдёте урок о букве Т на странице 30 и о знаке 'Дефис' на странице 31. @@ -763,9 +745,6 @@ internal val someMoreGolubinaLessons by lessons { """Введите по буквам слово ТОРТ""" ) inputChars("ТОРТ") - +Info( - """Введите буквы, составляющие слово ТЕАТР""" - ) +Info( """ С этого момента мы начинаем осваивать символы, содержащие точку 6. @@ -797,7 +776,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 15. Буква У. + Урок 15. Буква У.

В книге Голубиной буква У вводится на странице 32.
@@ -847,7 +826,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 16. Буква Щ. + Урок 16. Буква Щ.

Букварь содержит урок с буквой Щ на странице 33.
@@ -897,7 +876,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 17. Буква З. + Урок 17. Буква З.

В книге Голубиной букву З можно найти на странице 34.
@@ -947,7 +926,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 18. Буква "И краткое". + Урок 18. Буква "И краткое".

Букварь Голубиной содержит материал на тему буквы Й на странице 35.
@@ -1005,7 +984,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 19. Твёрдый знак и литературная точка. + Урок 19. Твёрдый знак и литературная точка.

Желательно во время или после занятия прочесть соответствующий раздел букваря на странице 36. @@ -1063,7 +1042,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 20. Буква Ы. + Урок 20. Буква Ы.

Если Вы хотите найти урок с буквой Ы в букваре, посетите страницы 37 и 38.
@@ -1118,7 +1097,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 21. Мягкий знак. + Урок 21. Мягкий знак.

В дополнение к этому уроку можно изучить страницы 39 и 40 в букваре.
@@ -1169,7 +1148,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 22. Буквы Ё и Ш. + Урок 22. Буквы Ё и Ш.

Вводный текст о буквах Ё и Ш можно найти в букваре на страницах 41 и 42.
@@ -1235,7 +1214,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 23. Буква 'Я'. + Урок 23. Буква 'Я'.

Изучению буквы 'Я' отведены страницы 43 и 44 в букваре Голубиной.
@@ -1288,7 +1267,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 24. Буква 'Ю'. + Урок 24. Буква 'Ю'.

Буква 'Ю' и слова с ней находятся на страницах 45 и 46 в букваре.
@@ -1345,7 +1324,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 25. Буква 'Э'. + Урок 25. Буква 'Э'.

Для развития памяти пальцев рекомендуется в дополнение к этому уроку просмотреть материал о букве Э на страницах 47 и 48 в букваре. @@ -1409,7 +1388,7 @@ internal val someMoreGolubinaLessons by lessons { ) { +Info( """ - Урок 26. Буква 'В'. + Урок 26. Буква 'В'.

Для более тщательного изучения буквы 'В' мы рекомендуем обратиться к букварю Голубиной, страницы 49 и 50. From f87e23222994e4718da2d6918e2779f08443ed1d Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Fri, 22 May 2020 13:51:35 +0400 Subject: [PATCH 59/63] Add separate intros for show steps --- .../learnbraille/res/Materials.kt | 85 +++++++++++++------ .../learnbraille/ui/screens/Messages.kt | 32 ++++--- .../ui/screens/practice/CardFragment.kt | 2 +- .../theory/steps/InputSymbolFragment.kt | 4 +- .../theory/steps/ShowSymbolFragment.kt | 3 +- .../learnbraille/ui/views/BigLetterView.kt | 41 ++++++++- .../app/src/main/res/layout/fragment_card.xml | 2 +- .../layout/fragment_lessons_input_symbol.xml | 2 +- .../layout/fragment_lessons_show_symbol.xml | 2 +- android/app/src/main/res/values/strings.xml | 13 ++- 10 files changed, 138 insertions(+), 48 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt index 73a27f3a..e16cf2e6 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt @@ -93,33 +93,66 @@ private val uebDigits by symbols(SymbolType.digit) { /** * Add here rules, how to display hints for symbols. */ -val Context.symbolTypeIntroList: List> by lazyWithContext { +object SymbolTypeIntro { + // Prevent lambda of capturing context that will be invalid next time fragment entered, // so use `Fragment.getString` outside of lambdas. - listOfP2F( - getString(R.string.input_letter_intro_template).let { - ruSymbols.map::containsKey to { c: Char -> it.format(c) } - }, - - getString(R.string.input_digit_intro_template).let { - uebDigits.map::containsKey to { c: Char -> it.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) + + val Context.input: List> by lazyWithContext { + listOfP2F( + getString(R.string.input_letter_intro_template).let { + ruSymbols.map::containsKey to { c: Char -> it.format(c) } + }, + + getString(R.string.input_digit_intro_template).let { + uebDigits.map::containsKey to { c: Char -> it.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) + } + } + }() + ) + } + + val Context.show: List> by lazyWithContext { + listOfP2F( + getString(R.string.show_letter_intro_template).let { + ruSymbols.map::containsKey to { c: Char -> it.format(c) } + }, + + getString(R.string.show_digit_intro_template).let { + uebDigits.map::containsKey to { c: Char -> it.format(c) } + }, + + { + val other = getString(R.string.show_special_intro_template) + val numSign = getString(R.string.show_special_intro_num_sign) + 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 -> + when (c) { + ']' -> numSign + '.' -> dotIntro + ',' -> commaIntro + '-' -> hyphenIntro + else -> other.format(c) + } } - } - }() - ) + }() + ) + } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt index c9dd61d7..34d0aa87 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt @@ -7,32 +7,44 @@ import com.github.braillesystems.learnbraille.data.entities.BrailleDots 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.res.symbolTypeIntroList +import com.github.braillesystems.learnbraille.res.SymbolTypeIntro import com.github.braillesystems.learnbraille.utils.checkedToast import com.github.braillesystems.learnbraille.utils.peek import com.github.braillesystems.learnbraille.utils.toast import timber.log.Timber +enum class IntroMode { + INPUT, SHOW +} + fun Fragment.showCorrectToast(): Unit = toast(getString(R.string.input_correct)) fun Fragment.showIncorrectToast(material: Material? = null): Unit = if (material == null) toast(getString(R.string.input_incorrect)) - else toast("${getString(R.string.input_incorrect)} ${introString(material).orEmpty()}") + else toast( + "${getString(R.string.input_incorrect)} " + + introString(material, IntroMode.INPUT).orEmpty() + ) fun Fragment.showHintDotsToast(expectedDots: BrailleDots) = checkedToast(getString(R.string.input_dots_hint_template).format(expectedDots.spelling)) -fun Context.introString(material: Material): String? = +fun Context.introString(material: Material, mode: IntroMode): String? = when (material.data) { - is Symbol -> symbolTypeIntroList.peek(material.data.char) + is Symbol -> SymbolTypeIntro.run { + when (mode) { + IntroMode.INPUT -> input.peek(material.data.char) + IntroMode.SHOW -> show.peek(material.data.char) + } + } } -fun Context.introStringNotNullLogged(material: Material): String = introString(material) - ?: "".also { Timber.e("Intro should be available") } +fun Context.introStringNotNullLogged(material: Material, mode: IntroMode): String = + introString(material, mode) ?: "".also { Timber.e("Intro should be available") } -fun Fragment.introString(material: Material): String? = +fun Fragment.introString(material: Material, mode: IntroMode): String? = (context ?: null.also { Timber.w("Context is not available") }) - ?.run { introString(material) } + ?.run { introString(material, mode) } -fun Fragment.introStringNotNullLogged(material: Material): String = - context?.introStringNotNullLogged(material) ?: error("Context is not available") +fun Fragment.introStringNotNullLogged(material: Material, mode: IntroMode): String = + context?.introStringNotNullLogged(material, mode) ?: error("Context is not available") diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt index f459e31b..ff6ea3c6 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt @@ -136,7 +136,7 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { private fun announceIntro(symbol: String) { require(symbol.length == 1) val material = dummyMaterialOf(symbol.first()) - val intro = introStringNotNullLogged(material) + val intro = introStringNotNullLogged(material, IntroMode.INPUT) checkedAnnounce(intro) } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt index 2fe9b909..96e0b5c0 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt @@ -54,7 +54,7 @@ class InputSymbolFragment : AbstractStepFragment(R.string.lessons_help_input_sym val symbol = step.data.material.data letter.text = symbol.char.toString() brailleDots.dotsState.display(symbol.brailleDots) - checkedAnnounce(introStringNotNullLogged(step.data.material)) + checkedAnnounce(introStringNotNullLogged(step.data.material, IntroMode.INPUT)) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_input_symbol) setHasOptionsMenu(true) @@ -115,7 +115,7 @@ class InputSymbolFragment : AbstractStepFragment(R.string.lessons_help_input_sym viewModel.observeEventPassHint( viewLifecycleOwner, dotsState ) { - val msg = introStringNotNullLogged(step.data.material) + val msg = introStringNotNullLogged(step.data.material, IntroMode.INPUT) announce(msg) } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt index 2683d008..9e0f16d9 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt @@ -8,6 +8,7 @@ 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.screens.IntroMode import com.github.braillesystems.learnbraille.ui.screens.introStringNotNullLogged import com.github.braillesystems.learnbraille.ui.screens.theory.getStepArg import com.github.braillesystems.learnbraille.ui.screens.theory.toNextStep @@ -37,7 +38,7 @@ class ShowSymbolFragment : AbstractStepFragment(R.string.lessons_help_show_symbo require(step.data.material.data is Symbol) letter.text = step.data.material.data.char.toString() brailleDots.dotsState.display(step.data.material.data.brailleDots) - checkedAnnounce(introStringNotNullLogged(step.data.material)) + checkedAnnounce(introStringNotNullLogged(step.data.material, IntroMode.SHOW)) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_show_symbol) setHasOptionsMenu(true) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt index 940cd819..995312c7 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt @@ -5,10 +5,11 @@ import android.util.AttributeSet import android.widget.TextView import androidx.core.widget.addTextChangedListener import com.github.braillesystems.learnbraille.data.entities.dummyMaterialOf +import com.github.braillesystems.learnbraille.ui.screens.IntroMode import com.github.braillesystems.learnbraille.ui.screens.introString import timber.log.Timber -class BigLetterView : TextView { +open class BigLetterView : TextView { constructor(context: Context) : super(context) @@ -20,13 +21,13 @@ class BigLetterView : TextView { context, attrSet, defStyleAttr ) - init { + protected fun addContextListener(mode: IntroMode) { addTextChangedListener( afterTextChanged = { text -> if (text == null) return@addTextChangedListener require(text.length == 1) contentDescription = context.introString( - dummyMaterialOf(text.first()) + dummyMaterialOf(text.first()), mode ) ?: text.first().toLowerCase().toString().also { Timber.e("Symbol intro not found: $text") } @@ -34,3 +35,37 @@ class BigLetterView : TextView { ) } } + +class InputBigLetterView : BigLetterView { + + constructor(context: Context) : super(context) + + constructor(context: Context, attrSet: AttributeSet) : super(context, attrSet) + + constructor( + context: Context, attrSet: AttributeSet, defStyleAttr: Int + ) : super( + context, attrSet, defStyleAttr + ) + + init { + addContextListener(IntroMode.INPUT) + } +} + +class ShowBigLetterView : BigLetterView { + + constructor(context: Context) : super(context) + + constructor(context: Context, attrSet: AttributeSet) : super(context, attrSet) + + constructor( + context: Context, attrSet: AttributeSet, defStyleAttr: Int + ) : super( + context, attrSet, defStyleAttr + ) + + init { + addContextListener(IntroMode.SHOW) + } +} diff --git a/android/app/src/main/res/layout/fragment_card.xml b/android/app/src/main/res/layout/fragment_card.xml index 776a7a3f..6b9d3a9e 100644 --- a/android/app/src/main/res/layout/fragment_card.xml +++ b/android/app/src/main/res/layout/fragment_card.xml @@ -16,7 +16,7 @@ android:layout_height="match_parent" tools:context=".ui.screens.practice.CardFragment"> - - - Правильно!
Неправильно! + Ответ: точки %s + Подождите, задание загружается + Введите букву: %s Введите цифру: %s Введите символ: %s @@ -29,8 +32,14 @@ Введите символ: Литературная точка Введите символ: Дефис Введите запятую - Ответ: точки %s - Подождите, задание загружается + + Буква: %s + Цифра: %s + Символ: %s + Цифровой знак + Литературная точка + Дефис + Запятая From 8e72f15c0dac5093bc625fb993d9f195c63a00a3 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Fri, 22 May 2020 14:30:31 +0400 Subject: [PATCH 60/63] Move Messages to UI package --- .../braillesystems/learnbraille/ui/{screens => }/Messages.kt | 2 +- .../learnbraille/ui/screens/practice/CardFragment.kt | 1 + .../learnbraille/ui/screens/theory/steps/InputDotsFragment.kt | 3 +++ .../ui/screens/theory/steps/InputSymbolFragment.kt | 1 + .../ui/screens/theory/steps/ShowSymbolFragment.kt | 4 ++-- .../braillesystems/learnbraille/ui/views/BigLetterView.kt | 4 ++-- 6 files changed, 10 insertions(+), 5 deletions(-) rename android/app/src/main/java/com/github/braillesystems/learnbraille/ui/{screens => }/Messages.kt (97%) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/Messages.kt similarity index 97% rename from android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt rename to android/app/src/main/java/com/github/braillesystems/learnbraille/ui/Messages.kt index 34d0aa87..54fd0de6 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/Messages.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/Messages.kt @@ -1,4 +1,4 @@ -package com.github.braillesystems.learnbraille.ui.screens +package com.github.braillesystems.learnbraille.ui import android.content.Context import androidx.fragment.app.Fragment diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt index ff6ea3c6..727739e5 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt @@ -12,6 +12,7 @@ import com.github.braillesystems.learnbraille.data.entities.dummyMaterialOf import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository import com.github.braillesystems.learnbraille.databinding.FragmentCardBinding import com.github.braillesystems.learnbraille.res.deckTagToName +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.* diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt index 6cbc5d2b..0980f29b 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt @@ -19,6 +19,9 @@ import com.github.braillesystems.learnbraille.ui.screens.* 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 diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt index 96e0b5c0..7e6cf6a5 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt @@ -14,6 +14,7 @@ import com.github.braillesystems.learnbraille.data.entities.Input import com.github.braillesystems.learnbraille.data.entities.Symbol import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository import com.github.braillesystems.learnbraille.databinding.FragmentLessonsInputSymbolBinding +import com.github.braillesystems.learnbraille.ui.* import com.github.braillesystems.learnbraille.ui.screens.* import com.github.braillesystems.learnbraille.ui.screens.theory.getStepArg import com.github.braillesystems.learnbraille.ui.screens.theory.toNextStep diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt index 9e0f16d9..806a9288 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt @@ -8,8 +8,8 @@ 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.screens.IntroMode -import com.github.braillesystems.learnbraille.ui.screens.introStringNotNullLogged +import com.github.braillesystems.learnbraille.ui.IntroMode +import com.github.braillesystems.learnbraille.ui.introStringNotNullLogged 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 diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt index 995312c7..2b6529b3 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt @@ -5,8 +5,8 @@ import android.util.AttributeSet import android.widget.TextView import androidx.core.widget.addTextChangedListener import com.github.braillesystems.learnbraille.data.entities.dummyMaterialOf -import com.github.braillesystems.learnbraille.ui.screens.IntroMode -import com.github.braillesystems.learnbraille.ui.screens.introString +import com.github.braillesystems.learnbraille.ui.IntroMode +import com.github.braillesystems.learnbraille.ui.introString import timber.log.Timber open class BigLetterView : TextView { From 15724a34077d03748f2b64f9736cd10e8e50ef76 Mon Sep 17 00:00:00 2001 From: Andrey Stoyan Date: Fri, 22 May 2020 15:43:32 +0400 Subject: [PATCH 61/63] Add rules mechanism --- .../learnbraille/data/entities/Materials.kt | 4 - .../learnbraille/res/Materials.kt | 141 +++++++++--------- .../learnbraille/ui/Messages.kt | 38 ++--- .../ui/screens/practice/CardFragment.kt | 11 +- .../screens/theory/steps/InputDotsFragment.kt | 5 +- .../theory/steps/InputSymbolFragment.kt | 11 +- .../theory/steps/ShowSymbolFragment.kt | 6 +- .../learnbraille/ui/views/BigLetterView.kt | 21 ++- .../learnbraille/utils/_Kotlin.kt | 19 ++- 9 files changed, 133 insertions(+), 123 deletions(-) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Materials.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Materials.kt index d88f8596..652cf664 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Materials.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/data/entities/Materials.kt @@ -1,7 +1,6 @@ package com.github.braillesystems.learnbraille.data.entities import androidx.room.* -import com.github.braillesystems.learnbraille.data.dsl.DEFAULT_ID import kotlinx.serialization.Serializable @@ -24,6 +23,3 @@ interface MaterialDao { @Query("select * from materials order by RANDOM() limit 1") suspend fun getRandomMaterial(): Material? } - -fun dummyMaterialOf(symbol: Char): Material = - Material(DEFAULT_ID, Symbol(symbol, BrailleDots(), "")) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt index e16cf2e6..9b8a4a22 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/res/Materials.kt @@ -8,9 +8,7 @@ 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.utils.P2F -import com.github.braillesystems.learnbraille.utils.lazyWithContext -import com.github.braillesystems.learnbraille.utils.listOfP2F +import com.github.braillesystems.learnbraille.utils.rules /** @@ -28,6 +26,76 @@ val knownMaterials by known( 'А', 'Б', 'Ц', 'Д', 'Е', 'Ф', 'Г' ) +/* + * Add here rules, how to display hints for symbols. + * + * Prevent lambda of capturing context that will be invalid next time fragment entered, + * so use `Fragment.getString` outside of lambdas. + */ + +val Context.inputSymbolPrintRules by rules( + { + getString(R.string.input_letter_intro_template).let { + ruSymbols.map::containsKey to { c: Char -> it.format(c) } + } + }, + + { + getString(R.string.input_digit_intro_template).let { + uebDigits.map::containsKey to { c: Char -> it.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) + } + } + } +) + +val Context.showSymbolPrintRules by rules( + { + getString(R.string.show_letter_intro_template).let { + ruSymbols.map::containsKey to { c: Char -> it.format(c) } + } + }, + + { + getString(R.string.show_digit_intro_template).let { + uebDigits.map::containsKey to { c: Char -> it.format(c) } + } + }, + + { + val other = getString(R.string.show_special_intro_template) + val numSign = getString(R.string.show_special_intro_num_sign) + 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 -> + when (c) { + ']' -> numSign + '.' -> dotIntro + ',' -> commaIntro + '-' -> hyphenIntro + else -> other.format(c) + } + } + } +) + + object SymbolType { const val ru = "ru" const val special = "special" @@ -89,70 +157,3 @@ private val uebDigits by symbols(SymbolType.digit) { symbol(char = '9', brailleDots = BrailleDots(E, F, E, F, E, E)) symbol(char = '0', brailleDots = BrailleDots(E, F, E, F, F, E)) } - -/** - * Add here rules, how to display hints for symbols. - */ -object SymbolTypeIntro { - - // Prevent lambda of capturing context that will be invalid next time fragment entered, - // so use `Fragment.getString` outside of lambdas. - - val Context.input: List> by lazyWithContext { - listOfP2F( - getString(R.string.input_letter_intro_template).let { - ruSymbols.map::containsKey to { c: Char -> it.format(c) } - }, - - getString(R.string.input_digit_intro_template).let { - uebDigits.map::containsKey to { c: Char -> it.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) - } - } - }() - ) - } - - val Context.show: List> by lazyWithContext { - listOfP2F( - getString(R.string.show_letter_intro_template).let { - ruSymbols.map::containsKey to { c: Char -> it.format(c) } - }, - - getString(R.string.show_digit_intro_template).let { - uebDigits.map::containsKey to { c: Char -> it.format(c) } - }, - - { - val other = getString(R.string.show_special_intro_template) - val numSign = getString(R.string.show_special_intro_num_sign) - 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 -> - when (c) { - ']' -> numSign - '.' -> dotIntro - ',' -> commaIntro - '-' -> hyphenIntro - else -> other.format(c) - } - } - }() - ) - } -} diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/Messages.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/Messages.kt index 54fd0de6..2d06111c 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/Messages.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/Messages.kt @@ -4,47 +4,41 @@ import android.content.Context import androidx.fragment.app.Fragment import com.github.braillesystems.learnbraille.R import com.github.braillesystems.learnbraille.data.entities.BrailleDots -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.res.SymbolTypeIntro +import com.github.braillesystems.learnbraille.res.inputSymbolPrintRules +import com.github.braillesystems.learnbraille.res.showSymbolPrintRules import com.github.braillesystems.learnbraille.utils.checkedToast -import com.github.braillesystems.learnbraille.utils.peek import com.github.braillesystems.learnbraille.utils.toast import timber.log.Timber -enum class IntroMode { +enum class PrintMode { INPUT, SHOW } fun Fragment.showCorrectToast(): Unit = toast(getString(R.string.input_correct)) -fun Fragment.showIncorrectToast(material: Material? = null): Unit = - if (material == null) toast(getString(R.string.input_incorrect)) +fun Fragment.showIncorrectToast(c: Char? = null): Unit = + if (c == null) toast(getString(R.string.input_incorrect)) else toast( "${getString(R.string.input_incorrect)} " + - introString(material, IntroMode.INPUT).orEmpty() + printString(c, PrintMode.INPUT).orEmpty() ) fun Fragment.showHintDotsToast(expectedDots: BrailleDots) = checkedToast(getString(R.string.input_dots_hint_template).format(expectedDots.spelling)) -fun Context.introString(material: Material, mode: IntroMode): String? = - when (material.data) { - is Symbol -> SymbolTypeIntro.run { - when (mode) { - IntroMode.INPUT -> input.peek(material.data.char) - IntroMode.SHOW -> show.peek(material.data.char) - } - } +fun Context.printString(c: Char, mode: PrintMode): String? = + when (mode) { + PrintMode.INPUT -> inputSymbolPrintRules[c] + PrintMode.SHOW -> showSymbolPrintRules[c] } -fun Context.introStringNotNullLogged(material: Material, mode: IntroMode): String = - introString(material, mode) ?: "".also { Timber.e("Intro should be available") } +fun Context.printStringNotNullLogged(c: Char, mode: PrintMode): String = + printString(c, mode) ?: "".also { Timber.e("Intro should be available") } -fun Fragment.introString(material: Material, mode: IntroMode): String? = +fun Fragment.printString(c: Char, mode: PrintMode): String? = (context ?: null.also { Timber.w("Context is not available") }) - ?.run { introString(material, mode) } + ?.run { printString(c, mode) } -fun Fragment.introStringNotNullLogged(material: Material, mode: IntroMode): String = - context?.introStringNotNullLogged(material, mode) ?: error("Context is not available") +fun Fragment.printStringNotNullLogged(c: Char, mode: PrintMode): String = + context?.printStringNotNullLogged(c, mode) ?: error("Context is not available") diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt index 727739e5..2435695f 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/practice/CardFragment.kt @@ -8,7 +8,6 @@ import androidx.databinding.DataBindingUtil import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import com.github.braillesystems.learnbraille.R -import com.github.braillesystems.learnbraille.data.entities.dummyMaterialOf import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository import com.github.braillesystems.learnbraille.databinding.FragmentCardBinding import com.github.braillesystems.learnbraille.res.deckTagToName @@ -93,7 +92,7 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { ) { viewModel.symbol.value?.let { symbol -> require(symbol.length == 1) - showIncorrectToast(dummyMaterialOf(symbol.first())) + showIncorrectToast(symbol.first()) } ?: checkedToast(getString(R.string.input_loading)) updateTitle(title) } @@ -107,8 +106,9 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { viewModel.observeEventPassHint( viewLifecycleOwner, dotsState ) { - val symbol = viewModel.symbol.value ?: return@observeEventPassHint - announceIntro(symbol) + viewModel.symbol.value?.let { + announceIntro(it) + } } viewModel.symbol.observe( @@ -136,8 +136,7 @@ class CardFragment : AbstractFragmentWithHelp(R.string.practice_help) { private fun announceIntro(symbol: String) { require(symbol.length == 1) - val material = dummyMaterialOf(symbol.first()) - val intro = introStringNotNullLogged(material, IntroMode.INPUT) + val intro = printStringNotNullLogged(symbol.first(), PrintMode.INPUT) checkedAnnounce(intro) } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt index 0980f29b..e4f0ed75 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputDotsFragment.kt @@ -15,7 +15,10 @@ import com.github.braillesystems.learnbraille.data.entities.InputDots import com.github.braillesystems.learnbraille.data.entities.spelling import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository import com.github.braillesystems.learnbraille.databinding.FragmentLessonsInputDotsBinding -import com.github.braillesystems.learnbraille.ui.screens.* +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 diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt index 7e6cf6a5..eace0e97 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/InputSymbolFragment.kt @@ -15,7 +15,10 @@ import com.github.braillesystems.learnbraille.data.entities.Symbol import com.github.braillesystems.learnbraille.data.repository.PreferenceRepository import com.github.braillesystems.learnbraille.databinding.FragmentLessonsInputSymbolBinding import com.github.braillesystems.learnbraille.ui.* -import com.github.braillesystems.learnbraille.ui.screens.* +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 @@ -55,7 +58,7 @@ class InputSymbolFragment : AbstractStepFragment(R.string.lessons_help_input_sym val symbol = step.data.material.data letter.text = symbol.char.toString() brailleDots.dotsState.display(symbol.brailleDots) - checkedAnnounce(introStringNotNullLogged(step.data.material, IntroMode.INPUT)) + checkedAnnounce(printStringNotNullLogged(symbol.char, PrintMode.INPUT)) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_input_symbol) setHasOptionsMenu(true) @@ -99,7 +102,7 @@ class InputSymbolFragment : AbstractStepFragment(R.string.lessons_help_input_sym viewLifecycleOwner, dotsState ) { val notify = { - showIncorrectToast(step.data.material) + showIncorrectToast(symbol.char) buzzer.checkedBuzz(preferenceRepository.incorrectBuzzPattern, preferenceRepository) } if (userTouchedDots) notify() @@ -116,7 +119,7 @@ class InputSymbolFragment : AbstractStepFragment(R.string.lessons_help_input_sym viewModel.observeEventPassHint( viewLifecycleOwner, dotsState ) { - val msg = introStringNotNullLogged(step.data.material, IntroMode.INPUT) + val msg = printStringNotNullLogged(symbol.char, PrintMode.INPUT) announce(msg) } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt index 806a9288..5052b98b 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/screens/theory/steps/ShowSymbolFragment.kt @@ -8,8 +8,8 @@ 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.IntroMode -import com.github.braillesystems.learnbraille.ui.introStringNotNullLogged +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.screens.theory.toNextStep import com.github.braillesystems.learnbraille.ui.screens.theory.toPrevStep @@ -38,7 +38,7 @@ class ShowSymbolFragment : AbstractStepFragment(R.string.lessons_help_show_symbo require(step.data.material.data is Symbol) letter.text = step.data.material.data.char.toString() brailleDots.dotsState.display(step.data.material.data.brailleDots) - checkedAnnounce(introStringNotNullLogged(step.data.material, IntroMode.SHOW)) + checkedAnnounce(printStringNotNullLogged(step.data.material.data.char, PrintMode.SHOW)) updateStepTitle(step.lessonId, step.id, R.string.lessons_title_show_symbol) setHasOptionsMenu(true) diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt index 2b6529b3..e6b908b2 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/ui/views/BigLetterView.kt @@ -4,9 +4,8 @@ import android.content.Context import android.util.AttributeSet import android.widget.TextView import androidx.core.widget.addTextChangedListener -import com.github.braillesystems.learnbraille.data.entities.dummyMaterialOf -import com.github.braillesystems.learnbraille.ui.IntroMode -import com.github.braillesystems.learnbraille.ui.introString +import com.github.braillesystems.learnbraille.ui.PrintMode +import com.github.braillesystems.learnbraille.ui.printString import timber.log.Timber open class BigLetterView : TextView { @@ -21,16 +20,16 @@ open class BigLetterView : TextView { context, attrSet, defStyleAttr ) - protected fun addContextListener(mode: IntroMode) { + protected fun addContextListener(mode: PrintMode) { addTextChangedListener( afterTextChanged = { text -> if (text == null) return@addTextChangedListener require(text.length == 1) - contentDescription = context.introString( - dummyMaterialOf(text.first()), mode - ) ?: text.first().toLowerCase().toString().also { - Timber.e("Symbol intro not found: $text") - } + contentDescription = context + .printString(text.first(), mode) + ?: text.first().toLowerCase().toString().also { + Timber.e("Symbol intro not found: $text") + } } ) } @@ -49,7 +48,7 @@ class InputBigLetterView : BigLetterView { ) init { - addContextListener(IntroMode.INPUT) + addContextListener(PrintMode.INPUT) } } @@ -66,6 +65,6 @@ class ShowBigLetterView : BigLetterView { ) init { - addContextListener(IntroMode.SHOW) + addContextListener(PrintMode.SHOW) } } diff --git a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Kotlin.kt b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Kotlin.kt index 555a1ee3..cce418c0 100644 --- a/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Kotlin.kt +++ b/android/app/src/main/java/com/github/braillesystems/learnbraille/utils/_Kotlin.kt @@ -29,14 +29,29 @@ inline fun T?.executeIf(cond: Boolean, block: T.() -> Unit) { if (this != null && cond) block() } + typealias P2F = Pair<(T) -> Boolean, (T) -> R> -fun List>.peek(key: T): R? { +fun listOfP2F(vararg p2f: P2F): List> = p2f.toList() + +fun Iterable>.peek(key: T): R? { forEach { (p, f) -> if (p(key)) return f(key) } return null } -fun listOfP2F(vararg p2f: P2F): List> = p2f.toList() + +typealias Rule = P2F + +class rules(private vararg val ruleProviders: C.() -> Rule) { + var value: Rules? = null + operator fun getValue(thisRef: C, property: KProperty<*>): Rules = + value ?: Rules(ruleProviders.map { thisRef.it() }).also { value = it } +} + +class Rules(private val rules: Iterable>) { + operator fun get(x: T): R? = rules.peek(x) +} + class lazyWithContext(private val getter: C.(String) -> R) { var value: R? = null From 4dd9f029300ea76413fc04d8d7491c9b5765aadc Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Fri, 22 May 2020 15:30:51 +0300 Subject: [PATCH 62/63] 7.11 shorten setting label for small screens --- android/app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index b19956d0..591d48e8 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -314,7 +314,7 @@ средством экранного чтения, не дожидаясь фокусировки на текстовом поле. - Дополнительные кнопки навигации + Добавочные кнопки навигации В главном меню кнопка \"Выход из приложения\", в разделе \"Теория\" - From ce5134c9743e7a6766a05058d900d7b9d036f12f Mon Sep 17 00:00:00 2001 From: Valerii Zuev Date: Fri, 22 May 2020 15:32:45 +0300 Subject: [PATCH 63/63] 7.11 update version tag and index --- android/app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index b05f86e2..a7f547d3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -24,8 +24,8 @@ android { applicationId "com.github.braillesystems.learnbraille" minSdkVersion 19 targetSdkVersion 29 - versionCode 4 - versionName "0.6.3" + versionCode 5 + versionName "1.0.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true multiDexEnabled = true