Initial commit

This commit is contained in:
Sebastian Seedorf
2020-12-06 20:17:19 +01:00
commit d9d598b021
83 changed files with 3527 additions and 0 deletions

1
app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

199
app/app.iml Normal file

File diff suppressed because one or more lines are too long

43
app/build.gradle Normal file
View File

@@ -0,0 +1,43 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
defaultConfig {
applicationId "de.sebse.mentalarithmetic"
minSdkVersion 15
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation "androidx.cardview:cardview:1.0.0"
implementation "androidx.gridlayout:gridlayout:1.0.0"
implementation "androidx.preference:preference:1.1.0"
implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation 'com.google.android.material:material:1.2.0-alpha03'
implementation 'me.zhanghai.android.materialprogressbar:library:1.6.1'
implementation 'me.relex:circleindicator:2.1.4'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

21
app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,24 @@
package de.sebse.mentalarithmetic
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("de.sebse.mentalarithmetic", appContext.packageName)
}
}

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.sebse.mentalarithmetic">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,100 @@
package de.sebse.mentalarithmetic
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import de.sebse.mentalarithmetic.fragments.*
import de.sebse.mentalarithmetic.types.ItemObject
import de.sebse.mentalarithmetic.types.TaskObject
import de.sebse.mentalarithmetic.types.TipObject
class MainActivity : AppCompatActivity(), TaskItemFragment.OnListFragmentInteractionListener,
TaskFragment.IGameListener, SummaryFragment.IGameSummaryListener {
var currentFragment: Fragment? = null
val CURRENT_FRAGMENT_STATE: String = "CURRENT_FRAGMENT_STATE"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
savedInstanceState?.let {
//Restore the fragment's instance
Log.d("APP", "here1")
val fragment = supportFragmentManager.getFragment(savedInstanceState, CURRENT_FRAGMENT_STATE)
fragment?.let {
Log.d("APP", "here")
val ft: FragmentTransaction = supportFragmentManager.beginTransaction()
ft.replace(R.id.fragment_holder, fragment)
ft.commit()
currentFragment = fragment
return
}
}
val fragment = TaskPagerFragment.newInstance()
val ft: FragmentTransaction = supportFragmentManager.beginTransaction()
ft.replace(R.id.fragment_holder, fragment)
ft.commit()
currentFragment = fragment
}
override fun onListFragmentInteraction(item: ItemObject) {
val fragment: Fragment? = if (item is TaskObject) {
item.start(applicationContext)
TaskFragment.newInstance(item)
} else if (item is TipObject) {
TipFragment.newInstance(item)
} else {
null
}
if (fragment != null) {
val ft: FragmentTransaction = supportFragmentManager.beginTransaction()
ft.replace(R.id.fragment_holder, fragment)
ft.commit()
currentFragment = fragment
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
currentFragment?.let { supportFragmentManager.putFragment(outState, CURRENT_FRAGMENT_STATE, it) }
}
override fun onGameFinished(item: TaskObject) {
val fragment = SummaryFragment.newInstance(item)
val ft: FragmentTransaction = supportFragmentManager.beginTransaction()
ft.replace(R.id.fragment_holder, fragment)
ft.commit()
currentFragment = fragment
}
override fun onGameSummaryRetry(item: TaskObject) {
onListFragmentInteraction(item)
}
override fun onGameSummaryFinished() {
goToList()
}
fun goToList() {
val fragment = TaskPagerFragment.newInstance()
val ft: FragmentTransaction = supportFragmentManager.beginTransaction()
ft.replace(R.id.fragment_holder, fragment)
ft.commit()
currentFragment = fragment
}
override fun onBackPressed() {
if (currentFragment is TaskPagerFragment) {
if (!(currentFragment as TaskPagerFragment).previous()) {
super.onBackPressed()
}
} else {
goToList()
}
}
}

View File

