diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 88ea3aa..3cc336b 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,6 +1,22 @@ + + diff --git a/.idea/dictionaries/fabian.xml b/.idea/dictionaries/fabian.xml new file mode 100644 index 0000000..0fad81e --- /dev/null +++ b/.idea/dictionaries/fabian.xml @@ -0,0 +1,10 @@ + + + + checkmark + tichu + tichucounter + zobrist + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 7bfef59..ae2bfea 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,44 @@ + + + + diff --git a/app/build.gradle b/app/build.gradle index d57e2cd..335d9cc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,12 +34,12 @@ android { dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.core:core-ktx:1.1.0' - implementation 'androidx.appcompat:appcompat:1.1.0' - implementation 'com.google.android.material:material:1.0.0' + implementation 'androidx.core:core-ktx:1.3.1' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.2.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.navigation:navigation-fragment-ktx:2.1.0' - implementation 'androidx.navigation:navigation-ui-ktx:2.1.0' + implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0' + implementation 'androidx.navigation:navigation-ui-ktx:2.3.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 17bec36..d7ced19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ android:theme="@style/AppTheme"> diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000..cca0394 Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/me/zobrist/tichucounter/History.kt b/app/src/main/java/me/zobrist/tichucounter/History.kt new file mode 100644 index 0000000..9f2cd3a --- /dev/null +++ b/app/src/main/java/me/zobrist/tichucounter/History.kt @@ -0,0 +1,83 @@ +@file:Suppress("unused") + +package me.zobrist.tichucounter + +import android.os.Parcel +import android.os.Parcelable + +class History() : Parcelable { + private var scores: ArrayList = ArrayList() + + constructor(parcel: Parcel) : this() { + scores = parcel.readSerializable() as ArrayList + } + + fun getScoreA(): Int { + var tempScore = 0 + scores.forEach { + tempScore += it.scoreA + } + return tempScore + } + + fun getScoreB(): Int { + var tempScore = 0 + scores.forEach { + tempScore += it.scoreB + } + return tempScore + } + + fun getHistoryA(): String { + var tempHistory = String() + scores.forEach { + tempHistory = tempHistory.plus(it.scoreA.toString()).plus("\n") + } + return tempHistory + } + + fun getHistoryB(): String { + var tempHistory = String() + scores.forEach { + tempHistory = tempHistory.plus(it.scoreB.toString()).plus("\n") + } + return tempHistory + } + + fun logRound(round: Round) { + scores.add(round) + } + + fun revertLastRound() { + if (scores.isNotEmpty()) { + scores.removeAt(scores.size - 1) + } + } + + fun clearAll() { + scores.clear() + } + + fun isEmpty(): Boolean { + return scores.isEmpty() + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeSerializable(scores) + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): History { + return History(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt b/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt index bdcdcbf..7da4264 100644 --- a/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt +++ b/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt @@ -1,141 +1,384 @@ package me.zobrist.tichucounter +import android.app.AlertDialog +import android.content.Context import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity +import android.text.InputType import android.view.Menu +import android.view.MenuItem +import android.widget.ScrollView +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.app.AppCompatDelegate import androidx.core.widget.doOnTextChanged import kotlinx.android.synthetic.main.content_main.* -import kotlinx.coroutines.sync.Mutex -import kotlin.Exception class MainActivity : AppCompatActivity() { - var mut = Mutex() + private var invertA: Boolean = false + private var invertB: Boolean = false - private var listA: List = emptyList() - private var listB: List = emptyList() + private var updateOnChange: Boolean = true - private var tempCounterTeamA: Int = 0 - private var tempCounterTeamB: Int = 0 - - private var counterTeamA: Int = 0 - private var counterTeamB: Int = 0 + private lateinit var history: History + private var currentRound = Round() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setSupportActionBar(findViewById(R.id.toolbar)) + inputTeamA.setRawInputType(InputType.TYPE_NULL) + inputTeamB.setRawInputType(InputType.TYPE_NULL) + inputTeamA.requestFocus() + disableSubmitButton() + updateTheme(this.getSharedPreferences("Settings", Context.MODE_PRIVATE).getInt("Theme", 2)) + history = savedInstanceState?.getParcelable("history") ?: History() + updateView() inputTeamA.doOnTextChanged { text, start, count, after -> if (inputTeamA.isFocused) { - inputTeamB.setText(updateNumber(text, tempCounterTeamA + tempCounterTeamB)) + if (inputTeamA.text.isNotEmpty()) { + if (updateOnChange) { + currentRound = try { + Round(text.toString().toInt(), true) + + } catch (e: java.lang.Exception) { + Round(0, 0) + } + inputTeamB.setText(currentRound.scoreB.toString()) + }else{ + updateOnChange = true + } + }else{ + inputTeamA.text.clear() + inputTeamB.text.clear() + } + } + + if(currentRound.isValidRound()){ + enableSubmitButton() + }else{ + disableSubmitButton() } } + + inputTeamB.doOnTextChanged { text, start, count, after -> if (inputTeamB.isFocused) { - inputTeamA.setText(updateNumber(text, tempCounterTeamB + tempCounterTeamA)) + if (inputTeamB.text.isNotEmpty()){ + if(updateOnChange){ + currentRound = try { + Round(text.toString().toInt(), false) + + } catch (e: java.lang.Exception){ + Round(0, 0) + } + inputTeamA.setText(currentRound.scoreA.toString()) + + }else{ + updateOnChange = true + } + + }else{ + inputTeamA.text.clear() + inputTeamB.text.clear() + } + } + + if(currentRound.isValidRound()){ + enableSubmitButton() + }else{ + disableSubmitButton() } } - add100.setOnClickListener { + buttonAdd100.setOnClickListener { if (inputTeamA.isFocused) { - tempCounterTeamA += 100 + val temp = try { inputTeamA.text.toString().toInt() + 100 } catch (e: Exception) { + inputTeamB.setText(0.toString()) 100 + } + updateOnChange = false inputTeamA.setText(temp.toString()) } if (inputTeamB.isFocused) { - tempCounterTeamB += 100 val temp = try { inputTeamB.text.toString().toInt() + 100 } catch (e: Exception) { + inputTeamA.setText(0.toString()) 100 + } + updateOnChange = false inputTeamB.setText(temp.toString()) } - - updateTemp() } - sub100.setOnClickListener { + buttonSub100.setOnClickListener { if (inputTeamA.isFocused) { - tempCounterTeamA -= 100 val temp = try { inputTeamA.text.toString().toInt() - 100 } catch (e: Exception) { -100 } + updateOnChange = false inputTeamA.setText(temp.toString()) } if (inputTeamB.isFocused) { - tempCounterTeamB -= 100 val temp = try { inputTeamB.text.toString().toInt() - 100 } catch (e: Exception) { -100 } + updateOnChange = false inputTeamB.setText(temp.toString()) } - - updateTemp() } - add.setOnClickListener { + button0.setOnClickListener { + giveFocusToAIfNone() + appendToFocusedInput('0') + } + + button1.setOnClickListener { + giveFocusToAIfNone() + appendToFocusedInput('1') + } + + button2.setOnClickListener { + giveFocusToAIfNone() + appendToFocusedInput('2') + } + + button3.setOnClickListener { + giveFocusToAIfNone() + appendToFocusedInput('3') + } + + button4.setOnClickListener { + giveFocusToAIfNone() + appendToFocusedInput('4') + } + + button5.setOnClickListener { + giveFocusToAIfNone() + appendToFocusedInput('5') + } + + button6.setOnClickListener { + giveFocusToAIfNone() + appendToFocusedInput('6') + } + + button7.setOnClickListener { + giveFocusToAIfNone() + appendToFocusedInput('7') + } + + button8.setOnClickListener { + giveFocusToAIfNone() + appendToFocusedInput('8') + } + + button9.setOnClickListener { + giveFocusToAIfNone() + appendToFocusedInput('9') + } + + buttonInv.setOnClickListener { + val tempInt: Int + + giveFocusToAIfNone() + + if(inputTeamA.isFocused ){ + if (inputTeamA.text.isNotEmpty()){ + tempInt = inputTeamA.text.toString().toInt() * -1 + inputTeamA.setText(tempInt.toString()) + }else{ + invertB = false + invertA = true + } + + + }else if(inputTeamB.isFocused) { + if(inputTeamB.text.isNotEmpty()){ + tempInt = inputTeamB.text.toString().toInt() * -1 + inputTeamB.setText(tempInt.toString()) + } else{ + invertA = false + invertB = true + } + } + } + + buttonBack.setOnClickListener { + giveFocusToAIfNone() + + if (inputTeamA.isFocused) { + if (inputTeamA.text.isNotEmpty()) { + val string = inputTeamA.text.toString() + inputTeamA.setText(string.substring(0, string.length - 1)) + } + + } else if (inputTeamB.isFocused) { + if (inputTeamB.text.isNotEmpty()) { + val string = inputTeamB.text.toString() + inputTeamB.setText(string.substring(0, string.length - 1)) + } + } + } + + submit.setOnClickListener { + giveFocusToAIfNone() if (inputTeamA.text.isNotEmpty() && inputTeamB.text.isNotEmpty()) { - tempCounterTeamA = 0 - tempCounterTeamB = 0 - counterTeamA += inputTeamA.text.toString().toInt() - counterTeamB += inputTeamB.text.toString().toInt() + history.logRound(Round(inputTeamA.text.toString().toInt(), inputTeamB.text.toString().toInt())) - historyA.text = historyA.text.toString().plus(scoreA.text.toString().plus("\n")) - historyB.text = historyB.text.toString().plus(scoreB.text.toString().plus("\n")) - scoreA.text = counterTeamA.toString() - scoreB.text = counterTeamB.toString() + updateView() + + + inputTeamA.text.clear() + inputTeamB.text.clear() + + scrollViewHistory.fullScroll(ScrollView.FOCUS_DOWN) - inputTeamA.setText("") - inputTeamB.setText("") } - } } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putParcelable("history", history) + } + override fun onCreateOptionsMenu(menu: Menu): Boolean { // Inflate the menu; this adds items to the action bar if it is present. menuInflater.inflate(R.menu.menu_main, menu) return true } - private fun updateNumber(inputText: CharSequence?, offset: Int): String { - var toSet: Int = 0 + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.action_clear -> { + clearAll() + true + } + R.id.action_undo -> { + undoLastRound() + true + } + R.id.action_theme -> { + chooseThemeDialog() + true + } + else -> super.onOptionsItemSelected(item) + } + } - toSet = try { - 100 - inputText.toString().toInt() + private fun giveFocusToAIfNone() { + if (!inputTeamA.isFocused && !inputTeamB.isFocused) { + inputTeamA.requestFocus() + } + } - } catch (e: Exception) { - 0 + private fun undoLastRound() { + + history.revertLastRound() + updateView() + + } + + private fun updateView() { + scoreA.text = history.getScoreA().toString() + scoreB.text = history.getScoreB().toString() + + historyA.text = history.getHistoryA() + historyB.text = history.getHistoryB() + } + + private fun clearAll() { + historyA.text = "" + historyB.text = "" + inputTeamA.text.clear() + inputTeamB.text.clear() + scoreA.text = "0" + scoreB.text = "0" + + history.clearAll() + } + + private fun appendToFocusedInput(toAppend: Char){ + if(inputTeamA.isFocused){ + if(invertA){ + invertA = false + inputTeamA.text.append('-') + } + + inputTeamA.text.append(toAppend) + }else if(inputTeamB.isFocused) + { + if(invertB){ + invertB = false + inputTeamB.text.append('-') + } + inputTeamB.text.append(toAppend) } - toSet += offset - return "$toSet" } - private fun updateTemp() { - nameTeamA.text = tempCounterTeamA.toString() - nameTeamB.text = tempCounterTeamB.toString() + private fun enableSubmitButton() { + submit.imageAlpha = 255 // 0 being transparent and 255 being opaque + submit.isEnabled = true } + private fun disableSubmitButton(){ + submit.imageAlpha = 60 // 0 being transparent and 255 being opaque + submit.isEnabled = false + } + private fun chooseThemeDialog() { + + val builder = AlertDialog.Builder(this) + builder.setTitle(getString(R.string.choose_theme_text)) + val styles = arrayOf("Light","Dark","System default") + + val checkedItem = this.getSharedPreferences("", Context.MODE_PRIVATE).getInt("Theme", 2) + + val prefs = this.getSharedPreferences("Settings", Context.MODE_PRIVATE).edit() + + + builder.setSingleChoiceItems(styles, checkedItem) { dialog, which -> + + prefs.putInt("Theme", which) + prefs.apply() + + updateTheme(which) + + dialog.dismiss() + } + + val dialog = builder.create() + dialog.show() + } + + private fun updateTheme(which: Int) { + when (which) { + 0 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + 1 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + 2 -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) + } + delegate.applyDayNight() + } } \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/Round.kt b/app/src/main/java/me/zobrist/tichucounter/Round.kt new file mode 100644 index 0000000..fad6b61 --- /dev/null +++ b/app/src/main/java/me/zobrist/tichucounter/Round.kt @@ -0,0 +1,36 @@ +package me.zobrist.tichucounter + +class Round() { + var scoreA: Int = 0 + var scoreB: Int = 0 + + constructor(score: Int, isScoreA: Boolean) : this() { + if (isScoreA) { + scoreA = score + scoreB = calculateOtherScore(scoreA) + } else { + scoreB = score + scoreA = calculateOtherScore(scoreB) + } + } + + constructor(scoreA: Int, scoreB: Int) : this() { + this.scoreA = scoreA + this.scoreB = scoreB + } + + private fun calculateOtherScore(score: Int): Int { + if (isMultipleOf100(score)) { + return 0 + } + return 100 - (score % 100) + } + + private fun isMultipleOf100(score: Int): Boolean { + return (score / 100) >= 1 && (score % 100) == 0 + } + + fun isValidRound(): Boolean { + return (scoreA % 5 == 0) && (scoreB % 5 == 0) && ((scoreA + scoreB) % 100 == 0) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-mdpi/back.png b/app/src/main/res/drawable-mdpi/back.png new file mode 100644 index 0000000..72b8bd7 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/back.png differ diff --git a/app/src/main/res/drawable-mdpi/checkmark.png b/app/src/main/res/drawable-mdpi/checkmark.png new file mode 100644 index 0000000..75d2263 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/checkmark.png differ diff --git a/app/src/main/res/drawable-night/back.png b/app/src/main/res/drawable-night/back.png new file mode 100644 index 0000000..770c83c Binary files /dev/null and b/app/src/main/res/drawable-night/back.png differ diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout-land/content_main.xml b/app/src/main/res/layout-land/content_main.xml new file mode 100644 index 0000000..de790fa --- /dev/null +++ b/app/src/main/res/layout-land/content_main.xml @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +