18 Commits

Author SHA1 Message Date
d81e197d8c Remove redundant conversion. Use val where possible.
All checks were successful
Build Android / build (push) Successful in 7m19s
2023-07-18 20:18:35 +02:00
7c940eacb0 Remove obsolete null check. 2023-07-18 20:16:46 +02:00
410c948882 Update dependencies 2023-07-18 20:16:02 +02:00
4cccc26849 Show fabs next to each other. Optimize imports.
All checks were successful
Build Android / build (push) Successful in 7m52s
2023-07-09 16:08:55 +02:00
ac615560ae Add setting and a button to change between modes.
All checks were successful
Build Android / build (push) Successful in 9m0s
2023-07-04 23:23:39 +02:00
6fe4e1f790 Show graph 2023-07-04 22:07:07 +02:00
ff5495249f Merge pull request 'feature/act_runner' (#43) from feature/act_runner into develop
All checks were successful
Build Android / build (push) Successful in 7m41s
Reviewed-on: #43
2023-07-01 11:28:47 +02:00
217370079d Publish tagged builds.
All checks were successful
Build Android / build (push) Successful in 7m58s
Build Android / build (pull_request) Has been cancelled
2023-06-30 16:21:13 +02:00
add100146d Notify on slack.
All checks were successful
Build Android / build (push) Successful in 8m40s
2023-06-30 12:06:24 +02:00
ceebe92f8b Deploy latest to nextcloud.
All checks were successful
Build Android / build (push) Successful in 7m46s
2023-06-30 10:47:19 +02:00
4f87321e2c Use android build box iamge.
All checks were successful
Build Android / build (push) Successful in 7m25s
2023-06-30 10:33:02 +02:00
81c540c2a5 Add signing and version code.
Some checks failed
Build Android / build (push) Failing after 10m19s
2023-06-25 20:02:44 +02:00
a45043424e Use act runner
All checks were successful
Build Android / build (push) Has been cancelled
2023-06-25 19:48:31 +02:00
18326e952f Merge pull request 'release/2.2' (#40) from release/2.2 into develop
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #40
2023-06-16 11:11:33 +02:00
a099659b2c Try to make DRONE_TAG work.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2023-06-02 22:16:40 +02:00
61745c95a4 Fix tagged upload url
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-06-02 16:58:36 +02:00
164cf6900f fix tagged upload link
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-02 15:03:13 +02:00
89ab0afaf5 upload to nextcloud instead of seafile
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is failing
fix nextcloud upload
2023-06-02 14:40:23 +02:00
12 changed files with 396 additions and 132 deletions

View File

@@ -1,75 +0,0 @@
---
kind: pipeline
type: docker
name: Android
steps:
- name: prepare signing
image: busybox
environment:
STOREPASSWORD:
from_secret: StorePassword
KEYPASSWORD:
from_secret: KeyPassword
commands:
- touch keystore.properties
- echo "storePassword=$STOREPASSWORD" >> keystore.properties
- echo "keyPassword=$KEYPASSWORD" >> keystore.properties
- echo "keyAlias=key0" >> keystore.properties
- echo "storeFile=../AndroidKey" >> keystore.properties
- name: generate versionCode
image: busybox
commands:
- touch version.properties
- let timestamp=$(date +%s)/10
- echo "versionCode=$timestamp" >> version.properties
- name: build
image: mingc/android-build-box
commands:
- ./gradlew test
- ./gradlew assembleRelease
- ./gradlew bundleRelease
- name: deploy latest build
image: curlimages/curl
environment:
SEAFILE_API_KEY:
from_secret: SeafileApiKey
APK_FILE: app/build/outputs/apk/release/app-release.apk
BUNDLE_FILE: app/build/outputs/bundle/release/app-release.aab
SEAFILE_REPO: 6debeef9-121e-46ba-acc7-81e109fdcbdd
commands:
- 'UPLOAD_URL=$(curl -H "Authorization: Token $SEAFILE_API_KEY" https://seafile.zobrist.me/api2/repos/$SEAFILE_REPO/upload-link/ | tr -d "\"")'
- echo $UPLOAD_URL
- 'curl -H "Authorization: Token $SEAFILE_API_KEY" -F file=@$APK_FILE -F parent_dir=/ -F relative_path=latest/ -F replace=1 "$UPLOAD_URL"'
- 'curl -H "Authorization: Token $SEAFILE_API_KEY" -F file=@$BUNDLE_FILE -F parent_dir=/ -F relative_path=latest/ -F replace=1 "$UPLOAD_URL"'
- name: deploy tagged build
image: curlimages/curl
environment:
SEAFILE_API_KEY:
from_secret: SeafileApiKey
APK_FILE: app/build/outputs/apk/release/app-release.apk
BUNDLE_FILE: app/build/outputs/bundle/release/app-release.aab
SEAFILE_REPO: 6debeef9-121e-46ba-acc7-81e109fdcbdd
commands:
- 'UPLOAD_URL=$(curl -H "Authorization: Token $SEAFILE_API_KEY" https://seafile.zobrist.me/api2/repos/$SEAFILE_REPO/upload-link/ | tr -d "\"")'
- 'curl -H "Authorization: Token $SEAFILE_API_KEY" -F file=@$APK_FILE -F parent_dir=/ -F relative_path=tagged/$DRONE_TAG/ -F replace=1 "$UPLOAD_URL"'
- 'curl -H "Authorization: Token $SEAFILE_API_KEY" -F file=@$BUNDLE_FILE -F parent_dir=/ -F relative_path=tagged/$DRONE_TAG/ -F replace=1 "$UPLOAD_URL"'
- 'curl -d "operation=rename&newname=app-release$DRONE_TAG.apk" -H "Authorization: Token $SEAFILE_API_KEY" https://seafile.zobrist.me/api2/repos/$SEAFILE_REPO/file/?p=/tagged/$DRONE_TAG/app-release.apk'
- 'curl -d "operation=rename&newname=app-release$DRONE_TAG.aab" -H "Authorization: Token $SEAFILE_API_KEY" https://seafile.zobrist.me/api2/repos/$SEAFILE_REPO/file/?p=/tagged/$DRONE_TAG/app-release.aab'
when:
event:
- tag
- name: slack notification
image: plugins/slack
settings:
webhook:
from_secret: SlackWebhook
when:
status:
- failure
- success

View File

@@ -0,0 +1,48 @@
name: Build Android
on: [pull_request, push]
jobs:
build:
runs-on: ubuntu-latest-android
steps:
- name: Checkout the code
uses: actions/checkout@v2
- name: Prepare signing
run: |
touch keystore.properties
echo "storePassword=${{ secrets.STOREPASSWORD }}" >> keystore.properties
echo "keyPassword=${{ secrets.KEYPASSWORD }}" >> keystore.properties
echo "keyAlias=key0" >> keystore.properties
echo "storeFile=../AndroidKey" >> keystore.properties
- name: Generate versionCode
run: |
touch version.properties
let timestamp=$(date +%s)/10
echo "versionCode=$timestamp" >> version.properties
- name: Test the app
run: ./gradlew test
- name: Build apk
run: ./gradlew assembleRelease
- name: Build abb
run: ./gradlew bundleRelease
- name: Deploy latest to Nextcloud
run: |
curl -k -u "${{ secrets.NEXTCLOUD_USERNAME }}:${{ secrets.NEXTCLOUD_PASSWORD }}" -T "app/build/outputs/apk/release/app-release.apk" "https://nextcloud.zobrist.me/remote.php/dav/files/deploy/TichuCounter/latest/app-release.apk"
curl -k -u "${{ secrets.NEXTCLOUD_USERNAME }}:${{ secrets.NEXTCLOUD_PASSWORD }}" -T "app/build/outputs/bundle/release/app-release.aab" "https://nextcloud.zobrist.me/remote.php/dav/files/deploy/TichuCounter/latest/app-release.aab"
- name: Deploy tagged build to Nextcloud
run: |
curl -k -u "${{ secrets.NEXTCLOUD_USERNAME }}:${{ secrets.NEXTCLOUD_PASSWORD }}" -T "app/build/outputs/apk/release/app-release.apk" "https://nextcloud.zobrist.me/remote.php/dav/files/deploy/TichuCounter/tagged/app-release-${GITHUB_REF_NAME##*/}.apk"
curl -k -u "${{ secrets.NEXTCLOUD_USERNAME }}:${{ secrets.NEXTCLOUD_PASSWORD }}" -T "app/build/outputs/bundle/release/app-release.aab" "https://nextcloud.zobrist.me/remote.php/dav/files/deploy/TichuCounter/tagged/app-release-${GITHUB_REF_NAME##*/}.aab"
- uses: https://github.com/ravsamhq/notify-slack-action@v2
if: always()
with:
status: ${{ job.status }} # required
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required

View File

@@ -91,17 +91,17 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation "androidx.compose.material3:material3:1.1.0"
implementation "androidx.compose.material3:material3:1.1.1"
implementation 'com.google.android.play:core-ktx:1.8.1'
implementation 'com.google.android.play:core-ktx:1.8.1'
implementation 'com.google.code.gson:gson:2.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
implementation 'androidx.navigation:navigation-fragment-ktx:2.6.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.6.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
implementation 'androidx.fragment:fragment-ktx:1.5.7'
implementation 'androidx.fragment:fragment-ktx:1.6.0'
implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'androidx.recyclerview:recyclerview:1.3.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
@@ -111,7 +111,7 @@ dependencies {
implementation "androidx.compose.ui:ui:1.4.3"
implementation "androidx.compose.ui:ui-tooling-preview:1.4.3"
implementation "androidx.compose.runtime:runtime-livedata:1.4.3"
implementation "androidx.navigation:navigation-compose:2.5.3"
implementation "androidx.navigation:navigation-compose:2.6.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
@@ -121,12 +121,13 @@ dependencies {
debugImplementation "androidx.compose.ui:ui-tooling:1.4.3"
debugImplementation "androidx.compose.ui:ui-test-manifest:1.4.3"
kapt "com.google.dagger:hilt-compiler:2.44"
implementation "androidx.room:room-runtime:2.5.1"
annotationProcessor "androidx.room:room-compiler:2.5.1"
kapt "androidx.room:room-compiler:2.5.1"
implementation "androidx.room:room-ktx:2.5.1"
implementation "androidx.room:room-runtime:2.5.2"
annotationProcessor "androidx.room:room-compiler:2.5.2"
kapt "androidx.room:room-compiler:2.5.2"
implementation "androidx.room:room-ktx:2.5.2"
implementation "androidx.multidex:multidex:2.0.1"
api "androidx.navigation:navigation-fragment-ktx:2.5.3"
api "androidx.navigation:navigation-fragment-ktx:2.6.0"
implementation "com.patrykandpatrick.vico:compose-m3:1.6.5"
}
// Allow references to generated code

View File

@@ -6,14 +6,33 @@ import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.material.icons.outlined.Calculate
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.outlined.Keyboard
import androidx.compose.material.icons.outlined.List
import androidx.compose.material.icons.outlined.MoreVert
import androidx.compose.material.icons.outlined.Redo
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.Undo
import androidx.compose.material3.DrawerState
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Scaffold
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.currentBackStackEntryAsState
@@ -22,12 +41,23 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import me.zobrist.tichucounter.domain.*
import me.zobrist.tichucounter.domain.DrawerItem
import me.zobrist.tichucounter.domain.ISystemSettingsListener
import me.zobrist.tichucounter.domain.KeepScreenOn
import me.zobrist.tichucounter.domain.Language
import me.zobrist.tichucounter.domain.Route
import me.zobrist.tichucounter.domain.SettingsAdapter
import me.zobrist.tichucounter.domain.Theme
import me.zobrist.tichucounter.domain.TopBarAction
import me.zobrist.tichucounter.domain.TopBarState
import me.zobrist.tichucounter.domain.composable
import me.zobrist.tichucounter.domain.navigate
import me.zobrist.tichucounter.ui.AppTheme
import me.zobrist.tichucounter.ui.MainViewModel
import me.zobrist.tichucounter.ui.about.AboutView
import me.zobrist.tichucounter.ui.composables.DropDownMenu
import me.zobrist.tichucounter.ui.counter.*
import me.zobrist.tichucounter.ui.counter.Counter
import me.zobrist.tichucounter.ui.counter.CounterViewModel
import me.zobrist.tichucounter.ui.history.HistoryList
import me.zobrist.tichucounter.ui.history.HistoryViewModel
import me.zobrist.tichucounter.ui.layout.DrawerContent
@@ -37,7 +67,7 @@ import me.zobrist.tichucounter.ui.settings.SettingsViewModel
import javax.inject.Inject
@AndroidEntryPoint
class MainActivity : AppCompatActivity(), ISettingsChangeListener {
class MainActivity : AppCompatActivity(), ISystemSettingsListener {
@Inject
lateinit var settingsAdapter: SettingsAdapter
@@ -156,6 +186,7 @@ class MainActivity : AppCompatActivity(), ISettingsChangeListener {
floatingActionButton = {
if (showFab) {
FloatingActionButton(
modifier = Modifier.padding(end = 75.dp),
onClick = { fabAction() }) {
Icon(Icons.Outlined.Keyboard, null)
}

View File

@@ -14,19 +14,26 @@ enum class Language(val value: LocaleListCompat) {
GERMAN(LocaleListCompat.forLanguageTags("de"))
}
enum class CounterMode { LIST, GRAPH }
enum class KeepScreenOn(val value: Boolean) { ON(true), OFF(false) }
interface ISettingsChangeListener {
interface ISystemSettingsListener {
fun onLanguageChanged(language: Language)
fun onThemeChanged(theme: Theme)
fun onScreenOnChanged(keepOn: KeepScreenOn)
}
interface IDisplaySettingsListener {
fun onCounterModeChanged(counterMode: CounterMode)
}
@Singleton
class SettingsAdapter @Inject constructor(@ApplicationContext private val context: Context) {
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
private var listenerList = mutableListOf<ISettingsChangeListener>()
private var systemSettingsListeners = mutableListOf<ISystemSettingsListener>()
private var displaySettingsListeners = mutableListOf<IDisplaySettingsListener>()
var language: Language
private set
@@ -37,37 +44,58 @@ class SettingsAdapter @Inject constructor(@ApplicationContext private val contex
var keepScreenOn: KeepScreenOn
private set
var counterMode: CounterMode
private set
init {
language = try {
enumValueOf(sharedPreferences.getString(Language::class.simpleName, null)!!)
} catch (_: NullPointerException) {
} catch (_: Exception) {
Language.DEFAULT
}
theme = try {
enumValueOf(sharedPreferences.getString(Theme::class.simpleName, null)!!)
} catch (_: java.lang.Exception) {
} catch (_: Exception) {
Theme.DEFAULT
}
keepScreenOn = try {
enumValueOf(sharedPreferences.getString(KeepScreenOn::class.simpleName, null)!!)
} catch (_: java.lang.Exception) {
} catch (_: Exception) {
KeepScreenOn.OFF
}
counterMode = try {
enumValueOf(sharedPreferences.getString(CounterMode::class.simpleName, null)!!)
} catch (_: Exception) {
CounterMode.LIST
}
}
fun registerOnChangeListener(listener: ISettingsChangeListener) {
listenerList.add(listener)
fun registerOnChangeListener(listener: IDisplaySettingsListener) {
displaySettingsListeners.add(listener)
listener.onCounterModeChanged(counterMode)
}
fun unregisterOnChangeListener(listener: IDisplaySettingsListener?) {
if (listener != null) {
displaySettingsListeners.remove(listener)
}
}
fun registerOnChangeListener(listener: ISystemSettingsListener) {
systemSettingsListeners.add(listener)
listener.onThemeChanged(theme)
listener.onLanguageChanged(language)
listener.onScreenOnChanged(keepScreenOn)
}
fun unregisterOnChangeListener(listener: ISettingsChangeListener?) {
fun unregisterOnChangeListener(listener: ISystemSettingsListener?) {
if (listener != null) {
listenerList.remove(listener)
systemSettingsListeners.remove(listener)
}
}
@@ -89,6 +117,12 @@ class SettingsAdapter @Inject constructor(@ApplicationContext private val contex
notifyListeners(setting)
}
fun setCounterMode(counterMode: CounterMode) {
this.counterMode = counterMode
updatePreference(CounterMode::class.simpleName, counterMode.name)
notifyListeners(counterMode)
}
private fun updatePreference(name: String?, value: String) {
val editor = sharedPreferences.edit()
editor.putString(name, value)
@@ -96,21 +130,27 @@ class SettingsAdapter @Inject constructor(@ApplicationContext private val contex
}
private fun notifyListeners(language: Language) {
listenerList.forEach {
systemSettingsListeners.forEach {
it.onLanguageChanged(language)
}
}
private fun notifyListeners(theme: Theme) {
listenerList.forEach {
systemSettingsListeners.forEach {
it.onThemeChanged(theme)
}
}
private fun notifyListeners(keepScreenOn: KeepScreenOn) {
listenerList.forEach {
systemSettingsListeners.forEach {
it.onScreenOnChanged(keepScreenOn)
}
}
private fun notifyListeners(counterMode: CounterMode) {
displaySettingsListeners.forEach {
it.onCounterModeChanged(counterMode)
}
}
}

View File

@@ -1,19 +1,31 @@
package me.zobrist.tichucounter.ui.counter
import android.content.res.Configuration
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ShowChart
import androidx.compose.material.icons.outlined.TableRows
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import me.zobrist.tichucounter.data.entity.Round
import me.zobrist.tichucounter.domain.CounterMode
import me.zobrist.tichucounter.ui.AppTheme
@@ -50,10 +62,13 @@ fun Landscape(viewModel: ICounterViewModel) {
viewModel.totalScoreB
)
RoundListView(
viewModel.roundScoreList,
Modifier.weight(1f)
RoundView(
Modifier.weight(1f),
viewModel.counterMode,
viewModel.roundScoreList
)
{ viewModel.changeCounterMode() }
}
if (!viewModel.keyboardHidden) {
Column(Modifier.weight(1f)) {
@@ -81,10 +96,12 @@ fun Portrait(viewModel: ICounterViewModel) {
viewModel.totalScoreB
)
RoundListView(
viewModel.roundScoreList,
Modifier.weight(1f)
RoundView(
Modifier.weight(1f),
viewModel.counterMode,
viewModel.roundScoreList
)
{ viewModel.changeCounterMode() }
if (!viewModel.keyboardHidden) {
KeyBoardView(viewModel = viewModel)
@@ -92,6 +109,41 @@ fun Portrait(viewModel: ICounterViewModel) {
}
}
@Composable
private fun RoundView(
modifier: Modifier = Modifier,
counterMode: CounterMode,
rounds: List<Round>,
changeCounterMode: () -> Unit
) {
Box(modifier) {
if (counterMode == CounterMode.LIST) {
RoundListView(
rounds,
Modifier.fillMaxHeight()
)
} else {
RoundGraphView(
rounds,
Modifier.fillMaxHeight()
)
}
FloatingActionButton(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(15.dp),
onClick = { changeCounterMode() }) {
Icon(getIcon(counterMode), null)
}
}
}
private fun getIcon(counterMode: CounterMode): ImageVector {
return when (counterMode) {
CounterMode.LIST -> Icons.Outlined.ShowChart
CounterMode.GRAPH -> Icons.Outlined.TableRows
}
}
@Preview(name = "Light Mode")
@Preview(name = "Dark Mode", uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)
@@ -123,6 +175,7 @@ internal class PreviewViewModel : ICounterViewModel {
listOf("TeamA", "asdffd", "TeamB", "really really long Team Name that is way too long")
override val teamNameSuggestionsB: List<String> =
listOf("TeamA", "asdffd", "TeamB", "really really long Team Name that is way too long")
override val counterMode: CounterMode = CounterMode.GRAPH
override fun focusLastInput() {
}
@@ -155,6 +208,9 @@ internal class PreviewViewModel : ICounterViewModel {
override fun updateNameB(value: String) {
}
override fun changeCounterMode() {
}
override fun updateFocusStateA(state: Boolean) {
}

View File

@@ -11,6 +11,9 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import me.zobrist.tichucounter.data.entity.Round
import me.zobrist.tichucounter.domain.CounterMode
import me.zobrist.tichucounter.domain.IDisplaySettingsListener
import me.zobrist.tichucounter.domain.SettingsAdapter
import me.zobrist.tichucounter.domain.Tichu
import me.zobrist.tichucounter.domain.digitCount
import me.zobrist.tichucounter.domain.getTotalPoints
@@ -57,16 +60,19 @@ interface ICounterViewModel : IKeyBoardViewModel {
val teamNameB: String
val teamNameSuggestionsA: List<String>
val teamNameSuggestionsB: List<String>
val counterMode: CounterMode
fun updateNameA(value: String)
fun updateNameB(value: String)
fun changeCounterMode()
}
@HiltViewModel
class CounterViewModel @Inject constructor(
private val gameRepository: GameRepository
private val gameRepository: GameRepository,
private val settingsAdapter: SettingsAdapter
) :
ViewModel(), ICounterViewModel {
ViewModel(), ICounterViewModel, IDisplaySettingsListener {
override var roundScoreList by mutableStateOf(emptyList<Round>())
private set
@@ -113,6 +119,17 @@ class CounterViewModel @Inject constructor(
override var teamNameSuggestionsB by mutableStateOf(listOf<String>())
private set
override var counterMode by mutableStateOf(CounterMode.LIST)
private set
init {
settingsAdapter.registerOnChangeListener(this)
}
override fun onCleared() {
settingsAdapter.unregisterOnChangeListener(this)
}
override var activeValue: String
get() {
return if (isBFocused) {
@@ -157,8 +174,6 @@ class CounterViewModel @Inject constructor(
init {
viewModelScope.launch {
gameRepository.getActiveGameFlow().collect {
if (it != null) {
val score = it.getTotalPoints()
roundScoreList = it.rounds
@@ -169,8 +184,6 @@ class CounterViewModel @Inject constructor(
teamNameB = it.game.nameB
buildTeamNameSuggestions()
}
}
}
@@ -289,6 +302,14 @@ class CounterViewModel @Inject constructor(
}
}
override fun changeCounterMode() {
val nextMode = when (settingsAdapter.counterMode) {
CounterMode.LIST -> CounterMode.GRAPH
CounterMode.GRAPH -> CounterMode.LIST
}
settingsAdapter.setCounterMode(nextMode)
}
override fun updateFocusStateA(state: Boolean) {
isAFocused = state
if (state) {
@@ -362,4 +383,8 @@ class CounterViewModel @Inject constructor(
return filtered.sorted().sortedBy { it.length }.take(10)
}
override fun onCounterModeChanged(counterMode: CounterMode) {
this.counterMode = counterMode
}
}

View File

@@ -1,17 +1,40 @@
package me.zobrist.tichucounter.ui.counter
import android.content.res.Configuration
import androidx.compose.animation.core.*
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Backspace
import androidx.compose.material.icons.outlined.Check
import androidx.compose.material.icons.outlined.KeyboardHide
import androidx.compose.material.icons.outlined.SwapHoriz
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.material3.Divider
import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester

View File

@@ -0,0 +1,104 @@
package me.zobrist.tichucounter.ui.counter
import android.content.res.Configuration
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.tooling.preview.Preview
import com.patrykandpatrick.vico.compose.axis.vertical.startAxis
import com.patrykandpatrick.vico.compose.chart.Chart
import com.patrykandpatrick.vico.compose.chart.line.lineChart
import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollSpec
import com.patrykandpatrick.vico.core.axis.formatter.DecimalFormatAxisValueFormatter
import com.patrykandpatrick.vico.core.chart.line.LineChart
import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider
import com.patrykandpatrick.vico.core.entry.FloatEntry
import com.patrykandpatrick.vico.core.entry.composed.ComposedChartEntryModelProducer.Companion.composedChartEntryModelOf
import com.patrykandpatrick.vico.core.entry.entryModelOf
import me.zobrist.tichucounter.data.entity.Round
import me.zobrist.tichucounter.ui.AppTheme
import kotlin.math.ceil
import kotlin.math.floor
@Composable
fun RoundGraphView(rounds: List<Round>, modifier: Modifier = Modifier) {
val points = getPoints(rounds)
val range = getRange(points.first, points.second, 50)
val specA = LineChart.LineSpec(MaterialTheme.colorScheme.primary.toArgb())
val specB = LineChart.LineSpec(MaterialTheme.colorScheme.secondary.toArgb())
Chart(
chart = lineChart(
lines = listOf(specA, specB),
axisValuesOverrider = AxisValuesOverrider.fixed(
minY = range.first,
maxY = range.second
)
),
model = composedChartEntryModelOf(
listOf(
entryModelOf(points.first),
entryModelOf(points.second)
)
),
startAxis = startAxis(
maxLabelCount = 10,
valueFormatter = DecimalFormatAxisValueFormatter("#")
),
chartScrollSpec = rememberChartScrollSpec(isScrollEnabled = false),
modifier = modifier
)
}
private fun getPoints(rounds: List<Round>): Pair<List<FloatEntry>, List<FloatEntry>> {
val a = mutableListOf(FloatEntry(0f, 0f))
val b = mutableListOf(FloatEntry(0f, 0f))
var sumA = 0
var sumB = 0
rounds.forEachIndexed { index, round ->
sumA += round.scoreA
sumB += round.scoreB
a.add(FloatEntry((index + 1).toFloat(), sumA.toFloat()))
b.add(FloatEntry((index + 1).toFloat(), sumB.toFloat()))
}
return Pair(a, b)
}
private fun getRange(a: List<FloatEntry>, b: List<FloatEntry>, step: Int): Pair<Float, Float> {
val min = (a + b).minBy { it.y }
val max = (a + b).maxBy { it.y }
val lo = floor(min.y / step) * step
val hi = ceil(max.y / step) * step
return Pair(lo, hi)
}
@Preview(name = "Light Mode")
@Preview(name = "Dark Mode", uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)
@Composable
fun RoundGraphViewPreview() {
val rounds = listOf(
Round(1, 10, -10),
Round(1, 5, 95),
Round(1, 100, 0),
Round(1, 125, -25),
Round(1, 50, 50)
)
AppTheme {
Surface {
RoundGraphView(rounds, Modifier)
}
}
}

View File

@@ -21,7 +21,7 @@ import me.zobrist.tichucounter.data.entity.Round
import me.zobrist.tichucounter.ui.AppTheme
@Composable
fun RoundListView(rounds: List<Round>, modifier: Modifier) {
fun RoundListView(rounds: List<Round>, modifier: Modifier = Modifier) {
val lazyListState = rememberLazyListState()
val scope = rememberCoroutineScope()

View File

@@ -1,9 +1,12 @@
package me.zobrist.tichucounter.ui.counter
import android.content.res.Configuration
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview

View File

@@ -1,20 +1,28 @@
package me.zobrist.tichucounter.ui.layout
import android.content.res.Configuration
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.material.icons.outlined.Calculate
import androidx.compose.material.icons.outlined.List
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.NavigationDrawerItemDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import me.zobrist.tichucounter.R
import me.zobrist.tichucounter.domain.*
import me.zobrist.tichucounter.domain.DrawerItem
import me.zobrist.tichucounter.domain.Route
import me.zobrist.tichucounter.ui.AppTheme
import me.zobrist.tichucounter.ui.counter.*
@Composable
fun DrawerContent(