@@ -0,0 +1,96 @@
package de.sebse.mentalarithmetic.fragments
/*import android.inputmethodservice.Keyboard
import android.inputmethodservice.KeyboardView
import android.inputmethodservice.KeyboardView.OnKeyboardActionListener*/
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.fragment.app.Fragment
import de.sebse.mentalarithmetic.R
import de.sebse.mentalarithmetic.types.ItemFactory
import de.sebse.mentalarithmetic.types.TaskObject
/**
* A simple [Fragment] subclass.
* Use the [SummaryFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class SummaryFragment : Fragment() {
private lateinit var listener: IGameSummaryListener
private var item: TaskObject? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("SummaryFragment", (savedInstanceState != null).toString())
arguments?.let {
val param1 = it.getString(ARG_PARAM1)
item = ItemFactory.ITEM_MAP[param1] as TaskObject
}
savedInstanceState?.let {
val param1 = it.getString(ARG_PARAM1)
item = ItemFactory.ITEM_MAP[param1] as TaskObject
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_summary, container, false)?.also { view ->
view.findViewById<Button>(R.id.retry).setOnClickListener{v -> item?.let {this.listener.onGameSummaryRetry(it)} }
view.findViewById<Button>(R.id.exit).setOnClickListener{v -> this.listener.onGameSummaryFinished() }
view.findViewById<TextView>(R.id.time).text = (item?.getLiveTime() ?: "").toString()
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
item?.let { outState.putString(ARG_PARAM1, it.ID) }
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is IGameSummaryListener) {
this.listener = context
} else {
throw RuntimeException(context.toString() + " must implement IGameSummaryListener")
}
}
interface IGameSummaryListener {
fun onGameSummaryRetry(item: TaskObject)
fun onGameSummaryFinished()
}
companion object {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @return A new instance of fragment SummaryFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: TaskObject) =
SummaryFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1.ID)
}
}
}
}

View File

@@ -0,0 +1,261 @@
package de.sebse.mentalarithmetic.fragments
/*import android.inputmethodservice.Keyboard
import android.inputmethodservice.KeyboardView
import android.inputmethodservice.KeyboardView.OnKeyboardActionListener*/
import android.content.Context
import android.graphics.Typeface
import android.os.Bundle
import android.os.Handler
import android.text.Spannable
import android.text.SpannableString
import android.text.style.StrikethroughSpan
import android.text.style.StyleSpan
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import de.sebse.mentalarithmetic.R
import de.sebse.mentalarithmetic.types.ItemFactory
import de.sebse.mentalarithmetic.types.TaskObject
import de.sebse.mentalarithmetic.utils.BoardView
import de.sebse.mentalarithmetic.utils.Keyboard
import kotlinx.android.synthetic.main.fragment_task.*
import me.zhanghai.android.materialprogressbar.MaterialProgressBar
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
/**
* A simple [Fragment] subclass.
* Use the [TaskFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class TaskFragment : Fragment() {
//private var mKeyboardView: KeyboardView? = null
//private var mKeyboard: Keyboard? = null
// TODO: Rename and change types of parameters
private var item: TaskObject? = null
private var inputView: TextView? = null
private var questionView: TextView? = null
private var questionCorrectView: TextView? = null
private var questionCountView: TextView? = null
private var boardView: BoardView? = null
private var gameListener: IGameListener? = null
interface IGameListener {
fun onGameFinished(item: TaskObject)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("TaskFragment", (savedInstanceState != null).toString())
arguments?.let {
val param1 = it.getString(ARG_PARAM1)
item = ItemFactory.ITEM_MAP[param1] as TaskObject
}
savedInstanceState?.let {
val param1 = it.getString(ARG_PARAM1)
item = ItemFactory.ITEM_MAP[param1] as TaskObject
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_task, container, false)?.also { view ->
item?.let {
view.findViewById<TextView>(R.id.name).apply {
text = getString(it.NAME)
}
questionView = view.findViewById<TextView>(R.id.question).apply {
text = ""
}
questionCountView = view.findViewById<TextView>(R.id.question_count).apply {
text = ""
}
questionCorrectView = view.findViewById<TextView>(R.id.question_correct).apply {
text = ""
}
inputView = view.findViewById<TextView>(R.id.answer).apply {
text = ""
}
boardView = view.findViewById<BoardView>(R.id.keyboardview).apply {
setBtnListener(::btnListener)
setSubmitListener(::submitListener)
}
view.findViewById<TextView>(R.id.time).let { time ->
view.findViewById<MaterialProgressBar>(R.id.progress2).let { progress2 ->
val handler = Handler()
val runnable = object : Runnable {
override fun run() {
time.text = it.getLiveTime().toString()
val twentyPercent = it.SILVER_TIME - it.GOLD_TIME
val progress100 = it.GOLD_TIME - twentyPercent
val progress0 = it.GOLD_TIME + twentyPercent * 4
val prog = min(progress0, max(progress100, (it.getLiveTime() / (it.getCountCurrent()+1) * it.getCountTotal()).toInt()))
progress2.progress =
(100 - (prog - progress100) / (twentyPercent * 0.05)).roundToInt()
handler.postDelayed(this, 50)
}
}
handler.postDelayed(runnable, 1)
}}
}
setQuestion()
}
}
override fun onResume() {
super.onResume()
setQuestion()
}
private fun setQuestion() {
Log.d("ff", item?.getCurrect().toString())
item?.getCurrect()?.let {
questionView?.text = it.getQuestion()
view?.let { view ->
val keyboard = Keyboard(view.context, it.getKeyboard())
boardView?.setKeyboard(keyboard)
}
}
item?.let {
questionCountView?.text = getString(R.string.question_count, it.getCountCurrent())
questionCorrectView?.text = getString(R.string.question_currect, it.getCountCorrect(), it.COUNT)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
item?.let { outState.putString(ARG_PARAM1, it.ID) }
}
fun btnListener(code: Int) {
item?.let {
inputView?.let {v ->
v.text = it.getCurrect()?.translate(boardView?.getText() ?: "") ?: ""
}
it.getCurrect()?.let { current ->
if (!current.getMultipleInput()) {
submit(listOf(code))
}
}
}
}
fun submitListener(text: List<Int>) {
submit(text)
}
private fun submit(text: List<Int>) {
item?.let {
val answer = text.joinToString(separator = "")
Log.d("FRAG", answer)
val (valid, correct, question) = it.answer(answer, requireContext())
it.pause()
setAnswerColor(valid, correct, it)
keyboardview?.lock()
keyboardview?.reset()
Handler().postDelayed({
if (question != null) {
unsetAnswerColor()
setQuestion()
keyboardview?.unlock()
it.resume()
} else {
val time: Long = it.getLiveTime()
val score = it.getScore(requireContext())
if (time > Integer.MIN_VALUE && time < Integer.MAX_VALUE && (score == 0 || time < score)) {
it.setScore(requireContext(), time.toInt())
}
this.gameListener?.onGameFinished(it)
keyboardview?.unlock()
}
}, 300)
}
}
private fun setAnswerColor(valid: Boolean, correct: String, item: TaskObject) {
context?.let {context ->
val correctFormatted = item.getCurrect()?.translate(correct) ?: ""
val color = if (valid) {
ContextCompat.getColor(context, android.R.color.holo_green_light)
} else {
ContextCompat.getColor(context, android.R.color.holo_red_light)
}
keyboardview?.apply {
setBackgroundColor(color)
}
inputView?.apply {
setBackgroundColor(color)
if (!valid) {
text = SpannableString("$text $correctFormatted").apply {
setSpan(
StrikethroughSpan(),
0, text.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
setSpan(
StyleSpan(Typeface.BOLD),
text.length, "$text $correctFormatted".length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
}
}
}
private fun unsetAnswerColor() {
context?.let {context ->
val color = ContextCompat.getColor(context, R.color.colorPrimaryDark)
keyboardview?.apply {
setBackgroundColor(color)
}
inputView?.apply {
setBackgroundColor(color)
text = ""
}
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is IGameListener) {
this.gameListener = context
} else {
throw RuntimeException(context.toString() + " must implement IGameListener")
}
}
companion object {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @return A new instance of fragment TaskFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: TaskObject) =
TaskFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1.ID)
}
}
}
}

View File

@@ -0,0 +1,144 @@
package de.sebse.mentalarithmetic.fragments
import android.util.Log
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.google.android.material.textview.MaterialTextView
import de.sebse.mentalarithmetic.R
import de.sebse.mentalarithmetic.fragments.TaskItemFragment.OnListFragmentInteractionListener
import de.sebse.mentalarithmetic.types.ItemObject
import de.sebse.mentalarithmetic.types.TaskObject
import de.sebse.mentalarithmetic.types.TipObject
import de.sebse.mentalarithmetic.types.getString
import kotlinx.android.synthetic.main.fragment_taskitem.view.*
import kotlinx.android.synthetic.main.fragment_taskitem.view.content
import kotlinx.android.synthetic.main.fragment_tipitem.view.*
import me.zhanghai.android.materialprogressbar.MaterialProgressBar
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
/**
* [RecyclerView.Adapter] that can display a [TaskObject] and makes a call to the
* specified [OnListFragmentInteractionListener].
* TODO: Replace the implementation with code for your data type.
*/
class TaskItemAdapter(
private val mValues: List<ItemObject>,
private val mListener: OnListFragmentInteractionListener?
) : RecyclerView.Adapter<TaskItemAdapter.ViewHolder>() {
private val mOnClickListener: View.OnClickListener
init {
mOnClickListener = View.OnClickListener { v ->
val item = v.tag as ItemObject
// Notify the active callbacks interface (the activity, if the fragment is attached to
// one) that an item has been selected.
mListener?.onListFragmentInteraction(item)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return if (viewType == 0) ViewHolderTask(LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_taskitem, parent, false))
else {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_tipitem, parent, false)
Log.d("TIP", view.toString())
ViewHolderTip(view)
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = mValues[position]
holder.mContentView.text = holder.mView.context.getString(item.NAME)
if (item is TaskObject) {
holder as ViewHolderTask
holder.mPersonalTime.text = holder.mView.context.getString(R.string.time, item.getScore(holder.mView.context) / 1000, item.getScore(holder.mView.context) % 1000)
holder.mGoldTime.text = (item.GOLD_TIME / 1000).toString()
holder.mSilverTime.text = (item.SILVER_TIME / 1000).toString()
val score = item.getScore(holder.mView.context)
if (score > 0) {
val twentyPercent = item.SILVER_TIME - item.GOLD_TIME
val progress100 = item.GOLD_TIME - twentyPercent
val progress0 = item.GOLD_TIME + twentyPercent * 4
val progress = min(progress0, max(progress100, score))
holder.mProgressView.progress = (100 - (progress - progress100) / (twentyPercent * 0.05)).roundToInt()
if (score < item.SILVER_TIME) {
holder.mSilverTime.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_star_silver, 0, 0, 0)
}
if (score < item.GOLD_TIME) {
holder.mGoldTime.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_star_gold, 0, 0, 0)
}
}
} else {
holder as ViewHolderTip
holder.mDescription.text = holder.mView.context.getString(item.DESCRIPTION)
}
if (position > 0) {
var previous: TaskObject?
var pos = position
do {
pos--
previous = if (mValues[pos] is TaskObject) mValues[pos] as TaskObject else null
} while (previous == null && pos > 0)
if (previous != null && (previous.getScore(holder.mView.context) == 0 || previous.getScore(holder.mView.context) > previous.SILVER_TIME)) {
holder.mView.alpha = 0.5f
with(holder.mView) {
tag = item
setOnClickListener(mOnClickListener)
}
return
}
}
holder.mView.alpha = 1f
with(holder.mView) {
tag = item
setOnClickListener(mOnClickListener)
}
}
override fun getItemViewType(position: Int): Int {
val item = mValues[position]
return if (item is TaskObject) {
0
} else {
1
}
}
override fun getItemCount(): Int = mValues.size
open inner class ViewHolder(open val mView: View): RecyclerView.ViewHolder(mView) {
val mContentView: TextView by lazy { mView.content }
override fun toString(): String {
return super.toString() + " '" + mContentView.text + "'"
}
}
inner class ViewHolderTask(override val mView: View): ViewHolder(mView) {
val mProgressView: MaterialProgressBar = mView.progress2
val mGoldTime: MaterialTextView = mView.gold_time
val mSilverTime: MaterialTextView = mView.silver_time
val mPersonalTime: MaterialTextView = mView.personal_time
}
inner class ViewHolderTip(override val mView: View): ViewHolder(mView) {
val mDescription: MaterialTextView = mView.description
}
}

View File

@@ -0,0 +1,86 @@
package de.sebse.mentalarithmetic.fragments
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import de.sebse.mentalarithmetic.R
import de.sebse.mentalarithmetic.types.Categories
import de.sebse.mentalarithmetic.types.ItemFactory
import de.sebse.mentalarithmetic.types.ItemObject
import de.sebse.mentalarithmetic.types.TaskObject
/**
* A fragment representing a list of Items.
* Activities containing this fragment MUST implement the
* [TaskItemFragment.OnListFragmentInteractionListener] interface.
*/
class TaskItemFragment : Fragment() {
private val KEY_CATEGORY: String = "KEY_CATEGORY"
private var listener: OnListFragmentInteractionListener? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_taskitem_list, container, false)
// Set the adapter
if (view is RecyclerView) {
with(view) {
layoutManager = LinearLayoutManager(context)
adapter = TaskItemAdapter(ItemFactory.CATEGORIES_LIST[arguments?.get(KEY_CATEGORY)] ?: ArrayList(), listener)
}
}
return view
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is OnListFragmentInteractionListener) {
listener = context
} else {
throw RuntimeException(context.toString() + " must implement OnListFragmentInteractionListener")
}
}
override fun onDetach() {
super.onDetach()
listener = null
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
*
*
* See the Android Training lesson
* [Communicating with Other Fragments](http://developer.android.com/training/basics/fragments/communicating.html)
* for more information.
*/
interface OnListFragmentInteractionListener {
// TODO: Update argument type and name
fun onListFragmentInteraction(item: ItemObject)
}
companion object {
// TODO: Customize parameter initialization
@JvmStatic
fun newInstance(category: Categories): TaskItemFragment {
return TaskItemFragment().apply {
arguments = Bundle().apply {
putSerializable(KEY_CATEGORY, category)
}
}
}
}
}

View File

@@ -0,0 +1,15 @@
package de.sebse.mentalarithmetic.fragments
import android.util.Log
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import de.sebse.mentalarithmetic.types.ItemFactory
class TaskPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
override fun getItemCount(): Int {
Log.d("Adapter", ItemFactory.CATEGORIES.size.toString())
return ItemFactory.CATEGORIES.size
}
override fun createFragment(position: Int): Fragment = TaskItemFragment.newInstance(ItemFactory.CATEGORIES[position])
}

