Compare commits
2 Commits
b40f7f1f4f
...
3c8c5a38c0
Author | SHA1 | Date | |
---|---|---|---|
3c8c5a38c0 | |||
dfa41e9c94 |
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -1,6 +1,7 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'kotlin-kapt'
|
||||
}
|
||||
|
||||
android {
|
||||
@ -9,7 +10,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.polyfish0.fitnessjourney"
|
||||
minSdk 24
|
||||
minSdk 26
|
||||
targetSdk 33
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
@ -47,6 +48,31 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
def room_version = "2.5.2"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
annotationProcessor "androidx.room:room-compiler:$room_version"
|
||||
|
||||
// To use Kotlin annotation processing tool (kapt)
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
|
||||
// optional - RxJava2 support for Room
|
||||
//implementation "androidx.room:room-rxjava2:$room_version"
|
||||
|
||||
// optional - RxJava3 support for Room
|
||||
//implementation "androidx.room:room-rxjava3:$room_version"
|
||||
|
||||
// optional - Guava support for Room, including Optional and ListenableFuture
|
||||
//implementation "androidx.room:room-guava:$room_version"
|
||||
|
||||
// optional - Test helpers
|
||||
testImplementation "androidx.room:room-testing:$room_version"
|
||||
|
||||
// optional - Paging 3 Integration
|
||||
//implementation "androidx.room:room-paging:$room_version"
|
||||
|
||||
implementation "com.google.accompanist:accompanist-systemuicontroller:0.27.0"
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
implementation "androidx.navigation:navigation-compose:2.5.3"
|
||||
@ -60,6 +86,8 @@ dependencies {
|
||||
implementation 'androidx.compose.ui:ui-graphics'
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||
implementation 'androidx.compose.material3:material3'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
|
@ -3,6 +3,7 @@ package de.polyfish0.fitnessjourney
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
@ -25,6 +26,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
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 androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
@ -32,6 +34,7 @@ import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import de.polyfish0.fitnessjourney.routes.MainScreenRoute
|
||||
import de.polyfish0.fitnessjourney.routes.Routes
|
||||
import de.polyfish0.fitnessjourney.routes.bodycomposition.AddBodyCompositionRoute
|
||||
import de.polyfish0.fitnessjourney.routes.bodycomposition.BodyCompositionMainRoute
|
||||
import de.polyfish0.fitnessjourney.routes.settings.SettingsMainRoute
|
||||
import de.polyfish0.fitnessjourney.routes.trainingplans.TrainingPlansRoute
|
||||
@ -112,29 +115,46 @@ fun MainCompose(
|
||||
)
|
||||
}
|
||||
) { padding ->
|
||||
Surface(modifier = Modifier.padding(padding)) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = Routes.MAIN,
|
||||
modifier = Modifier.padding(8.dp)
|
||||
) {
|
||||
composable(Routes.MAIN) {
|
||||
MainScreenRoute(navigation = navController)
|
||||
}
|
||||
SideMenuComposable(navController = navController, padding = padding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
composable(Routes.BODY_COMPOSITION_MAIN) {
|
||||
BodyCompositionMainRoute(navigation = navController)
|
||||
}
|
||||
@Composable
|
||||
fun SideMenuComposable(navController: NavHostController, padding: PaddingValues) {
|
||||
Surface(modifier = Modifier.padding(padding)) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = Routes.MAIN,
|
||||
modifier = Modifier.padding(8.dp)
|
||||
) {
|
||||
composable(Routes.MAIN) {
|
||||
MainScreenRoute(navigation = navController)
|
||||
}
|
||||
|
||||
composable(Routes.TRAINING_PLANS_MAIN) {
|
||||
TrainingPlansRoute(navigation = navController)
|
||||
}
|
||||
|
||||
composable(Routes.SETTINGS) {
|
||||
SettingsMainRoute(navigation = navController)
|
||||
}
|
||||
}
|
||||
composable(Routes.BODY_COMPOSITION_MAIN) {
|
||||
BodyCompositionMainRoute(navigation = navController)
|
||||
}
|
||||
|
||||
composable(Routes.TRAINING_PLANS_MAIN) {
|
||||
TrainingPlansRoute(navigation = navController)
|
||||
}
|
||||
|
||||
composable(Routes.SETTINGS) {
|
||||
SettingsMainRoute(navigation = navController)
|
||||
}
|
||||
|
||||
composable(Routes.ADDING_BODY_COMPOSITION) {
|
||||
AddBodyCompositionRoute(navigation = navController)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun PreviewMainComposable() {
|
||||
FitnessJourneyTheme {
|
||||
MainCompose()
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package de.polyfish0.fitnessjourney.database
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import de.polyfish0.fitnessjourney.database.dao.BodyDataDao
|
||||
import de.polyfish0.fitnessjourney.database.dao.PicturesDAO
|
||||
import de.polyfish0.fitnessjourney.database.entities.Pictures
|
||||
|
||||
@Database(entities = [Pictures::class], version = 1)
|
||||
abstract class FitnessDatabase : RoomDatabase() {
|
||||
abstract fun picturesDao(): PicturesDAO
|
||||
abstract fun bodyDataDao(): BodyDataDao
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package de.polyfish0.fitnessjourney.database
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
|
||||
object InstanceManager {
|
||||
lateinit var fitnessDatabase: FitnessDatabase
|
||||
|
||||
fun getFitnessDB(context: Context): FitnessDatabase {
|
||||
if(!this::fitnessDatabase.isInitialized)
|
||||
fitnessDatabase = Room.databaseBuilder(
|
||||
context,
|
||||
FitnessDatabase::class.java,
|
||||
"fitness-data"
|
||||
).build()
|
||||
|
||||
return fitnessDatabase
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package de.polyfish0.fitnessjourney.database.converter
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import java.time.LocalDateTime
|
||||
|
||||
class LocalDateTimeConverter {
|
||||
@TypeConverter
|
||||
fun fromTimestamp(value: String?): LocalDateTime? {
|
||||
return value?.let { LocalDateTime.parse(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun dateToTimestamp(date: LocalDateTime?): String? {
|
||||
return date?.toString()
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package de.polyfish0.fitnessjourney.database.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import de.polyfish0.fitnessjourney.database.entities.BodyData
|
||||
import de.polyfish0.fitnessjourney.database.entities.Pictures
|
||||
|
||||
@Dao
|
||||
interface BodyDataDao {
|
||||
@Query("SELECT * FROM BodyData")
|
||||
suspend fun getAll(): List<BodyData>
|
||||
|
||||
@Query("SELECT * FROM BodyData WHERE pid IN (:pictureIds)")
|
||||
suspend fun loadAllByIds(pictureIds: IntArray): List<BodyData>
|
||||
|
||||
@Insert
|
||||
suspend fun insertAll(vararg pictures: BodyData)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(picture: BodyData)
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package de.polyfish0.fitnessjourney.database.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import de.polyfish0.fitnessjourney.database.entities.Pictures
|
||||
|
||||
@Dao
|
||||
interface PicturesDAO {
|
||||
@Query("SELECT * FROM Pictures")
|
||||
suspend fun getAll(): List<Pictures>
|
||||
|
||||
@Query("SELECT * FROM Pictures WHERE pid IN (:pictureIds)")
|
||||
suspend fun loadAllByIds(pictureIds: IntArray): List<Pictures>
|
||||
|
||||
@Insert
|
||||
suspend fun insertAll(vararg pictures: Pictures)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(picture: Pictures)
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package de.polyfish0.fitnessjourney.database.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverters
|
||||
import de.polyfish0.fitnessjourney.database.converter.LocalDateTimeConverter
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Entity(
|
||||
foreignKeys = [ForeignKey(
|
||||
entity = Pictures::class,
|
||||
parentColumns = arrayOf("id"),
|
||||
childColumns = arrayOf("pid"),
|
||||
onDelete = ForeignKey.SET_NULL
|
||||
), ForeignKey(
|
||||
entity = UserData::class,
|
||||
parentColumns = arrayOf("id"),
|
||||
childColumns = arrayOf("uid"),
|
||||
onDelete = ForeignKey.SET_NULL
|
||||
)]
|
||||
)
|
||||
@TypeConverters(LocalDateTimeConverter::class)
|
||||
data class BodyData(
|
||||
@PrimaryKey(autoGenerate = true) val bdid: Int = 0,
|
||||
@ColumnInfo(name = "pid") val pid: Int,
|
||||
@ColumnInfo(name = "uid") val uid: Int,
|
||||
@ColumnInfo(name = "weight") val weight: Float,
|
||||
@ColumnInfo(name = "size") val size: Int,
|
||||
@ColumnInfo(name = "kfa") val kfa: Float,
|
||||
@ColumnInfo(name = "date")
|
||||
val date: LocalDateTime = LocalDateTime.now()
|
||||
)
|
@ -0,0 +1,18 @@
|
||||
package de.polyfish0.fitnessjourney.database.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverters
|
||||
import de.polyfish0.fitnessjourney.database.converter.LocalDateTimeConverter
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Entity
|
||||
@TypeConverters(LocalDateTimeConverter::class)
|
||||
data class Pictures(
|
||||
@PrimaryKey(autoGenerate = true) val pid: Int = 0,
|
||||
@ColumnInfo(name = "path") val path: String,
|
||||
|
||||
@ColumnInfo(name = "date")
|
||||
val date: LocalDateTime = LocalDateTime.now()
|
||||
)
|
@ -0,0 +1,13 @@
|
||||
package de.polyfish0.fitnessjourney.database.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity
|
||||
data class UserData(
|
||||
@PrimaryKey(autoGenerate = true) val uid: Int = 0,
|
||||
@ColumnInfo(name = "pid") val pid: Int,
|
||||
@ColumnInfo(name = "size") val size: Int,
|
||||
@ColumnInfo(name = "male") val male: Boolean
|
||||
)
|
@ -3,6 +3,7 @@ package de.polyfish0.fitnessjourney.routes
|
||||
object Routes {
|
||||
const val MAIN = "Main"
|
||||
const val BODY_COMPOSITION_MAIN = "BodyCompositionMain"
|
||||
const val ADDING_BODY_COMPOSITION = "AddingBodyComposition"
|
||||
const val TRAINING_PLANS_MAIN = "TrainingPlansMain"
|
||||
const val SETTINGS = "Settings"
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package de.polyfish0.fitnessjourney.routes.bodycomposition
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Scaffold
|
||||
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.lifecycle.ViewModel
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import de.polyfish0.fitnessjourney.R
|
||||
import de.polyfish0.fitnessjourney.ui.theme.FitnessJourneyTheme
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
@Composable
|
||||
fun AddBodyCompositionRoute(navigation: NavController) {
|
||||
Scaffold (bottomBar = {
|
||||
Button(modifier = Modifier.fillMaxWidth(), onClick = { Log.d("LOL", "Rent") }) {
|
||||
Text(text = stringResource(id = R.string.save))
|
||||
}
|
||||
}) { padding ->
|
||||
Surface(modifier = Modifier.padding(padding)) {
|
||||
LazyColumn {
|
||||
items(5) { index ->
|
||||
Text(text = "$index")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AddBodyCompositionViewModel : ViewModel() {
|
||||
private val _isLoading = MutableStateFlow(true)
|
||||
val isLoading: StateFlow<Boolean> get() = _isLoading
|
||||
|
||||
fun checkUserInput() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
fun AddBodyCompositionRoutePreview() {
|
||||
FitnessJourneyTheme {
|
||||
AddBodyCompositionRoute(navigation = rememberNavController())
|
||||
}
|
||||
}
|
@ -1,37 +1,109 @@
|
||||
package de.polyfish0.fitnessjourney.routes.bodycomposition
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import de.polyfish0.fitnessjourney.R
|
||||
import de.polyfish0.fitnessjourney.database.InstanceManager
|
||||
import de.polyfish0.fitnessjourney.database.entities.Pictures
|
||||
import de.polyfish0.fitnessjourney.routes.Routes
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun BodyCompositionMainRoute(navigation: NavController) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Card(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(modifier = Modifier.padding(4.dp)) {
|
||||
Text(text = "Hier gibts noch nichts aber hier sollen krasse Fitness Daten und Statistiken angezeigt werden c:")
|
||||
Button(onClick = {
|
||||
navigation.navigate(Routes.MAIN)
|
||||
}) {
|
||||
Text(text = stringResource(R.string.app_name))
|
||||
val viewModel = viewModel { BodyCompositionDataViewModel() }
|
||||
val dataState by viewModel.dataFlow.collectAsState()
|
||||
val isLoading by viewModel.isLoading.collectAsState()
|
||||
|
||||
if (isLoading) {
|
||||
val context = LocalContext.current
|
||||
LaunchedEffect(1) {
|
||||
viewModel.loadData(context = context)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
Scaffold(modifier = Modifier.fillMaxSize(), floatingActionButton = {
|
||||
FloatingActionButton(onClick = { navigation.navigate(Routes.ADDING_BODY_COMPOSITION) }) {
|
||||
Icon(
|
||||
Icons.Rounded.Add,
|
||||
contentDescription = stringResource(id = R.string.add_body_comp)
|
||||
)
|
||||
}
|
||||
}) { padding ->
|
||||
LazyColumn(modifier = Modifier.padding(padding)) {
|
||||
itemsIndexed(dataState) { _, item ->
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(4.dp)
|
||||
.clickable { Log.d("Yeet", item.path) }) {
|
||||
Column(modifier = Modifier.padding(4.dp)) {
|
||||
Text(text = item.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BodyCompositionDataViewModel : ViewModel() {
|
||||
private val _isLoading = MutableStateFlow(true)
|
||||
val isLoading: StateFlow<Boolean> get() = _isLoading
|
||||
|
||||
private val _dataFlow = MutableStateFlow<ArrayList<Pictures>>(arrayListOf())
|
||||
val dataFlow: StateFlow<ArrayList<Pictures>> = _dataFlow
|
||||
|
||||
fun loadData(context: Context) {
|
||||
viewModelScope.launch {
|
||||
_dataFlow.value.clear()
|
||||
_dataFlow.value.addAll(InstanceManager.getFitnessDB(context).picturesDao().getAll())
|
||||
_isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
fun insertData(context: Context, vararg data: Pictures) {
|
||||
viewModelScope.launch {
|
||||
InstanceManager.getFitnessDB(context).picturesDao().insertAll(*data)
|
||||
}
|
||||
}
|
||||
}
|
@ -4,4 +4,6 @@
|
||||
<string name="training_plans">Trainingspläne</string>
|
||||
<string name="body_composition">Körperdaten</string>
|
||||
<string name="settings">Einstellungen</string>
|
||||
<string name="add_body_comp">Einfügen einer neuen Messung</string>
|
||||
<string name="save">Speichern</string>
|
||||
</resources>
|
@ -3,4 +3,6 @@
|
||||
<string name="training_plans">Training plans</string>
|
||||
<string name="body_composition">Body composition</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="add_body_comp">Add a new measurement</string>
|
||||
<string name="save">Save</string>
|
||||
</resources>
|
Loading…
Reference in New Issue
Block a user