From 3c8c5a38c036cf207da61bd575e2fec4c1f15dbd Mon Sep 17 00:00:00 2001 From: Mika Date: Sat, 5 Aug 2023 19:17:16 +0200 Subject: [PATCH] f --- app/build.gradle | 8 +- .../polyfish0/fitnessjourney/MainActivity.kt | 60 ++++++++---- .../database/FitnessDatabase.kt | 13 +++ .../database/InstanceManager.kt | 19 ++++ .../fitnessjourney/database/PictureEntity.kt | 10 -- .../database/dao/BodyDataDao.kt | 23 +++++ .../database/dao/PicturesDAO.kt | 22 +++++ .../database/entities/BodyData.kt | 34 +++++++ .../database/entities/Pictures.kt | 18 ++++ .../database/entities/UserData.kt | 13 +++ .../polyfish0/fitnessjourney/routes/Routes.kt | 1 + .../AddBodyCompositionRoute.kt | 60 ++++++++++++ .../BodyCompositionMainRoute.kt | 98 ++++++++++++++++--- app/src/main/res/values-de/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + build.gradle | 4 +- 16 files changed, 340 insertions(+), 47 deletions(-) create mode 100644 app/src/main/java/de/polyfish0/fitnessjourney/database/FitnessDatabase.kt create mode 100644 app/src/main/java/de/polyfish0/fitnessjourney/database/InstanceManager.kt delete mode 100644 app/src/main/java/de/polyfish0/fitnessjourney/database/PictureEntity.kt create mode 100644 app/src/main/java/de/polyfish0/fitnessjourney/database/dao/BodyDataDao.kt create mode 100644 app/src/main/java/de/polyfish0/fitnessjourney/database/dao/PicturesDAO.kt create mode 100644 app/src/main/java/de/polyfish0/fitnessjourney/database/entities/BodyData.kt create mode 100644 app/src/main/java/de/polyfish0/fitnessjourney/database/entities/Pictures.kt create mode 100644 app/src/main/java/de/polyfish0/fitnessjourney/database/entities/UserData.kt create mode 100644 app/src/main/java/de/polyfish0/fitnessjourney/routes/bodycomposition/AddBodyCompositionRoute.kt diff --git a/app/build.gradle b/app/build.gradle index 4b75865..6562e1d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,12 +6,12 @@ plugins { android { namespace 'de.polyfish0.fitnessjourney' - compileSdk 34 + compileSdk 33 defaultConfig { applicationId "de.polyfish0.fitnessjourney" minSdk 26 - targetSdk 34 + targetSdk 33 versionCode 1 versionName "1.0" @@ -56,6 +56,8 @@ dependencies { // 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" @@ -84,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' diff --git a/app/src/main/java/de/polyfish0/fitnessjourney/MainActivity.kt b/app/src/main/java/de/polyfish0/fitnessjourney/MainActivity.kt index 20587ef..53a87ae 100644 --- a/app/src/main/java/de/polyfish0/fitnessjourney/MainActivity.kt +++ b/app/src/main/java/de/polyfish0/fitnessjourney/MainActivity.kt @@ -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() + } } \ No newline at end of file diff --git a/app/src/main/java/de/polyfish0/fitnessjourney/database/FitnessDatabase.kt b/app/src/main/java/de/polyfish0/fitnessjourney/database/FitnessDatabase.kt new file mode 100644 index 0000000..8870ed0 --- /dev/null +++ b/app/src/main/java/de/polyfish0/fitnessjourney/database/FitnessDatabase.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/de/polyfish0/fitnessjourney/database/InstanceManager.kt b/app/src/main/java/de/polyfish0/fitnessjourney/database/InstanceManager.kt new file mode 100644 index 0000000..40223d4 --- /dev/null +++ b/app/src/main/java/de/polyfish0/fitnessjourney/database/InstanceManager.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/de/polyfish0/fitnessjourney/database/PictureEntity.kt b/app/src/main/java/de/polyfish0/fitnessjourney/database/PictureEntity.kt deleted file mode 100644 index 8c08863..0000000 --- a/app/src/main/java/de/polyfish0/fitnessjourney/database/PictureEntity.kt +++ /dev/null @@ -1,10 +0,0 @@ -package de.polyfish0.fitnessjourney.database - -import androidx.room.ColumnInfo -import androidx.room.PrimaryKey - -data class PictureEntity( - @PrimaryKey val pid: Int, - @ColumnInfo(name = "path") val path: String, - @ColumnInfo(name = "date") val date: Date -) \ No newline at end of file diff --git a/app/src/main/java/de/polyfish0/fitnessjourney/database/dao/BodyDataDao.kt b/app/src/main/java/de/polyfish0/fitnessjourney/database/dao/BodyDataDao.kt new file mode 100644 index 0000000..cdac042 --- /dev/null +++ b/app/src/main/java/de/polyfish0/fitnessjourney/database/dao/BodyDataDao.kt @@ -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 + + @Query("SELECT * FROM BodyData WHERE pid IN (:pictureIds)") + suspend fun loadAllByIds(pictureIds: IntArray): List + + @Insert + suspend fun insertAll(vararg pictures: BodyData) + + @Delete + suspend fun delete(picture: BodyData) +} \ No newline at end of file diff --git a/app/src/main/java/de/polyfish0/fitnessjourney/database/dao/PicturesDAO.kt b/app/src/main/java/de/polyfish0/fitnessjourney/database/dao/PicturesDAO.kt new file mode 100644 index 0000000..515960e --- /dev/null +++ b/app/src/main/java/de/polyfish0/fitnessjourney/database/dao/PicturesDAO.kt @@ -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 + + @Query("SELECT * FROM Pictures WHERE pid IN (:pictureIds)") + suspend fun loadAllByIds(pictureIds: IntArray): List + + @Insert + suspend fun insertAll(vararg pictures: Pictures) + + @Delete + suspend fun delete(picture: Pictures) +} \ No newline at end of file diff --git a/app/src/main/java/de/polyfish0/fitnessjourney/database/entities/BodyData.kt b/app/src/main/java/de/polyfish0/fitnessjourney/database/entities/BodyData.kt new file mode 100644 index 0000000..4a4cea1 --- /dev/null +++ b/app/src/main/java/de/polyfish0/fitnessjourney/database/entities/BodyData.kt @@ -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() +) \ No newline at end of file diff --git a/app/src/main/java/de/polyfish0/fitnessjourney/database/entities/Pictures.kt b/app/src/main/java/de/polyfish0/fitnessjourney/database/entities/Pictures.kt new file mode 100644 index 0000000..8e2f52a --- /dev/null +++ b/app/src/main/java/de/polyfish0/fitnessjourney/database/entities/Pictures.kt @@ -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() +) \ No newline at end of file diff --git a/app/src/main/java/de/polyfish0/fitnessjourney/database/entities/UserData.kt b/app/src/main/java/de/polyfish0/fitnessjourney/database/entities/UserData.kt new file mode 100644 index 0000000..753603d --- /dev/null +++ b/app/src/main/java/de/polyfish0/fitnessjourney/database/entities/UserData.kt @@ -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 +) \ No newline at end of file diff --git a/app/src/main/java/de/polyfish0/fitnessjourney/routes/Routes.kt b/app/src/main/java/de/polyfish0/fitnessjourney/routes/Routes.kt index 82ee123..e2d633b 100644 --- a/app/src/main/java/de/polyfish0/fitnessjourney/routes/Routes.kt +++ b/app/src/main/java/de/polyfish0/fitnessjourney/routes/Routes.kt @@ -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" } \ No newline at end of file diff --git a/app/src/main/java/de/polyfish0/fitnessjourney/routes/bodycomposition/AddBodyCompositionRoute.kt b/app/src/main/java/de/polyfish0/fitnessjourney/routes/bodycomposition/AddBodyCompositionRoute.kt new file mode 100644 index 0000000..5e1235f --- /dev/null +++ b/app/src/main/java/de/polyfish0/fitnessjourney/routes/bodycomposition/AddBodyCompositionRoute.kt @@ -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 get() = _isLoading + + fun checkUserInput() { + + } +} + +@Composable +@Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +fun AddBodyCompositionRoutePreview() { + FitnessJourneyTheme { + AddBodyCompositionRoute(navigation = rememberNavController()) + } +} \ No newline at end of file diff --git a/app/src/main/java/de/polyfish0/fitnessjourney/routes/bodycomposition/BodyCompositionMainRoute.kt b/app/src/main/java/de/polyfish0/fitnessjourney/routes/bodycomposition/BodyCompositionMainRoute.kt index 829683f..41abfcc 100644 --- a/app/src/main/java/de/polyfish0/fitnessjourney/routes/bodycomposition/BodyCompositionMainRoute.kt +++ b/app/src/main/java/de/polyfish0/fitnessjourney/routes/bodycomposition/BodyCompositionMainRoute.kt @@ -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 get() = _isLoading + + private val _dataFlow = MutableStateFlow>(arrayListOf()) + val dataFlow: StateFlow> = _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) + } + } } \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 29c56b3..39ebdd8 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -4,4 +4,6 @@ Trainingspläne Körperdaten Einstellungen + Einfügen einer neuen Messung + Speichern \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 51f787a..70c552d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,4 +3,6 @@ Training plans Body composition Settings + Add a new measurement + Save \ No newline at end of file diff --git a/build.gradle b/build.gradle index d4fde78..4314313 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '8.1.0-rc01' apply false - id 'com.android.library' version '8.1.0-rc01' apply false + id 'com.android.application' version '8.0.2' apply false + id 'com.android.library' version '8.0.2' apply false id 'org.jetbrains.kotlin.android' version '1.7.20' apply false } \ No newline at end of file