View File

@@ -0,0 +1,50 @@
package de.sebse.mentalarithmetic.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.viewpager2.widget.ViewPager2
import de.sebse.mentalarithmetic.R
import kotlinx.android.synthetic.main.fragment_taskitem_pager.view.*
import me.relex.circleindicator.CircleIndicator3
class TaskPagerFragment : Fragment() {
private lateinit var viewPager: ViewPager2
private lateinit var titleStrip: CircleIndicator3
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_taskitem_pager, container, false)
viewPager = view.findViewById(R.id.pager)
viewPager.adapter = TaskPagerAdapter(this)
titleStrip = view.findViewById(R.id.titlestrip)
titleStrip.setViewPager(viewPager)
return view
}
fun previous(): Boolean {
return if (viewPager.currentItem > 0) {
viewPager.currentItem--
true
} else {
false
}
}
companion object {
// TODO: Customize parameter initialization
@JvmStatic
fun newInstance() =
TaskPagerFragment().apply {
arguments = Bundle()
}
}
}

View File

@@ -0,0 +1,83 @@
package de.sebse.mentalarithmetic.fragments
import android.os.Build
import android.os.Bundle
import android.text.Html
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.text.HtmlCompat
import androidx.fragment.app.Fragment
import de.sebse.mentalarithmetic.R
import de.sebse.mentalarithmetic.types.ItemFactory
import de.sebse.mentalarithmetic.types.TaskObject
import de.sebse.mentalarithmetic.types.TipObject
import de.sebse.mentalarithmetic.utils.ImageGetter
import de.sebse.mentalarithmetic.utils.TagHandler
import kotlinx.android.synthetic.main.fragment_tip.view.*
/**
* A simple [Fragment] subclass.
* Use the [TipFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class TipFragment : Fragment() {
private var item: TipObject? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("TaskFragment", (savedInstanceState != null).toString())
arguments?.let {
val param1 = it.getString(ARG_PARAM1)
item = ItemFactory.ITEM_MAP[param1] as TipObject
}
savedInstanceState?.let {
val param1 = it.getString(ARG_PARAM1)
item = ItemFactory.ITEM_MAP[param1] as TipObject
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_tip, container, false)?.also { view ->
item?.let {
val name = view.name
name.text = HtmlCompat.fromHtml(getString(it.HTML), HtmlCompat.FROM_HTML_MODE_LEGACY, ImageGetter(requireContext()), TagHandler())
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
item?.let { outState.putString(ARG_PARAM1, it.ID) }
}
companion object {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @return A new instance of fragment TaskFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: TipObject) =
TipFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1.ID)
}
}
}
}

View File

@@ -0,0 +1,7 @@
package de.sebse.mentalarithmetic.types
enum class Categories {
BASIC,
TRICKS,
TIME
}

View File

@@ -0,0 +1,72 @@
package de.sebse.mentalarithmetic.types
import androidx.annotation.StringRes
import de.sebse.mentalarithmetic.R
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
object ItemFactory {
/**
* An array of sample (dummy) items.
*/
val ITEMS: MutableList<ItemObject> = ArrayList()
/**
* A map of sample (dummy) items, by ID.
*/
val ITEM_MAP: MutableMap<String, ItemObject> = HashMap()
val CATEGORIES: MutableList<Categories> = ArrayList()
val CATEGORIES_MAP: MutableMap<Categories, MutableMap<String, ItemObject>> = EnumMap(Categories::class.java)
val CATEGORIES_LIST: MutableMap<Categories, MutableList<ItemObject>> = EnumMap(Categories::class.java)
init {
addItem(Tip0101)
addItem(Task0101)
addItem(Task0102)
addItem(Task0103)
addItem(Tip0201)
addItem(Task0201)
addItem(Tip0202)
addItem(Task0202)
addItem(Task0203)
addItem(Task0204)
addItem(Task0205)
addItem(Task0206)
addItem(Task0207)
addItem(Task0208)
addItem(Tip0203)
addItem(Task0209)
addItem(Tip0204)
addItem(Task0210)
addItem(Task0211)
addItem(Task0212)
addItem(Task0213)
addItem(Task0214)
addItem(Task0215)
addItem(Task0216)
}
private fun addItem(item: ItemObject) {
val category = item.CATEGORY
if (!CATEGORIES.contains(category)) {
CATEGORIES.add(category)
}
if (!CATEGORIES_MAP.containsKey(category))
CATEGORIES_MAP[category] = HashMap()
CATEGORIES_MAP[category]!![item.ID] = item
if (!CATEGORIES_LIST.containsKey(category))
CATEGORIES_LIST[category] = ArrayList()
CATEGORIES_LIST[category]!!.add(item)
ITEMS.add(item)
ITEM_MAP[item.ID] = item
}
}

View File

@@ -0,0 +1,12 @@
package de.sebse.mentalarithmetic.types
import android.content.Context
import androidx.annotation.StringRes
import java.util.prefs.Preferences
abstract class ItemObject {
abstract val ID: String
abstract val NAME: Int
abstract val DESCRIPTION: Int
abstract val CATEGORY: Categories
}

View File

@@ -0,0 +1,56 @@
package de.sebse.mentalarithmetic.types
import android.content.Context
import androidx.preference.PreferenceManager
fun set(context: Context, key: String, value: Int) {
val settings = PreferenceManager.getDefaultSharedPreferences(context)
val editor = settings.edit()
editor.putInt(key, value)
editor.apply()
}
fun set(context: Context, key: String, value: String) {
val settings = PreferenceManager.getDefaultSharedPreferences(context)
val editor = settings.edit()
editor.putString(key, value)
editor.apply()
}
fun set(context: Context, key: String, value: Boolean) {
val settings = PreferenceManager.getDefaultSharedPreferences(context)
val editor = settings.edit()
editor.putBoolean(key, value)
editor.apply()
}
fun set(context: Context, key: String, value: Float) {
val settings = PreferenceManager.getDefaultSharedPreferences(context)
val editor = settings.edit()
editor.putFloat(key, value)
editor.apply()
}
fun getInt(context: Context, key: String): Int? {
val settings = PreferenceManager.getDefaultSharedPreferences(context)
return if (settings.contains(key)) settings.getInt(key, -1) else null
}
fun getString(context: Context, key: String): String? {
val settings = PreferenceManager.getDefaultSharedPreferences(context)
return if (settings.contains(key)) settings.getString(key, null) else null
}
fun getBoolean(context: Context, key: String): Boolean? {
val settings = PreferenceManager.getDefaultSharedPreferences(context)
return if (settings.contains(key)) settings.getBoolean(key, false) else null
}
fun getFloat(context: Context, key: String): Float? {
val settings = PreferenceManager.getDefaultSharedPreferences(context)
return if (settings.contains(key)) settings.getFloat(key, -1f) else null
}

View File

@@ -0,0 +1,30 @@
package de.sebse.mentalarithmetic.types
import android.content.Context
import androidx.annotation.XmlRes
class Question(private val mQuestion: String, private val mAnswer: String, @XmlRes private val mKeyboard: Int, private val mMultipleInput: Boolean, private val translateValue: Function1<String, String>? = null) {
private var mGivenAnswer: String? = null
fun getQuestion(): String {
return mQuestion
}
fun getMultipleInput(): Boolean {
return mMultipleInput
}
fun translate(value: String): String {
return this.translateValue?.let{ it(value) } ?: value
}
@XmlRes
fun getKeyboard(): Int {
return mKeyboard
}
fun setAnswer(answer: String): Pair<Boolean, String> {
mGivenAnswer = answer
return Pair(mAnswer == mGivenAnswer, mAnswer)
}
}

View File

@@ -0,0 +1,90 @@
package de.sebse.mentalarithmetic.types
import android.content.Context
import androidx.annotation.StringRes
import java.util.prefs.Preferences
abstract class TaskObject: ItemObject() {
abstract val COUNT: Int
abstract val SILVER_TIME: Int
abstract val GOLD_TIME: Int
private var paused: Boolean = false
private var startTime: Long = 0
private var elapsedTime: Long = 0
private var questionCount: Int = 0
private var questionCorrect: Int = 0
private var question: Question? = null
protected abstract fun generateQuestion(context: Context): Question
fun start(context: Context): Question? {
startTime = System.currentTimeMillis()
elapsedTime = 0
questionCount = 0
questionCorrect = 0
paused = false
return nextQuestion(context)
}
fun getCurrect(): Question? = question
fun answer(answer: String, context: Context): Triple<Boolean, String, Question?> {
return question?.let {
val (valid, correct) = question!!.setAnswer(answer)
if (valid)
questionCorrect++
return Triple(valid, correct, nextQuestion(context))
} ?: Triple(true, "", null)
}
fun pause() {
elapsedTime += System.currentTimeMillis() - startTime
paused = true
}
fun resume() {
startTime = System.currentTimeMillis()
paused = false
}
fun getLiveTime(): Long {
return if (paused) {
elapsedTime
} else {
elapsedTime + System.currentTimeMillis() - startTime
}
}
fun getCountCurrent(): Int {
return questionCount
}
fun getCountCorrect(): Int {
return questionCorrect
}
fun getCountTotal(): Int {
return questionCount + COUNT - questionCorrect
}
fun getScore(context: Context): Int {
return getInt(context, "TaskObject.$ID.score") ?: 0
}
fun setScore(context: Context, score: Int) {
set(context, "TaskObject.$ID.score", score)
}
private fun nextQuestion(context: Context): Question? {
questionCount++
elapsedTime += System.currentTimeMillis() - startTime
startTime = System.currentTimeMillis()
question = if (questionCorrect >= COUNT)
null
else
generateQuestion(context)
return question
}
}

View File

@@ -0,0 +1,395 @@
package de.sebse.mentalarithmetic.types
import android.content.Context
import de.sebse.mentalarithmetic.R
import java.util.*
import kotlin.random.Random
object Task0101: TaskObject() {
override val ID: String = "Task-01-01"
override val NAME: Int = R.string.task_01_01_name
override val DESCRIPTION: Int = R.string.task_01_01_desc
override val CATEGORY: Categories = Categories.BASIC
override val COUNT: Int = 20
override val SILVER_TIME: Int = 15 * 1000
override val GOLD_TIME: Int = 10 * 1000
override fun generateQuestion(context: Context): Question {
val num: Int = (0..99).random()
return Question(num.toString(), (num % 2).toString(), R.xml.keyboard_odd, false) { value -> when (value) {
"0" -> context.getString(R.string.even)
"1" -> context.getString(R.string.odd)
else -> value
}.toUpperCase(Locale.getDefault()) }
}
}
object Task0102: TaskObject() {
override val ID: String = "Task-01-02"
override val NAME: Int = R.string.task_01_02_name
override val DESCRIPTION: Int = R.string.task_01_02_desc
override val CATEGORY: Categories = Categories.BASIC
override val COUNT: Int = 20
override val SILVER_TIME: Int = 25 * 1000
override val GOLD_TIME: Int = 15 * 1000
override fun generateQuestion(context: Context): Question {
val sum: Int = (2..9).random()
val num1: Int = (1 until sum).random()
val num2 = sum - num1
return Question("$num1 + $num2", sum.toString(), R.xml.keyboard_decimal, false)
}
}
object Task0103: TaskObject() {
override val ID: String = "Task-01-03"
override val NAME: Int = R.string.task_01_03_name
override val DESCRIPTION: Int = R.string.task_01_03_desc
override val CATEGORY: Categories = Categories.BASIC
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val num1: Int = (1..9).random()
val num2: Int = (1..9).random()
val sum = num1 + num2
return Question("$num1 + $num2", sum.toString(), R.xml.keyboard_decimal_multi, true)
}
}
object Task0104: TaskObject() {
override val ID: String = "Task-01-04"
override val NAME: Int = R.string.task_01_04_name
override val DESCRIPTION: Int = R.string.task_01_04_desc
override val CATEGORY: Categories = Categories.BASIC
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val num1: Int = (1..9).random() + 10 * (1..3).random()
val num2: Int = (1..9).random() + 10 * (1..3).random()
val sum = num1 + num2
return Question("$num1 + $num2", sum.toString(), R.xml.keyboard_decimal_multi, true)
}
}
object Task0105: TaskObject() {
override val ID: String = "Task-01-05"
override val NAME: Int = R.string.task_01_05_name
override val DESCRIPTION: Int = R.string.task_01_05_desc
override val CATEGORY: Categories = Categories.BASIC
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val num1: Int = (10..99).random()
val num2: Int = (10..99).random()
val sum = num1 + num2
return Question("$num1 + $num2", sum.toString(), R.xml.keyboard_decimal_multi, true)
}
}
object Task0201: TaskObject() {
override val ID: String = "Task-02-01"
override val NAME: Int = R.string.task_02_01_name
override val DESCRIPTION: Int = R.string.task_02_01_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val res: Int = (0..6).random()
val question = when (res) {
0 -> context.getString(R.string.sunday)
1 -> context.getString(R.string.monday)
2 -> context.getString(R.string.tuesday)
3 -> context.getString(R.string.wednesday)
4 -> context.getString(R.string.thursday)
5 -> context.getString(R.string.friday)
6 -> context.getString(R.string.saturday)
else -> ""
}
return Question(question, res.toString(), R.xml.keyboard_dec7, false)
}
}
object Task0202: TaskObject() {
override val ID: String = "Task-02-02"
override val NAME: Int = R.string.task_02_02_name
override val DESCRIPTION: Int = R.string.task_02_02_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val month: Int = (0..2).random()
val (res, question) = getMonthDetails(month, context)
return Question(question, res.toString(), R.xml.keyboard_dec7, false)
}
}
object Task0203: TaskObject() {
override val ID: String = "Task-02-03"
override val NAME: Int = R.string.task_02_03_name
override val DESCRIPTION: Int = R.string.task_02_03_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val month: Int = (3..5).random()
val (res, question) = getMonthDetails(month, context)
return Question(question, res.toString(), R.xml.keyboard_dec7, false)
}
}
object Task0204: TaskObject() {
override val ID: String = "Task-02-04"
override val NAME: Int = R.string.task_02_04_name
override val DESCRIPTION: Int = R.string.task_02_04_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val month: Int = (0..5).random()
val (res, question) = getMonthDetails(month, context)
return Question(question, res.toString(), R.xml.keyboard_dec7, false)
}
}
object Task0205: TaskObject() {
override val ID: String = "Task-02-05"
override val NAME: Int = R.string.task_02_05_name
override val DESCRIPTION: Int = R.string.task_02_05_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val month: Int = (6..8).random()
val (res, question) = getMonthDetails(month, context)
return Question(question, res.toString(), R.xml.keyboard_dec7, false)
}
}
object Task0206: TaskObject() {
override val ID: String = "Task-02-06"
override val NAME: Int = R.string.task_02_06_name
override val DESCRIPTION: Int = R.string.task_02_06_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val month: Int = (9..11).random()
val (res, question) = getMonthDetails(month, context)
return Question(question, res.toString(), R.xml.keyboard_dec7, false)
}
}
object Task0207: TaskObject() {
override val ID: String = "Task-02-07"
override val NAME: Int = R.string.task_02_07_name
override val DESCRIPTION: Int = R.string.task_02_07_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val month: Int = (6..11).random()
val (res, question) = getMonthDetails(month, context)
return Question(question, res.toString(), R.xml.keyboard_dec7, false)
}
}
object Task0208: TaskObject() {
override val ID: String = "Task-02-08"
override val NAME: Int = R.string.task_02_08_name
override val DESCRIPTION: Int = R.string.task_02_08_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val month: Int = (0..11).random()
val (res, question) = getMonthDetails(month, context)
return Question(question, res.toString(), R.xml.keyboard_dec7, false)
}
}
object Task0209: TaskObject() {
override val ID: String = "Task-02-09"
override val NAME: Int = R.string.task_02_09_name
override val DESCRIPTION: Int = R.string.task_02_09_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val day: Int = (0..31).random()
val res = day % 7
return Question("$day mod 7", res.toString(), R.xml.keyboard_dec7, false)
}
}
object Task0210: TaskObject() {
override val ID: String = "Task-02-10"
override val NAME: Int = R.string.task_02_10_name
override val DESCRIPTION: Int = R.string.task_02_10_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val year: Int = (0..99).random()
val res = year / 4
return Question("$year div 4", res.toString(), R.xml.keyboard_decimal_multi, true)
}
}
object Task0211: TaskObject() {
override val ID: String = "Task-02-11"
override val NAME: Int = R.string.task_02_11_name
override val DESCRIPTION: Int = R.string.task_02_11_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 15
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val year: Int = (0..99).random()
val res = year + (year / 4)
return Question("$year + ($year div 4)", res.toString(), R.xml.keyboard_decimal_multi, true)
}
}
object Task0212: TaskObject() {
override val ID: String = "Task-02-12"
override val NAME: Int = R.string.task_02_12_name
override val DESCRIPTION: Int = R.string.task_02_12_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val year: Int = (0..99).random()
val year2 = year + (year / 4)
val res = year2 % 7
return Question("$year2 % 7", res.toString(), R.xml.keyboard_dec7, false)
}
}
object Task0213: TaskObject() {
override val ID: String = "Task-02-13"
override val NAME: Int = R.string.task_02_13_name
override val DESCRIPTION: Int = R.string.task_02_13_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 10
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val year: Int = (0..99).random()
val year2 = year + (year / 4)
val res = year2 % 7
return Question("($year + ($year div 4)) % 7", res.toString(), R.xml.keyboard_dec7, false)
}
}
object Task0214: TaskObject() {
override val ID: String = "Task-02-14"
override val NAME: Int = R.string.task_02_14_name
override val DESCRIPTION: Int = R.string.task_02_14_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 10
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val century: Int = (16..22).random()
val year: Int = (0..99).random()
val year2 = year + (year / 4)
val res = year2 % 7
return Question("Year code of $century$year", res.toString(), R.xml.keyboard_dec7, false)
}
}
object Task0215: TaskObject() {
override val ID: String = "Task-02-15"
override val NAME: Int = R.string.task_02_15_name
override val DESCRIPTION: Int = R.string.task_02_15_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val century: Int = (16..22).random()
val year: Int = (0..99).random()
val res = 2 * ((3 - century) % 4)
return Question("Century code of $century$year", res.toString(), R.xml.keyboard_decimal, false)
}
}
object Task0216: TaskObject() {
override val ID: String = "Task-02-16"
override val NAME: Int = R.string.task_02_16_name
override val DESCRIPTION: Int = R.string.task_02_16_desc
override val CATEGORY: Categories = Categories.TIME
override val COUNT: Int = 20
override val SILVER_TIME: Int = 60 * 1000
override val GOLD_TIME: Int = 25 * 1000
override fun generateQuestion(context: Context): Question {
val century: Int = (16..22).random()
val year: Int = (0..99).random()
val res = if (year % 4 == 0 && (century % 4 == 0 || year != 0)) 1 else 0
return Question("Is $century${if (year < 10) "0" else ""}$year a leap yeaar?", res.toString(), R.xml.keyboard_yes_no, false) { value -> when (value) {
"0" -> context.getString(R.string.no)
"1" -> context.getString(R.string.yes)
else -> value
}.toUpperCase(Locale.getDefault()) }
}
}
private fun getMonthDetails(
month: Int,
context: Context
): Pair<Int, String> {
return when (month) {
0 -> Pair(0, context.getString(R.string.january))
1 -> Pair(3, context.getString(R.string.february))
2 -> Pair(3, context.getString(R.string.march))
3 -> Pair(6, context.getString(R.string.april))
4 -> Pair(1, context.getString(R.string.may))
5 -> Pair(4, context.getString(R.string.june))
6 -> Pair(6, context.getString(R.string.july))
7 -> Pair(2, context.getString(R.string.august))
8 -> Pair(5, context.getString(R.string.september))
9 -> Pair(0, context.getString(R.string.october))
10 -> Pair(3, context.getString(R.string.november))
11 -> Pair(5, context.getString(R.string.december))
else -> Pair(0, "")
}
}

View File

@@ -0,0 +1,9 @@
package de.sebse.mentalarithmetic.types
import android.content.Context
import androidx.annotation.StringRes
import java.util.prefs.Preferences
abstract class TipObject: ItemObject() {
abstract val HTML: Int
}

View File

@@ -0,0 +1,43 @@
package de.sebse.mentalarithmetic.types
import de.sebse.mentalarithmetic.R
object Tip0101: TipObject() {
override val ID: String = "Tip-01-01"
override val NAME: Int = R.string.tip_01_01_name
override val DESCRIPTION: Int = R.string.tip_01_01_desc
override val CATEGORY: Categories = Categories.BASIC
override val HTML: Int = R.string.tip_01_01_html
}
object Tip0201: TipObject() {
override val ID: String = "Tip-02-01"
override val NAME: Int = R.string.tip_02_01_name
override val DESCRIPTION: Int = R.string.tip_02_01_desc
override val CATEGORY: Categories = Categories.TIME
override val HTML: Int = R.string.tip_02_01_html
}
object Tip0202: TipObject() {
override val ID: String = "Tip-03-02"
override val NAME: Int = R.string.tip_02_02_name
override val DESCRIPTION: Int = R.string.tip_02_02_desc
override val CATEGORY: Categories = Categories.TIME
override val HTML: Int = R.string.tip_02_02_html
}
object Tip0203: TipObject() {
override val ID: String = "Tip-03-03"
override val NAME: Int = R.string.tip_02_03_name
override val DESCRIPTION: Int = R.string.tip_02_03_desc
override val CATEGORY: Categories = Categories.TIME
override val HTML: Int = R.string.tip_02_03_html
}
object Tip0204: TipObject() {
override val ID: String = "Tip-03-04"
override val NAME: Int = R.string.tip_02_04_name
override val DESCRIPTION: Int = R.string.tip_02_04_desc
override val CATEGORY: Categories = Categories.TIME
override val HTML: Int = R.string.tip_02_04_html
}

View File

@@ -0,0 +1,115 @@
package de.sebse.mentalarithmetic.utils
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.Button
import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.gridlayout.widget.GridLayout
import com.google.android.material.button.MaterialButton
import de.sebse.mentalarithmetic.R
class BoardView(
context: Context,
attrs: AttributeSet
) : GridLayout(context, attrs) {
private var locked: Boolean = false
private var callbackSubmit: ((List<Int>) -> Unit)? = null
private var callback: ((Int) -> Unit)? = null
var text = ArrayList<Keyboard.Key>()
init {
this.setBackgroundColor(ContextCompat.getColor(context, R.color.colorPrimaryDark))
this.rowCount = 9
this.columnCount = 9
}
fun setKeyboard(board: Keyboard) {
this.rowCount = board.entries.size
this.columnCount = board.maxWidth
this.removeAllViews()
board.entries.forEachIndexed { index, row ->
var colIdx = 0
row.items.forEach { elem ->
if (elem is Keyboard.Key) {
this.addView(MaterialButton(context).apply {
text = elem.label
if (elem.icon != null) {
icon = ContextCompat.getDrawable(context, elem.icon)
iconGravity = MaterialButton.ICON_GRAVITY_TEXT_START
}
layoutParams = LayoutParams().apply {
height = LayoutParams.WRAP_CONTENT
width = 0
columnSpec = spec(colIdx, elem.width, 1f)
rowSpec = spec(index, 1, 1f)
}
setOnClickListener { onClick(elem) }
})
}
colIdx += elem.width
}
}
}
fun onClick(key: Keyboard.Key) {
if (this.locked)
return
when (key.code) {
-1 -> {
if (text.size > 0) text.removeAt(text.size-1)
}
-2 -> {
callbackSubmit?.let{ it(text.map { it.code }) }
}
-3 -> {
text.clear()
}
else -> {
text.add(key)
}
}
callback?.let{ it(key.code) }
}
fun getText(): String {
return text.joinToString(separator = "") { it.code.toString() }
}
fun getValues(): ArrayList<Keyboard.Key> {
return text
}
fun reset() {
text.clear()
}
fun setBtnListener(callback: Function1<Int, Unit>) {
this.callback = callback
}
fun setSubmitListener(callback: Function1<List<Int>, Unit>) {
this.callbackSubmit = callback
}
fun lock() {
this.locked = true
this.children.forEach {
it.isEnabled = false
}
}
fun unlock() {
this.locked = false
this.children.forEach {
it.isEnabled = true
}
}
}

View File

@@ -0,0 +1,24 @@
package de.sebse.mentalarithmetic.utils
import android.content.Context
import android.graphics.drawable.Drawable
import android.text.Html
import androidx.core.content.ContextCompat
class ImageGetter(val context: Context) : Html.ImageGetter {
override fun getDrawable(source: String): Drawable? {
var id: Int
id = context.resources.getIdentifier(source, "drawable", context.packageName)
if (id == 0) { // the drawable resource wasn't found in our package, maybe it is a stock android drawable?
id = context.resources.getIdentifier(source, "drawable", "android")
}
return if (id == 0) { // prevent a crash if the resource still can't be found
null
} else {
val d: Drawable? = ContextCompat.getDrawable(context, id)
d?.setBounds(0, 0, d.intrinsicWidth, d.intrinsicHeight)
d
}
}
}

View File

@@ -0,0 +1,76 @@
package de.sebse.mentalarithmetic.utils
import android.content.Context
import android.util.Log
import androidx.annotation.DrawableRes
import androidx.annotation.XmlRes
import org.xmlpull.v1.XmlPullParser
class Keyboard(context: Context, @XmlRes res: Int) {
val entries: ArrayList<Row> = ArrayList()
init {
val parser = context.getResources().getXml(res)
var items: ArrayList<RowElement> = ArrayList()
try {
var event: Int = parser.eventType
while (event != XmlPullParser.END_DOCUMENT) {
if (event == XmlPullParser.START_TAG) {
when (parser.name) {
"Row" -> {
items = ArrayList()
}
"Key" -> {
val code = parser.getAttributeIntValue(null, "codes", -1)
val keyLabel = parser.getAttributeValue(null, "keyLabel") ?: ""
val resource = parser.getAttributeResourceValue(null, "keyIcon", -1)
val width = parser.getAttributeIntValue(null, "width", 1)
items.add(Key(code, keyLabel, if (resource == -1) null else resource, width))
}
"Gap" -> {
val width = parser.getAttributeIntValue(null, "width", 1)
items.add(Gap(width))
}
}
} else if (event == XmlPullParser.END_TAG) {
if ("Row" == parser.name) {
entries.add(Row(items))
}
}
event = parser.next()
}
} catch (e: Exception) {
e.printStackTrace()
}
Log.d("Keyboard", entries.toString())
}
val maxWidth: Int
get() {
return entries.maxBy { it.width }?.width ?: 0
}
data class Row(val items: ArrayList<RowElement>) {
val width: Int
get() {
return items.sumBy { it.width }
}
}
data class Key(
val code: Int,
val label: String,
@DrawableRes val icon: Int?,
override val width: Int
): RowElement
interface RowElement {
val width: Int
}
data class Gap(
override val width: Int
): RowElement
}

View File

@@ -0,0 +1,84 @@
package de.sebse.mentalarithmetic.utils
import android.text.Editable
import android.text.Html.TagHandler
import android.text.Spannable
import android.text.style.BulletSpan
import android.text.style.LeadingMarginSpan
import android.text.style.TypefaceSpan
import android.text.style.UnderlineSpan
import android.util.Log
import org.xml.sax.XMLReader
import java.util.*
class TagHandler: TagHandler {
private var mListItemCount = 0
private val mListParents: Vector<String> = Vector()
override fun handleTag(
opening: Boolean,
tag: String,
output: Editable,
xmlReader: XMLReader
) {
if (tag == "ul" || tag == "ol" || tag == "dd") {
if (opening) {
mListParents.add(tag)
} else mListParents.remove(tag)
mListItemCount = 0
} else if (tag == "li" && !opening) {
handleListTag(output)
} else if (tag.equals("code", ignoreCase = true)) {
if (opening) {
output.setSpan(
TypefaceSpan("monospace"),
output.length,
output.length,
Spannable.SPAN_MARK_MARK
)
} else {
Log.d("Code Tag", "Code tag encountered")
val obj = getLast(output, TypefaceSpan::class.java)
val where = output.getSpanStart(obj)
output.setSpan(TypefaceSpan("monospace"), where, output.length, 0)
}
}
}
private fun getLast(text: Editable, kind: Class<*>): Any? {
val objs: Array<out Any> = text.getSpans(0, text.length, kind)
return if (objs.isEmpty()) {
null
} else {
for (i in objs.size downTo 1) {
if (text.getSpanFlags(objs[i - 1]) == Spannable.SPAN_MARK_MARK) {
return objs[i - 1]
}
}
null
}
}
private fun handleListTag(output: Editable) {
if (mListParents.lastElement().equals("ul")) {
output.append("\n")
val split = output.toString().split("\n").toTypedArray()
val lastIndex = split.size - 1
val start = output.length - split[lastIndex].length - 1
output.setSpan(BulletSpan(15 * mListParents.size), start, output.length, 0)
} else if (mListParents.lastElement().equals("ol")) {
mListItemCount++
output.append("\n")
val split = output.toString().split("\n").toTypedArray()
val lastIndex = split.size - 1
val start = output.length - split[lastIndex].length - 1
output.insert(start, "$mListItemCount. ")
output.setSpan(
LeadingMarginSpan.Standard(15 * mListParents.size),
start,
output.length,
0
)
}
}
}

View File

@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M22,3L7,3c-0.69,0 -1.23,0.35 -1.59,0.88L0,12l5.41,8.11c0.36,0.53 0.9,0.89 1.59,0.89h15c1.1,0 2,-0.9 2,-2L24,5c0,-1.1 -0.9,-2 -2,-2zM19,15.59L17.59,17 14,13.41 10.41,17 9,15.59 12.59,12 9,8.41 10.41,7 14,10.59 17.59,7 19,8.41 15.41,12 19,15.59z"/>
</vector>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -0,0 +1,54 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="930.9091"
android:viewportHeight="930.9091">
<group android:translateX="209.45454"
android:translateY="209.45454">
<path
android:strokeWidth="1"
android:pathData="M306,83c-5.52,0 -10,4.48 -10,10s4.48,10 10,10c5.52,0 10,-4.48 10,-10S311.52,83 306,83z"
android:fillColor="#fff"
android:strokeColor="#fff"/>
<path
android:strokeWidth="1"
android:pathData="M206,83c-5.52,0 -10,4.48 -10,10s4.48,10 10,10s10,-4.48 10,-10S211.52,83 206,83z"
android:fillColor="#fff"
android:strokeColor="#fff"/>
<path
android:strokeWidth="1"
android:pathData="M473.123,298.882C481.706,286 486,269.927 486,256c0,-20.996 -9.327,-40.503 -25.332,-53.731C464.21,193.805 466,185.002 466,176c0,-27.855 -16.256,-53.803 -40.425,-65.92c3.667,-30.414 -16.151,-58.292 -45.52,-65.403C368.56,18.104 342.975,0 316,0c-25.11,0 -47.631,14.646 -60,36.016C243.63,14.646 221.109,0 196,0c-26.977,0 -52.562,18.104 -64.055,44.678c-29.545,7.156 -49.177,35.173 -45.52,65.402C62.257,122.196 46,148.145 46,176c0,9.002 1.79,17.805 5.332,26.269C35.327,215.498 26,235.004 26,256c0,14 4.33,30.052 12.877,42.881C30.525,310.671 26,324.644 26,339c0,18.616 7.548,36.35 20.899,49.518C40.035,430.01 73.125,472 116,472c0.31,0 0.615,-0.005 0.914,-0.014C131.129,496.508 157.515,512 186,512c29.688,0 56.216,-17.82 70,-43.494C269.784,494.18 296.312,512 326,512c28.485,0 54.871,-15.492 69.086,-40.014c0.3,0.009 0.604,0.014 0.914,0.014c37.944,0 70,-33.43 70,-73c0,-3.369 -0.296,-6.834 -0.899,-10.482C478.452,375.35 486,357.617 486,339C486,324.644 481.475,310.671 473.123,298.882zM246,151.381c-5.888,-3.415 -12.717,-5.381 -20,-5.381c-5.523,0 -10,4.477 -10,10s4.477,10 10,10c11.028,0 20,8.972 20,20v190.169C231.329,359.528 209.877,349 186,349c-5.523,0 -10,4.477 -10,10s4.477,10 10,10c33.084,0 60,26.916 60,60c0,34.149 -27.477,63 -60,63c-23.152,0 -44.465,-13.691 -54.299,-34.88c-1.813,-3.905 -5.919,-6.208 -10.2,-5.726c-0.707,0.081 -1.41,0.175 -2.113,0.271c-28.46,3.892 -60.51,-28.335 -51.834,-64.387c0.891,-3.703 -0.399,-7.589 -3.326,-10.025C52.644,367.614 46,353.671 46,339c0,-9.071 2.565,-17.948 7.338,-25.732C65.332,323.098 80.251,329 96,329c5.523,0 10,-4.477 10,-10s-4.477,-10 -10,-10c-27.139,0 -50,-24.77 -50,-53c0,-17.125 8.729,-32.879 23.352,-42.142c4.505,-2.854 5.978,-8.741 3.347,-13.38C68.254,192.639 66,184.403 66,176c0,-26.99 22.002,-53 50,-53c26.636,0 50,24.767 50,53c0,5.523 4.477,10 10,10s10,-4.477 10,-10c0,-39.57 -32.056,-73 -70,-73c-3.348,0 -6.678,0.271 -9.975,0.784C106.02,103.521 106,103.265 106,103c0,-22.317 18.117,-40 40,-40c10.682,0 20.73,4.161 28.292,11.715c3.907,3.903 10.238,3.899 14.142,-0.007c3.903,-3.908 3.9,-10.239 -0.007,-14.142c-9.123,-9.113 -20.662,-14.925 -33.167,-16.851C164.079,30.624 178.744,20 196,20c26.636,0 50,24.766 50,53V151.381zM416,329c15.75,0 30.669,-5.903 42.663,-15.729C463.435,321.054 466,329.93 466,339c0,14.671 -6.644,28.614 -18.227,38.253c-2.928,2.436 -4.217,6.323 -3.326,10.025c1.06,4.403 1.553,8.128 1.553,11.721c0,31.04 -28.075,56.126 -53.387,52.666c-0.703,-0.096 -1.407,-0.191 -2.114,-0.271c-4.287,-0.485 -8.388,1.821 -10.199,5.726C370.466,478.309 349.151,492 326,492c-32.523,0 -60,-28.851 -60,-63c0,-33.084 26.916,-60 60,-60c5.522,0 10,-4.477 10,-10s-4.478,-10 -10,-10c-23.877,0 -45.329,10.528 -60,27.169V186c0,-11.028 8.972,-20 20,-20c5.522,0 10,-4.477 10,-10s-4.478,-10 -10,-10c-7.283,0 -14.112,1.966 -20,5.381V73c0,-28.234 23.364,-53 50,-53c17.256,0 31.92,10.624 40.739,23.714c-12.504,1.926 -24.042,7.738 -33.166,16.851c-3.908,3.903 -3.912,10.234 -0.009,14.142c3.903,3.908 10.236,3.911 14.142,0.008C345.271,67.161 355.318,63 366,63c21.993,0 40,17.787 40,40c0,0.264 -0.02,0.521 -0.025,0.784C402.678,103.271 399.348,103 396,103c-37.944,0 -70,33.43 -70,73c0,5.523 4.478,10 10,10c5.522,0 10,-4.477 10,-10c0,-28.233 23.364,-53 50,-53c27.982,0 50,25.997 50,53c0,8.403 -2.254,16.639 -6.699,24.478c-2.63,4.639 -1.157,10.526 3.348,13.38C457.271,223.121 466,238.875 466,256c0,28.331 -22.941,53 -50,53c-5.522,0 -10,4.477 -10,10S410.478,329 416,329z"
android:fillColor="#fff"
android:strokeColor="#fff"/>
<path
android:strokeWidth="1"
android:pathData="M223.453,214.053c-3.682,-4.116 -10.003,-4.468 -14.12,-0.786C177.265,241.952 126,219.027 126,176c0,-5.523 -4.477,-10 -10,-10s-10,4.477 -10,10c0,34.715 25.407,63.595 58.6,69.057C160.75,257.185 149.386,266 136,266c-5.523,0 -10,4.477 -10,10s4.477,10 10,10c24.357,0 44.69,-17.51 49.102,-40.602c13.907,-1.819 26.996,-7.77 37.565,-17.225C226.783,224.491 227.135,218.169 223.453,214.053z"
android:fillColor="#fff"
android:strokeColor="#fff"/>
<path
android:strokeWidth="1"
android:pathData="M116,389c-5.523,0 -10,4.477 -10,10s4.477,10 10,10c26.636,0 50,24.767 50,53c0,5.523 4.477,10 10,10s10,-4.477 10,-10C186,422.43 153.944,389 116,389z"
android:fillColor="#fff"
android:strokeColor="#fff"/>
<path
android:strokeWidth="1"
android:pathData="M196,309c-33.886,0 -62.98,18.585 -78.3,45.608c-2.723,4.805 -1.036,10.908 3.768,13.631c4.805,2.723 10.907,1.037 13.631,-3.768C147.034,343.415 169.68,329 196,329c5.523,0 10,-4.477 10,-10S201.523,309 196,309z"
android:fillColor="#fff"
android:strokeColor="#fff"/>
<path
android:strokeWidth="1"
android:pathData="M376,266c-13.386,0 -24.75,-8.815 -28.6,-20.943C380.593,239.595 406,210.715 406,176c0,-5.523 -4.478,-10 -10,-10c-5.522,0 -10,4.477 -10,10c0,27.57 -22.43,50 -50,50c-12.316,0 -24.153,-4.522 -33.333,-12.733c-4.115,-3.682 -10.438,-3.331 -14.12,0.786c-3.683,4.116 -3.33,10.438 0.786,14.12c10.569,9.455 23.659,15.406 37.565,17.225C331.31,268.49 351.643,286 376,286c5.522,0 10,-4.477 10,-10S381.522,266 376,266z"
android:fillColor="#fff"
android:strokeColor="#fff"/>
<path
android:strokeWidth="1"
android:pathData="M396,389c-37.944,0 -70,33.43 -70,73c0,5.523 4.478,10 10,10c5.522,0 10,-4.477 10,-10c0,-28.233 23.364,-53 50,-53c5.522,0 10,-4.477 10,-10S401.522,389 396,389z"
android:fillColor="#fff"
android:strokeColor="#fff"/>
<path
android:strokeWidth="1"
android:pathData="M394.3,354.609C379.018,327.65 349.964,309 316,309c-5.522,0 -10,4.477 -10,10s4.478,10 10,10c26.301,0 48.964,14.411 60.899,35.471c2.727,4.811 8.834,6.489 13.631,3.769C395.335,365.517 397.022,359.414 394.3,354.609z"
android:fillColor="#fff"
android:strokeColor="#fff"/>
</group>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="@color/gold"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="@color/silver"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="@color/gold"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="@color/silver"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
</vector>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/fragment_holder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.SummaryFragment"
android:orientation="vertical">
<com.google.android.material.button.MaterialButton
android:id="@+id/retry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/retry"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/exit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/to_main_menu"/>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/primary_text_light"
android:textSize="15sp"
android:textAlignment="center"/>
</LinearLayout>

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".fragments.TaskFragment">
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.MaterialComponents.Overline"
android:textAlignment="center"/>
<com.google.android.material.card.MaterialCardView
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="100sp"
android:layout_margin="10sp"
android:layout_below="@id/name"
app:cardBackgroundColor="@color/colorPrimaryDark">
<TextView
android:id="@+id/question"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="@android:color/primary_text_dark"
android:textSize="30sp"
android:textAlignment="center"/>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/time"
android:layout_below="@id/card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
android:textAlignment="center"/>
<TextView
android:id="@+id/question_count"
android:layout_below="@id/card"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
android:layout_alignStart="@id/card"
android:layout_alignLeft="@id/card" />
<TextView
android:id="@+id/question_correct"
android:layout_below="@id/card"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
android:layout_alignEnd="@id/card"
android:layout_alignRight="@id/card" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="8dp"
android:layout_below="@id/time"
android:indeterminate="false"
app:mpb_progressStyle="horizontal"
android:background="@color/gold"
app:mpb_useIntrinsicPadding="false"
app:mpb_secondaryProgressTint="@color/silver"
app:mpb_progressTint="@color/bronze"
android:secondaryProgress="80"
android:progress="60"
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal"
android:layout_alignStart="@id/card"
android:layout_alignLeft="@id/card"
android:layout_alignEnd="@id/card"
android:layout_alignRight="@id/card" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progress2"
android:layout_width="match_parent"
android:layout_height="8dp"
android:layout_below="@id/progress"
android:indeterminate="false"
app:mpb_progressStyle="horizontal"
android:background="@color/colorPrimaryLight"
app:mpb_useIntrinsicPadding="false"
android:secondaryProgress="100"
android:progress="0"
app:mpb_secondaryProgressTint="@color/colorPrimaryLight"
app:mpb_progressTint="@color/colorPrimary"
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal"
android:layout_alignStart="@id/card"
android:layout_alignLeft="@id/card"
android:layout_alignEnd="@id/card"
android:layout_alignRight="@id/card" />
<TextView
android:id="@+id/answer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/keyboardview"
android:background="@color/colorPrimaryDark"
android:gravity="center"
android:text="@string/hello_blank_fragment"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<de.sebse.mentalarithmetic.utils.BoardView xmlns:custom="http://schemas.android.com/apk/res/de.sebse.mentalarithmetic"
android:id="@+id/keyboardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:focusable="true"
android:focusableInTouchMode="true"
custom:showText="true"
custom:labelPosition="left"
/>
</RelativeLayout>

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_gap"
android:layout_marginBottom="0dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/personal_time"
android:layout_alignTop="@id/content"
android:layout_toLeftOf="@id/silver_time"
android:layout_toStartOf="@id/silver_time"
tools:ignore="RelativeOverlap" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/silver_time"
android:drawableStart="@drawable/ic_star_border_silver"
android:drawableLeft="@drawable/ic_star_border_silver"
android:layout_alignTop="@id/content"
android:layout_toLeftOf="@id/gold_time"
android:layout_toStartOf="@id/gold_time"
tools:ignore="RelativeOverlap" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/gold_time"
android:drawableStart="@drawable/ic_star_border_gold"
android:drawableLeft="@drawable/ic_star_border_gold"
android:layout_alignTop="@id/content"
android:layout_alignEnd="@id/progress"
android:layout_alignRight="@id/progress" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
tools:text="Test Task Name" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="4dp"
android:layout_below="@id/content"
android:indeterminate="false"
app:mpb_progressStyle="horizontal"
android:background="@color/gold"
app:mpb_useIntrinsicPadding="false"
app:mpb_secondaryProgressTint="@color/silver"
app:mpb_progressTint="@color/bronze"
android:secondaryProgress="80"
android:progress="60"
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal" />
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/progress2"
android:layout_width="match_parent"
android:layout_height="4dp"
android:layout_below="@id/progress"
android:indeterminate="false"
app:mpb_progressStyle="horizontal"
android:background="@color/colorPrimaryLight"
app:mpb_useIntrinsicPadding="false"
android:secondaryProgress="100"
android:progress="0"
app:mpb_secondaryProgressTint="@color/colorPrimaryLight"
app:mpb_progressTint="@color/colorPrimary"
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal" />
</RelativeLayout>
</com.google.android.material.card.MaterialCardView>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/list"
android:name="de.sebse.mentalarithmetic.fragments.TaskItemFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:context=".fragments.TaskItemFragment"
tools:listitem="@layout/fragment_taskitem" />

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<me.relex.circleindicator.CircleIndicator3
android:id="@+id/titlestrip"
android:layout_width="match_parent"
android:layout_height="16dp"
android:background="@color/colorAccent"/>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".fragments.TaskFragment">
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
/>
</RelativeLayout>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_gap"
android:layout_marginBottom="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
tools:text="Test Task Name" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
tools:text="Test Task Name" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="BoardView">
<attr name="showText" format="boolean" />
<attr name="labelPosition" format="enum">
<enum name="left" value="0"/>
<enum name="right" value="1"/>
</attr>
</declare-styleable>
</resources>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorPrimaryLight">#5CB1A8</color>
<color name="colorAccent">#D81B60</color>
<color name="gold">#D4AF37</color>
<color name="silver">#ACACAC</color>
<color name="bronze">#88540B</color>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="text_margin">16dp</dimen>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#000064</color>
</resources>

View File

@@ -0,0 +1,34 @@
<resources>
<string name="app_name">Mental Arithmetic</string>
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="even">even</string>
<string name="odd">odd</string>
<string name="retry">Retry</string>
<string name="to_main_menu">To Main Menu</string>
<string name="category_basic">Basic</string>
<string name="sunday">Sunday</string>
<string name="monday">Monday</string>
<string name="tuesday">Tuesday</string>
<string name="wednesday">Wednesday</string>
<string name="thursday">Thursday</string>
<string name="friday">Friday</string>
<string name="saturday">Saturday</string>
<string name="january">January</string>
<string name="february">February</string>
<string name="march">March</string>
<string name="april">April</string>
<string name="may">May</string>
<string name="june">June</string>
<string name="july">July</string>
<string name="august">August</string>
<string name="september">September</string>
<string name="october">October</string>
<string name="november">November</string>
<string name="december">December</string>
<string name="no">No</string>
<string name="yes">Yes</string>
<string name="question_count">Question Count: %1$d</string>
<string name="question_currect">%1$d / %2$d</string>
<string name="time">%1$d.%2$03d</string>
</resources>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="task_01_01_name">Even-Odd</string>
<string name="task_01_01_desc">Let\'s first train our reaction time!</string>
<string name="task_01_02_name">Tiny Addition 1</string>
<string name="task_01_02_desc">Train addition pairs</string>
<string name="task_01_03_name">Tiny Addition 2</string>
<string name="task_01_03_desc">Train addition pairs with carry over</string>
<string name="task_01_04_name">Larger numbers</string>
<string name="task_01_04_desc">Let\'s get large and add bigger (but easy) numbers!</string>
<string name="task_01_05_name">Addition</string>
<string name="task_01_05_desc">Add 2-digit numbers</string>
<string name="task_02_01_name">Week day codes</string>
<string name="task_02_01_desc">Each week day is assigned to a specific code</string>
<string name="task_02_02_name">Month codes: Jan - Mar</string>
<string name="task_02_02_desc">Learn the first 3 month codes!</string>
<string name="task_02_03_name">Month codes: Apr - Jun</string>
<string name="task_02_03_desc">Learn the next 3 month codes!</string>
<string name="task_02_04_name">Month codes: Jan - Jun</string>
<string name="task_02_04_desc">Show what you have learned so far!</string>
<string name="task_02_05_name">Month codes: Jul - Sep</string>
<string name="task_02_05_desc">Learn the month codes 7 to 9! Nearly!</string>
<string name="task_02_06_name">Month codes: Oct - Dec</string>
<string name="task_02_06_desc">Learn the last 3 month codes!</string>
<string name="task_02_07_name">Month codes: Jul - Dec</string>
<string name="task_02_07_desc">Are you ready for the ultimate test?</string>
<string name="task_02_08_name">Month codes</string>
<string name="task_02_08_desc">Now combine all your knowledge!</string>
<string name="task_02_09_name">Day code</string>
<string name="task_02_09_desc">What is the code of the day?</string>
<string name="task_02_10_name">Year code 1</string>
<string name="task_02_10_desc">Start simple with the first step only</string>
<string name="task_02_11_name">Year code 2</string>
<string name="task_02_11_desc">Getting more complicated</string>
<string name="task_02_12_name">Year code 3</string>
<string name="task_02_12_desc">And even more!</string>
<string name="task_02_13_name">Year code 4</string>
<string name="task_02_13_desc">And now the complete calculation!</string>
<string name="task_02_14_name">Year code 5</string>
<string name="task_02_14_desc">What is the correct year code for the last 2 digits</string>
<string name="task_02_15_name">Century code</string>
<string name="task_02_15_desc">Answer with the correct century code 0, 2, 4 or 6</string>
<string name="task_02_16_name">Leap Year</string>
<string name="task_02_16_desc">Check if the year is a leap year</string>
</resources>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="tip_01_01_name">Welcome!</string>
<string name="tip_01_01_desc">Introduction</string>
<string name="tip_01_01_html"><![CDATA[ <h1>Introduction</h1>This is <b>pretty</b> <img src="ic_star_gold" />cool stuff! ]]></string>
<string name="tip_02_01_name">Week Day Calculation</string>
<string name="tip_02_01_desc">Introduction</string>
<string name="tip_02_01_html"><![CDATA[ <h1>Week Day Calculation</h1> Week days for each dayx can be calculated. We calculate a "day code", a "month code" and a "year code".<br/><br/> ]]></string>
<string name="tip_02_02_name">Month Codes</string>
<string name="tip_02_02_desc">A list of each month code</string>
<string name="tip_02_02_html"><![CDATA[ <h1>Month Codes</h1> Month codes are the only number you really have to learn. Here is a list with every code:<br/><ul>
<li>January: <b>0</b></li>
<li>February: <b>3</b></li>
<li>March: <b>3</b></li>
<li>April: <b>6</b></li>
<li>May: <b>1</b></li>
<li>June: <b>4</b></li>
<li>July: <b>6</b></li>
<li>August: <b>2</b></li>
<li>September: <b>5</b></li>
<li>October: <b>0</b></li>
<li>November: <b>3</b></li>
<li>December: <b>5</b></li>
</ul>
]]></string>
<string name="tip_02_03_name">Day Codes</string>
<string name="tip_02_03_desc">How to calculate the date codes</string>
<string name="tip_02_03_html"><![CDATA[ <h1>Day Codes</h1>
This part is relatively simple. The day code is the day of the month modulo 7. "Modulo" is the remainder when dividing by 7, as explained in the following examples.<br/><ul>
<li>0 mod 7 = 0</li>
<li>5 mod 7 = 5</li>
<li>7 mod 7 = 0</li>
<li>8 mod 7 = 1</li>
<li>14 mod 7 = 0</li>
<li>18 mod 7 = 4</li>
</ul>
]]></string>
<string name="tip_02_04_name">Year Codes</string>
<string name="tip_02_04_desc">How to calculate the year codes</string>
<string name="tip_02_04_html"><![CDATA[ <h1>Year Codes</h1>
Now it gets a bit more complicated, but don\'t worry: We break it down into small pieces and learn step by step.
<h2> division into century and year</h2>
Let\'s take the year 2013 as an example: In the following we will name the front two digits, "20", the century and the back two digits, "13", the year.
<h2>The year code</h2>
TL;DR: the formula is: (YY + (YY div 4)) mod 7
<ol>
<li> dividing the year by 4 and ignoring the rest: 13 div 4 = (<b>3</b>*4 + 1) div 4 = 3</li>
<li>Totalize the last result with the year: 13 + <b>3</b> = 16</li>
<li> Divide the result by 7 and select the rest: 16 mod 7 = (2*7 + <b>2</b>) mod 7 = 2</li>
</ol>
The year code for 13 is 2.
Translated with www.DeepL.com/Translator (free version)
]]></string>
</resources>

View File

@@ -0,0 +1,15 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="ShapeAppearance.MaterialComponents.SmallComponent">
<item name="cornerSize">10dp</item>
</style>
</resources>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Board>
<Row>
<Key codes="4" keyLabel="4"/>
<Key codes="5" keyLabel="5"/>
<Key codes="6" keyLabel="6"/>
</Row>
<Row>
<Key codes="1" keyLabel="1"/>
<Key codes="2" keyLabel="2"/>
<Key codes="3" keyLabel="3"/>
</Row>
<Row>
<Gap/>
<Key codes="0" keyLabel="0"/>
<Gap/>
</Row>
</Board>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<Board>
<Row>
<Key codes="7" keyLabel="7"/>
<Key codes="8" keyLabel="8"/>
<Key codes="9" keyLabel="9"/>
</Row>
<Row>
<Key codes="4" keyLabel="4"/>
<Key codes="5" keyLabel="5"/>
<Key codes="6" keyLabel="6"/>
</Row>
<Row>
<Key codes="1" keyLabel="1"/>
<Key codes="2" keyLabel="2"/>
<Key codes="3" keyLabel="3"/>
</Row>
<Row>
<Key codes="-1" keyIcon="@drawable/ic_backspace"/>
<Key codes="0" keyLabel="0"/>
<Key codes="-2" keyLabel="ENTER"/>
</Row>
</Board>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<Board>
<Row>
<Key codes="7" keyLabel="7"/>
<Key codes="8" keyLabel="8"/>
<Key codes="9" keyLabel="9"/>
</Row>
<Row>
<Key codes="4" keyLabel="4"/>
<Key codes="5" keyLabel="5"/>
<Key codes="6" keyLabel="6"/>
</Row>
<Row>
<Key codes="1" keyLabel="1"/>
<Key codes="2" keyLabel="2"/>
<Key codes="3" keyLabel="3"/>
</Row>
<Row>
<Gap/>
<Key codes="0" keyLabel="0"/>
<Gap/>
</Row>
</Board>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<Board>
<Row>
<Key codes="7" keyLabel="7"/>
<Key codes="8" keyLabel="8"/>
<Key codes="9" keyLabel="9"/>
</Row>
<Row>
<Key codes="4" keyLabel="4"/>
<Key codes="5" keyLabel="5"/>
<Key codes="6" keyLabel="6"/>
</Row>
<Row>
<Key codes="1" keyLabel="1"/>
<Key codes="2" keyLabel="2"/>
<Key codes="3" keyLabel="3"/>
</Row>
<Row>
<Key codes="-1" keyIcon="@drawable/ic_backspace"/>
<Key codes="0" keyLabel="0"/>
<Key codes="-2" keyLabel="ENTER"/>
</Row>
</Board>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Board>
<Row>
<Key codes="0" keyLabel="Even"/>
<Key codes="1" keyLabel="Odd"/>
</Row>
</Board>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Board>
<Row>
<Key codes="0" keyLabel="No"/>
<Key codes="1" keyLabel="Yes"/>
</Row>
</Board>

View File

@@ -0,0 +1,17 @@
package de.sebse.mentalarithmetic
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}